Archived
0

72 Commits

Author SHA1 Message Date
7046cd5f92 gradle upgrade 2021-07-18 14:48:10 +03:00
df6e6d7be2 PlayerBlockPlacementPacket 2021-07-18 14:37:39 +03:00
6c51d8ee30 HandAnimationPacket 2021-07-18 14:16:22 +03:00
31d795e0d9 PlayerDiggingAndMorePacket 2021-07-18 13:57:52 +03:00
376a5264e3 PlayerOnGroundPacket 2021-07-18 12:36:19 +03:00
a1d3cf5825 CPlayerAbilitiesPacket 2021-07-18 12:32:26 +03:00
bbdd2bc280 Merge branch 'dev/world' into develop 2021-07-18 12:23:01 +03:00
90f223160c изменен конфиг карты 2021-07-18 12:22:24 +03:00
ee82930426 генератор плоской карты 2021-07-18 12:15:10 +03:00
f280e9beaa стабилизируем код 2021-07-18 00:01:44 +03:00
b0bb86f215 Merge branch 'dev/pool' into dev/world
# Conflicts:
#	protocol/src/main/java/mc/protocol/packets/play/client/CPlayerPositionAndLookPacket.java
#	protocol/src/main/java/mc/protocol/packets/play/client/PlayerLookPacket.java
#	protocol/src/main/java/mc/protocol/packets/play/client/PlayerPositionPacket.java
#	protocol/src/main/java/mc/protocol/packets/play/server/ChunkDataPacket.java
#	protocol/src/test/java/mc/protocol/buffer/NetByteBufReadTest.java
#	protocol/src/test/java/mc/protocol/buffer/NetByteBufWriteTest.java
#	server/src/main/resources/logback-default.xml
2021-07-17 19:10:08 +03:00
910ed30360 Merge branch 'dev/utils' into dev/world 2021-07-17 17:37:07 +03:00
ee394bf183 add BlockLocation, ChunkSectionLocation 2021-07-12 19:10:57 +03:00
9c9523d629 used Vector 2021-07-12 19:10:40 +03:00
51f01f84cf add Vector 2021-07-12 19:10:21 +03:00
c1854c8f73 сериализация полного чанка 2021-07-10 16:24:16 +03:00
f149019b88 fix tests 2021-07-09 14:22:39 +03:00
ee8c9e4a3e рефакторинг object pool для NetByteBuf 2021-07-09 14:19:05 +03:00
84b4069f7b рефакторинг object pool 2021-07-09 13:40:31 +03:00
41acf32cb8 перенос pool фабрик в модуль utils 2021-07-09 12:26:15 +03:00
9ed6db2484 NibbleArray -> BitArray 2021-06-27 05:44:14 +03:00
fd074ae690 Merge branch 'dev/utils' into dev/world 2021-06-27 05:37:16 +03:00
a348689b37 add BitArray 2021-06-27 05:33:38 +03:00
c189472244 new module: utils 2021-06-27 02:50:02 +03:00
0a5eeb9e86 Processor* -> Scenario* 2021-06-24 20:46:08 +03:00
f9ca87cfc4 поправка к работе NibbleArray 2021-06-24 20:42:18 +03:00
b9af693a34 поправлена работа с pool object в сериализации чанков 2021-06-24 13:27:14 +03:00
eda9219ea0 disable logger 2021-06-24 13:24:55 +03:00
3424830d95 update tests 2021-06-24 13:18:23 +03:00
72b7b22e32 отказываемся от Palette в пользу Direct mode 2021-06-24 13:17:52 +03:00
06be69f3e4 первый удачный алгоритм загрузки мира 2021-06-21 00:22:55 +03:00
bb3f0bbdcb refactoring object pool 2021-06-19 16:46:45 +03:00
b1307442e1 gradle: add args for compile 2021-06-19 16:45:48 +03:00
4b587c55e9 реорганизация pool objects 2021-06-18 00:49:28 +03:00
bbf6fde3a1 Merge branch 'refactory' into develop 2021-06-17 15:10:29 +03:00
696d18cf41 refactoring: swap modules 2021-06-17 15:09:29 +03:00
e7f7b9654e refactoring 2021-06-17 15:06:10 +03:00
e7b5120661 refactoring 2021-06-15 23:56:53 +03:00
475f1a28ca refactoring 2021-06-13 17:06:46 +03:00
222f2dba61 refactoring: реорганизация загрузки конфигураций 2021-06-13 14:37:45 +03:00
e76f7ff375 BUG: field collision 2021-06-13 14:33:18 +03:00
95474a32c4 refactoring: Cli-Parser 2021-06-13 14:32:00 +03:00
59b374e623 refactoring: Config
YAML -> HOCON
2021-05-11 16:07:46 +03:00
2b9f021419 refactoring: move package 2021-05-10 20:10:24 +03:00
3e9649a8e0 refactoring: PacketInboundHandler 2021-05-10 18:55:36 +03:00
c63f5ce3eb refactoring: ProtocolConstant 2021-05-10 16:08:39 +03:00
dbb476bf11 refactoring: ProtocolDecoder 2021-05-10 16:08:39 +03:00
a5f68e76e5 refactoring: Pooled objects 2021-05-10 16:04:40 +03:00
38918f5eaf refactoring: ProtocolEncoder 2021-05-10 16:04:40 +03:00
f9a71250b1 refactoring: Packets 2021-05-10 16:04:39 +03:00
70d8efe421 refactoring: State 2021-05-10 02:27:12 +03:00
a3fcfcf65a refactoring: Packet interfaces 2021-05-10 02:11:04 +03:00
3165eca0ca refactoring: add mc.utils.pool 2021-05-10 02:06:49 +03:00
50fc39e924 refactoring: ProtocolSplitter 2021-05-09 23:28:36 +03:00
7f7fefdc98 refactoring: NetByteBuf 2021-05-09 22:53:07 +03:00
72b06bca7b начало рефакторинга 2021-05-09 22:49:04 +03:00
74fc258834 Merge branch 'master' into develop 2021-05-09 22:17:44 +03:00
325546a76d update README 2021-05-09 22:12:15 +03:00
4215b5615e update version 2021-05-09 22:07:25 +03:00
10af38e102 Merge branch 'develop' 2021-05-09 22:05:23 +03:00
b049352fe3 PlayerManager 2021-05-09 20:07:08 +03:00
c4767bd240 config: fake online 2021-05-09 18:54:23 +03:00
2d4895fef0 Merge branch 'dev/world' into develop 2021-05-09 18:48:54 +03:00
04316d9cbd грузим чанки при входе 2021-05-09 18:47:51 +03:00
20791ed881 VoidWorld, VoidChunk 2021-05-09 18:43:57 +03:00
2b0ad9895b add World, Chunk interfaces 2021-05-09 18:42:33 +03:00
ab17160f9d config: add view-distance 2021-05-09 18:41:31 +03:00
8a6f37924e PingPacket -> KeepAlivePacket 2021-05-09 17:00:25 +03:00
f10fb46d23 уменьшена скорость отдачи KeepAlive 2021-05-09 16:59:20 +03:00
c6669af651 EntityActionPacket 2021-05-08 20:14:14 +03:00
3984ab3fca порядок пакетов в State 2021-05-08 19:50:09 +03:00
bc2d5a7e75 правки режима и координат спавна 2021-05-08 18:13:24 +03:00
165 changed files with 3510 additions and 1755 deletions

View File

