Archived
0

5 Commits

Author SHA1 Message Date
c705a2090d Scoreboard works 2021-05-08 17:58:15 +03:00
6dbf785d22 ScoreboardUpdateScorePacket 2021-05-08 17:01:32 +03:00
050130be60 ScoreboardObjectivePacket 2021-05-08 15:36:10 +03:00
9ccb9a6221 TeamsPacket 2021-05-08 04:48:28 +03:00
fda80d9f9d ScoreboardDisplayPacket 2021-05-06 17:05:20 +03:00
165 changed files with 1753 additions and 3508 deletions

View File

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

View File

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

View File

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

View File

@@ -1,17 +0,0 @@
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

@@ -1,52 +0,0 @@
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

@@ -1,25 +0,0 @@
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

@@ -1,73 +0,0 @@
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 @@
prj.group=mc-project project.group=mc-project
prj.name=mc-server project.name=mc-server
prj.version=1.1 project.version=1.1-SNAPSHOT

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.1.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -13,20 +13,16 @@ 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',
hocon : 'com.typesafe:config:1.4.1', yaml : 'org.yaml:snakeyaml:1.28',
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,28 +1,21 @@
//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', 'prj.group') project.group = getProperty1('module.group', 'project.group')
project.version = getProperty1('module.version', 'prj.version') project.version = getProperty1('module.version', 'project.version')
jar.archiveBaseName.set(getProperty1('module.name', 'prj.name')) jar.archiveBaseName.set(getProperty1('module.name', 'project.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()
@@ -38,18 +31,13 @@ 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,11 +1,9 @@
apply from: rootDir.toPath().resolve('logic.gradle').toFile() apply from: rootDir.toPath().resolve('logic.gradle').toFile()
dependencies { dependencies {
api project(':utils') implementation libs.netty
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

@@ -0,0 +1,52 @@
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

@@ -0,0 +1,103 @@
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

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

View File

@@ -0,0 +1,28 @@
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

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

View File

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

View File

@@ -0,0 +1,16 @@
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

@@ -0,0 +1,18 @@
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,8 +0,0 @@
package mc.protocol.buffer;
public class NetIOException extends RuntimeException {
public NetIOException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,42 @@
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

@@ -0,0 +1,14 @@
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

@@ -0,0 +1,57 @@
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

@@ -0,0 +1,10 @@
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

@@ -0,0 +1,17 @@
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

@@ -0,0 +1,26 @@
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

@@ -1,31 +0,0 @@
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

@@ -1,42 +0,0 @@
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

@@ -1,67 +0,0 @@
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

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

View File

@@ -1,4 +1,4 @@
package mc.protocol.buffer; package mc.protocol.io;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@@ -6,8 +6,6 @@ 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;
@@ -34,33 +32,29 @@ 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 байт |
* | Text | | JSON | По файту является String (n), который имеет формат JSON | * | Position | 8 | 64-bit число разделённое на три части: x, y, z | Кодируется формулой: |
* | | | | ((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=Data_types&oldid=14345#Definitions">Data types</a> * @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=Chat&oldid=14272">Chat</a>
*/ */
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@ToString @ToString
public abstract class NetByteBuf extends ByteBuf { public class NetByteBuf extends ByteBuf {
@Delegate @Delegate
protected ByteBuf byteBuf; private final 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);
@@ -73,9 +67,9 @@ public abstract class NetByteBuf extends ByteBuf {
if (length == 0) { if (length == 0) {
return ""; return "";
} else if (length > maxLength) { } else if (length > maxLength) {
throw new NetIOException("String length exceeds maximum length: " + length + " > " + maxLength); throw new DecoderException("String length exceeds maximum length: " + length + " > " + maxLength);
} else if (length < 0) { } else if (length < 0) {
throw new NetIOException("String length less zero!"); throw new DecoderException("String length less zero!");
} }
byte[] bytes = new byte[length * 4]; byte[] bytes = new byte[length * 4];
@@ -105,6 +99,11 @@ public abstract 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) {
@@ -120,7 +119,22 @@ public abstract class NetByteBuf extends ByteBuf {
//region VarInt //region VarInt
public int readVarInt() { public int readVarInt() {
return readVarInt(this.byteBuf); int numRead = 0;
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) {
@@ -174,23 +188,4 @@ public abstract 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,89 @@
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

@@ -1,42 +1,35 @@
package mc.protocol.handler.codec; package mc.protocol.io.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.ProtocolAttributes; import mc.protocol.NetworkAttributes;
import mc.protocol.State; import mc.protocol.State;
import mc.protocol.buffer.NetByteBuf; import mc.protocol.io.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 = Objects.requireNonNull(ctx.channel().attr(ProtocolAttributes.STATE).get()); State state = ctx.channel().attr(NetworkAttributes.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;
} }
if (log.isDebugEnabled()) { log.debug("OUT: {}:{}", state, packet);
log.debug("OUT: {}:{}", state, packet);
}
NetByteBuf buffer = ProtocolObjectPool.netByteBuf().borrowObject(); NetByteBuf buffer = new NetByteBuf(Unpooled.buffer());
buffer.writeVarInt(packetId); buffer.writeVarInt(packetId);
packet.writeSelf(buffer); packet.writeSelf(buffer);
NetByteBuf netByteBuf = ProtocolObjectPool.netByteBuf().borrowObject(out); NetByteBuf netByteBuf = new NetByteBuf(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,11 +1,10 @@
package mc.protocol.handler.codec; package mc.protocol.io.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.buffer.NetByteBuf; import mc.protocol.io.NetByteBuf;
import mc.protocol.pool.ProtocolObjectPool;
import java.util.List; import java.util.List;
@@ -13,7 +12,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 = ProtocolObjectPool.netByteBuf().borrowObject(in); NetByteBuf netByteBuf = new NetByteBuf(in);
netByteBuf.markReaderIndex(); netByteBuf.markReaderIndex();
do { do {
@@ -26,7 +25,7 @@ public class ProtocolSplitter extends ByteToMessageDecoder {
} }
} }
int sizePacket = NetByteBuf.readVarInt(Unpooled.wrappedBuffer(sizePacketRaw)); int sizePacket = new NetByteBuf(Unpooled.wrappedBuffer(sizePacketRaw)).readVarInt();
if (netByteBuf.readableBytes() >= sizePacket) { if (netByteBuf.readableBytes() >= sizePacket) {
byte[] bytes = new byte[sizePacket]; byte[] bytes = new byte[sizePacket];
@@ -37,7 +36,5 @@ public class ProtocolSplitter extends ByteToMessageDecoder {
break; break;
} }
} while (netByteBuf.readableBytes() > 0); } while (netByteBuf.readableBytes() > 0);
ProtocolObjectPool.netByteBuf().returnObject(netByteBuf);
} }
} }

View File

@@ -1,11 +0,0 @@
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

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

View File

@@ -1,26 +1,11 @@
package mc.protocol.model; package mc.protocol.model;
import lombok.EqualsAndHashCode; import lombok.AllArgsConstructor;
import lombok.ToString; import lombok.Data;
import mc.utils.vector.Vector2f;
@EqualsAndHashCode(callSuper = true) @AllArgsConstructor
@ToString(callSuper = true) @Data
public class Look extends Vector2f { public class Look {
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,7 +1,7 @@
package mc.protocol.packets; package mc.protocol.packets;
import mc.protocol.buffer.NetByteBuf; import mc.protocol.io.NetByteBuf;
import mc.utils.pool.Passivable; import mc.protocol.pool.Passivable;
/** /**
* Пакеты отправляемые клиентом. * Пакеты отправляемые клиентом.

View File

@@ -0,0 +1,21 @@
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,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,13 +1,20 @@
package mc.protocol.packets; package mc.protocol.packets;
import lombok.Data; import lombok.Data;
import mc.protocol.buffer.NetByteBuf; import mc.protocol.io.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 |
@@ -15,19 +22,12 @@ import mc.protocol.buffer.NetByteBuf;
* | Payload | Long | Любое уникальное число | * | Payload | Long | Любое уникальное число |
* </pre> * </pre>
* *
* <p>По спецификации:</p> * @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=7368#Keep_Alive">Keep Alive</a>
* <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 KeepAlivePacket implements ClientSidePacket, ServerSidePacket { public class PingPacket implements ClientSidePacket, ServerSidePacket {
private long payload; private Long payload;
@Override @Override
public void readSelf(NetByteBuf netByteBuf) { public void readSelf(NetByteBuf netByteBuf) {
@@ -35,12 +35,12 @@ public class KeepAlivePacket implements ClientSidePacket, ServerSidePacket {
} }
@Override @Override
public void writeSelf(NetByteBuf netByteBuf) { public void passivate() {
netByteBuf.writeLong(payload); this.payload = null;
} }
@Override @Override
public void passivate() { public void writeSelf(NetByteBuf netByteBuf) {
this.payload = 0; netByteBuf.writeLong(payload);
} }
} }

View File

@@ -1,6 +1,6 @@
package mc.protocol.packets; package mc.protocol.packets;
import mc.protocol.buffer.NetByteBuf; import mc.protocol.io.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.buffer.NetByteBuf; import mc.protocol.io.NetByteBuf;
@NoArgsConstructor @NoArgsConstructor
@Data @Data

View File

@@ -1,14 +1,13 @@
package mc.protocol.packets.play.client; package mc.protocol.packets.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.buffer.NetByteBuf; import mc.protocol.io.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;
/** /**
* Клиент сообщает о движении и повороте головы Игрока. * Клиент сообщает о движении и повороте головы Игрока.
@@ -43,27 +42,19 @@ 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 = ProtocolObjectPool.location().borrowObject(); this.position = new Location(x, y, z);
position.set(x, y, z);
float yaw = netByteBuf.readFloat(); float yaw = netByteBuf.readFloat();
float pitch = netByteBuf.readFloat(); float pitch = netByteBuf.readFloat();
this.look = ProtocolObjectPool.look().borrowObject(); this.look = new Look(yaw, pitch);
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.play.client; package mc.protocol.packets.client;
import lombok.*; import lombok.*;
import mc.protocol.utils.ChatMode; import mc.protocol.utils.ChatMode;
import mc.protocol.utils.Hand; import mc.protocol.utils.MainHand;
import mc.protocol.buffer.NetByteBuf; import mc.protocol.io.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 Hand mainHand; private MainHand 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 = Hand.valueById(netByteBuf.readVarInt()); this.mainHand = MainHand.valueById(netByteBuf.readVarInt());
} }
@Override @Override

View File

@@ -1,11 +1,11 @@
package mc.protocol.packets.handshaking.client; package mc.protocol.packets.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.buffer.NetByteBuf; import mc.protocol.io.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 (255) | Hostname или IP | * | Server address | Stirng | Hostname или IP |
* | Server port | Unsigned Short | Порт сервера | * | Server port | Unsigned Short | Порт сервера |
* | Next state | VarInt | ID State на который необходимо переключиться | * | Next stage | VarInt | ID State на который необходимо переключиться |
* *
* [1] - <a href="https://wiki.vg/Protocol_version_numbers">Protocol version numbers</a> * [1] - <a href="https://wiki.vg/Protocol_version_numbers" target="_top">Protocol version numbers</a>
* </pre> * </pre>
* *
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Handshake">Handshake</a> * @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=7368#Handshake" target="_top">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) {
this.protocolVersion = netByteBuf.readVarInt(); protocolVersion = netByteBuf.readVarInt();
this.host = netByteBuf.readString(255); host = netByteBuf.readString(255);
this.port = netByteBuf.readUnsignedShort(); port = netByteBuf.readUnsignedShort();
this.nextState = State.getById(netByteBuf.readVarInt()); nextState = State.getById(netByteBuf.readVarInt());
} }
@Override @Override
@@ -54,4 +54,5 @@ 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.login.client; package mc.protocol.packets.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.buffer.NetByteBuf; import mc.protocol.io.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=14204#Login_Start">Login start</a> * @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=7368#Login_Start" target="_top">Login start</a>
*/ */
@NoArgsConstructor @NoArgsConstructor
@Getter @Getter

View File

@@ -1,13 +1,12 @@
package mc.protocol.packets.play.client; package mc.protocol.packets.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.buffer.NetByteBuf; import mc.protocol.io.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;
/** /**
* Клиент сообщает о повороте головы Игрока. * Клиент сообщает о повороте головы Игрока.
@@ -36,18 +35,14 @@ 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 = ProtocolObjectPool.look().borrowObject(); this.look = new Look(yaw, pitch);
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

@@ -1,13 +1,12 @@
package mc.protocol.packets.play.client; package mc.protocol.packets.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.buffer.NetByteBuf; import mc.protocol.io.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;
/** /**
* Клиент сообщает о движении Игрока. * Клиент сообщает о движении Игрока.
@@ -39,18 +38,14 @@ 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 = ProtocolObjectPool.location().borrowObject(); this.position = new Location(x, y, z);
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.play.client; package mc.protocol.packets.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.buffer.NetByteBuf; import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
/** /**

View File

@@ -0,0 +1,18 @@
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,20 +1,20 @@
package mc.protocol.packets.play.client; package mc.protocol.packets.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.buffer.NetByteBuf; import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
import mc.protocol.packets.play.server.SPlayerPositionAndLookPacket; import mc.protocol.packets.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

@@ -1,68 +0,0 @@
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,77 +0,0 @@
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

@@ -1,42 +0,0 @@
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

@@ -1,78 +0,0 @@
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

@@ -1,82 +0,0 @@
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,39 +0,0 @@
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,121 +0,0 @@
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

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

View File

@@ -1,10 +1,10 @@
package mc.protocol.packets.play.server; package mc.protocol.packets.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.buffer.NetByteBuf; import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;
/** /**

View File

@@ -1,7 +1,7 @@
package mc.protocol.packets.login.server; package mc.protocol.packets.server;
import lombok.Data; import lombok.Data;
import mc.protocol.buffer.NetByteBuf; import mc.protocol.io.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(this.uuid.toString()); netByteBuf.writeString(uuid.toString());
netByteBuf.writeString(this.name); netByteBuf.writeString(name);
} }
} }

View File

@@ -1,7 +1,7 @@
package mc.protocol.packets.play.server; package mc.protocol.packets.server;
import lombok.Data; import lombok.Data;
import mc.protocol.buffer.NetByteBuf; import mc.protocol.io.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 SPlayerAbilitiesPacket implements ServerSidePacket { public class PlayerAbilitiesPacket 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.play.server; package mc.protocol.packets.server;
import lombok.Data; import lombok.Data;
import mc.protocol.buffer.NetByteBuf; import mc.protocol.io.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.play.client.TeleportConfirmPacket; import mc.protocol.packets.client.TeleportConfirmPacket;
/** /**
* Установка позиции и угла осмотра Игрока. * Установка позиции и угла осмотра Игрока.

View File

@@ -0,0 +1,39 @@
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

@@ -0,0 +1,44 @@
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

@@ -0,0 +1,43 @@
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,7 +1,7 @@
package mc.protocol.packets.play.server; package mc.protocol.packets.server;
import lombok.Data; import lombok.Data;
import mc.protocol.buffer.NetByteBuf; import mc.protocol.io.NetByteBuf;
import mc.protocol.model.Location; import mc.protocol.model.Location;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;

View File

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

View File

@@ -0,0 +1,119 @@
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

@@ -1,34 +0,0 @@
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,39 +0,0 @@
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

@@ -0,0 +1,24 @@
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

@@ -0,0 +1,30 @@
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

@@ -0,0 +1,26 @@
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

@@ -1,4 +1,4 @@
package mc.utils.pool; package mc.protocol.pool;
public interface Passivable { public interface Passivable {

View File

@@ -1,18 +0,0 @@
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

@@ -1,42 +0,0 @@
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

@@ -1,11 +0,0 @@
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

@@ -1,10 +1,10 @@
package mc.protocol.model; package mc.protocol.serializer;
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.TextSerializer; import mc.protocol.model.ServerInfo;
import java.util.Spliterator; import java.util.Spliterator;
import java.util.Spliterators; import java.util.Spliterators;
@@ -14,11 +14,7 @@ import java.util.stream.StreamSupport;
@UtilityClass @UtilityClass
public class ServerInfoSerializer { public class ServerInfoSerializer {
public String toStringPlain(ServerInfo info) { public JsonObject toJsonObject(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,9 +1,12 @@
package mc.protocol.model.text; package mc.protocol.serializer;
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;
@@ -13,10 +16,6 @@ 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

@@ -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

@@ -1,32 +0,0 @@
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

@@ -1,31 +0,0 @@
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

@@ -1,52 +0,0 @@
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

@@ -2,17 +2,17 @@ package mc.protocol.utils;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public enum Hand { public enum MainHand {
LEFT, LEFT,
RIGHT; RIGHT;
@Nullable @Nullable
public static Hand valueById(int id) { public static MainHand 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,14 @@
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

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

View File

@@ -0,0 +1,10 @@
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

@@ -0,0 +1,13 @@
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

@@ -1,77 +0,0 @@
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,4 +1,4 @@
package mc.utils; package mc.protocol.utils;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.HashMap; import java.util.HashMap;

View File

@@ -0,0 +1,15 @@
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

@@ -0,0 +1,16 @@
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

@@ -0,0 +1,15 @@
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

@@ -1,14 +0,0 @@
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

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

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