Archived
0

Merge branch 'dev/network-api' into develop

This commit is contained in:
2021-05-06 14:44:40 +03:00
40 changed files with 581 additions and 155 deletions

View File

@@ -19,7 +19,8 @@ ext {
yaml : 'org.yaml:snakeyaml:1.28', 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'
] ]
libs.logger = [ libs.logger = [

View File

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

View File

@@ -1,20 +0,0 @@
package mc.protocol;
import io.netty.channel.ChannelHandlerContext;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import mc.protocol.packets.ClientSidePacket;
@RequiredArgsConstructor
public class ChannelContext<P extends ClientSidePacket> {
@Getter
private final ChannelHandlerContext ctx;
@Getter
private final P packet;
public void setState(State state) {
ctx.channel().attr(NetworkAttributes.STATE).set(state);
}
}

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

@@ -1,22 +1,46 @@
package mc.protocol; package mc.protocol;
import io.netty.bootstrap.ServerBootstrap; 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.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import mc.protocol.di.DaggerProtocolComponent; import mc.protocol.api.ConnectionContext;
import mc.protocol.di.ProtocolComponent; 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 @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class NettyServer { public class NettyServer implements Server {
private final ServerBootstrap serverBootstrap; 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) { public void bind(String host, int port) {
log.info("Network starting: {}:{}", host, port); log.info("Network starting: {}:{}", host, port);
try { try {
serverBootstrap.bind(host, port).sync().channel().closeFuture().sync(); createServerBootstrap().bind(host, port).sync().channel().closeFuture().sync();
} catch (InterruptedException e) { } catch (InterruptedException e) {
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
log.trace("{}: {}", e.getClass().getSimpleName(), e.getMessage(), e); log.trace("{}: {}", e.getClass().getSimpleName(), e.getMessage(), e);
@@ -24,8 +48,56 @@ public class NettyServer {
} }
} }
public static NettyServer createServer() { @Override
ProtocolComponent component = DaggerProtocolComponent.create(); public void onNewConnect(Consumer<ConnectionContext> consumer) {
return component.getNettyServer(); 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,19 +4,25 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
import reactor.core.publisher.Sinks; import mc.protocol.event.EventBus;
import mc.protocol.pool.PacketPool;
import org.apache.commons.pool2.ObjectPool;
@RequiredArgsConstructor @RequiredArgsConstructor
public class PacketInboundHandler extends SimpleChannelInboundHandler<ClientSidePacket> { public class PacketInboundHandler extends SimpleChannelInboundHandler<ClientSidePacket> {
@SuppressWarnings("rawtypes") private final ObjectPool<NettyConnectionContext> poolNettyConnectionContext;
@Override private final PacketPool poolPackets;
protected void channelRead0(ChannelHandlerContext ctx, ClientSidePacket packet) { private final EventBus eventBus;
Sinks.Many<ChannelContext> packetSinks = ctx.channel().attr(NetworkAttributes.STATE)
.get().getPacketSinks(packet.getClass());
if (packetSinks != null) { @Override
packetSinks.tryEmitNext(new ChannelContext<>(ctx, packet)); 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

@@ -8,12 +8,9 @@ import mc.protocol.packets.PingPacket;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;
import mc.protocol.packets.client.*; import mc.protocol.packets.client.*;
import mc.protocol.packets.server.*; import mc.protocol.packets.server.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
@RequiredArgsConstructor @RequiredArgsConstructor
@@ -80,12 +77,10 @@ public enum State {
@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;
@SuppressWarnings("rawtypes")
private final Map<Class<? extends ClientSidePacket>, Sinks.Many<ChannelContext>> observedMap = new HashMap<>();
State(int id, Map<Integer, Class<? extends ClientSidePacket>> clientSidePackets) { State(int id, Map<Integer, Class<? extends ClientSidePacket>> clientSidePackets) {
this.id = id; this.id = id;
this.clientSidePackets = clientSidePackets; this.clientSidePackets = clientSidePackets;
@@ -101,16 +96,4 @@ public enum State {
public Integer getServerSidePacketId(Class<? extends Packet> clazz) { public Integer getServerSidePacketId(Class<? extends Packet> clazz) {
return serverSidePackets == null ? null : serverSidePackets.get(clazz); return serverSidePackets == null ? null : serverSidePackets.get(clazz);
} }
@SuppressWarnings("rawtypes")
public <P extends ClientSidePacket> Sinks.Many<ChannelContext> getPacketSinks(Class<P> packetClass) {
return observedMap.get(packetClass);
}
@SuppressWarnings("unchecked")
public <P extends ClientSidePacket> Flux<ChannelContext<P>> packetFlux(Class<P> packetClass) {
return observedMap.computeIfAbsent(packetClass, aClass -> Sinks.many().multicast().directBestEffort())
.asFlux().map(ChannelContext.class::cast);
}
} }

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

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

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

View File

@@ -2,68 +2,58 @@ package mc.protocol.di;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import io.netty.bootstrap.ServerBootstrap; import lombok.RequiredArgsConstructor;
import io.netty.channel.ChannelHandler; import mc.protocol.NettyConnectionContext;
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 mc.protocol.NettyServer; import mc.protocol.NettyServer;
import mc.protocol.PacketInboundHandler; 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.io.codec.ProtocolDecoder;
import mc.protocol.io.codec.ProtocolEncoder; import mc.protocol.pool.PacketPool;
import mc.protocol.io.codec.ProtocolSplitter; import org.apache.commons.pool2.ObjectPool;
import javax.annotation.Nonnull;
import javax.inject.Provider; import javax.inject.Provider;
import java.util.LinkedHashMap;
import java.util.Map;
@Module @Module
@RequiredArgsConstructor
public class ProtocolModule { public class ProtocolModule {
private final boolean readUnknownPackets;
@Provides @Provides
NettyServer provideServer(ServerBootstrap serverBootstrap) { @ServerScope
return new NettyServer(serverBootstrap); Server provideServer(
Provider<ProtocolDecoder> protocolDecoderProvider,
Provider<PacketInboundHandler> packetInboundHandlerProvider,
EventBus eventBus
) {
return new NettyServer(protocolDecoderProvider, packetInboundHandlerProvider, eventBus);
} }
@Provides @Provides
ServerBootstrap provideServerBootstrap(ChannelInitializer<SocketChannel> channelChannelInitializer) { @ServerScope
ServerBootstrap bootstrap = new ServerBootstrap(); ProtocolDecoder provideProtocolDecoder(
ObjectPool<NettyConnectionContext> poolNettyConnectionContext,
bootstrap.group(new NioEventLoopGroup(1), new NioEventLoopGroup()) PacketPool poolPackets
.channel(NioServerSocketChannel.class) ) {
.childHandler(channelChannelInitializer); return new ProtocolDecoder(readUnknownPackets, poolNettyConnectionContext, poolPackets);
return bootstrap;
} }
@Provides @Provides
ChannelInitializer<SocketChannel> provideChannelChannelInitializer( @ServerScope
Provider<Map<String, ChannelHandler>> channelHandlerMapProvider) { PacketInboundHandler providePacketInboundHandler(
ObjectPool<NettyConnectionContext> poolNettyConnectionContext,
return new ChannelInitializer<>() { PacketPool packetPool,
@Override EventBus eventBus
protected void initChannel(@Nonnull SocketChannel socketChannel) { ) {
ChannelPipeline pipeline = socketChannel.pipeline(); return new PacketInboundHandler(poolNettyConnectionContext, packetPool, eventBus);
channelHandlerMapProvider.get().forEach(pipeline::addLast);
}
};
} }
@Provides @Provides
Map<String, ChannelHandler> provideChannelHandlerMap() { @ServerScope
Map<String, ChannelHandler> map = new LinkedHashMap<>(); EventBus provideEventBus() {
return new SimpleEventBus();
map.put("packet_splitter", new ProtocolSplitter());
map.put("logger", new LoggingHandler(LogLevel.DEBUG));
map.put("packet_decoder", new ProtocolDecoder(true));
map.put("packet_encoder", new ProtocolEncoder());
map.put("packet_handler", new PacketInboundHandler());
return map;
} }
} }

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

@@ -4,31 +4,51 @@ import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import mc.protocol.NettyConnectionContext;
import mc.protocol.NetworkAttributes; import mc.protocol.NetworkAttributes;
import mc.protocol.State; import mc.protocol.State;
import mc.protocol.api.ConnectionContext;
import mc.protocol.io.NetByteBuf; import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
import mc.protocol.packets.UnknownPacket; 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.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer;
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j
public class ProtocolDecoder extends ByteToMessageDecoder { public class ProtocolDecoder extends ByteToMessageDecoder {
private final boolean readUnknownPackets; private final boolean readUnknownPackets;
private final ObjectPool<NettyConnectionContext> poolNettyConnectionContext;
private final PacketPool poolPackets;
@Setter
private Consumer<ConnectionContext> consumerNewConnection;
@Setter
private Consumer<ConnectionContext> consumerDisconnect;
@Override @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception { public void channelActive(@Nonnull ChannelHandlerContext ctx) throws Exception {
ctx.channel().attr(NetworkAttributes.STATE).set(State.HANDSHAKING); NettyConnectionContext context = poolNettyConnectionContext.borrowObject().setCtx(ctx);
consumerNewConnection.accept(context);
poolNettyConnectionContext.returnObject(context);
super.channelActive(ctx); super.channelActive(ctx);
} }
@Override @Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception { public void channelInactive(@Nonnull ChannelHandlerContext ctx) throws Exception {
ctx.channel().attr(NetworkAttributes.STATE).set(null); NettyConnectionContext context = poolNettyConnectionContext.borrowObject().setCtx(ctx);
consumerDisconnect.accept(context);
poolNettyConnectionContext.returnObject(context);
super.channelInactive(ctx); super.channelInactive(ctx);
} }
@@ -43,14 +63,17 @@ public class ProtocolDecoder extends ByteToMessageDecoder {
log.warn("Unknown packet: State {} ; Id 0x{}", state, packetIdAsHexcode(packetId)); log.warn("Unknown packet: State {} ; Id 0x{}", state, packetIdAsHexcode(packetId));
if (readUnknownPackets) { if (readUnknownPackets) {
UnknownPacket unknownPacket = new UnknownPacket(state, packetId, netByteBuf.readableBytes()); UnknownPacket unknownPacket = poolPackets.borrowObject(UnknownPacket.class);
unknownPacket.setState(state);
unknownPacket.setId(packetId);
unknownPacket.setDataSize(netByteBuf.readableBytes());
unknownPacket.readSelf(netByteBuf); unknownPacket.readSelf(netByteBuf);
out.add(unknownPacket); out.add(unknownPacket);
} else { } else {
netByteBuf.skipBytes(netByteBuf.readableBytes()); netByteBuf.skipBytes(netByteBuf.readableBytes());
} }
} else { } else {
ClientSidePacket packet = packetClass.getDeclaredConstructor().newInstance(); ClientSidePacket packet = poolPackets.borrowObject(packetClass);
packet.readSelf(netByteBuf); packet.readSelf(netByteBuf);
log.debug("IN: {}:{}", state, packet); log.debug("IN: {}:{}", state, packet);
out.add(packet); out.add(packet);

View File

@@ -1,11 +1,12 @@
package mc.protocol.packets; package mc.protocol.packets;
import mc.protocol.io.NetByteBuf; import mc.protocol.io.NetByteBuf;
import mc.protocol.pool.Passivable;
/** /**
* Пакеты отправляемые клиентом. * Пакеты отправляемые клиентом.
*/ */
public interface ClientSidePacket extends Packet { public interface ClientSidePacket extends Packet, Passivable {
void readSelf(NetByteBuf netByteBuf); void readSelf(NetByteBuf netByteBuf);
} }

View File

@@ -13,4 +13,9 @@ public abstract class EmptyPacket implements ClientSidePacket, ServerSidePacket
public void writeSelf(NetByteBuf netByteBuf) { public void writeSelf(NetByteBuf netByteBuf) {
// empty // empty
} }
@Override
public void passivate() {
// pass
}
} }

View File

@@ -34,6 +34,11 @@ public class PingPacket implements ClientSidePacket, ServerSidePacket {
payload = netByteBuf.readLong(); payload = netByteBuf.readLong();
} }
@Override
public void passivate() {
this.payload = null;
}
@Override @Override
public void writeSelf(NetByteBuf netByteBuf) { public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeLong(payload); netByteBuf.writeLong(payload);

View File

@@ -1,17 +1,19 @@
package mc.protocol.packets; package mc.protocol.packets;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import mc.protocol.State; import mc.protocol.State;
import mc.protocol.io.NetByteBuf; import mc.protocol.io.NetByteBuf;
@NoArgsConstructor
@Data @Data
@ToString(exclude = "rawData") @ToString(exclude = "rawData")
public class UnknownPacket implements ClientSidePacket { public class UnknownPacket implements ClientSidePacket {
private final State state; private State state;
private final int id; private int id;
private final int dataSize; private int dataSize;
private byte[] rawData; private byte[] rawData;
@Override @Override
@@ -19,4 +21,12 @@ public class UnknownPacket implements ClientSidePacket {
rawData = new byte[dataSize]; rawData = new byte[dataSize];
netByteBuf.readBytes(rawData); netByteBuf.readBytes(rawData);
} }
@Override
public void passivate() {
this.state = null;
this.id = 0;
this.dataSize = 0;
this.rawData = null;
}
} }

View File

@@ -51,6 +51,13 @@ public class CPlayerPositionAndLookPacket implements ClientSidePacket {
this.onGround = netByteBuf.readBoolean(); this.onGround = netByteBuf.readBoolean();
} }
@Override
public void passivate() {
this.position = null;
this.look = null;
this.onGround = false;
}
public double getYPositionHead() { public double getYPositionHead() {
return this.position.getY() + 1.62f; return this.position.getY() + 1.62f;
} }

View File

@@ -1,8 +1,8 @@
package mc.protocol.packets.client; package mc.protocol.packets.client;
import lombok.*; import lombok.*;
import mc.protocol.ChatMode; import mc.protocol.utils.ChatMode;
import mc.protocol.MainHand; import mc.protocol.utils.MainHand;
import mc.protocol.io.NetByteBuf; import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
@@ -64,6 +64,16 @@ public class ClientSettingsPacket implements ClientSidePacket {
this.mainHand = MainHand.valueById(netByteBuf.readVarInt()); this.mainHand = MainHand.valueById(netByteBuf.readVarInt());
} }
@Override
public void passivate() {
this.locale = null;
this.viewDistance = 0;
this.chatMode = null;
this.chatColors = false;
this.$displayedSkinPartsBitMask = 0;
this.mainHand = null;
}
public boolean isCapeEnabled() { public boolean isCapeEnabled() {
return ($displayedSkinPartsBitMask & 0x01) > 0; return ($displayedSkinPartsBitMask & 0x01) > 0;
} }

View File

@@ -47,4 +47,12 @@ public class HandshakePacket implements ClientSidePacket {
nextState = State.getById(netByteBuf.readVarInt()); nextState = State.getById(netByteBuf.readVarInt());
} }
@Override
public void passivate() {
this.protocolVersion = 0;
this.host = null;
this.port = 0;
this.nextState = null;
}
} }

View File

@@ -34,4 +34,9 @@ public class LoginStartPacket implements ClientSidePacket {
this.name = netByteBuf.readString(); this.name = netByteBuf.readString();
} }
@Override
public void passivate() {
this.name = null;
}
} }

View File

@@ -39,4 +39,10 @@ public class PlayerLookPacket implements ClientSidePacket {
this.onGround = netByteBuf.readBoolean(); this.onGround = netByteBuf.readBoolean();
} }
@Override
public void passivate() {
this.look = null;
this.onGround = false;
}
} }

View File

@@ -43,6 +43,12 @@ public class PlayerPositionPacket implements ClientSidePacket {
this.onGround = netByteBuf.readBoolean(); this.onGround = netByteBuf.readBoolean();
} }
@Override
public void passivate() {
this.position = null;
this.onGround = false;
}
public double getYPositionHead() { public double getYPositionHead() {
return this.position.getY() + 1.62f; return this.position.getY() + 1.62f;
} }