@@ -1,12 +1,13 @@
# MC-SERVER # MC-SERVER
![version: 1.0-SNAPSHOT](https://img.shields.io/badge/version-1.0-05b.svg?style=flat) ![version: 1.1](https://img.shields.io/badge/version-1.1-05b.svg?style=flat)
![codename: ZERO](https://img.shields.io/badge/codename-ZERO-509.svg?style=flat) ![codename: VOID](https://img.shields.io/badge/codename-VOID-509.svg?style=flat)
![protocol: 1.12.2](https://img.shields.io/badge/protocol-1.12.2-075.svg?style=flat) ![protocol: 1.12.2](https://img.shields.io/badge/protocol-1.12.2-075.svg?style=flat)
Написанный с нуля сервер **Minecraft 1.12.2**. Написанный с нуля сервер **Minecraft 1.12.2**.
На данный момент может только показывать информацию о себе. Подключение к серверу не возможно. На данный момент сервер может показывать о себе информацию в списке серверов (motd, онлайн, иконка) и позволять
игрокам подключиться к себе. Загружается пустой мир.
--- ---

2
cli-parser/build.gradle Normal file
View File

@@ -0,0 +1,2 @@
//file:noinspection GrUnresolvedAccess
apply from: rootDir.toPath().resolve('logic.gradle').toFile()

View File

@@ -0,0 +1,2 @@
# suppress inspection "UnusedProperty" for whole file
module.name=cli-parser

View File

@@ -0,0 +1,17 @@
package mc.cliparser;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import java.util.Set;
@RequiredArgsConstructor
@ToString
public class CommandLine {
private final Set<Option> options;
public boolean has(Option option) {
return options.contains(option);
}
}

View File

@@ -0,0 +1,52 @@
package mc.cliparser;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
public class CommandLineParser {
private final Set<Option> options = new HashSet<>();
public void addOption(Option option) {
options.add(option);
}
public CommandLine parse(String[] args) {
Set<Option> foundOptions = new HashSet<>();
AtomicReference<Option> refCurrentOption = new AtomicReference<>(null);
for (String arg : args) {
if (refCurrentOption.get() != null) {
refCurrentOption.get().value(arg);
foundOptions.add(refCurrentOption.get());
refCurrentOption.set(null);
} else {
parseOptArgs(arg, foundOptions, refCurrentOption);
}
}
return new CommandLine(foundOptions);
}
@SuppressWarnings("java:S125")
private void parseOptArgs(String arg, Set<Option> foundOptions, AtomicReference<Option> refCurrentOption) {
String optName;
if (arg.startsWith("--")) {
optName = arg.substring(2);
} else /*if (args[i].startsWith("-"))*/ {
optName = arg.substring(1);
}
for (Option option : options) {
if (optName.equals(option.shortName()) || optName.equals(option.longName())) {
if (option.hasArgs()) {
refCurrentOption.set(option);
} else {
foundOptions.add(option);
}
}
}
}
}

View File

@@ -0,0 +1,25 @@
package mc.cliparser;
import lombok.*;
import lombok.experimental.Accessors;
@Accessors(fluent = true)
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@EqualsAndHashCode
@ToString
public class Option {
@Getter
private final String shortName;
@Getter
private final String longName;
@Getter
private final boolean hasArgs;
@Getter
@Setter
private String value;
}

View File

@@ -0,0 +1,73 @@
package mc.cliparser;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CommandLineParserTest {
@Test
void optionTest() {
Option option = Option.builder().shortName("v").build();
assertNull(option.longName());
assertFalse(option.hasArgs());
option = Option.builder().longName("version").build();
assertNull(option.shortName());
assertFalse(option.hasArgs());
option = Option.builder().shortName("v").longName("value1").hasArgs(true).build();
assertNotNull(option.shortName());
assertNotNull(option.longName());
assertTrue(option.hasArgs());
}
@Test
void shortOptionFlag() {
Option option = Option.builder().shortName("v").build();
var parser = new CommandLineParser();
parser.addOption(option);
CommandLine commandLine = parser.parse(new String[]{ "-v" });
assertTrue(commandLine.has(option));
}
@Test
void longOptionFlag() {
Option option = Option.builder().longName("version").build();
var parser = new CommandLineParser();
parser.addOption(option);
CommandLine commandLine = parser.parse(new String[]{ "--version" });
assertTrue(commandLine.has(option));
}
@Test
void argsOption() {
Option option1 = Option.builder().shortName("v").longName("value1").hasArgs(true).build();
Option option2 = Option.builder().shortName("a").longName("value2").hasArgs(true).build();
var parser = new CommandLineParser();
parser.addOption(option1);
parser.addOption(option2);
CommandLine commandLine = parser.parse(new String[]{ "--value1", "arg1", "-a", "arg2" });
assertTrue(commandLine.has(option1));
assertEquals("arg1", option1.value());
assertTrue(commandLine.has(option2));
assertEquals("arg2", option2.value());
}
@Test
void noPassOption() {
Option option = Option.builder().longName("version").build();
var parser = new CommandLineParser();
parser.addOption(option);
CommandLine commandLine = parser.parse(new String[]{ "--any-opt" });
assertFalse(commandLine.has(option));
}
}

View File

@@ -1,3 +1,3 @@
project.group=mc-project prj.group=mc-project
project.name=mc-server prj.name=mc-server
project.version=1.1-SNAPSHOT prj.version=1.1

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -13,16 +13,20 @@ ext {
lombok : 'org.projectlombok:lombok:1.18.12', lombok : 'org.projectlombok:lombok:1.18.12',
annotations: 'com.google.code.findbugs:jsr305:3.0.2', annotations: 'com.google.code.findbugs:jsr305:3.0.2',
lang3 : 'org.apache.commons:commons-lang3:3.11', lang3 : 'org.apache.commons:commons-lang3:3.11',
netty : ["io.netty:netty-transport:${netty_version}",
"io.netty:netty-handler:${netty_version}"],
reactor : 'io.projectreactor:reactor-core:3.4.5', reactor : 'io.projectreactor:reactor-core:3.4.5',
yaml : 'org.yaml:snakeyaml:1.28', hocon : 'com.typesafe:config:1.4.1',
json : 'com.eclipsesource.minimal-json:minimal-json:0.9.5', json : 'com.eclipsesource.minimal-json:minimal-json:0.9.5',
ioutils : 'commons-io:commons-io:2.6', ioutils : 'commons-io:commons-io:2.6',
jopt : 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3', jopt : 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3',
objpool : 'org.apache.commons:commons-pool2:2.9.0' objpool : 'org.apache.commons:commons-pool2:2.9.0'
] ]
libs.netty = [
transport: "io.netty:netty-transport:${netty_version}",
codec : "io.netty:netty-codec:${netty_version}",
handler : "io.netty:netty-handler:${netty_version}"
]
libs.logger = [ libs.logger = [
slf4j : ["org.slf4j:slf4j-api:${slf4j_version}", slf4j : ["org.slf4j:slf4j-api:${slf4j_version}",
"org.slf4j:jcl-over-slf4j:${slf4j_version}"], "org.slf4j:jcl-over-slf4j:${slf4j_version}"],

View File

@@ -1,21 +1,28 @@
//file:noinspection GrUnresolvedAccess //file:noinspection GrUnresolvedAccess
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'java-library' apply plugin: 'java-library'
apply plugin: 'jacoco'
apply from: rootDir.toPath().resolve('libs.gradle').toFile() apply from: rootDir.toPath().resolve('libs.gradle').toFile()
String getProperty1(String propertyName1, String propertyName2) { String getProperty1(String propertyName1, String propertyName2) {
return (String) (project.hasProperty(propertyName1) ? project.property(propertyName1) : project.property(propertyName2)) return (String) (project.hasProperty(propertyName1) ? project.property(propertyName1) : project.property(propertyName2))
} }
project.group = getProperty1('module.group', 'project.group') project.group = getProperty1('module.group', 'prj.group')
project.version = getProperty1('module.version', 'project.version') project.version = getProperty1('module.version', 'prj.version')
jar.archiveBaseName.set(getProperty1('module.name', 'project.name')) jar.archiveBaseName.set(getProperty1('module.name', 'prj.name'))
compileJava { compileJava {
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_11 sourceCompatibility = targetCompatibility = JavaVersion.VERSION_11
options.encoding = 'UTF-8' options.encoding = 'UTF-8'
} }
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
}
}
repositories { repositories {
mavenLocal() mavenLocal()
mavenCentral() mavenCentral()
@@ -31,13 +38,18 @@ dependencies {
implementation libs.dagger2.implementation implementation libs.dagger2.implementation
annotationProcessor libs.dagger2.annotationProcessor annotationProcessor libs.dagger2.annotationProcessor
testAnnotationProcessor libs.lombok
testCompileOnly libs.lombok
testImplementation libs.test.junit5.api testImplementation libs.test.junit5.api
testImplementation libs.test.junit5.params testImplementation libs.test.junit5.params
testRuntimeOnly libs.test.junit5.engine testRuntimeOnly libs.test.junit5.engine
testRuntimeOnly libs.test.logger
} }
test { test {
useJUnitPlatform() useJUnitPlatform()
} }
jacoco {
toolVersion = '0.8.7'
}

View File

@@ -1,9 +1,11 @@
apply from: rootDir.toPath().resolve('logic.gradle').toFile() apply from: rootDir.toPath().resolve('logic.gradle').toFile()
dependencies { dependencies {
implementation libs.netty api project(':utils')
implementation libs.netty.transport
implementation libs.netty.codec
implementation libs.json implementation libs.json
implementation libs.objpool
testImplementation libs.lang3 testImplementation libs.lang3
} }

View File

@@ -1,52 +0,0 @@
package mc.protocol;
import io.netty.channel.ChannelHandlerContext;
import lombok.EqualsAndHashCode;
import lombok.Setter;
import lombok.experimental.Accessors;
import mc.protocol.api.ConnectionContext;
import mc.protocol.packets.ServerSidePacket;
import mc.protocol.pool.Passivable;
@EqualsAndHashCode
public class NettyConnectionContext implements ConnectionContext, Passivable {
@Accessors(chain = true)
@Setter
private ChannelHandlerContext ctx;
@Override
public State getState() {
return ctx.channel().attr(NetworkAttributes.STATE).get();
}
@Override
public void setState(State state) {
ctx.channel().attr(NetworkAttributes.STATE).set(state);
}
@Override
public void send(ServerSidePacket packet) {
ctx.write(packet);
}
@Override
public void sendNow(ServerSidePacket packet) {
ctx.writeAndFlush(packet);
}
@Override
public void flushSending() {
ctx.flush();
}
@Override
public void disconnect() {
ctx.disconnect();
}
@Override
public void passivate() {
this.ctx = null;
}
}

View File

@@ -1,103 +0,0 @@
package mc.protocol;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import mc.protocol.api.ConnectionContext;
import mc.protocol.api.Server;
import mc.protocol.event.EventBus;
import mc.protocol.io.codec.ProtocolDecoder;
import mc.protocol.io.codec.ProtocolEncoder;
import mc.protocol.io.codec.ProtocolSplitter;
import mc.protocol.packets.ClientSidePacket;
import javax.annotation.Nonnull;
import javax.inject.Provider;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Consumer;
@Slf4j
@RequiredArgsConstructor
public class NettyServer implements Server {
private final Provider<ProtocolDecoder> protocolDecoderProvider;
private final Provider<PacketInboundHandler> packetInboundHandlerProvider;
private final EventBus eventBus;
private Consumer<ConnectionContext> consumerNewConnection;
private Consumer<ConnectionContext> consumerDisconnect;
@Override
public void bind(String host, int port) {
log.info("Network starting: {}:{}", host, port);
try {
createServerBootstrap().bind(host, port).sync().channel().closeFuture().sync();
} catch (InterruptedException e) {
if (log.isTraceEnabled()) {
log.trace("{}: {}", e.getClass().getSimpleName(), e.getMessage(), e);
}
}
}
@Override
public void onNewConnect(Consumer<ConnectionContext> consumer) {
this.consumerNewConnection = consumer;
}
@Override
public void onDisonnect(Consumer<ConnectionContext> consumer) {
this.consumerDisconnect = consumer;
}
@Override
@SuppressWarnings("java:S2326") // Сонар, ты бредишь
public <P extends ClientSidePacket> void listenPacket(State state, Class<P> packetClass, EventBus.EventHandler<P> eventHandler) {
this.eventBus.subscribe(state, packetClass, eventHandler);
}
private ServerBootstrap createServerBootstrap() {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(new NioEventLoopGroup(1), new NioEventLoopGroup())
.channel(NioServerSocketChannel.class)
.childHandler(createChannelChannelInitializer());
return bootstrap;
}
private ChannelInitializer<SocketChannel> createChannelChannelInitializer() {
return new ChannelInitializer<>() {
@Override
protected void initChannel(@Nonnull SocketChannel socketChannel) {
ChannelPipeline pipeline = socketChannel.pipeline();
createChannelHandlerMap().forEach(pipeline::addLast);
}
};
}
private Map<String, ChannelHandler> createChannelHandlerMap() {
Map<String, ChannelHandler> map = new LinkedHashMap<>();
map.put("packet_splitter", new ProtocolSplitter());
map.put("logger", new LoggingHandler(LogLevel.DEBUG));
ProtocolDecoder protocolDecoder = protocolDecoderProvider.get();
protocolDecoder.setConsumerNewConnection(consumerNewConnection);
protocolDecoder.setConsumerDisconnect(consumerDisconnect);
map.put("packet_decoder", protocolDecoder);
map.put("packet_encoder", new ProtocolEncoder());
map.put("packet_handler", packetInboundHandlerProvider.get());
return map;
}
}

View File

@@ -1,28 +0,0 @@
package mc.protocol;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.RequiredArgsConstructor;
import mc.protocol.packets.ClientSidePacket;
import mc.protocol.event.EventBus;
import mc.protocol.pool.PacketPool;
import org.apache.commons.pool2.ObjectPool;
@RequiredArgsConstructor
public class PacketInboundHandler extends SimpleChannelInboundHandler<ClientSidePacket> {
private final ObjectPool<NettyConnectionContext> poolNettyConnectionContext;
private final PacketPool poolPackets;
private final EventBus eventBus;
@Override
protected void channelRead0(ChannelHandlerContext ctx, ClientSidePacket packet) throws Exception {
State state = ctx.channel().attr(NetworkAttributes.STATE).get();
NettyConnectionContext context = poolNettyConnectionContext.borrowObject().setCtx(ctx);
eventBus.emit(state, context, packet);
poolNettyConnectionContext.returnObject(context);
poolPackets.returnObject(packet);
}
}

View File

@@ -4,7 +4,7 @@ import io.netty.util.AttributeKey;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
@UtilityClass @UtilityClass
public class NetworkAttributes { public class ProtocolAttributes {
public static final AttributeKey<State> STATE = AttributeKey.newInstance("STATE"); public static final AttributeKey<State> STATE = AttributeKey.newInstance("STATE");
} }

View File

@@ -5,6 +5,6 @@ import lombok.experimental.UtilityClass;
@UtilityClass @UtilityClass
public class ProtocolConstant { public class ProtocolConstant {
public static final String PROTOCOL_NAME = "1.12.2"; public final String PROTOCOL_NAME = "1.12.2";
public static final int PROTOCOL_NUMBER = 340; public final int PROTOCOL_NUMBER = 340;
} }

View File

@@ -3,11 +3,16 @@ package mc.protocol;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
import mc.protocol.packets.Packet; import mc.protocol.packets.KeepAlivePacket;
import mc.protocol.packets.PingPacket;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;
import mc.protocol.packets.client.*; import mc.protocol.packets.handshaking.client.HandshakePacket;
import mc.protocol.packets.server.*; import mc.protocol.packets.login.client.LoginStartPacket;
import mc.protocol.packets.login.server.DisconnectPacket;
import mc.protocol.packets.login.server.LoginSuccessPacket;
import mc.protocol.packets.play.client.*;
import mc.protocol.packets.play.server.*;
import mc.protocol.packets.status.client.StatusServerRequestPacket;
import mc.protocol.packets.status.server.StatusServerResponse;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Collections; import java.util.Collections;
@@ -16,88 +21,87 @@ import java.util.Map;
@RequiredArgsConstructor @RequiredArgsConstructor
public enum State { public enum State {
HANDSHAKING(-1, HANDSHAKING(0,
// client side // client side
Map.of(0x00, HandshakePacket.class) Map.of(0x00, HandshakePacket.class),
// server side
Collections.emptyMap()
), ),
STATUS(1, STATUS(1,
// client side // client side
Map.of( Map.of(
0x00, StatusServerRequestPacket.class, 0x00, StatusServerRequestPacket.class,
0x01, PingPacket.class 0x01, KeepAlivePacket.class
), ),
// server side // server side
Map.of( Map.of(
StatusServerResponse.class, 0x00, StatusServerResponse.class, 0x00,
PingPacket.class, 0x01 KeepAlivePacket.class, 0x01
) )
), ),
LOGIN(2, LOGIN(2,
// server bound // client side
Map.of(0x00, LoginStartPacket.class), Map.of(0x00, LoginStartPacket.class),
// client bound // server side
Map.of( Map.of(
DisconnectPacket.class, 0x00, DisconnectPacket.class, 0x00,
LoginSuccessPacket.class, 0x02 LoginSuccessPacket.class, 0x02
) )
), ),
PLAY(3, PLAY(3,
// server bound // client side
Map.of( Map.ofEntries(
0x00, TeleportConfirmPacket.class, Map.entry(0x00, TeleportConfirmPacket.class),
0x04, ClientSettingsPacket.class, Map.entry(0x04, ClientSettingsPacket.class),
0x09, PluginMessagePacket.class, Map.entry(0x09, PluginMessagePacket.class),
0x0B, PingPacket.class, Map.entry(0x0B, KeepAlivePacket.class),
0x0D, PlayerPositionPacket.class, Map.entry(0x0C, PlayerOnGroundPacket.class),
0x0E, CPlayerPositionAndLookPacket.class, Map.entry(0x0D, PlayerPositionPacket.class),
0x0F, PlayerLookPacket.class Map.entry(0x0E, CPlayerPositionAndLookPacket.class),
Map.entry(0x0F, PlayerLookPacket.class),
Map.entry(0x13, CPlayerAbilitiesPacket.class),
Map.entry(0x14, PlayerDiggingAndMorePacket.class),
Map.entry(0x15, EntityActionPacket.class),
Map.entry(0x1D, HandAnimationPacket.class),
Map.entry(0x1F, PlayerBlockPlacementPacket.class)
), ),
// client bound // server side
Map.of( Map.of(
PingPacket.class, 0x1F, KeepAlivePacket.class, 0x1F,
JoinGamePacket.class, 0x23,
ScoreboardDisplayPacket.class, 0x3B,
ScoreboardObjectivePacket.class, 0x42,
TeamsPacket.class, 0x44,
ScoreboardUpdateScorePacket.class, 0x45,
SpawnPositionPacket.class, 0x46,
ChunkDataPacket.class, 0x20, ChunkDataPacket.class, 0x20,
PlayerAbilitiesPacket.class,0x2C, JoinGamePacket.class, 0x23,
SPlayerPositionAndLookPacket.class, 0x2F SPlayerAbilitiesPacket.class,0x2C,
SPlayerPositionAndLookPacket.class, 0x2F,
SpawnPositionPacket.class, 0x46
) )
); );
@Nullable
public static State getById(int id) { public static State getById(int id) {
for (State state : State.values()) { // а зачем усложнять?
if (state.id == id) { //@formatter:off
return state; if (id == 1) return STATUS;
} else if (id == 2) return LOGIN;
} else if (id == 3) return PLAY;
else return HANDSHAKING;
return null; //@formatter:on
} }
@Getter @Getter
private final int id; private final int id;
@Getter
private final Map<Integer, Class<? extends ClientSidePacket>> clientSidePackets; private final Map<Integer, Class<? extends ClientSidePacket>> clientSidePackets;
private final Map<Class<? extends ServerSidePacket>, Integer> serverSidePackets; private final Map<Class<? extends ServerSidePacket>, Integer> serverSidePackets;
State(int id, Map<Integer, Class<? extends ClientSidePacket>> clientSidePackets) {
this.id = id;
this.clientSidePackets = clientSidePackets;
this.serverSidePackets = Collections.emptyMap();
}
@Nullable @Nullable
public Class<? extends ClientSidePacket> getClientSidePacketById(int id) { public Class<? extends ClientSidePacket> getClientSidePacketById(int id) {
return clientSidePackets == null ? null : clientSidePackets.get(id); return clientSidePackets == null ? null : clientSidePackets.get(id);
} }
@Nullable @Nullable
public Integer getServerSidePacketId(Class<? extends Packet> clazz) { public Integer getServerSidePacketId(Class<? extends ServerSidePacket> clazz) {
return serverSidePackets == null ? null : serverSidePackets.get(clazz); return serverSidePackets == null ? null : serverSidePackets.get(clazz);
} }
} }

View File

@@ -1,16 +0,0 @@
package mc.protocol.api;
import mc.protocol.State;
import mc.protocol.packets.ServerSidePacket;
public interface ConnectionContext {
State getState();
void setState(State state);
void send(ServerSidePacket packet);
void sendNow(ServerSidePacket packet);
void flushSending();
void disconnect();
}

View File

@@ -1,18 +0,0 @@
package mc.protocol.api;
import mc.protocol.State;
import mc.protocol.packets.ClientSidePacket;
import mc.protocol.event.EventBus;
import java.util.function.Consumer;
public interface Server {
void bind(String host, int port);
void onNewConnect(Consumer<ConnectionContext> consumer);
void onDisonnect(Consumer<ConnectionContext> consumer);
@SuppressWarnings("java:S2326") // Сонар, ты бредишь
<P extends ClientSidePacket> void listenPacket(State state, Class<P> packetClass, EventBus.EventHandler<P> eventHandler);
}

View File

@@ -1,4 +1,4 @@
package mc.protocol.io; package mc.protocol.buffer;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@@ -6,6 +6,8 @@ import lombok.RequiredArgsConstructor;
import lombok.ToString; import lombok.ToString;
import lombok.experimental.Delegate; import lombok.experimental.Delegate;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import mc.protocol.model.text.Text;
import mc.protocol.model.text.TextSerializer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.UUID; import java.util.UUID;
@@ -32,29 +34,33 @@ import java.util.UUID;
* | | | | этого числа). | * | | | | этого числа). |
* | VarInt | >= 1 ; <= 5 | Число от -2147483648 и 2147483647 | 32-bit число с плавающей размерностью от 1 до 5 байт | * | VarInt | >= 1 ; <= 5 | Число от -2147483648 и 2147483647 | 32-bit число с плавающей размерностью от 1 до 5 байт |
* | VarLong | >= 1 ; <= 10 | Число от -9223372036854775808 и 9223372036854775807 | 64-bit число с плавающей размерностью от 1 до 10 байт | * | VarLong | >= 1 ; <= 10 | Число от -9223372036854775808 и 9223372036854775807 | 64-bit число с плавающей размерностью от 1 до 10 байт |
* | Position | 8 | 64-bit число разделённое на три части: x, y, z | Кодируется формулой: | * | Text | | JSON | По файту является String (n), который имеет формат JSON |
* | | | | ((x & 0x3FFFFFF) << 38) | ((y & 0xFFF) << 26) | (z & 0x3FFFFFF) |
* *
* [1] - <a href="https://en.wikipedia.org/wiki/Single-precision_floating-point_format">Single-precision floating-point format</a> * [1] - <a href="https://en.wikipedia.org/wiki/Single-precision_floating-point_format">Single-precision floating-point format</a>
* [2] - <a href="https://en.wikipedia.org/wiki/Double-precision_floating-point_format">Double-precision floating-point format</a> * [2] - <a href="https://en.wikipedia.org/wiki/Double-precision_floating-point_format">Double-precision floating-point format</a>
* [3] - <a href="http://unicode.org/glossary/#unicode_scalar_value">Unicode Scalar Value</a> * [3] - <a href="http://unicode.org/glossary/#unicode_scalar_value">Unicode Scalar Value</a>
* </pre> * </pre>
* *
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=7368#Data_types">Data types</a> * @see <a href="https://wiki.vg/index.php?title=Data_types&oldid=14345#Definitions">Data types</a>
* @see <a href="https://wiki.vg/index.php?title=Chat&oldid=14272">Chat</a>
*/ */
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@ToString @ToString
public class NetByteBuf extends ByteBuf { public abstract class NetByteBuf extends ByteBuf {
@Delegate @Delegate
private final ByteBuf byteBuf; protected ByteBuf byteBuf;
public void writeUnsignedByte(int value) { public void writeUnsignedByte(int value) {
byteBuf.writeByte((byte)(value & 0xFF)); byteBuf.writeByte((byte)(value & 0xFF));
} }
public void writeText(Text text) {
writeString(TextSerializer.toStringPlain(text));
}
//region String //region String
public String readString() { public String readString() {
return readString(Short.MAX_VALUE); return readString(Short.MAX_VALUE);
@@ -67,9 +73,9 @@ public class NetByteBuf extends ByteBuf {
if (length == 0) { if (length == 0) {
return ""; return "";
} else if (length > maxLength) { } else if (length > maxLength) {
throw new DecoderException("String length exceeds maximum length: " + length + " > " + maxLength); throw new NetIOException("String length exceeds maximum length: " + length + " > " + maxLength);
} else if (length < 0) { } else if (length < 0) {
throw new DecoderException("String length less zero!"); throw new NetIOException("String length less zero!");
} }
byte[] bytes = new byte[length * 4]; byte[] bytes = new byte[length * 4];
@@ -99,11 +105,6 @@ public class NetByteBuf extends ByteBuf {
} }
public void writeString(String string) { public void writeString(String string) {
if (string == null) {
writeVarInt(0);
return;
}
byte[] buf = string.getBytes(StandardCharsets.UTF_8); byte[] buf = string.getBytes(StandardCharsets.UTF_8);
if (buf.length > Short.MAX_VALUE) { if (buf.length > Short.MAX_VALUE) {
@@ -119,22 +120,7 @@ public class NetByteBuf extends ByteBuf {
//region VarInt //region VarInt
public int readVarInt() { public int readVarInt() {
int numRead = 0; return readVarInt(this.byteBuf);
int result = 0;
byte read;
do {
if ((numRead + 1) > 5) {
log.warn("VarInt is too big");
break;
}
read = readByte();
int value = (read & 0b01111111);
result |= (value << (7 * numRead));
numRead++;
} while ((read & 0b10000000) != 0);
return result;
} }
public void writeVarInt(int value) { public void writeVarInt(int value) {
@@ -188,4 +174,23 @@ public class NetByteBuf extends ByteBuf {
writeLong(uuid.getLeastSignificantBits()); writeLong(uuid.getLeastSignificantBits());
} }
//endregion //endregion
public static int readVarInt(ByteBuf byteBuf) {
int numRead = 0;
int result = 0;
byte read;
do {
if ((numRead + 1) > 5) {
log.warn("VarInt is too big");
break;
}
read = byteBuf.readByte();
int value = (read & 0b01111111);
result |= (value << (7 * numRead));
numRead++;
} while ((read & 0b10000000) != 0);
return result;
}
} }

View File

@@ -0,0 +1,8 @@
package mc.protocol.buffer;
public class NetIOException extends RuntimeException {
public NetIOException(String message) {
super(message);
}
}

View File

@@ -1,42 +0,0 @@
package mc.protocol.di;
import dagger.Module;
import dagger.Provides;
import mc.protocol.NettyConnectionContext;
import mc.protocol.State;
import mc.protocol.packets.ClientSidePacket;
import mc.protocol.packets.UnknownPacket;
import mc.protocol.pool.NettyConnectionContextFactory;
import mc.protocol.pool.PacketFactory;
import mc.protocol.pool.PacketPool;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Module
public class PoolModule {
@Provides
@ServerScope
@SuppressWarnings({ "rawtypes", "unchecked" })
PacketPool providePacketPool() {
Map<Class<? extends ClientSidePacket>, ObjectPool> map = Stream.of(State.values())
.flatMap(state -> state.getClientSidePackets().values().stream())
.distinct()
.collect(Collectors.toMap(
packetClass -> packetClass,
packetClass -> new GenericObjectPool(new PacketFactory<>(packetClass))));
map.put(UnknownPacket.class, new GenericObjectPool(new PacketFactory<>(UnknownPacket.class)));
return new PacketPool(map);
}
@Provides
@ServerScope
ObjectPool<NettyConnectionContext> providePoolNettyConnectionContext() {
return new GenericObjectPool<>(new NettyConnectionContextFactory());
}
}

View File

@@ -1,14 +0,0 @@
package mc.protocol.di;
import dagger.Component;
import mc.protocol.api.Server;
@Component(modules = {
ProtocolModule.class,
PoolModule.class
})
@ServerScope
public interface ProtocolComponent {
Server getServer();
}

View File

@@ -1,57 +0,0 @@
package mc.protocol.di;
import dagger.Module;
import dagger.Provides;
import lombok.RequiredArgsConstructor;
import mc.protocol.NettyConnectionContext;
import mc.protocol.NettyServer;
import mc.protocol.PacketInboundHandler;
import mc.protocol.api.Server;
import mc.protocol.event.EventBus;
import mc.protocol.event.SimpleEventBus;
import mc.protocol.io.codec.ProtocolDecoder;
import mc.protocol.pool.PacketPool;
import org.apache.commons.pool2.ObjectPool;
import javax.inject.Provider;
@Module
@RequiredArgsConstructor
public class ProtocolModule {
private final boolean readUnknownPackets;
@Provides
@ServerScope
Server provideServer(
Provider<ProtocolDecoder> protocolDecoderProvider,
Provider<PacketInboundHandler> packetInboundHandlerProvider,
EventBus eventBus
) {
return new NettyServer(protocolDecoderProvider, packetInboundHandlerProvider, eventBus);
}
@Provides
ProtocolDecoder provideProtocolDecoder(
ObjectPool<NettyConnectionContext> poolNettyConnectionContext,
PacketPool poolPackets
) {
return new ProtocolDecoder(readUnknownPackets, poolNettyConnectionContext, poolPackets);
}
@Provides
PacketInboundHandler providePacketInboundHandler(
ObjectPool<NettyConnectionContext> poolNettyConnectionContext,
PacketPool packetPool,
EventBus eventBus
) {
return new PacketInboundHandler(poolNettyConnectionContext, packetPool, eventBus);
}
@Provides
@ServerScope
EventBus provideEventBus() {
return new SimpleEventBus();
}
}

View File

@@ -1,10 +0,0 @@
package mc.protocol.di;
import javax.inject.Scope;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ServerScope {
}

View File

@@ -1,17 +0,0 @@
package mc.protocol.event;
import mc.protocol.State;
import mc.protocol.api.ConnectionContext;
import mc.protocol.packets.ClientSidePacket;
public interface EventBus {
<P extends ClientSidePacket> void subscribe(State state, Class<P> packetClass, EventHandler<P> eventHandler);
<P extends ClientSidePacket> void emit(State state, ConnectionContext channelContext, P packet);
@FunctionalInterface
interface EventHandler<P extends ClientSidePacket> {
void handle(ConnectionContext channelContext, P packet);
}
}

View File

@@ -1,26 +0,0 @@
package mc.protocol.event;
import mc.protocol.State;
import mc.protocol.api.ConnectionContext;
import mc.protocol.packets.ClientSidePacket;
import mc.protocol.utils.Table;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class SimpleEventBus implements EventBus {
private final Table<State, Class<? extends ClientSidePacket>, EventHandler> table = new Table<>();
@Override
public <P extends ClientSidePacket> void subscribe(State state, Class<P> packetClass, EventHandler<P> eventHandler) {
table.put(state, packetClass, eventHandler);
}
@Override
public <P extends ClientSidePacket> void emit(State state, ConnectionContext channelContext, P packet) {
EventHandler eventHandler = table.getColumnAndRow(state, packet.getClass());
if (eventHandler != null) {
eventHandler.handle(channelContext, packet);
}
}
}

View File

@@ -0,0 +1,31 @@
package mc.protocol.handler;
import io.netty.channel.ChannelHandlerContext;
import mc.protocol.State;
import mc.protocol.packets.ClientSidePacket;
import mc.utils.Table;
public class ProtocolHandlersBus {
@SuppressWarnings("rawtypes")
private final Table<State, Class<? extends ClientSidePacket>, Handler> table = new Table<>();
public <P extends ClientSidePacket> ProtocolHandlersBus addHandler(State state, Class<P> packetClass, Handler<P> handler) {
table.put(state, packetClass, handler);
return this;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public <P extends ClientSidePacket> void process(State state, ChannelHandlerContext ctx, P packet) {
Handler handler = table.getColumnAndRow(state, packet.getClass());
if (handler != null) {
handler.handle(ctx, packet);
}
}
@FunctionalInterface
public interface Handler<P extends ClientSidePacket> {
void handle(ChannelHandlerContext ctx, P packet);
}
}

View File

@@ -0,0 +1,42 @@
package mc.protocol.handler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import mc.protocol.ProtocolAttributes;
import mc.protocol.State;
import mc.protocol.packets.ClientSidePacket;
import mc.protocol.pool.ProtocolObjectPool;
import java.io.IOException;
import java.util.Objects;
@Slf4j
@RequiredArgsConstructor
public class ProtocolInboundHandler extends SimpleChannelInboundHandler<ClientSidePacket> {
private static final String CLIENT_FORCE_DISCONNECTED_IOEXCEPTION_MESSAGE_RU = "Программа на вашем хост-компьютере разорвала установленное подключение";
private final ProtocolHandlersBus protocolHandlersBus;
@Override
protected void channelRead0(ChannelHandlerContext ctx, ClientSidePacket packet) {
State state = Objects.requireNonNull(ctx.channel().attr(ProtocolAttributes.STATE).get());
protocolHandlersBus.process(state, ctx, packet);
ProtocolObjectPool.packet().returnObject(packet);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
if (cause instanceof IOException && cause.getLocalizedMessage().equalsIgnoreCase(CLIENT_FORCE_DISCONNECTED_IOEXCEPTION_MESSAGE_RU)) {
log.warn("Client '{}' force disconnected", ctx.channel().remoteAddress());
if (log.isTraceEnabled()) {
log.trace("{}", cause.getMessage(), cause);
}
} else {
log.error("{}", cause.getMessage(), cause);
}
}
}

View File

@@ -0,0 +1,67 @@
package mc.protocol.handler.codec;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import mc.protocol.ProtocolAttributes;
import mc.protocol.State;
import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ClientSidePacket;
import mc.protocol.packets.UnknownPacket;
import mc.protocol.pool.ProtocolObjectPool;
import java.util.List;
import java.util.Objects;
@Slf4j
@RequiredArgsConstructor
public class ProtocolDecoder extends ByteToMessageDecoder {
private final boolean readUnknownPackets;
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.channel().attr(ProtocolAttributes.STATE).set(State.HANDSHAKING);
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
State state = Objects.requireNonNull(ctx.channel().attr(ProtocolAttributes.STATE).get());
NetByteBuf netByteBuf = ProtocolObjectPool.netByteBuf().borrowObject(in);
int packetId = netByteBuf.readVarInt();
Class<? extends ClientSidePacket> packetClass = state.getClientSidePacketById(packetId);
if (packetClass == null) {
log.warn("Unknown packet: State {} ; Id 0x{}", state, packetIdAsHexcode(packetId));
if (readUnknownPackets) {
UnknownPacket unknownPacket = ProtocolObjectPool.packet().borrowObject(UnknownPacket.class);
unknownPacket.setState(state);
unknownPacket.setId(packetId);
unknownPacket.setDataSize(netByteBuf.readableBytes());
unknownPacket.readSelf(netByteBuf);
out.add(unknownPacket);
} else {
netByteBuf.skipBytes(netByteBuf.readableBytes());
}
} else {
ClientSidePacket packet = ProtocolObjectPool.packet().borrowObject(packetClass);
packet.readSelf(netByteBuf);
if (log.isDebugEnabled()) {
log.debug("IN: {}:{}", state, packet);
}
out.add(packet);
}
ProtocolObjectPool.netByteBuf().returnObject(netByteBuf);
}
private static String packetIdAsHexcode(int packetId) {
String hexPacketId = Integer.toHexString(packetId).toUpperCase();
if (hexPacketId.length() == 1) hexPacketId = "0" + hexPacketId;
return hexPacketId;
}
}

View File

@@ -1,35 +1,42 @@
package mc.protocol.io.codec; package mc.protocol.handler.codec;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToByteEncoder;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import mc.protocol.NetworkAttributes; import mc.protocol.ProtocolAttributes;
import mc.protocol.State; import mc.protocol.State;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;
import mc.protocol.pool.ProtocolObjectPool;
import java.util.Objects;
@Slf4j @Slf4j
public class ProtocolEncoder extends MessageToByteEncoder<ServerSidePacket> { public class ProtocolEncoder extends MessageToByteEncoder<ServerSidePacket> {
@Override @Override
protected void encode(ChannelHandlerContext ctx, ServerSidePacket packet, ByteBuf out) { protected void encode(ChannelHandlerContext ctx, ServerSidePacket packet, ByteBuf out) {
State state = ctx.channel().attr(NetworkAttributes.STATE).get(); State state = Objects.requireNonNull(ctx.channel().attr(ProtocolAttributes.STATE).get());
Integer packetId = state.getServerSidePacketId(packet.getClass()); Integer packetId = state.getServerSidePacketId(packet.getClass());
if (packetId == null) { if (packetId == null) {
log.error("Unknown send packet: State {} ; Class {}", state, packet.getClass()); log.error("Unknown send packet: State {} ; Class {}", state, packet.getClass());
return; return;
} }
log.debug("OUT: {}:{}", state, packet); if (log.isDebugEnabled()) {
log.debug("OUT: {}:{}", state, packet);
}
NetByteBuf buffer = new NetByteBuf(Unpooled.buffer()); NetByteBuf buffer = ProtocolObjectPool.netByteBuf().borrowObject();
buffer.writeVarInt(packetId); buffer.writeVarInt(packetId);
packet.writeSelf(buffer); packet.writeSelf(buffer);
NetByteBuf netByteBuf = new NetByteBuf(out); NetByteBuf netByteBuf = ProtocolObjectPool.netByteBuf().borrowObject(out);
netByteBuf.writeVarInt(buffer.readableBytes()); netByteBuf.writeVarInt(buffer.readableBytes());
netByteBuf.writeBytes(buffer); netByteBuf.writeBytes(buffer);
ProtocolObjectPool.netByteBuf().returnObject(netByteBuf);
ProtocolObjectPool.netByteBuf().returnObject(buffer);
} }
} }

View File

@@ -1,10 +1,11 @@
package mc.protocol.io.codec; package mc.protocol.handler.codec;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.ByteToMessageDecoder;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.pool.ProtocolObjectPool;
import java.util.List; import java.util.List;
@@ -12,7 +13,7 @@ public class ProtocolSplitter extends ByteToMessageDecoder {
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) { protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
NetByteBuf netByteBuf = new NetByteBuf(in); NetByteBuf netByteBuf = ProtocolObjectPool.netByteBuf().borrowObject(in);
netByteBuf.markReaderIndex(); netByteBuf.markReaderIndex();
do { do {
@@ -25,7 +26,7 @@ public class ProtocolSplitter extends ByteToMessageDecoder {
} }
} }
int sizePacket = new NetByteBuf(Unpooled.wrappedBuffer(sizePacketRaw)).readVarInt(); int sizePacket = NetByteBuf.readVarInt(Unpooled.wrappedBuffer(sizePacketRaw));
if (netByteBuf.readableBytes() >= sizePacket) { if (netByteBuf.readableBytes() >= sizePacket) {
byte[] bytes = new byte[sizePacket]; byte[] bytes = new byte[sizePacket];
@@ -36,5 +37,7 @@ public class ProtocolSplitter extends ByteToMessageDecoder {
break; break;
} }
} while (netByteBuf.readableBytes() > 0); } while (netByteBuf.readableBytes() > 0);
ProtocolObjectPool.netByteBuf().returnObject(netByteBuf);
} }
} }

View File

@@ -1,8 +0,0 @@
package mc.protocol.io;
public class DecoderException extends RuntimeException {
public DecoderException(String message) {
super(message);
}
}

View File

@@ -1,89 +0,0 @@
package mc.protocol.io.codec;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import mc.protocol.NettyConnectionContext;
import mc.protocol.NetworkAttributes;
import mc.protocol.State;
import mc.protocol.api.ConnectionContext;
import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ClientSidePacket;
import mc.protocol.packets.UnknownPacket;
import mc.protocol.pool.PacketPool;
import org.apache.commons.pool2.ObjectPool;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@RequiredArgsConstructor
@Slf4j
public class ProtocolDecoder extends ByteToMessageDecoder {
private final boolean readUnknownPackets;
private final ObjectPool<NettyConnectionContext> poolNettyConnectionContext;
private final PacketPool poolPackets;
@Setter
private Consumer<ConnectionContext> consumerNewConnection;
@Setter
private Consumer<ConnectionContext> consumerDisconnect;
@Override
public void channelActive(@Nonnull ChannelHandlerContext ctx) throws Exception {
NettyConnectionContext context = poolNettyConnectionContext.borrowObject().setCtx(ctx);
consumerNewConnection.accept(context);
poolNettyConnectionContext.returnObject(context);
super.channelActive(ctx);
}
@Override
public void channelInactive(@Nonnull ChannelHandlerContext ctx) throws Exception {
NettyConnectionContext context = poolNettyConnectionContext.borrowObject().setCtx(ctx);
consumerDisconnect.accept(context);
poolNettyConnectionContext.returnObject(context);
super.channelInactive(ctx);
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
State state = Objects.requireNonNull(ctx.channel().attr(NetworkAttributes.STATE).get());
NetByteBuf netByteBuf = new NetByteBuf(in);
int packetId = netByteBuf.readVarInt();
Class<? extends ClientSidePacket> packetClass = state.getClientSidePacketById(packetId);
if (packetClass == null) {
log.warn("Unknown packet: State {} ; Id 0x{}", state, packetIdAsHexcode(packetId));
if (readUnknownPackets) {
UnknownPacket unknownPacket = poolPackets.borrowObject(UnknownPacket.class);
unknownPacket.setState(state);
unknownPacket.setId(packetId);
unknownPacket.setDataSize(netByteBuf.readableBytes());
unknownPacket.readSelf(netByteBuf);
out.add(unknownPacket);
} else {
netByteBuf.skipBytes(netByteBuf.readableBytes());
}
} else {
ClientSidePacket packet = poolPackets.borrowObject(packetClass);
packet.readSelf(netByteBuf);
log.debug("IN: {}:{}", state, packet);
out.add(packet);
}
}
private String packetIdAsHexcode(int packetId) {
String hexPacketId = Integer.toHexString(packetId).toUpperCase();
if (hexPacketId.length() == 1) hexPacketId = "0" + hexPacketId;
return hexPacketId;
}
}

View File

@@ -0,0 +1,11 @@
package mc.protocol.model;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import mc.utils.vector.Vector3i;
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BlockLocation extends Vector3i {
}

View File

@@ -0,0 +1,10 @@
package mc.protocol.model;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import mc.utils.vector.Vector3i;
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ChunkSectionLocation extends Vector3i {
}

View File

@@ -1,12 +1,10 @@
package mc.protocol.model; package mc.protocol.model;
import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode;
import lombok.Data; import lombok.ToString;
import mc.utils.vector.Vector3d;
@AllArgsConstructor @EqualsAndHashCode(callSuper = true)
@Data @ToString(callSuper = true)
public class Location { public class Location extends Vector3d {
private double x;
private double y;
private double z;
} }

View File

@@ -1,11 +1,26 @@
package mc.protocol.model; package mc.protocol.model;
import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode;
import lombok.Data; import lombok.ToString;
import mc.utils.vector.Vector2f;
@AllArgsConstructor @EqualsAndHashCode(callSuper = true)
@Data @ToString(callSuper = true)
public class Look { public class Look extends Vector2f {
private float yaw;
private float pitch; /**
* Equal X
* @return X
*/
public float getYaw() {
return this.getX();
}
/**
* Equal Y
* @return Y
*/
public float getPitch() {
return this.getY();
}
} }

View File

@@ -1,10 +1,10 @@
package mc.protocol.serializer; package mc.protocol.model;
import com.eclipsesource.json.Json; import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonArray; import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject; import com.eclipsesource.json.JsonObject;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import mc.protocol.model.ServerInfo; import mc.protocol.model.text.TextSerializer;
import java.util.Spliterator; import java.util.Spliterator;
import java.util.Spliterators; import java.util.Spliterators;
@@ -14,7 +14,11 @@ import java.util.stream.StreamSupport;
@UtilityClass @UtilityClass
public class ServerInfoSerializer { public class ServerInfoSerializer {
public JsonObject toJsonObject(ServerInfo info) { public String toStringPlain(ServerInfo info) {
return toJsonObject(info).toString();
}
private JsonObject toJsonObject(ServerInfo info) {
JsonObject jsonObject = Json.object() JsonObject jsonObject = Json.object()
.add("version", createVersionObj(info)) .add("version", createVersionObj(info))
.add("players", createPlayersObj(info)) .add("players", createPlayersObj(info))

View File

@@ -1,12 +1,9 @@
package mc.protocol.serializer; package mc.protocol.model.text;
import com.eclipsesource.json.Json; import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonArray; import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject; import com.eclipsesource.json.JsonObject;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import mc.protocol.model.text.Text;
import mc.protocol.model.text.TextColor;
import mc.protocol.model.text.TextStyle;
import java.util.Map; import java.util.Map;
@@ -16,6 +13,10 @@ public class TextSerializer {
private static final Map<Character, TextStyle> legacyStyleCodes; private static final Map<Character, TextStyle> legacyStyleCodes;
private static final Map<Character, TextColor> legacyColorCodes; private static final Map<Character, TextColor> legacyColorCodes;
public String toStringPlain(Text text) {
return toJsonObject(text).toString();
}
public JsonObject toJsonObject(Text text) { public JsonObject toJsonObject(Text text) {
JsonObject jsonObject = Json.object(); JsonObject jsonObject = Json.object();

View File

@@ -1,7 +1,7 @@
package mc.protocol.packets; package mc.protocol.packets;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.pool.Passivable; import mc.utils.pool.Passivable;
/** /**
* Пакеты отправляемые клиентом. * Пакеты отправляемые клиентом.

View File

@@ -1,21 +0,0 @@
package mc.protocol.packets;
import mc.protocol.io.NetByteBuf;
public abstract class EmptyPacket implements ClientSidePacket, ServerSidePacket {
@Override
public void readSelf(NetByteBuf netByteBuf) {
// empty
}
@Override
public void writeSelf(NetByteBuf netByteBuf) {
// empty
}
@Override
public void passivate() {
// pass
}
}

View File

@@ -1,20 +1,13 @@
package mc.protocol.packets; package mc.protocol.packets;
import lombok.Data; import lombok.Data;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
/** /**
* Пинг-пакет. * Пинг-пакет.
* *
* <p>Эхо-пакет, которым проверяется качество соединения между <b>Клиентом</b> и <b>Сервером</b>.</p> * <p>Эхо-пакет, которым проверяется качество соединения между <b>Клиентом</b> и <b>Сервером</b>.</p>
* *
* <p>По спецификации:</p>
* <oi>
* <li>если <b>Сервер</b> не ответил <b>Клиенту</b> в течении 20 секунд, <b>Клиент</b> отключается
* и выдаёт ошибку <i>"Timed out"</i>.</li>
* <li>если <b>Клиент</b> не отвечает <b>Серверу</b> в течении 30 секунд, <b>Сервер</b> отключает <b>Клиента</b>.</li>
* </oi>
*
* <p>Структура пакета</p> * <p>Структура пакета</p>
* <pre> * <pre>
* | FIELD | TYPE | NOTES | * | FIELD | TYPE | NOTES |
@@ -22,25 +15,32 @@ import mc.protocol.io.NetByteBuf;
* | Payload | Long | Любое уникальное число | * | Payload | Long | Любое уникальное число |
* </pre> * </pre>
* *
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=7368#Keep_Alive">Keep Alive</a> * <p>По спецификации:</p>
* <oi>
* <li>если Сервер не ответил Клиенту в течении 20 секунд, Клиент отключается и выдаёт ошибку "Timed out";</li>
* <li>если Клиент не отвечает Серверу в течении 30 секунд, Сервер отключает Клиента.</li>
* </oi>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Keep_Alive_.28clientbound.29">Keep Alive (clientbound)</a>
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Keep_Alive_.28serverbound.29">Keep Alive (serverbound)</a>
*/ */
@Data @Data
public class PingPacket implements ClientSidePacket, ServerSidePacket { public class KeepAlivePacket implements ClientSidePacket, ServerSidePacket {
private Long payload; private long payload;
@Override @Override
public void readSelf(NetByteBuf netByteBuf) { public void readSelf(NetByteBuf netByteBuf) {
payload = netByteBuf.readLong(); payload = netByteBuf.readLong();
} }
@Override
public void passivate() {
this.payload = null;
}
@Override @Override
public void writeSelf(NetByteBuf netByteBuf) { public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeLong(payload); netByteBuf.writeLong(payload);
} }
@Override
public void passivate() {
this.payload = 0;
}
} }

View File

@@ -1,9 +1,9 @@
package mc.protocol.packets; package mc.protocol.packets;
/** /**
* Пакет. * Сетевой пакет.
* *
* <p>Структура пакета</p> * <p>Структура</p>
* <pre> * <pre>
* | FIELD | TYPE | NOTES | * | FIELD | TYPE | NOTES |
* |-------------|--------|-------------------------------------------| * |-------------|--------|-------------------------------------------|

View File

@@ -1,6 +1,6 @@
package mc.protocol.packets; package mc.protocol.packets;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
/** /**
* Пакеты отправляемые сервером. * Пакеты отправляемые сервером.

View File

@@ -4,7 +4,7 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import mc.protocol.State; import mc.protocol.State;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
@NoArgsConstructor @NoArgsConstructor
@Data @Data

View File

@@ -1,18 +0,0 @@
package mc.protocol.packets.client;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import mc.protocol.packets.EmptyPacket;
/**
* Status server packet, request.
*
* <p>Клиент запрашивает получение информации о сервере</p>
*/
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@ToString
public class StatusServerRequestPacket extends EmptyPacket {
}

View File

@@ -1,11 +1,11 @@
package mc.protocol.packets.client; package mc.protocol.packets.handshaking.client;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import mc.protocol.State; import mc.protocol.State;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
/** /**
@@ -18,14 +18,14 @@ import mc.protocol.packets.ClientSidePacket;
* | FIELD | TYPE | NOTES | * | FIELD | TYPE | NOTES |
* |------------------|----------------|----------------------------------------------| * |------------------|----------------|----------------------------------------------|
* | Protocol version | VarInt | Версия протокола [1] | * | Protocol version | VarInt | Версия протокола [1] |
* | Server address | Stirng | Hostname или IP | * | Server address | Stirng (255) | Hostname или IP |
* | Server port | Unsigned Short | Порт сервера | * | Server port | Unsigned Short | Порт сервера |
* | Next stage | VarInt | ID State на который необходимо переключиться | * | Next state | VarInt | ID State на который необходимо переключиться |
* *
* [1] - <a href="https://wiki.vg/Protocol_version_numbers" target="_top">Protocol version numbers</a> * [1] - <a href="https://wiki.vg/Protocol_version_numbers">Protocol version numbers</a>
* </pre> * </pre>
* *
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=7368#Handshake" target="_top">Handshake</a> * @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Handshake">Handshake</a>
* @see State * @see State
*/ */
@NoArgsConstructor @NoArgsConstructor
@@ -41,10 +41,10 @@ public class HandshakePacket implements ClientSidePacket {
@Override @Override
public void readSelf(NetByteBuf netByteBuf) { public void readSelf(NetByteBuf netByteBuf) {
protocolVersion = netByteBuf.readVarInt(); this.protocolVersion = netByteBuf.readVarInt();
host = netByteBuf.readString(255); this.host = netByteBuf.readString(255);
port = netByteBuf.readUnsignedShort(); this.port = netByteBuf.readUnsignedShort();
nextState = State.getById(netByteBuf.readVarInt()); this.nextState = State.getById(netByteBuf.readVarInt());
} }
@Override @Override
@@ -54,5 +54,4 @@ public class HandshakePacket implements ClientSidePacket {
this.port = 0; this.port = 0;
this.nextState = null; this.nextState = null;
} }
} }

View File

@@ -1,10 +1,10 @@
package mc.protocol.packets.client; package mc.protocol.packets.login.client;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
/** /**
@@ -19,7 +19,7 @@ import mc.protocol.packets.ClientSidePacket;
* | Name | String | Имя/Логин игрока | * | Name | String | Имя/Логин игрока |
* </pre> * </pre>
* *
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=7368#Login_Start" target="_top">Login start</a> * @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Login_Start">Login start</a>
*/ */
@NoArgsConstructor @NoArgsConstructor
@Getter @Getter

View File

@@ -1,11 +1,10 @@
package mc.protocol.packets.server; package mc.protocol.packets.login.server;
import lombok.Data; import lombok.Data;
import mc.protocol.State; import mc.protocol.State;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.model.text.Text; import mc.protocol.model.text.Text;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;
import mc.protocol.serializer.TextSerializer;
/** /**
* Diconnect packet. * Diconnect packet.
@@ -16,7 +15,7 @@ import mc.protocol.serializer.TextSerializer;
* <pre> * <pre>
* | FIELD | TYPE | NOTES | * | FIELD | TYPE | NOTES |
* |-------------|--------|----------------------------------| * |-------------|--------|----------------------------------|
* | JSON Reason | String | Причина отключения. Опционально. | * | JSON Reason | Text | Причина отключения. Опционально. |
* </pre> * </pre>
* *
* <p>Пример JSON Reason</p> * <p>Пример JSON Reason</p>
@@ -26,7 +25,7 @@ import mc.protocol.serializer.TextSerializer;
* } * }
* </pre> * </pre>
* *
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=7368#Disconnect_2" target="_top">Disconnect</a> * @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Disconnect_.28login.29">Disconnect (login)</a>
* @see State * @see State
*/ */
@Data @Data
@@ -39,6 +38,6 @@ public class DisconnectPacket implements ServerSidePacket {
@Override @Override
public void writeSelf(NetByteBuf netByteBuf) { public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeString(TextSerializer.toJsonObject(reason).toString()); netByteBuf.writeText(reason);
} }
} }

View File

@@ -1,7 +1,7 @@
package mc.protocol.packets.server; package mc.protocol.packets.login.server;
import lombok.Data; import lombok.Data;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;
import java.util.UUID; import java.util.UUID;
@@ -27,7 +27,7 @@ public class LoginSuccessPacket implements ServerSidePacket {
@Override @Override
public void writeSelf(NetByteBuf netByteBuf) { public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeString(uuid.toString()); netByteBuf.writeString(this.uuid.toString());
netByteBuf.writeString(name); netByteBuf.writeString(this.name);
} }
} }

View File

@@ -0,0 +1,68 @@
package mc.protocol.packets.play.client;
import lombok.Data;
import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ClientSidePacket;
import mc.protocol.packets.ServerSidePacket;
/**
* Характеристики игрока.
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |------------------------------|----------|-----------------------------------------|
* | Flags | Byte | Битовая маска флагов. См. ниже значения |
* | Flying Speed | Float | Скорость полёта |
* | Field of View (FOV) Modifier | Float | Поле зрения |
* </pre>
*
* <p>Флаги "Flags"</p>
* <pre>
* Bit 0x01 - Неуязвимость (Invulnerable)
* Bit 0x02 - В полёте (Flying)
* Bit 0x04 - Может летать (Allow Flying)
* Bit 0x08 - Creative Mode (Instant Break)
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Player_Abilities_(serverbound)">Player Abilities</a>
*/
@Data
public class CPlayerAbilitiesPacket implements ClientSidePacket {
@SuppressWarnings("java:S116")
private byte $flags = 0;
private float flyingSpeed;
private float fieldOfView;
@Override
public void readSelf(NetByteBuf netByteBuf) {
this.$flags = netByteBuf.readByte();
this.flyingSpeed = netByteBuf.readFloat();
this.fieldOfView = netByteBuf.readFloat();
}
//FIXME использование value значений
public void setInvulnerable(boolean value) {
this.$flags = (byte) (this.$flags | 0x01);
}
public void setFlying(boolean value) {
this.$flags = (byte) (this.$flags | 0x02);
}
public void setCatFly(boolean value) {
this.$flags = (byte) (this.$flags | 0x04);
}
public void setCreativeMode(boolean value) {
this.$flags = (byte) (this.$flags | 0x08);
}
@Override
public void passivate() {
$flags = 0;
flyingSpeed = 0;
fieldOfView = 0;
}
}

View File

@@ -1,13 +1,14 @@
package mc.protocol.packets.client; package mc.protocol.packets.play.client;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.model.Location; import mc.protocol.model.Location;
import mc.protocol.model.Look; import mc.protocol.model.Look;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
import mc.protocol.pool.ProtocolObjectPool;
/** /**
* Клиент сообщает о движении и повороте головы Игрока. * Клиент сообщает о движении и повороте головы Игрока.
@@ -42,19 +43,27 @@ public class CPlayerPositionAndLookPacket implements ClientSidePacket {
double x = netByteBuf.readDouble(); double x = netByteBuf.readDouble();
double y = netByteBuf.readDouble(); double y = netByteBuf.readDouble();
double z = netByteBuf.readDouble(); double z = netByteBuf.readDouble();
this.position = new Location(x, y, z); this.position = ProtocolObjectPool.location().borrowObject();
position.set(x, y, z);
float yaw = netByteBuf.readFloat(); float yaw = netByteBuf.readFloat();
float pitch = netByteBuf.readFloat(); float pitch = netByteBuf.readFloat();
this.look = new Look(yaw, pitch); this.look = ProtocolObjectPool.look().borrowObject();
this.look.set(yaw, pitch);
this.onGround = netByteBuf.readBoolean(); this.onGround = netByteBuf.readBoolean();
} }
@Override @Override
public void passivate() { public void passivate() {
this.position.set(0, 0, 0);
ProtocolObjectPool.location().returnObject(this.position);
this.position = null; this.position = null;
this.look.set(0, 0);
ProtocolObjectPool.look().returnObject(this.look);
this.look = null; this.look = null;
this.onGround = false; this.onGround = false;
} }

View File

@@ -1,9 +1,9 @@
package mc.protocol.packets.client; package mc.protocol.packets.play.client;
import lombok.*; import lombok.*;
import mc.protocol.utils.ChatMode; import mc.protocol.utils.ChatMode;
import mc.protocol.utils.MainHand; import mc.protocol.utils.Hand;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
/** /**
@@ -52,7 +52,7 @@ public class ClientSettingsPacket implements ClientSidePacket {
private boolean chatColors; private boolean chatColors;
@SuppressWarnings("java:S116") @SuppressWarnings("java:S116")
private int $displayedSkinPartsBitMask; private int $displayedSkinPartsBitMask;
private MainHand mainHand; private Hand mainHand;
@Override @Override
public void readSelf(NetByteBuf netByteBuf) { public void readSelf(NetByteBuf netByteBuf) {
@@ -61,7 +61,7 @@ public class ClientSettingsPacket implements ClientSidePacket {
this.chatMode = ChatMode.valueById(netByteBuf.readVarInt()); this.chatMode = ChatMode.valueById(netByteBuf.readVarInt());
this.chatColors = netByteBuf.readBoolean(); this.chatColors = netByteBuf.readBoolean();
this.$displayedSkinPartsBitMask = netByteBuf.readUnsignedByte(); this.$displayedSkinPartsBitMask = netByteBuf.readUnsignedByte();
this.mainHand = MainHand.valueById(netByteBuf.readVarInt()); this.mainHand = Hand.valueById(netByteBuf.readVarInt());
} }
@Override @Override

View File

@@ -0,0 +1,77 @@
package mc.protocol.packets.play.client;
import lombok.*;
import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ClientSidePacket;
import javax.annotation.Nullable;
/**
* Entity Action packet.
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |------------|--------|-------------------------------------------|
* | Entity ID | VarInt | ID игрока |
* | Action ID | VarInt | ID действия |
* | Jump Boost | VarInt | Используется только при "Action ID" = 5. |
* | | | В этом случае значение будет от 0 до 100. |
* | | | В остальных случаях значение 0. |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Entity_Action" target="_top">Entity Action</a>
*/
@NoArgsConstructor
@Getter
@EqualsAndHashCode
@ToString
public class EntityActionPacket implements ClientSidePacket {
private Integer entityId;
private Action action;
private Integer jumpBoost;
@Override
public void readSelf(NetByteBuf netByteBuf) {
this.entityId = netByteBuf.readVarInt();
int actionId = netByteBuf.readVarInt();
this.jumpBoost = netByteBuf.readVarInt();
this.action = Action.valueOfCode(actionId);
}
@Override
public void passivate() {
this.entityId = null;
this.action = null;
this.jumpBoost = null;
}
@RequiredArgsConstructor
public enum Action {
START_SNEAKING(0),
STOP_SNEAKING(1),
LEAVE_BED(2),
START_SPRINTING(3),
STOP_SPRINTING(4),
START_JUMP_WITH_HORSE(5),
STOP_JUMP_WITH_HORSE(6),
OPEN_HORSE_INVENTORY(7),
START_FLYING_WITH_ELYTRA(8);
@Nullable
public static Action valueOfCode(int code) {
for (Action action : Action.values()) {
if (action.code == code) {
return action;
}
}
return null;
}
@Getter
private final int code;
}
}

View File

@@ -0,0 +1,42 @@
package mc.protocol.packets.play.client;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ClientSidePacket;
import mc.protocol.utils.Hand;
/**
* Отправляется, когда Игрок взмахивает рукой.
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |-------|---------|------------------------------|
* | Hand | VarInt | Используемая рука в анимации |
* | | | - 0: основная рука |
* | | | - 1: левая рука |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Animation_.28serverbound.29">Animation (serverbound)</a>
*/
@NoArgsConstructor
@Getter
@EqualsAndHashCode
@ToString
public class HandAnimationPacket implements ClientSidePacket {
private Hand hand;
@Override
public void readSelf(NetByteBuf netByteBuf) {
this.hand = Hand.valueById(netByteBuf.readVarInt());
}
@Override
public void passivate() {
this.hand = null;
}
}

View File

@@ -0,0 +1,78 @@
package mc.protocol.packets.play.client;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import mc.protocol.buffer.NetByteBuf;
import mc.protocol.model.BlockLocation;
import mc.protocol.packets.ClientSidePacket;
import mc.protocol.utils.Face;
import mc.protocol.utils.Hand;
import mc.protocol.utils.SerializeUtil;
import mc.utils.vector.Vector3f;
/**
* Игрок размещает блок.
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |-------------------|----------|---------------------------------------------------------------------------|
* | Location | Position | Позиция блока |
* | Face | VarInt | Сторона блока, к которому ставится новый блок |
* | Hand | VarInt | Используемая рука |
* | | | - 0: основная рука |
* | | | - 1: левая рука |
* | Cursor Position X | Float | Положение перекрестия на блоке, от 0 до 1 с увеличением с West на East. |
* | Cursor Position Y | Float | Положение перекрестия на блоке, от 0 до 1 с увеличением с Bottom на Top. |
* | Cursor Position Z | Float | Положение перекрестия на блоке, от 0 до 1 с увеличением с North на South. |
* </pre>
*
* <p>Значения Стороны блока</p>
* <pre>
* | VALUE | OFFCET | FACE |
* |-------|--------|--------|
* | 0 | -Y | Bottom |
* | 1 | +Y | Top |
* | 2 | -Z | North |
* | 3 | +Z | South |
* | 4 | -X | West |
* | 5 | +X | East |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Player_Block_Placement">Player Block Placement</a>
*/
@NoArgsConstructor
@Getter
@EqualsAndHashCode
@ToString
public class PlayerBlockPlacementPacket implements ClientSidePacket {
private BlockLocation location;
private Face face;
private Hand hand;
private Vector3f cursorPosition;
@Override
public void readSelf(NetByteBuf netByteBuf) {
this.location = SerializeUtil.long2location(netByteBuf.readLong());
this.face = Face.valueById(netByteBuf.readVarInt());
this.hand = Hand.valueById(netByteBuf.readVarInt());
this.cursorPosition = new Vector3f();
this.cursorPosition.set(
netByteBuf.readFloat(),
netByteBuf.readFloat(),
netByteBuf.readFloat()
);
}
@Override
public void passivate() {
this.location = null;
this.face = null;
this.hand = null;
this.cursorPosition = null;
}
}

View File

@@ -0,0 +1,82 @@
package mc.protocol.packets.play.client;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import mc.protocol.buffer.NetByteBuf;
import mc.protocol.model.BlockLocation;
import mc.protocol.packets.ClientSidePacket;
import mc.protocol.utils.DiggingStatus;
import mc.protocol.utils.Face;
import mc.protocol.utils.SerializeUtil;
/**
* Клиент копает, ломает блок (и не только).
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |----------|----------|------------------------------------------|
* | Status | VarInt | Действие Игрока к блоку |
* | Location | Position | Позиция блока |
* | Face | Byte | Сторона блока, с которой взаимодействуют |
* </pre>
*
* <p>Возможные действия Игрока к блоку</p>
* <pre>
* | VALUE | DESCRIPTION | NOTES |
* |--------- |-------------------------|---------------------------------------------------------------------------------------|
* | 0 | Начал ломать | |
* | 1 | Прекратил ломать | Отправляется, когда Игрок отпустил клавишу "копания" |
* | 2 | Закончил ломать | Отправляется, когда Игрок закончил ломать блок. Т.е. блок готов сломаться |
* | 3 | Бросает стек предметов | Отправляется, когда Игрок с помощью клавиши "Выкинуть предмет"(Q) |
* | | | с модификатором(??) для выкидывания полного стека. |
* | | | Поле Location всегда будет 0/0/0, а Face всегда -Y. |
* | 4 | Бросает предмет | Отправляется, когда Игрок выкидывает предмет. |
* | | | Поле Location всегда будет 0/0/0, а Face всегда -Y. |
* | 5 | Стреляет стрелой / | Указывает, что текущего состояние удерживаемого предмета должно |
* | | Заканчивает есть / | быть обновлено. Например, поедание еды, натягивание луков, использование ведер и т.д. |
* | | и т.д. | Поле Location всегда будет 0/0/0, а Face всегда -Y. |
* | 6 | Поменять предмет в руке | Отправляется когда Игрок меняет предмет предмет во второй руке через "свап". |
* | | | Поле Location всегда будет 0/0/0, а Face всегда -Y. |
* </pre>
*
* <p>Значения Стороны блока</p>
* <pre>
* | VALUE | OFFCET | FACE |
* |-------|--------|--------|
* | 0 | -Y | Bottom |
* | 1 | +Y | Top |
* | 2 | -Z | North |
* | 3 | +Z | South |
* | 4 | -X | West |
* | 5 | +X | East |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Player_Digging">Player Digging</a>
*/
@NoArgsConstructor
@Getter
@EqualsAndHashCode
@ToString
public class PlayerDiggingAndMorePacket implements ClientSidePacket {
private DiggingStatus status;
private BlockLocation location;
private Face face;
@Override
public void readSelf(NetByteBuf netByteBuf) {
this.status = DiggingStatus.valueById(netByteBuf.readVarInt());
this.location = SerializeUtil.long2location(netByteBuf.readLong());
this.face = Face.valueById(netByteBuf.readByte());
}
@Override
public void passivate() {
this.status = null;
this.location = null;
this.face = null;
}
}

View File

@@ -1,12 +1,13 @@
package mc.protocol.packets.client; package mc.protocol.packets.play.client;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.model.Look; import mc.protocol.model.Look;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
import mc.protocol.pool.ProtocolObjectPool;
/** /**
* Клиент сообщает о повороте головы Игрока. * Клиент сообщает о повороте головы Игрока.
@@ -35,14 +36,18 @@ public class PlayerLookPacket implements ClientSidePacket {
public void readSelf(NetByteBuf netByteBuf) { public void readSelf(NetByteBuf netByteBuf) {
float yaw = netByteBuf.readFloat(); float yaw = netByteBuf.readFloat();
float pitch = netByteBuf.readFloat(); float pitch = netByteBuf.readFloat();
this.look = new Look(yaw, pitch); this.look = ProtocolObjectPool.look().borrowObject();
this.look.set(yaw, pitch);
this.onGround = netByteBuf.readBoolean(); this.onGround = netByteBuf.readBoolean();
} }
@Override @Override
public void passivate() { public void passivate() {
this.look.set(0, 0);
ProtocolObjectPool.look().returnObject(this.look);
this.look = null; this.look = null;
this.onGround = false; this.onGround = false;
} }
} }

View File

@@ -0,0 +1,39 @@
package mc.protocol.packets.play.client;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ClientSidePacket;
/**
* Клиент сообщает: на земле ли он.
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |-----------|---------|---------------------------------------------|
* | On Ground | Boolean | true, если Игрок находится на земле |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Player">Player</a>
*/
@NoArgsConstructor
@Getter
@EqualsAndHashCode
@ToString
public class PlayerOnGroundPacket implements ClientSidePacket {
private boolean onGround;
@Override
public void readSelf(NetByteBuf netByteBuf) {
this.onGround = netByteBuf.readBoolean();
}
@Override
public void passivate() {
onGround = false;
}
}

View File

@@ -1,12 +1,13 @@
package mc.protocol.packets.client; package mc.protocol.packets.play.client;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.model.Location; import mc.protocol.model.Location;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
import mc.protocol.pool.ProtocolObjectPool;
/** /**
* Клиент сообщает о движении Игрока. * Клиент сообщает о движении Игрока.
@@ -38,14 +39,18 @@ public class PlayerPositionPacket implements ClientSidePacket {
double x = netByteBuf.readDouble(); double x = netByteBuf.readDouble();
double y = netByteBuf.readDouble(); double y = netByteBuf.readDouble();
double z = netByteBuf.readDouble(); double z = netByteBuf.readDouble();
this.position = new Location(x, y, z); this.position = ProtocolObjectPool.location().borrowObject();
this.position.set(x, y, z);
this.onGround = netByteBuf.readBoolean(); this.onGround = netByteBuf.readBoolean();
} }
@Override @Override
public void passivate() { public void passivate() {
this.position.set(0, 0, 0);
ProtocolObjectPool.location().returnObject(this.position);
this.position = null; this.position = null;
this.onGround = false; this.onGround = false;
} }

View File

@@ -1,10 +1,10 @@
package mc.protocol.packets.client; package mc.protocol.packets.play.client;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
/** /**

View File

@@ -1,20 +1,20 @@
package mc.protocol.packets.client; package mc.protocol.packets.play.client;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
import mc.protocol.packets.server.SPlayerPositionAndLookPacket; import mc.protocol.packets.play.server.SPlayerPositionAndLookPacket;
/** /**
* Teleport сonfirm packet. * Teleport сonfirm packet.
* *
* <p>Структура пакета</p> * <p>Структура пакета</p>
* <pre> * <pre>
* | FIELD | TYPE | NOTES | * | FIELD | TYPE | NOTES |
* |-------------|--------|-----------------------------------------------------------| * |-------------|--------|------------------------------------------------------------|
* | Teleport ID | VarInt | ID, который был выдан пакетом {@link SPlayerPositionAndLookPacket} | * | Teleport ID | VarInt | ID, который был выдан пакетом {@link SPlayerPositionAndLookPacket} |
* </pre> * </pre>
* *

View File

@@ -0,0 +1,121 @@
package mc.protocol.packets.play.server;
import io.netty.buffer.Unpooled;
import lombok.Data;
import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ServerSidePacket;
import mc.protocol.pool.ProtocolObjectPool;
import mc.protocol.utils.SerializeUtil;
import mc.protocol.world.Chunk;
import mc.protocol.world.ChunkSection;
/**
* Данные чанка.
*
* <h2>Структура пакета</h2>
* <pre>
* | FIELD | TYPE | NOTES |
* |--------------------------|------------- |------------------------------------------------------------------------------------|
* | Chunk X | Integer | Координаты чанка (координата блока, делённая на 16, округленная в меньшую сторону) |
* | Chunk Z | Integer | Координаты чанка (координата блока, делённая на 16, округленная в меньшую сторону) |
* | Is Full chunk | Boolean | См. Chunk Format |
* | Available Sections | VarInt | Битовая маска, где каждый бит - это часть чанка (0-15) |
* | Size of Data | VarInt | Размер поля "Data" |
* | Data | Byte array | Данные чанка. См. Data Structure |
* | Number of block entities | VarInt | Количество элементов в поле "Block entities" |
* | Block entities | Array of NBT | Все сущности в чанке |
* </pre>
*
* <h2>Data Structure</h2>
* <pre>
* | FIELD | TYPE | NOTES |
* |--------|------------------------|------------------------------------------------------------|
* | Data | Array of Chunk Section | См. Chunk Section Structure |
* | Biomes | Byte array | Optional. Отправляются только если "Is Full chunk" == true |
* </pre>
*
* <h2>Chunk Section Structure</h2>
* <pre>
* | FIELD | TYPE | NOTES |
* |-------------------|---------------|---------------------------------------------------------------------|
* | Bits Per Block | Unsigned Byte | Определяет, сколько битов используется для кодирования блока |
* | Palette | Byte array | См. Palette Structure |
* | Data Array Length | VarInt | |
* | Data Array | Array of Long | |
* | Block Light | Byte array | Половина байна на блок |
* | Sky Light | Byte array | Optional. Только для LevelType == Overworld. Половина байна на блок |
* </pre>
*
* <h2>Palette Structure</h2>
* <p>Есть два типа: Indirect и Direct.</p>
* <p>
* Indirect используется, если "Bits Per Block" < 9. При этом, если "Bits Per Block" <= 4,
* то должно использоваться значение 4.
* </p>
* <p>Для Indirect формат следующий</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |----------------|-----------------|--------------------------------|
* | Palette Length | VarInt | Количество элементов в массиве |
* | Palette | Array of VarInt | Идентификаторы блоков |
* </pre>
*
* <p>Direct используется, если "Bits Per Block" >= 9</p>
* <p>Для Direct формат следующий</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |----------------------|--------|-------------|
* | Dummy Palette Length | VarInt | Всегда == 0 |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Chunk_Data">Chunk Data</a>
* @see <a href="https://wiki.vg/index.php?title=Chunk_Format&oldid=14135">Chunk Format</a>
*/
@Data
public class ChunkDataPacket implements ServerSidePacket {
private static final int FULL_BIT_MASK = 0b11111111_11111111;
private Chunk chunk;
@Override
public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeInt(chunk.getX()); // Chunk X
netByteBuf.writeInt(chunk.getZ()); // Chunk Z
netByteBuf.writeBoolean(true); // Is Full chunk
netByteBuf.writeVarInt(FULL_BIT_MASK); // Available Sections
NetByteBuf data = createDataStructure();
netByteBuf.writeVarInt(data.readableBytes()); // Size of Data
netByteBuf.writeBytes(data); // Data
netByteBuf.writeVarInt(0); // Number of block entities
// Block entities (NBT's)
ProtocolObjectPool.netByteBuf().returnObject(data);
}
private NetByteBuf createDataStructure() {
NetByteBuf dataStructure = ProtocolObjectPool.netByteBuf().borrowObject(Unpooled.buffer());
for (int h = 0; h < 16; h++) {
ChunkSection section = chunk.getSection(h);
NetByteBuf data = SerializeUtil.serializeChunkSection(section);
dataStructure.writeBytes(data); // Data
ProtocolObjectPool.netByteBuf().returnObject(data);
}
// <Biomes>
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
dataStructure.writeByte(chunk.getBiome(
(chunk.getX() << 4) + x,
(chunk.getZ() << 4) + z));
}
}
// </Biomes>
return dataStructure;
}
}

View File

@@ -1,10 +1,10 @@
package mc.protocol.packets.server; package mc.protocol.packets.play.server;
import lombok.Data; import lombok.Data;
import mc.protocol.utils.Difficulty; import mc.protocol.utils.Difficulty;
import mc.protocol.utils.GameMode; import mc.protocol.utils.GameMode;
import mc.protocol.utils.LevelType; import mc.protocol.utils.LevelType;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;
/** /**

View File

@@ -1,7 +1,7 @@
package mc.protocol.packets.server; package mc.protocol.packets.play.server;
import lombok.Data; import lombok.Data;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;
/** /**
@@ -27,7 +27,7 @@ import mc.protocol.packets.ServerSidePacket;
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Player_Abilities_.28clientbound.29">Player Abilities</a> * @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Player_Abilities_.28clientbound.29">Player Abilities</a>
*/ */
@Data @Data
public class PlayerAbilitiesPacket implements ServerSidePacket { public class SPlayerAbilitiesPacket implements ServerSidePacket {
@SuppressWarnings("java:S116") @SuppressWarnings("java:S116")
private byte $flags = 0; private byte $flags = 0;

View File

@@ -1,11 +1,11 @@
package mc.protocol.packets.server; package mc.protocol.packets.play.server;
import lombok.Data; import lombok.Data;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.model.Location; import mc.protocol.model.Location;
import mc.protocol.model.Look; import mc.protocol.model.Look;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;
import mc.protocol.packets.client.TeleportConfirmPacket; import mc.protocol.packets.play.client.TeleportConfirmPacket;
/** /**
* Установка позиции и угла осмотра Игрока. * Установка позиции и угла осмотра Игрока.

View File

@@ -1,7 +1,7 @@
package mc.protocol.packets.server; package mc.protocol.packets.play.server;
import lombok.Data; import lombok.Data;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.model.Location; import mc.protocol.model.Location;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;

View File

@@ -1,69 +0,0 @@
package mc.protocol.packets.server;
import io.netty.buffer.Unpooled;
import lombok.Data;
import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ServerSidePacket;
/**
* Данные чанка.
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |--------------------------|------------- |------------------------------------------------------------------------------------|
* | Chunk X | Integer | Координаты чанка (координата блока, делённая на 16, округленная в меньшую сторону) |
* | Chunk Z | Integer | Координаты чанка (координата блока, делённая на 16, округленная в меньшую сторону) |
* | Is Full chunk | Boolean | См. Chunk Format |
* | Available Sections | VarInt | Битовая маска, где каждый бит - это часть чанка (0-15) |
* | Size of Data | VarInt | Размер поля "Data" |
* | Data | Byte array | Данные чанка. См. Chunk Format |
* | Number of block entities | VarInt | Количество элементов в поле "Block entities" |
* | Block entities | Array of NBT | Все сущности в чанке |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Chunk_Data">Chunk Data</a>
* @see <a href="https://wiki.vg/index.php?title=Chunk_Format&oldid=14135">Chunk Format</a>
*/
@Data
public class ChunkDataPacket implements ServerSidePacket {
private int x;
private int z;
@Override
public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeInt(x);
netByteBuf.writeInt(z);
netByteBuf.writeBoolean(true); // Is Full chunk
netByteBuf.writeVarInt(0b11111111); // Available Sections
NetByteBuf data = new NetByteBuf(Unpooled.buffer());
// <Data>
for (int i = 0; i < 16; i++) {
NetByteBuf dataBuff = new NetByteBuf(Unpooled.wrappedBuffer(new byte[4096]));
NetByteBuf blockLight = new NetByteBuf(Unpooled.wrappedBuffer(new byte[2048]));
NetByteBuf skyLight = new NetByteBuf(Unpooled.wrappedBuffer(new byte[2048]));
NetByteBuf biomes = new NetByteBuf(Unpooled.wrappedBuffer(new byte[256]));
// <Chunk Section>
data.writeUnsignedByte(13); // Bits Per Block
// <Palette>
data.writeUnsignedByte(0); // Palette Length (for direct)
// <Palette Data/>
// </Palette>
data.writeVarInt(dataBuff.readableBytes()); // Data Array Length
data.writeBytes(dataBuff); // Data Array
data.writeBytes(blockLight); // Block Light
data.writeBytes(skyLight); // Sky Light
// </Chunk Section>
data.writeBytes(biomes); // Biomes
}
// </Data>
netByteBuf.writeVarInt(data.readableBytes()); // Size of Data
netByteBuf.writeBytes(data); // Data
netByteBuf.writeVarInt(0); // Number of block entities
/* write NBT's */
}
}

View File

@@ -1,39 +0,0 @@
package mc.protocol.packets.server;
import lombok.Data;
import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ServerSidePacket;
/**
* Отображение Scoreboard.
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |------------|-------------|--------------------------------|
* | Position | Byte | Положение: |
* | | | 0 - list |
* | | | 1 - sidebar |
* | | | 2 - below name |
* | | | 3-18 - team specific sidebar |
* | Score Name | String (16) | Уникальное название Scoreboard |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Display_Scoreboard" target="_top">Display Scoreboard</a>
*/
@Data
public class ScoreboardDisplayPacket implements ServerSidePacket {
private int position;
private String scoreName;
public void setPosition(int position) {
this.position = (position < 0) ? 0 : (Math.min(position, 18));
}
@Override
public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeByte(this.position);
netByteBuf.writeString(this.scoreName);
}
}

View File

@@ -1,44 +0,0 @@
package mc.protocol.packets.server;
import lombok.Data;
import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ServerSidePacket;
import mc.protocol.utils.ScoreboardObjectiveMode;
import mc.protocol.utils.ScoreboardObjectiveType;
/**
* Scoreboard objective packet.
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |-----------------|-------------|---------------------------------------------------|
* | Objective Name | String (16) | Уникальное наименование цели (objective) |
* | Mode | Byte | 0 - создание Scoreboard |
* | | | 1 - удаление Scoreboard |
* | | | 2 - обновление Scoreboard |
* | Objective Value | String (32) | Если "Mode" равен 0 или 2. Отображаемый текст |
* | Type | String (16) | Если "Mode" равен 0 или 2. "integer" или "hearts" |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Scoreboard_Objective" target="_top">Scoreboard Objective</a>
*/
@Data
public class ScoreboardObjectivePacket implements ServerSidePacket {
private String objectiveName;
private ScoreboardObjectiveMode mode;
private String objectiveValue;
private ScoreboardObjectiveType type;
@Override
public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeString(this.objectiveName);
netByteBuf.writeByte(this.mode.getCode());
if (ScoreboardObjectiveMode.CREATE.equals(this.mode) || ScoreboardObjectiveMode.UPDATE.equals(this.mode)) {
netByteBuf.writeString(this.objectiveValue);
netByteBuf.writeString(this.type.name().toLowerCase());
}
}
}

