diff --git a/server-api/build.gradle b/server-api/build.gradle new file mode 100644 index 0000000..1f51f59 --- /dev/null +++ b/server-api/build.gradle @@ -0,0 +1,18 @@ +ext { + netty_version = '4.1.22.Final'; + + library = [ + rxjava: ['io.reactivex.rxjava3:rxjava:3.0.3'], + netty: [ + "io.netty:netty-transport:$netty_version" + ] + ] +} + +dependencies { + /* COMPONENTS */ + compile project(':protocol') + compile library.rxjava + + implementation library.netty +} \ No newline at end of file diff --git a/server-api/src/main/java/mc/server/reactive/NetworkContainer.java b/server-api/src/main/java/mc/server/reactive/NetworkContainer.java new file mode 100644 index 0000000..5cbfcdf --- /dev/null +++ b/server-api/src/main/java/mc/server/reactive/NetworkContainer.java @@ -0,0 +1,12 @@ +package mc.server.reactive; + +import io.netty.channel.Channel; +import lombok.Data; +import mc.protocol.Packet; + +@Data +public class NetworkContainer { + + private final Channel channel; + private final T packet; +} diff --git a/server-api/src/main/java/mc/server/reactive/RxProvider.java b/server-api/src/main/java/mc/server/reactive/RxProvider.java new file mode 100644 index 0000000..cd7e933 --- /dev/null +++ b/server-api/src/main/java/mc/server/reactive/RxProvider.java @@ -0,0 +1,28 @@ +package mc.server.reactive; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.Subject; +import mc.protocol.Packet; + +import java.util.HashMap; +import java.util.Map; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class RxProvider { + + private final Map subjectMap = new HashMap<>(); + + public Observable> getPacketObservable(Class packetType) { + return giveSubject(packetType); + } + + public Observer> getPacketObserver(Class packetType) { + return giveSubject(packetType); + } + + private Subject giveSubject(Class packetType) { + return subjectMap.computeIfAbsent(packetType, key -> PublishSubject.create()); + } +} diff --git a/server/build.gradle b/server/build.gradle index 929988a..b9a2611 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -15,7 +15,6 @@ dependencies { implementation library.logger /* COMPONENTS */ - implementation project(':protocol') implementation project(':server-api') implementation library.guice implementation library.netty diff --git a/server/src/main/java/mc/server/logic/HandshakeLogic.java b/server/src/main/java/mc/server/logic/HandshakeLogic.java new file mode 100644 index 0000000..dcbcb88 --- /dev/null +++ b/server/src/main/java/mc/server/logic/HandshakeLogic.java @@ -0,0 +1,28 @@ +package mc.server.logic; + +import com.google.inject.Inject; +import io.netty.channel.Channel; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import mc.protocol.handshake.client.HandshakePacket; +import mc.server.network.impl.NettyConstants; +import mc.server.reactive.RxProvider; + +@Slf4j +@RequiredArgsConstructor(onConstructor = @__({ @Inject })) +public class HandshakeLogic { + + private final RxProvider rxProvider; + + public void setup() { + log.info("HandshakeLogic setup"); + rxProvider.getPacketObservable(HandshakePacket.class) + .doOnNext(container -> handle(container.getChannel(), container.getPacket())) + .subscribe(); + } + + private void handle(Channel channel, HandshakePacket packet) { + log.info("HandshakePacket handle"); + channel.attr(NettyConstants.ATTR_STATE).set(packet.getNextState()); + } +} diff --git a/server/src/main/java/mc/server/logic/LoginLogic.java b/server/src/main/java/mc/server/logic/LoginLogic.java new file mode 100644 index 0000000..1d2ac25 --- /dev/null +++ b/server/src/main/java/mc/server/logic/LoginLogic.java @@ -0,0 +1,38 @@ +package mc.server.logic; + +import com.google.inject.Inject; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import mc.protocol.login.client.LoginStartPacket; +import mc.protocol.login.server.DisconnectPacket; +import mc.protocol.text.Text; +import mc.protocol.text.TextColor; +import mc.protocol.text.TextStyle; +import mc.server.reactive.NetworkContainer; +import mc.server.reactive.RxProvider; + +@Slf4j +@RequiredArgsConstructor(onConstructor = @__({ @Inject })) +public class LoginLogic { + + private final RxProvider rxProvider; + + public void setup() { + log.info("LoginLogic setup"); + rxProvider.getPacketObservable(LoginStartPacket.class) + .map(NetworkContainer::getChannel) + .doOnNext(channel -> { + log.info("LoginLogic handle"); + + DisconnectPacket disconnectPacket = new DisconnectPacket(); + disconnectPacket.setReason(Text.builder() + .append(Text.of(TextColor.WHITE, "Server is ")) + .color(TextColor.RED).style(TextStyle.BOLD).append("NOT ") + .color(TextColor.WHITE).append("available.") + .build()); + + channel.writeAndFlush(disconnectPacket).channel().disconnect(); + }) + .subscribe(); + } +} diff --git a/server/src/main/java/mc/server/network/impl/handler/StatusHandler.java b/server/src/main/java/mc/server/logic/StatusLogic.java similarity index 58% rename from server/src/main/java/mc/server/network/impl/handler/StatusHandler.java rename to server/src/main/java/mc/server/logic/StatusLogic.java index 9011478..a6e8274 100644 --- a/server/src/main/java/mc/server/network/impl/handler/StatusHandler.java +++ b/server/src/main/java/mc/server/logic/StatusLogic.java @@ -1,12 +1,16 @@ -package mc.server.network.impl.handler; +package mc.server.logic; -import io.netty.channel.ChannelHandlerContext; +import com.google.inject.Inject; +import io.netty.channel.Channel; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import mc.protocol.ProtocolConstant; import mc.protocol.dto.ServerInfo; +import mc.protocol.status.PingPacket; import mc.protocol.status.client.StatusServerRequest; import mc.protocol.status.server.StatusServerResponse; import mc.protocol.text.Text; +import mc.server.reactive.RxProvider; import org.apache.commons.io.IOUtils; import java.io.IOException; @@ -14,12 +18,28 @@ import java.util.Base64; import java.util.Collections; @Slf4j -public class StatusHandler extends AbstractPacketHandler { +@RequiredArgsConstructor(onConstructor = @__({ @Inject })) +public class StatusLogic { private static final String FAVICON_HEADER = "data:image/png;base64,"; + private final RxProvider rxProvider; - @Override - protected void channelRead1(ChannelHandlerContext ctx, StatusServerRequest packet) throws Exception { + public void setup() { + log.info("StatusLogic setup"); + rxProvider.getPacketObservable(StatusServerRequest.class) + .doOnNext(container -> status(container.getChannel(), container.getPacket())) + .subscribe(); + + rxProvider.getPacketObservable(PingPacket.class) + .doOnNext(container -> { + log.info("StatusLogic ping handle"); + container.getChannel().writeAndFlush(container.getPacket()).channel().disconnect(); + }) + .subscribe(); + } + + private void status(Channel channel, StatusServerRequest packet) throws IOException { + log.info("StatusLogic handle"); log.info("{}", packet); final ServerInfo.Version version = new ServerInfo.Version(); @@ -40,7 +60,7 @@ public class StatusHandler extends AbstractPacketHandler { StatusServerResponse response = new StatusServerResponse(); response.setServerInfoDto(serverInfo); - ctx.channel().writeAndFlush(response); + channel.writeAndFlush(response); } private String getEmbeddedIconBase64() throws IOException { diff --git a/server/src/main/java/mc/server/network/config/NetworkModule.java b/server/src/main/java/mc/server/network/config/NetworkModule.java index 6b0cf2a..f5436d8 100644 --- a/server/src/main/java/mc/server/network/config/NetworkModule.java +++ b/server/src/main/java/mc/server/network/config/NetworkModule.java @@ -21,9 +21,10 @@ import mc.server.network.impl.NettyServer; import mc.server.network.impl.codec.PacketDecoder; import mc.server.network.impl.codec.PacketEncoder; import mc.server.network.impl.codec.PacketSplitter; -import mc.server.network.impl.handler.HandshakeHandler; +import mc.server.network.impl.handler.CommonPacketHandler; +import mc.server.reactive.RxProvider; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; public class NetworkModule extends AbstractModule { @@ -33,6 +34,7 @@ public class NetworkModule extends AbstractModule { bind(Server.class).to(NettyServer.class).in(Singleton.class); bind(EventLoopGroup.class).annotatedWith(Names.named("bossGroup")).toInstance(new NioEventLoopGroup(1)); bind(EventLoopGroup.class).annotatedWith(Names.named("workerGroup")).toInstance(new NioEventLoopGroup()); + bind(RxProvider.class).toInstance(new RxProvider()); } @Provides @@ -51,14 +53,14 @@ public class NetworkModule extends AbstractModule { @Provides @Named("channelHandlerMap") - Map channelHandlerMap(HandshakeHandler handshakeHandler) { - final Map map = new HashMap<>(); + Map channelHandlerMap(CommonPacketHandler commonPacketHandler) { + final Map map = new LinkedHashMap<>(); map.put("logger", new LoggingHandler()); map.put("packet_splitter", new PacketSplitter(new ProtocolSplitter())); map.put("packet_decoder", new PacketDecoder(new ProtocolDecoder(PacketDirection.SERVER_BOUND))); map.put("packet_encoder", new PacketEncoder(new ProtocolEncoder(PacketDirection.CLIENT_BOUND))); - map.put("handshake_handler", handshakeHandler); + map.put("common_handler", commonPacketHandler); return map; } diff --git a/server/src/main/java/mc/server/network/impl/NettyServer.java b/server/src/main/java/mc/server/network/impl/NettyServer.java index 304d575..9532df5 100644 --- a/server/src/main/java/mc/server/network/impl/NettyServer.java +++ b/server/src/main/java/mc/server/network/impl/NettyServer.java @@ -3,17 +3,28 @@ package mc.server.network.impl; import com.google.inject.Inject; import com.google.inject.Provider; import io.netty.bootstrap.ServerBootstrap; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import mc.server.logic.HandshakeLogic; +import mc.server.logic.LoginLogic; +import mc.server.logic.StatusLogic; import mc.server.network.Server; @Slf4j +@RequiredArgsConstructor(onConstructor = @__({ @Inject })) public class NettyServer implements Server { - @Inject - private Provider serverBootstrapProvider; + private final Provider serverBootstrapProvider; + private final HandshakeLogic handshakeLogic; + private final StatusLogic statusLogic; + private final LoginLogic loginLogic; @Override public void start(String host, int port) { + handshakeLogic.setup(); + statusLogic.setup(); + loginLogic.setup(); + try { log.info("Network starting: {}:{}", host, port); serverBootstrapProvider.get() diff --git a/server/src/main/java/mc/server/network/impl/handler/AbstractPacketHandler.java b/server/src/main/java/mc/server/network/impl/handler/AbstractPacketHandler.java deleted file mode 100644 index 09fa7f8..0000000 --- a/server/src/main/java/mc/server/network/impl/handler/AbstractPacketHandler.java +++ /dev/null @@ -1,15 +0,0 @@ -package mc.server.network.impl.handler; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import mc.protocol.Packet; - -public abstract class AbstractPacketHandler

extends SimpleChannelInboundHandler

{ - - @Override - protected void channelRead0(ChannelHandlerContext ctx, P msg) throws Exception { - channelRead1(ctx, msg); - } - - protected abstract void channelRead1(ChannelHandlerContext ctx, P packet) throws Exception; -} diff --git a/server/src/main/java/mc/server/network/impl/handler/CommonPacketHandler.java b/server/src/main/java/mc/server/network/impl/handler/CommonPacketHandler.java new file mode 100644 index 0000000..4a017bf --- /dev/null +++ b/server/src/main/java/mc/server/network/impl/handler/CommonPacketHandler.java @@ -0,0 +1,21 @@ +package mc.server.network.impl.handler; + +import com.google.inject.Inject; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import lombok.RequiredArgsConstructor; +import mc.protocol.Packet; +import mc.server.reactive.NetworkContainer; +import mc.server.reactive.RxProvider; + +@RequiredArgsConstructor(onConstructor = @__({ @Inject })) +public class CommonPacketHandler extends SimpleChannelInboundHandler { + + private final RxProvider rxProvider; + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Packet packet) { + rxProvider.getPacketObserver(packet.getClass()) + .onNext(new NetworkContainer<>(ctx.channel(), packet)); + } +} diff --git a/server/src/main/java/mc/server/network/impl/handler/HandshakeHandler.java b/server/src/main/java/mc/server/network/impl/handler/HandshakeHandler.java deleted file mode 100644 index 5f4b838..0000000 --- a/server/src/main/java/mc/server/network/impl/handler/HandshakeHandler.java +++ /dev/null @@ -1,34 +0,0 @@ -package mc.server.network.impl.handler; - -import com.google.inject.Inject; -import com.google.inject.Provider; -import io.netty.channel.ChannelHandlerContext; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import mc.protocol.State; -import mc.protocol.handshake.client.HandshakePacket; - -import static mc.server.network.impl.NettyConstants.ATTR_STATE; - -@Slf4j -@RequiredArgsConstructor(onConstructor = @__({ @Inject })) -public class HandshakeHandler extends AbstractPacketHandler { - - private final Provider statusHandlerProvider; - private final Provider pingHandlerProvider; - private final Provider loginHandlerProvider; - - @Override - protected void channelRead1(ChannelHandlerContext ctx, HandshakePacket packet) { - log.info("{}", packet); - - ctx.channel().attr(ATTR_STATE).set(packet.getNextState()); - - if (packet.getNextState() == State.STATUS) { - ctx.channel().pipeline().replace("handshake_handler", "status_handler", statusHandlerProvider.get()); - ctx.channel().pipeline().addAfter("status_handler", "ping_handler", pingHandlerProvider.get()); - } else if (packet.getNextState() == State.LOGIN) { - ctx.channel().pipeline().replace("handshake_handler", "login_handler", loginHandlerProvider.get()); - } - } -} diff --git a/server/src/main/java/mc/server/network/impl/handler/LoginHandler.java b/server/src/main/java/mc/server/network/impl/handler/LoginHandler.java deleted file mode 100644 index 29a56c7..0000000 --- a/server/src/main/java/mc/server/network/impl/handler/LoginHandler.java +++ /dev/null @@ -1,23 +0,0 @@ -package mc.server.network.impl.handler; - -import io.netty.channel.ChannelHandlerContext; -import mc.protocol.login.client.LoginStartPacket; -import mc.protocol.login.server.DisconnectPacket; -import mc.protocol.text.Text; -import mc.protocol.text.TextColor; -import mc.protocol.text.TextStyle; - -public class LoginHandler extends AbstractPacketHandler { - - @Override - protected void channelRead1(ChannelHandlerContext ctx, LoginStartPacket packet) { - DisconnectPacket disconnectPacket = new DisconnectPacket(); - disconnectPacket.setReason(Text.builder() - .append(Text.of(TextColor.WHITE, "Server is ")) - .color(TextColor.RED).style(TextStyle.BOLD).append("NOT ") - .color(TextColor.WHITE).append("available.") - .build()); - - ctx.channel().writeAndFlush(disconnectPacket).channel().disconnect(); - } -} diff --git a/server/src/main/java/mc/server/network/impl/handler/PingHandler.java b/server/src/main/java/mc/server/network/impl/handler/PingHandler.java deleted file mode 100644 index 21b9fcd..0000000 --- a/server/src/main/java/mc/server/network/impl/handler/PingHandler.java +++ /dev/null @@ -1,12 +0,0 @@ -package mc.server.network.impl.handler; - -import io.netty.channel.ChannelHandlerContext; -import mc.protocol.status.PingPacket; - -public class PingHandler extends AbstractPacketHandler { - - @Override - protected void channelRead1(ChannelHandlerContext ctx, PingPacket packet) { - ctx.writeAndFlush(packet).channel().disconnect(); - } -}