View File

@@ -39,4 +39,10 @@ public class PluginMessagePacket implements ClientSidePacket {
this.rawData = new byte[netByteBuf.readableBytes()]; this.rawData = new byte[netByteBuf.readableBytes()];
netByteBuf.readBytes(this.rawData); netByteBuf.readBytes(this.rawData);
} }
@Override
public void passivate() {
this.channelName = null;
this.rawData = null;
}
} }

View File

@@ -33,4 +33,9 @@ public class TeleportConfirmPacket implements ClientSidePacket {
public void readSelf(NetByteBuf netByteBuf) { public void readSelf(NetByteBuf netByteBuf) {
this.teleportId = netByteBuf.readVarInt(); this.teleportId = netByteBuf.readVarInt();
} }
@Override
public void passivate() {
this.teleportId = 0;
}
} }

View File

@@ -1,9 +1,9 @@
package mc.protocol.packets.server; package mc.protocol.packets.server;
import lombok.Data; import lombok.Data;
import mc.protocol.Difficulty; import mc.protocol.utils.Difficulty;
import mc.protocol.GameMode; import mc.protocol.utils.GameMode;
import mc.protocol.LevelType; import mc.protocol.utils.LevelType;
import mc.protocol.io.NetByteBuf; import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;

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

@@ -0,0 +1,6 @@
package mc.protocol.pool;
public interface Passivable {
void passivate();
}