View File

@@ -1,43 +0,0 @@
package mc.protocol.packets.server;
import lombok.Data;
import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ServerSidePacket;
import mc.protocol.utils.ScoreboardUpdateScoreAction;
/**
* Update score packet.
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |-----------------|-------------|--------------------------------------------------- |
* | Entity Name | String (40) | Сущность, которой принадлежит счет (score). |
* | | | Для Игроков - это ник |
* | | | Для других сущностей - это UUID |
* | Action | Byte | 0 - создать или обновить счет (score); 1 - удалить |
* | Objective Name | String (16) | Имя сущности, которой принадлежит счет (score) |
* | Value | VarInt | Если "Action" = 0. Значение счета (score) |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Update_Score" target="_top">Update Score</a>
*/
@Data
public class ScoreboardUpdateScorePacket implements ServerSidePacket {
private String entityName;
private ScoreboardUpdateScoreAction action;
private String objective;
private int value;
@Override
public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeString(this.entityName);
netByteBuf.writeByte(this.action.getCode());
netByteBuf.writeString(this.objective);
if (ScoreboardUpdateScoreAction.CREATE_OR_UPDATE.equals(this.action)) {
netByteBuf.writeVarInt(this.value);
}
}
}

View File

@@ -1,119 +0,0 @@
package mc.protocol.packets.server;
import lombok.Data;
import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ServerSidePacket;
import mc.protocol.utils.TeamsCollisionRule;
import mc.protocol.utils.TeamsMode;
import mc.protocol.utils.TeamsNameTagVisibility;
import java.util.ArrayList;
import java.util.List;
/**
* Teams packet.
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |------------ |-------------|-------------------------------------------------------|
* | Team Name | String (16) | Уникальное название команды (совместно со scoreboard) |
* | Mode | Byte | Режим. Определяет остальые поля пакета |
* | Data Fields | - | Определяется "Mode" |
* </pre>
*
* <p>Варианты "Mode"</p>
* <pre>
* | MODE | DATA FIELD | TYPE | NOTES |
* | VALUE | DESCRIPTION | | | |
* |-------|--------------------------|---------------------|----------------------|---------------------------------------------------|
* | 0 | create team | Team Display Name | String (32) | |
* | | | Team Prefix | String (16) | Отображается перед именем игроков текущей команды |
* | | | Team Suffix | String (16) | Отображается после имени игроков текущей команды |
* | | | Friendly Flags | Byte | Битовая маска: |
* | | | | | 0x01 - разрешён friendly fire |
* | | | | | 0x02 - могут видеть невидимок своей команды |
* | | | Name Tag Visibility | String (32) | фиксированные значения: |
* | | | | | - always |
* | | | | | - hideForOtherTeams |
* | | | | | - hideForOwnTeam |
* | | | | | - never |
* | | | Collision Rule | String (32) | фиксированные значения: |
* | | | | | - always |
* | | | | | - pushOtherTeams |
* | | | | | - pushOwnTeam |
* | | | | | - never |
* | | | Color | Byte | For colors, the same Chat colors (0-15). |
* | | | | | -1 indicates RESET/no color. |
* | | | Entity Count | VarInt | Количество элементов в поле "Entities" |
* | | | Entities | Array of String (40) | Уникальные идентификаторы участников команды. |
* | | | | | Для Игроков - это Имена |
* | | | | | Для любых других сущностей - это UUID |
* | 1 | remove team | - | - | удаление текущей команды |
* | 2 | update team info | Team Display Name | String (32) | |
* | | | Team Prefix | String (16) | (см. выше) |
* | | | Team Suffix | String (16) | (см. выше) |
* | | | Friendly Flags | Byte | (см. выше) |
* | | | Name Tag Visibility | String (32) | (см. выше) |
* | | | Collision Rule | String (32) | (см. выше) |
* | | | Color | Byte | (см. выше) |
* | 3 | add players to team | Entity Count | VarInt | (см. выше) |
* | | | Entities | Array of String (40) | (см. выше) |
* | 4 | remove players from team | Entity Count | VarInt | (см. выше) |
* | | | Entities | Array of String (40) | (см. выше) |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Teams" target="_top">Teams</a>
*/
@Data
public class TeamsPacket implements ServerSidePacket {
private final List<String> members = new ArrayList<>();
private String name;
private TeamsMode mode;
private String displayName;
private String prefix;
private String suffix;
private TeamsNameTagVisibility nameTagVisibility;
private TeamsCollisionRule collisionRule;
private int color;
@Override
public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeString(this.name);
netByteBuf.writeByte(this.mode.getCode());
switch (this.mode) {
case CREATE:
netByteBuf.writeString(this.displayName);
netByteBuf.writeString(this.prefix);
netByteBuf.writeString(this.suffix);
netByteBuf.writeByte(0); // Friendly Flags
netByteBuf.writeString(this.nameTagVisibility.getCode());
netByteBuf.writeString(this.collisionRule.getCode());
netByteBuf.writeByte(this.color);
netByteBuf.writeVarInt(this.members.size());
this.members.forEach(netByteBuf::writeString);
break;
case UPDATE:
netByteBuf.writeString(this.displayName);
netByteBuf.writeString(this.prefix);
netByteBuf.writeString(this.suffix);
netByteBuf.writeByte(0); // Friendly Flags
netByteBuf.writeString(this.nameTagVisibility.getCode());
netByteBuf.writeString(this.collisionRule.getCode());
netByteBuf.writeByte(this.color);
break;
case ADD_MEMBER:
case REMOVE_MEMBER:
netByteBuf.writeVarInt(this.members.size());
this.members.forEach(netByteBuf::writeString);
break;
case REMOVE:
default:
break;
}
}
}

