diff --git a/settings.properties b/settings.properties new file mode 100644 index 0000000..ebcc92a --- /dev/null +++ b/settings.properties @@ -0,0 +1,18 @@ +# +# NanoLimbo configuration +# + +host=localhost +port=65535 +max-players=100 +ping-version=NanoLimbo +ping-description={"text": "NanoLimbo"} + +# Player info forwarding support. Available types: NONE, LEGACY, MODERN +ip-forwarding=LEGACY + +# If you use MODERN type of forwarding, enter your secret code here +ip-forwarding-secret= + +# Read timeout for connections in milliseconds +read-timeout=30000 \ No newline at end of file diff --git a/src/main/java/ru/nanit/limbo/LimboConfig.java b/src/main/java/ru/nanit/limbo/LimboConfig.java index 43c11db..d14b0ab 100644 --- a/src/main/java/ru/nanit/limbo/LimboConfig.java +++ b/src/main/java/ru/nanit/limbo/LimboConfig.java @@ -17,7 +17,7 @@ public final class LimboConfig { public static void load(Path file) throws IOException { if (!Files.exists(file)){ - Files.copy(LimboConfig.class.getResourceAsStream("/settings.properties"), file); + Files.copy(NanoLimbo.getResource("/settings.properties"), file); } Properties properties = new Properties(); diff --git a/src/main/java/ru/nanit/limbo/NanoLimbo.java b/src/main/java/ru/nanit/limbo/NanoLimbo.java index 177be2d..1f5d957 100644 --- a/src/main/java/ru/nanit/limbo/NanoLimbo.java +++ b/src/main/java/ru/nanit/limbo/NanoLimbo.java @@ -2,28 +2,14 @@ package ru.nanit.limbo; import ru.nanit.limbo.server.LimboServer; import ru.nanit.limbo.util.Logger; -import ru.nanit.limbo.world.DefaultDimension; -import ru.nanit.limbo.world.DefaultWorld; import java.io.InputStream; -import java.nio.file.Paths; public final class NanoLimbo { - private LimboServer server; - - public void start() throws Exception { - LimboConfig.load(Paths.get("./settings.properties")); - DefaultDimension.init(); - DefaultWorld.init(); - - server = new LimboServer(); - server.start(); - } - public static void main(String[] args){ try { - new NanoLimbo().start(); + new LimboServer().start(); } catch (Exception e){ Logger.error("Cannot start server: ", e); } diff --git a/src/main/java/ru/nanit/limbo/connection/ClientConnection.java b/src/main/java/ru/nanit/limbo/connection/ClientConnection.java index 4e8e2ff..0ad737e 100644 --- a/src/main/java/ru/nanit/limbo/connection/ClientConnection.java +++ b/src/main/java/ru/nanit/limbo/connection/ClientConnection.java @@ -20,8 +20,8 @@ import ru.nanit.limbo.server.LimboServer; import ru.nanit.limbo.util.Logger; import ru.nanit.limbo.util.UuidUtil; import ru.nanit.limbo.world.DefaultDimension; -import ru.nanit.limbo.world.DefaultWorld; +import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; public class ClientConnection extends ChannelInboundHandlerAdapter { @@ -30,8 +30,18 @@ public class ClientConnection extends ChannelInboundHandlerAdapter { private final Channel channel; private State state; + + private UUID uuid; private String username; + public UUID getUuid() { + return uuid; + } + + public String getUsername() { + return username; + } + public ClientConnection(Channel channel, LimboServer server){ this.server = server; this.channel = channel; @@ -40,14 +50,14 @@ public class ClientConnection extends ChannelInboundHandlerAdapter { @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { if (state.equals(State.PLAY)){ - server.decrementPlayers(); + server.removeConnection(this); Logger.info("Player %s disconnected", this.username); } super.channelInactive(ctx); } @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { if (channel.isActive()){ Logger.error("Unhandled exception: %s", cause.getMessage()); } @@ -74,12 +84,13 @@ public class ClientConnection extends ChannelInboundHandlerAdapter { } if (packet instanceof PacketLoginStart){ - if (server.getPlayersCount() >= LimboConfig.getMaxPlayers()){ + if (server.getConnectionsCount() >= LimboConfig.getMaxPlayers()){ disconnect("Too many players connected"); return; } this.username = ((PacketLoginStart) packet).getUsername(); + this.uuid = UuidUtil.getOfflineModeUuid(this.username); PacketLoginSuccess loginSuccess = new PacketLoginSuccess(); @@ -89,7 +100,7 @@ public class ClientConnection extends ChannelInboundHandlerAdapter { sendPacket(loginSuccess); updateState(State.PLAY); - server.incrementPlayers(); + server.addConnection(this); Logger.info("Player %s connected", this.username); startJoinProcess(); @@ -122,34 +133,14 @@ public class ClientConnection extends ChannelInboundHandlerAdapter { PacketPlayerPositionAndLook positionAndLook = new PacketPlayerPositionAndLook(); positionAndLook.setX(0.0); - positionAndLook.setY(2.0); + positionAndLook.setY(0.0); positionAndLook.setZ(0.0); positionAndLook.setYaw(90.0F); positionAndLook.setPitch(0.0F); positionAndLook.setTeleportId(ThreadLocalRandom.current().nextInt()); - PacketUpdateViewPos updateViewPos = new PacketUpdateViewPos(); - - updateViewPos.setChunkX(0); - updateViewPos.setChunkY(0); - - PacketChunkData chunkData = new PacketChunkData(); - - chunkData.setChunkX(0); - chunkData.setChunkZ(0); - chunkData.setFullChunk(false); - chunkData.setPrimaryBitMask(1); - chunkData.setHeightMaps(DefaultWorld.getHeightMaps()); - chunkData.setData(new byte[0]); - chunkData.setBlockEntities(new CompoundBinaryTag[]{CompoundBinaryTag.empty()}); - sendPacket(joinGame); sendPacket(positionAndLook); - sendPacket(updateViewPos); - sendPacket(chunkData); - sendPacket(updateViewPos); - sendPacket(positionAndLook); - sendKeepAlive(); } diff --git a/src/main/java/ru/nanit/limbo/protocol/ByteMessage.java b/src/main/java/ru/nanit/limbo/protocol/ByteMessage.java index 0b0ecca..5619e56 100644 --- a/src/main/java/ru/nanit/limbo/protocol/ByteMessage.java +++ b/src/main/java/ru/nanit/limbo/protocol/ByteMessage.java @@ -27,6 +27,12 @@ public class ByteMessage extends ByteBuf { this.buf = buf; } + public byte[] toByteArray(){ + byte[] bytes = new byte[buf.readableBytes()]; + buf.readBytes(bytes); + return bytes; + } + /* Minecraft protocol methods */ public int readVarInt() { @@ -127,6 +133,13 @@ public class ByteMessage extends ByteBuf { } } + public void writeLongArray(long[] array) { + writeVarInt(array.length); + for (long i : array) { + writeLong(i); + } + } + public void writeCompoundTagArray(CompoundBinaryTag[] compoundTags) { try { ByteBufOutputStream stream = new ByteBufOutputStream(buf); @@ -1125,4 +1138,8 @@ public class ByteMessage extends ByteBuf { public boolean release(int decrement) { return buf.release(decrement); } + + public static ByteMessage create(){ + return new ByteMessage(Unpooled.buffer()); + } } diff --git a/src/main/java/ru/nanit/limbo/protocol/packets/play/PacketChunkData.java b/src/main/java/ru/nanit/limbo/protocol/packets/play/PacketChunkData.java deleted file mode 100644 index 9205877..0000000 --- a/src/main/java/ru/nanit/limbo/protocol/packets/play/PacketChunkData.java +++ /dev/null @@ -1,69 +0,0 @@ -package ru.nanit.limbo.protocol.packets.play; - -import net.kyori.adventure.nbt.CompoundBinaryTag; -import ru.nanit.limbo.protocol.ByteMessage; -import ru.nanit.limbo.protocol.PacketOut; -import ru.nanit.limbo.protocol.registry.Version; - -public class PacketChunkData implements PacketOut { - - private int chunkX; - private int chunkZ; - private boolean fullChunk; - private int primaryBitMask; - private CompoundBinaryTag heightMaps; - private int[] biomes; - private byte[] data; - private CompoundBinaryTag[] blockEntities; - - public void setChunkX(int chunkX) { - this.chunkX = chunkX; - } - - public void setChunkZ(int chunkZ) { - this.chunkZ = chunkZ; - } - - public void setFullChunk(boolean fullChunk) { - this.fullChunk = fullChunk; - } - - public void setPrimaryBitMask(int primaryBitMask) { - this.primaryBitMask = primaryBitMask; - } - - public void setHeightMaps(CompoundBinaryTag heightMaps) { - this.heightMaps = heightMaps; - } - - public void setBiomes(int[] biomes) { - this.biomes = biomes; - } - - public void setData(byte[] data) { - this.data = data; - } - - public void setBlockEntities(CompoundBinaryTag[] blockEntities) { - this.blockEntities = blockEntities; - } - - @Override - public void encode(ByteMessage msg, Version version) { - msg.writeInt(chunkX); - msg.writeInt(chunkZ); - msg.writeBoolean(fullChunk); - msg.writeVarInt(primaryBitMask); - msg.writeCompoundTag(heightMaps); - - if (fullChunk){ - msg.writeVarIntArray(biomes); - } - - - - msg.writeBytesArray(data); - msg.writeCompoundTagArray(blockEntities); - } - -} diff --git a/src/main/java/ru/nanit/limbo/protocol/packets/play/PacketUpdateViewPos.java b/src/main/java/ru/nanit/limbo/protocol/packets/play/PacketUpdateViewPos.java deleted file mode 100644 index 736eed9..0000000 --- a/src/main/java/ru/nanit/limbo/protocol/packets/play/PacketUpdateViewPos.java +++ /dev/null @@ -1,26 +0,0 @@ -package ru.nanit.limbo.protocol.packets.play; - -import ru.nanit.limbo.protocol.ByteMessage; -import ru.nanit.limbo.protocol.PacketOut; -import ru.nanit.limbo.protocol.registry.Version; - -public class PacketUpdateViewPos implements PacketOut { - - private int chunkX; - private int chunkY; - - public void setChunkX(int chunkX) { - this.chunkX = chunkX; - } - - public void setChunkY(int chunkY) { - this.chunkY = chunkY; - } - - @Override - public void encode(ByteMessage msg, Version version) { - msg.writeVarInt(chunkX); - msg.writeVarInt(chunkY); - } - -} diff --git a/src/main/java/ru/nanit/limbo/protocol/registry/State.java b/src/main/java/ru/nanit/limbo/protocol/registry/State.java index 4567119..9d60273 100644 --- a/src/main/java/ru/nanit/limbo/protocol/registry/State.java +++ b/src/main/java/ru/nanit/limbo/protocol/registry/State.java @@ -17,6 +17,8 @@ public enum State { HANDSHAKING(0){ { serverBound.register(Version.getMinimal(), 0x00, PacketHandshake::new); + + int[] i = new int[16 * 16 * 16]; } }, STATUS(1){ @@ -36,12 +38,10 @@ public enum State { }, PLAY(3){ { - clientBound.register(Version.V1_16_4, 0x20, PacketChunkData::new); clientBound.register(Version.V1_16_4, 0x24, PacketJoinGame::new); clientBound.register(Version.V1_16_4, 0x34, PacketPlayerPositionAndLook::new); - clientBound.register(Version.V1_16_4, 0x40, PacketUpdateViewPos::new); clientBound.register(Version.V1_16_4, 0x1F, PacketKeepAlive::new); - serverBound.register(Version.V1_16_4, 0x1F, PacketKeepAlive::new); + serverBound.register(Version.V1_16_4, 0x10, PacketKeepAlive::new); } }; diff --git a/src/main/java/ru/nanit/limbo/server/LimboServer.java b/src/main/java/ru/nanit/limbo/server/LimboServer.java index 99470b7..9bea27a 100644 --- a/src/main/java/ru/nanit/limbo/server/LimboServer.java +++ b/src/main/java/ru/nanit/limbo/server/LimboServer.java @@ -5,30 +5,42 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import ru.nanit.limbo.LimboConfig; import ru.nanit.limbo.connection.ClientChannelInitializer; +import ru.nanit.limbo.connection.ClientConnection; import ru.nanit.limbo.util.Logger; +import ru.nanit.limbo.world.DefaultDimension; -import java.util.concurrent.atomic.AtomicInteger; +import java.nio.file.Paths; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; public final class LimboServer { - private AtomicInteger playersCount; + private final Map connections = new ConcurrentHashMap<>(); + private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); - public int getPlayersCount(){ - return playersCount.get(); + public int getConnectionsCount(){ + return connections.size(); } - public void incrementPlayers(){ - playersCount.incrementAndGet(); + public void addConnection(ClientConnection connection){ + connections.put(connection.getUuid(), connection); } - public void decrementPlayers(){ - playersCount.decrementAndGet(); + public void removeConnection(ClientConnection connection){ + connections.remove(connection.getUuid()); } public void start() throws Exception { Logger.info("Starting server..."); - playersCount = new AtomicInteger(); + LimboConfig.load(Paths.get("./settings.properties")); + DefaultDimension.init(); + + executor.scheduleAtFixedRate(this::broadcastKeepAlive, 0L, 5L, TimeUnit.SECONDS); ServerBootstrap bootstrap = new ServerBootstrap() .group(new NioEventLoopGroup(), new NioEventLoopGroup()) @@ -36,7 +48,12 @@ public final class LimboServer { .childHandler(new ClientChannelInitializer(this)); bootstrap.bind(LimboConfig.getHost(), LimboConfig.getPort()); + Logger.info("Server started on %s:%d", LimboConfig.getHost(), LimboConfig.getPort()); } + private void broadcastKeepAlive(){ + connections.values().forEach(ClientConnection::sendKeepAlive); + } + } diff --git a/src/main/java/ru/nanit/limbo/world/DefaultWorld.java b/src/main/java/ru/nanit/limbo/world/DefaultWorld.java deleted file mode 100644 index 1fbe99a..0000000 --- a/src/main/java/ru/nanit/limbo/world/DefaultWorld.java +++ /dev/null @@ -1,32 +0,0 @@ -package ru.nanit.limbo.world; - -import net.kyori.adventure.nbt.CompoundBinaryTag; - -public final class DefaultWorld { - - private static CompoundBinaryTag heightMaps; - - private DefaultWorld(){} - - public static void init(){ - heightMaps = CompoundBinaryTag.builder() - .putLongArray("MOTION_BLOCKING", new long[]{ - 1371773531765642314L, 1389823183635651148L, 1371738278539598925L, - 1389823183635388492L, 1353688558756731469L, 1389823114781694027L, 1317765589597723213L, - 1371773531899860042L, 1389823183635651149L, 1371773462911685197L, 1389823183635650636L, - 1353688626805119565L, 1371773531900123211L, 1335639250618849869L, 1371738278674077258L, - 1389823114781694028L, 1353723811310638154L, 1371738278674077259L, 1335674228429068364L, - 1335674228429067338L, 1335674228698027594L, 1317624576693539402L, 1335709481520370249L, - 1299610178184057417L, 1335638906349064264L, 1299574993811968586L, 1299574924958011464L, - 1299610178184056904L, 1299574924958011464L, 1299610109330100296L, 1299574924958011464L, - 1299574924823793736L, 1299574924958011465L, 1281525273222484040L, 1299574924958011464L, - 1281525273222484040L, 9548107335L - }) - .build(); - } - - public static CompoundBinaryTag getHeightMaps(){ - return heightMaps; - } - -}