View File

@@ -1,4 +1,4 @@
package mc.protocol; package mc.protocol.utils;
import javax.annotation.Nullable; import javax.annotation.Nullable;

View File

@@ -1,4 +1,4 @@
package mc.protocol; package mc.protocol.utils;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;

View File

@@ -1,4 +1,4 @@
package mc.protocol; package mc.protocol.utils;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;

View File

@@ -1,4 +1,4 @@
package mc.protocol; package mc.protocol.utils;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;

View File

@@ -1,4 +1,4 @@
package mc.protocol; package mc.protocol.utils;
import javax.annotation.Nullable; import javax.annotation.Nullable;

View File

@@ -0,0 +1,23 @@
package mc.protocol.utils;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
public class Table<C, R, V> {
private final Map<C, Map<R, V>> map = new HashMap<>();
@Nullable
public V getColumnAndRow(C column, R row) {
if (!map.containsKey(column)) {
return null;
}
return map.get(column).get(row);
}
public void put(C column, R row, V value) {
map.computeIfAbsent(column, c -> new HashMap<>()).put(row, value);
}
}

View File

@@ -7,8 +7,11 @@ import joptsimple.OptionParser;
import joptsimple.OptionSet; import joptsimple.OptionSet;
import joptsimple.util.PathConverter; import joptsimple.util.PathConverter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import mc.protocol.NettyServer;
import mc.protocol.State; import mc.protocol.State;
import mc.protocol.api.Server;
import mc.protocol.di.DaggerProtocolComponent;
import mc.protocol.di.ProtocolComponent;
import mc.protocol.di.ProtocolModule;
import mc.protocol.packets.PingPacket; import mc.protocol.packets.PingPacket;
import mc.protocol.packets.client.HandshakePacket; import mc.protocol.packets.client.HandshakePacket;
import mc.protocol.packets.client.LoginStartPacket; import mc.protocol.packets.client.LoginStartPacket;
@@ -46,14 +49,21 @@ public class Main {
Config config = serverComponent.getConfig(); Config config = serverComponent.getConfig();
NettyServer server = NettyServer.createServer(); ProtocolComponent protocolComponent = DaggerProtocolComponent.builder()
.protocolModule(new ProtocolModule(true))
.build();
Server server = protocolComponent.getServer();
PacketHandler packetHandler = serverComponent.getPacketHandler(); PacketHandler packetHandler = serverComponent.getPacketHandler();
State.HANDSHAKING.packetFlux(HandshakePacket.class).subscribe(packetHandler::onHandshake); server.onNewConnect(connectionContext -> connectionContext.setState(State.HANDSHAKING));
State.STATUS.packetFlux(PingPacket.class).subscribe(packetHandler::onKeepAlive); server.onDisonnect(connectionContext -> connectionContext.setState(null));
State.STATUS.packetFlux(StatusServerRequestPacket.class).subscribe(packetHandler::onServerStatus);
State.LOGIN.packetFlux(LoginStartPacket.class).subscribe(packetHandler::onLoginStart); server.listenPacket(State.HANDSHAKING, HandshakePacket.class, packetHandler::onHandshake);
State.PLAY.packetFlux(PingPacket.class).subscribe(packetHandler::onKeepAlivePlay); server.listenPacket(State.STATUS, PingPacket.class, packetHandler::onKeepAlive);
server.listenPacket(State.STATUS, StatusServerRequestPacket.class, packetHandler::onServerStatus);
server.listenPacket(State.LOGIN, LoginStartPacket.class, packetHandler::onLoginStart);
server.listenPacket(State.PLAY, PingPacket.class, packetHandler::onKeepAlivePlay);
server.bind(config.server().host(), config.server().port()); server.bind(config.server().host(), config.server().port());
} }