View File

@@ -0,0 +1,34 @@
package mc.protocol.packets.status.client;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import mc.protocol.buffer.NetByteBuf;
import mc.protocol.packets.ClientSidePacket;
import mc.protocol.packets.ServerSidePacket;
/**
* Status server packet, request.
*
* <p>Клиент запрашивает получение информации о сервере</p>
*/
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@ToString
public class StatusServerRequestPacket implements ClientSidePacket, ServerSidePacket {
@Override
public void readSelf(NetByteBuf netByteBuf) {
// empty
}
@Override
public void writeSelf(NetByteBuf netByteBuf) {
// empty
}
@Override
public void passivate() {
// pass
}
}

View File

@@ -1,10 +1,8 @@
package mc.protocol.packets.server; package mc.protocol.packets.status.server;
import lombok.Data; import lombok.Data;
import mc.protocol.io.NetByteBuf; import mc.protocol.buffer.NetByteBuf;
import mc.protocol.model.ServerInfo;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;
import mc.protocol.serializer.ServerInfoSerializer;
/** /**
* Status server packet, response. * Status server packet, response.
@@ -52,10 +50,10 @@ public class StatusServerResponse implements ServerSidePacket {
/** /**
* Информация о серере. * Информация о серере.
*/ */
private ServerInfo info; private String info;
@Override @Override
public void writeSelf(NetByteBuf netByteBuf) { public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeString(ServerInfoSerializer.toJsonObject(info).toString()); netByteBuf.writeString(info);
} }
} }

