Merge branch 'dev/network-api' into develop
This commit is contained in:
@@ -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 = [
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
18
protocol/src/main/java/mc/protocol/api/Server.java
Normal file
18
protocol/src/main/java/mc/protocol/api/Server.java
Normal 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);
|
||||||
|
}
|
||||||
42
protocol/src/main/java/mc/protocol/di/PoolModule.java
Normal file
42
protocol/src/main/java/mc/protocol/di/PoolModule.java
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
17
protocol/src/main/java/mc/protocol/event/EventBus.java
Normal file
17
protocol/src/main/java/mc/protocol/event/EventBus.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
protocol/src/main/java/mc/protocol/event/SimpleEventBus.java
Normal file
26
protocol/src/main/java/mc/protocol/event/SimpleEventBus.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,4 +34,9 @@ public class LoginStartPacket implements ClientSidePacket {
|
|||||||
this.name = netByteBuf.readString();
|
this.name = netByteBuf.readString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void passivate() {
|
||||||
|
this.name = null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
30
protocol/src/main/java/mc/protocol/pool/PacketFactory.java
Normal file
30
protocol/src/main/java/mc/protocol/pool/PacketFactory.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
26
protocol/src/main/java/mc/protocol/pool/PacketPool.java
Normal file
26
protocol/src/main/java/mc/protocol/pool/PacketPool.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
protocol/src/main/java/mc/protocol/pool/Passivable.java
Normal file
6
protocol/src/main/java/mc/protocol/pool/Passivable.java
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package mc.protocol.pool;
|
||||||
|
|
||||||
|
public interface Passivable {
|
||||||
|
|
||||||
|
void passivate();
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package mc.protocol;
|
package mc.protocol.utils;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package mc.protocol;
|
package mc.protocol.utils;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package mc.protocol;
|
package mc.protocol.utils;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package mc.protocol;
|
package mc.protocol.utils;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package mc.protocol;
|
package mc.protocol.utils;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
23
protocol/src/main/java/mc/protocol/utils/Table.java
Normal file
23
protocol/src/main/java/mc/protocol/utils/Table.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user