View File

@@ -3,6 +3,7 @@ package mc.server;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import mc.protocol.*; import mc.protocol.*;
import mc.protocol.api.ConnectionContext;
import mc.protocol.model.Location; import mc.protocol.model.Location;
import mc.protocol.model.Look; import mc.protocol.model.Look;
import mc.protocol.model.ServerInfo; import mc.protocol.model.ServerInfo;
@@ -12,6 +13,9 @@ import mc.protocol.packets.client.LoginStartPacket;
import mc.protocol.packets.client.StatusServerRequestPacket; import mc.protocol.packets.client.StatusServerRequestPacket;
import mc.protocol.packets.server.*; import mc.protocol.packets.server.*;
import mc.protocol.serializer.TextSerializer; import mc.protocol.serializer.TextSerializer;
import mc.protocol.utils.Difficulty;
import mc.protocol.utils.GameMode;
import mc.protocol.utils.LevelType;
import mc.server.config.Config; import mc.server.config.Config;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@@ -27,22 +31,24 @@ import java.util.UUID;
@RequiredArgsConstructor @RequiredArgsConstructor
public class PacketHandler { public class PacketHandler {
private final Config config;
private final Random random = new Random(System.currentTimeMillis()); private final Random random = new Random(System.currentTimeMillis());
private final Config config;
public void onHandshake(ChannelContext<HandshakePacket> channel) { public void onHandshake(ConnectionContext context, HandshakePacket packet) {
channel.setState(channel.getPacket().getNextState()); context.setState(packet.getNextState());
} }
public void onKeepAlive(ChannelContext<PingPacket> channel) { public void onKeepAlive(ConnectionContext context, PingPacket packet) {
channel.getCtx().writeAndFlush(channel.getPacket()).channel().disconnect(); context.sendNow(packet);
context.disconnect();
} }
public void onKeepAlivePlay(ChannelContext<PingPacket> channel) { public void onKeepAlivePlay(ConnectionContext context, PingPacket packet) {
channel.getCtx().writeAndFlush(channel.getPacket()); context.sendNow(packet);
} }
public void onServerStatus(ChannelContext<StatusServerRequestPacket> channel) { @SuppressWarnings("unused")
public void onServerStatus(ConnectionContext context, StatusServerRequestPacket packet) {
ServerInfo serverInfo = new ServerInfo(); ServerInfo serverInfo = new ServerInfo();
serverInfo.version().name(ProtocolConstant.PROTOCOL_NAME); serverInfo.version().name(ProtocolConstant.PROTOCOL_NAME);
serverInfo.version().protocol(ProtocolConstant.PROTOCOL_NUMBER); serverInfo.version().protocol(ProtocolConstant.PROTOCOL_NUMBER);
@@ -58,18 +64,16 @@ public class PacketHandler {
StatusServerResponse response = new StatusServerResponse(); StatusServerResponse response = new StatusServerResponse();
response.setInfo(serverInfo); response.setInfo(serverInfo);
channel.getCtx().writeAndFlush(response); context.sendNow(response);
} }
public void onLoginStart(ChannelContext<LoginStartPacket> channel) { public void onLoginStart(ConnectionContext context, LoginStartPacket loginStartPacket) {
LoginStartPacket loginStartPacket = channel.getPacket();
var loginSuccessPacket = new LoginSuccessPacket(); var loginSuccessPacket = new LoginSuccessPacket();
loginSuccessPacket.setUuid(UUID.randomUUID()); loginSuccessPacket.setUuid(UUID.randomUUID());
loginSuccessPacket.setName(loginStartPacket.getName()); loginSuccessPacket.setName(loginStartPacket.getName());
channel.getCtx().writeAndFlush(loginSuccessPacket); context.sendNow(loginSuccessPacket);
channel.setState(State.PLAY); context.setState(State.PLAY);
var joinGamePacket = new JoinGamePacket(); var joinGamePacket = new JoinGamePacket();
joinGamePacket.setEntityId(random.nextInt()); joinGamePacket.setEntityId(random.nextInt());
@@ -78,14 +82,14 @@ public class PacketHandler {
joinGamePacket.setDifficulty(Difficulty.PEACEFUL); joinGamePacket.setDifficulty(Difficulty.PEACEFUL);
joinGamePacket.setLevelType(LevelType.FLAT); joinGamePacket.setLevelType(LevelType.FLAT);
channel.getCtx().write(joinGamePacket); context.send(joinGamePacket);
Location spawnLocation = new Location(0d, 63d, 0d); Location spawnLocation = new Location(0d, 63d, 0d);
var spawnPositionPacket = new SpawnPositionPacket(); var spawnPositionPacket = new SpawnPositionPacket();
spawnPositionPacket.setSpawn(spawnLocation); spawnPositionPacket.setSpawn(spawnLocation);
channel.getCtx().write(spawnPositionPacket); context.send(spawnPositionPacket);
var playerAbilitiesPacket = new PlayerAbilitiesPacket(); var playerAbilitiesPacket = new PlayerAbilitiesPacket();
playerAbilitiesPacket.setCatFly(true); playerAbilitiesPacket.setCatFly(true);
@@ -95,29 +99,29 @@ public class PacketHandler {
playerAbilitiesPacket.setFieldOfView(0.0f); playerAbilitiesPacket.setFieldOfView(0.0f);
playerAbilitiesPacket.setFlyingSpeed(0.05f); playerAbilitiesPacket.setFlyingSpeed(0.05f);
channel.getCtx().write(playerAbilitiesPacket); context.send(playerAbilitiesPacket);
channel.getCtx().flush(); context.flushSending();
var chunkDataPacket = new ChunkDataPacket(); var chunkDataPacket = new ChunkDataPacket();
chunkDataPacket.setX(0); chunkDataPacket.setX(0);
chunkDataPacket.setZ(0); chunkDataPacket.setZ(0);
channel.getCtx().writeAndFlush(chunkDataPacket); context.sendNow(chunkDataPacket);
var playerPositionAndLookPacket = new SPlayerPositionAndLookPacket(); var playerPositionAndLookPacket = new SPlayerPositionAndLookPacket();
playerPositionAndLookPacket.setPosition(spawnLocation); playerPositionAndLookPacket.setPosition(spawnLocation);
playerPositionAndLookPacket.setLook(new Look(0f, 0f)); playerPositionAndLookPacket.setLook(new Look(0f, 0f));
playerPositionAndLookPacket.setTeleportId(random.nextInt()); playerPositionAndLookPacket.setTeleportId(random.nextInt());
channel.getCtx().write(playerPositionAndLookPacket); context.send(playerPositionAndLookPacket);
PingPacket pingPacket = new PingPacket(); PingPacket pingPacket = new PingPacket();
pingPacket.setPayload(System.currentTimeMillis()); pingPacket.setPayload(System.currentTimeMillis());
channel.getCtx().write(pingPacket); context.send(pingPacket);
channel.getCtx().flush(); context.flushSending();
} }
private static String faviconToBase64(Path iconPath) { private static String faviconToBase64(Path iconPath) {