View File

@@ -0,0 +1,39 @@
package mc.protocol.pool;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import lombok.SneakyThrows;
import mc.protocol.buffer.NetByteBuf;
import mc.utils.pool.ObjectPool;
import mc.utils.pool.PassivablePooledObjectFactory;
import org.apache.commons.pool2.impl.GenericObjectPool;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class NetByteBufObjectPool implements ObjectPool<NetByteBuf> {
private final org.apache.commons.pool2.ObjectPool apacheObjectPool;
NetByteBufObjectPool() {
this.apacheObjectPool = new GenericObjectPool<>(new PassivablePooledObjectFactory<>(PooledNetByteBuf.class));
}
@Override
public NetByteBuf borrowObject() {
return borrowObject(PooledByteBufAllocator.DEFAULT.directBuffer());
}
@SneakyThrows
public NetByteBuf borrowObject(ByteBuf byteBuf) {
PooledNetByteBuf pooledNetByteBuf = (PooledNetByteBuf) apacheObjectPool.borrowObject();
pooledNetByteBuf.init(byteBuf);
return pooledNetByteBuf;
}
@Override
@SneakyThrows
public void returnObject(NetByteBuf netByteBuf) {
if (netByteBuf instanceof PooledNetByteBuf) {
apacheObjectPool.returnObject(netByteBuf);
}
}
}

View File

@@ -1,24 +0,0 @@
package mc.protocol.pool;
import mc.protocol.NettyConnectionContext;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
public class NettyConnectionContextFactory extends BasePooledObjectFactory<NettyConnectionContext> {
@Override
public NettyConnectionContext create() throws Exception {
return new NettyConnectionContext();
}
@Override
public PooledObject<NettyConnectionContext> wrap(NettyConnectionContext context) {
return new DefaultPooledObject<>(context);
}
@Override
public void passivateObject(PooledObject<NettyConnectionContext> pooledObj) {
pooledObj.getObject().passivate();
}
}

View File

@@ -1,30 +0,0 @@
package mc.protocol.pool;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import mc.protocol.packets.ClientSidePacket;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
@Slf4j
@RequiredArgsConstructor
public class PacketFactory<P extends ClientSidePacket> extends BasePooledObjectFactory<P> {
private final Class<P> clazz;
@Override
public P create() throws Exception {
return clazz.getDeclaredConstructor().newInstance();
}
@Override
public PooledObject<P> wrap(P packet) {
return new DefaultPooledObject<>(packet);
}
@Override
public void passivateObject(PooledObject<P> pooledPacket) {
pooledPacket.getObject().passivate();
}
}

View File

@@ -1,26 +0,0 @@
package mc.protocol.pool;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import mc.protocol.packets.ClientSidePacket;
import org.apache.commons.pool2.ObjectPool;
import java.util.Map;
@Slf4j
@RequiredArgsConstructor
public class PacketPool {
@SuppressWarnings("rawtypes")
private final Map<Class<? extends ClientSidePacket>, ObjectPool> mapPoolPackets;
@SuppressWarnings("unchecked")
public <P extends ClientSidePacket> P borrowObject(Class<P> packetClass) throws Exception {
return (P) mapPoolPackets.get(packetClass).borrowObject();
}
@SuppressWarnings("unchecked")
public <P extends ClientSidePacket> void returnObject(P packet) throws Exception {
mapPoolPackets.get(packet.getClass()).returnObject(packet);
}
}

View File

@@ -0,0 +1,18 @@
package mc.protocol.pool;
import io.netty.buffer.ByteBuf;
import mc.protocol.buffer.NetByteBuf;
import mc.utils.pool.Passivable;
public class PooledNetByteBuf extends NetByteBuf implements Passivable {
void init(ByteBuf byteBuf) {
this.byteBuf = byteBuf.retain();
}
@Override
public void passivate() {
this.byteBuf.release();
this.byteBuf = null;
}
}

View File

@@ -0,0 +1,42 @@
package mc.protocol.pool;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import mc.protocol.model.ChunkSectionLocation;
import mc.protocol.model.Location;
import mc.protocol.model.Look;
import mc.protocol.packets.ClientSidePacket;
import mc.utils.pool.MultiObjectPool;
import mc.utils.pool.ObjectPool;
import mc.utils.pool.PassivableMultiObjectPool;
import mc.utils.pool.SimpleObjectPool;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ProtocolObjectPool {
private static final ObjectPool<Location> LOCATION_POOL = new SimpleObjectPool<>(Location.class);
private static final ObjectPool<ChunkSectionLocation> CHUNK_SECTION_LOCATION_POOL = new SimpleObjectPool<>(ChunkSectionLocation.class);
private static final ObjectPool<Look> LOOK_POOL = new SimpleObjectPool<>(Look.class);
private static final NetByteBufObjectPool NETBYTEBUF_POOL = new NetByteBufObjectPool();
private static final MultiObjectPool<ClientSidePacket> PACKET_POOL = new PassivableMultiObjectPool<>();
public static ObjectPool<Location> location() {
return LOCATION_POOL;
}
public static ObjectPool<ChunkSectionLocation> chunkSectionLocation() {
return CHUNK_SECTION_LOCATION_POOL;
}
public static ObjectPool<Look> look() {
return LOOK_POOL;
}
public static NetByteBufObjectPool netByteBuf() {
return NETBYTEBUF_POOL;
}
public static MultiObjectPool<ClientSidePacket> packet() {
return PACKET_POOL;
}
}

View File

@@ -0,0 +1,11 @@
package mc.protocol.pool;
import io.netty.buffer.ByteBuf;
import mc.protocol.buffer.NetByteBuf;
public class UnpooledNetByteBuf extends NetByteBuf {
public UnpooledNetByteBuf(ByteBuf byteBuf) {
this.byteBuf = byteBuf;
}
}

View File

@@ -11,10 +11,10 @@ public enum ChatMode {
public static ChatMode valueById(int id) { public static ChatMode valueById(int id) {
// а зачем усложнять? // а зачем усложнять?
//@formatter:off //@formatter:off
if (id == 1) return FULL; if (id == 1) return FULL;
else if (id == 2) return COMMANDS_ONLY; else if (id == 2) return COMMANDS_ONLY;
else if (id == 3) return HIDDEN; else if (id == 3) return HIDDEN;
else return null; else return null;
//@formatter:on //@formatter:on
} }
} }

View File

@@ -0,0 +1,32 @@
package mc.protocol.utils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import javax.annotation.Nullable;
@RequiredArgsConstructor
public enum DiggingStatus {
STARTED(0),
CANCELLED(1),
FINISHED(2),
DROP_ITEM_STACK(3),
DROP_ITEM(4),
HELT_UPDATE_STATE(5),
SWAP_HAND(6);
@Nullable
public static DiggingStatus valueById(int id) {
for (DiggingStatus diggingStatus : DiggingStatus.values()) {
if (diggingStatus.getId() == id) {
return diggingStatus;
}
}
return null;
}
@Getter
private final int id;
}

View File

@@ -0,0 +1,31 @@
package mc.protocol.utils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import javax.annotation.Nullable;
@RequiredArgsConstructor
public enum Face {
BOTTOM(0),
TOP(1),
NORTH(2),
SOUTH(3),
WEST(4),
EAST(5);
@Nullable
public static Face valueById(int id) {
for (Face face : Face.values()) {
if (face.getId() == id) {
return face;
}
}
return null;
}
@Getter
private final int id;
}

View File

@@ -2,17 +2,17 @@ package mc.protocol.utils;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public enum MainHand { public enum Hand {
LEFT, LEFT,
RIGHT; RIGHT;
@Nullable @Nullable
public static MainHand valueById(int id) { public static Hand valueById(int id) {
// а зачем усложнять? // а зачем усложнять?
//@formatter:off //@formatter:off
if (id == 0) return LEFT; if (id == 0) return LEFT;
else if (id == 1) return RIGHT; else if (id == 1) return RIGHT;
else return null; else return null;
//@formatter:on //@formatter:on
} }
} }

View File

@@ -0,0 +1,52 @@
package mc.protocol.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import mc.protocol.model.BlockLocation;
import mc.protocol.model.ChunkSectionLocation;
import mc.protocol.model.Location;
import mc.protocol.pool.ProtocolObjectPool;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class LocationUtils {
//region Unpooled
public static ChunkSectionLocation location2chunk(Location location) {
var chunkSectionLocation = new ChunkSectionLocation();
chunkSectionLocation.set(
(int) location.getX() >> 4,
(int) location.getY() >> 4,
(int) location.getZ() >> 4);
return chunkSectionLocation;
}
public static ChunkSectionLocation location2chunk(BlockLocation blockLocation) {
var chunkSectionLocation = new ChunkSectionLocation();
chunkSectionLocation.set(
blockLocation.getX() >> 4,
blockLocation.getY() >> 4,
blockLocation.getZ() >> 4);
return chunkSectionLocation;
}
//endregion
//region Pooled
public static ChunkSectionLocation location2chunkPooled(Location location) {
ChunkSectionLocation chunkSectionLocation = ProtocolObjectPool.chunkSectionLocation().borrowObject();
chunkSectionLocation.set(
(int) location.getX() >> 4,
(int) location.getY() >> 4,
(int) location.getZ() >> 4);
return chunkSectionLocation;
}
public static ChunkSectionLocation location2chunkPooled(BlockLocation blockLocation) {
ChunkSectionLocation chunkSectionLocation = ProtocolObjectPool.chunkSectionLocation().borrowObject();
chunkSectionLocation.set(
blockLocation.getX() >> 4,
blockLocation.getY() >> 4,
blockLocation.getZ() >> 4);
return chunkSectionLocation;
}
//endregion
}

View File

@@ -1,14 +0,0 @@
package mc.protocol.utils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum ScoreboardObjectiveMode {
CREATE(0),
REMOVE(1),
UPDATE(2);
@Getter
private final int code;
}

View File

@@ -1,6 +0,0 @@
package mc.protocol.utils;
public enum ScoreboardObjectiveType {
INTEGER,
HEARTS
}

View File

@@ -1,10 +0,0 @@
package mc.protocol.utils;
import lombok.experimental.UtilityClass;
@UtilityClass
public class ScoreboardPosition {
public final int LIST = 0;
public final int SIDEBAR = 1;
public final int BELOW_NAME = 2;
}

View File

@@ -1,13 +0,0 @@
package mc.protocol.utils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum ScoreboardUpdateScoreAction {
CREATE_OR_UPDATE(0),
REMOVE(1);
@Getter
private final int code;
}

View File

@@ -0,0 +1,77 @@
package mc.protocol.utils;
import io.netty.buffer.Unpooled;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import mc.protocol.buffer.NetByteBuf;
import mc.protocol.model.BlockLocation;
import mc.protocol.pool.ProtocolObjectPool;
import mc.protocol.world.Block;
import mc.protocol.world.ChunkSection;
import mc.utils.array.BitArray;
import mc.utils.array.BitByteArray;
import mc.utils.array.BitLongArray;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class SerializeUtil {
private static final int BITS_PER_BLOCK = 13;
private static final int ALL_BLOCKS = 16 * 16 * 16;
public static NetByteBuf serializeChunkSection(ChunkSection section) {
BitArray blockArray = new BitLongArray(BITS_PER_BLOCK, ALL_BLOCKS);
BitArray blockLight = new BitByteArray(4, ALL_BLOCKS);
BitArray skyLight = new BitByteArray(4, ALL_BLOCKS);
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
Block block = section.getBlock(x, y, z);
int blockState = blockIdMetaSerialize(block.getId(), block.getMeta());
blockArray.put(blockState);
blockLight.put(block.getLight());
skyLight.put(section.getSkyLight(x, y, z));
}
}
}
NetByteBuf result = ProtocolObjectPool.netByteBuf().borrowObject(Unpooled.buffer());
result.writeUnsignedByte(BITS_PER_BLOCK); // Bits Per Block
result.writeVarInt(0); // Palette, Direct mode
result.writeVarInt(blockArray.size()); // Data Array Length
result.writeBytes(blockArray.byteBuffer()); // Data Array
result.writeBytes(blockLight.byteBuffer()); // Block Light
result.writeBytes(skyLight.byteBuffer()); // Sky Light
return result;
}
public static int blockIdMetaSerialize(int id, int meta) {
return (id << 4) | meta;
}
public static int[] blockIdMetaDeserialize(int blockState) {
return new int[]{
blockState >> 4,
blockState & 0b1111
};
}
public static long location2long(BlockLocation blockLocation) {
return ((long) (blockLocation.getX() & 0x3FFFFFF) << 38)
| ((long) (blockLocation.getZ() & 0x3FFFFFF) << 12)
| (blockLocation.getY() & 0xFFF);
}
public static BlockLocation long2location(long value) {
BlockLocation blockLocation = new BlockLocation();
blockLocation.set(
(int) (value >> 38),
(int) (value & 0xFFF),
(int) (value << 26 >> 38)
);
return blockLocation;
}
}

View File

@@ -1,15 +0,0 @@
package mc.protocol.utils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum TeamsCollisionRule {
ALWAYS("always"),
PUSH_OTHER_TEAMS("pushOtherTeams"),
PUSH_OWN_TEAM("pushOwnTeam"),
NEVER("never");
@Getter
private final String code;
}

View File

@@ -1,16 +0,0 @@
package mc.protocol.utils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum TeamsMode {
CREATE(0),
REMOVE(1),
UPDATE(2),
ADD_MEMBER(3),
REMOVE_MEMBER(4);
@Getter
private final int code;
}

View File

@@ -1,15 +0,0 @@
package mc.protocol.utils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum TeamsNameTagVisibility {
ALWAYS("always"),
HIDE_FOR_OTHER_TEAMS("hideForOtherTeams"),
HIDE_FOR_OWN_TEAM("hideForOwnTeam"),
NEVER("never");
@Getter
private final String code;
}

View File

@@ -0,0 +1,14 @@
package mc.protocol.world;
import mc.protocol.model.BlockLocation;
public interface Block {
int getId();
int getMeta();
BlockLocation getLocation();
int getLight();
void setLight(int value);
}

View File

@@ -0,0 +1,10 @@
package mc.protocol.world;
public interface Chunk {
int getX();
int getZ();
ChunkSection getSection(int height);
byte getBiome(int x, int z);
}

View File

@@ -0,0 +1,14 @@
package mc.protocol.world;
import mc.protocol.model.BlockLocation;
public interface ChunkSection {
int getY();
Block getBlock(int x, int y, int z);
Block getBlock(BlockLocation blockLocation);
int getSkyLight(int x, int y, int z);
int getSkyLight(BlockLocation blockLocation);
}

View File

@@ -0,0 +1,15 @@
package mc.protocol.world;
import mc.protocol.model.ChunkSectionLocation;
import mc.protocol.model.Location;
import mc.protocol.utils.LevelType;
public interface World {
LevelType getLevelType();
Location getSpawn();
Chunk getChunk(int x, int z);
Chunk getChunk(ChunkSectionLocation chunkSectionLocation);
}

Some files were not shown because too many files have changed in this diff Show More