mirror of
https://github.com/Nan1t/NanoLimbo.git
synced 2025-07-14 21:20:15 +02:00
Send keep alive frequently. Removed unused packets. Moved initialization in server class
This commit is contained in:
parent
7eb6f75908
commit
bc05228da3
18
settings.properties
Normal file
18
settings.properties
Normal file
@ -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=<YOUR_SECRET_HERE>
|
||||||
|
|
||||||
|
# Read timeout for connections in milliseconds
|
||||||
|
read-timeout=30000
|
@ -17,7 +17,7 @@ public final class LimboConfig {
|
|||||||
|
|
||||||
public static void load(Path file) throws IOException {
|
public static void load(Path file) throws IOException {
|
||||||
if (!Files.exists(file)){
|
if (!Files.exists(file)){
|
||||||
Files.copy(LimboConfig.class.getResourceAsStream("/settings.properties"), file);
|
Files.copy(NanoLimbo.getResource("/settings.properties"), file);
|
||||||
}
|
}
|
||||||
|
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
|
@ -2,28 +2,14 @@ package ru.nanit.limbo;
|
|||||||
|
|
||||||
import ru.nanit.limbo.server.LimboServer;
|
import ru.nanit.limbo.server.LimboServer;
|
||||||
import ru.nanit.limbo.util.Logger;
|
import ru.nanit.limbo.util.Logger;
|
||||||
import ru.nanit.limbo.world.DefaultDimension;
|
|
||||||
import ru.nanit.limbo.world.DefaultWorld;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Paths;
|
|
||||||
|
|
||||||
public final class NanoLimbo {
|
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){
|
public static void main(String[] args){
|
||||||
try {
|
try {
|
||||||
new NanoLimbo().start();
|
new LimboServer().start();
|
||||||
} catch (Exception e){
|
} catch (Exception e){
|
||||||
Logger.error("Cannot start server: ", e);
|
Logger.error("Cannot start server: ", e);
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ import ru.nanit.limbo.server.LimboServer;
|
|||||||
import ru.nanit.limbo.util.Logger;
|
import ru.nanit.limbo.util.Logger;
|
||||||
import ru.nanit.limbo.util.UuidUtil;
|
import ru.nanit.limbo.util.UuidUtil;
|
||||||
import ru.nanit.limbo.world.DefaultDimension;
|
import ru.nanit.limbo.world.DefaultDimension;
|
||||||
import ru.nanit.limbo.world.DefaultWorld;
|
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
public class ClientConnection extends ChannelInboundHandlerAdapter {
|
public class ClientConnection extends ChannelInboundHandlerAdapter {
|
||||||
@ -30,8 +30,18 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
|
|||||||
private final Channel channel;
|
private final Channel channel;
|
||||||
|
|
||||||
private State state;
|
private State state;
|
||||||
|
|
||||||
|
private UUID uuid;
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
|
public UUID getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
public ClientConnection(Channel channel, LimboServer server){
|
public ClientConnection(Channel channel, LimboServer server){
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
@ -40,14 +50,14 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
|
|||||||
@Override
|
@Override
|
||||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||||
if (state.equals(State.PLAY)){
|
if (state.equals(State.PLAY)){
|
||||||
server.decrementPlayers();
|
server.removeConnection(this);
|
||||||
Logger.info("Player %s disconnected", this.username);
|
Logger.info("Player %s disconnected", this.username);
|
||||||
}
|
}
|
||||||
super.channelInactive(ctx);
|
super.channelInactive(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||||
if (channel.isActive()){
|
if (channel.isActive()){
|
||||||
Logger.error("Unhandled exception: %s", cause.getMessage());
|
Logger.error("Unhandled exception: %s", cause.getMessage());
|
||||||
}
|
}
|
||||||
@ -74,12 +84,13 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (packet instanceof PacketLoginStart){
|
if (packet instanceof PacketLoginStart){
|
||||||
if (server.getPlayersCount() >= LimboConfig.getMaxPlayers()){
|
if (server.getConnectionsCount() >= LimboConfig.getMaxPlayers()){
|
||||||
disconnect("Too many players connected");
|
disconnect("Too many players connected");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.username = ((PacketLoginStart) packet).getUsername();
|
this.username = ((PacketLoginStart) packet).getUsername();
|
||||||
|
this.uuid = UuidUtil.getOfflineModeUuid(this.username);
|
||||||
|
|
||||||
PacketLoginSuccess loginSuccess = new PacketLoginSuccess();
|
PacketLoginSuccess loginSuccess = new PacketLoginSuccess();
|
||||||
|
|
||||||
@ -89,7 +100,7 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
|
|||||||
sendPacket(loginSuccess);
|
sendPacket(loginSuccess);
|
||||||
updateState(State.PLAY);
|
updateState(State.PLAY);
|
||||||
|
|
||||||
server.incrementPlayers();
|
server.addConnection(this);
|
||||||
Logger.info("Player %s connected", this.username);
|
Logger.info("Player %s connected", this.username);
|
||||||
|
|
||||||
startJoinProcess();
|
startJoinProcess();
|
||||||
@ -122,34 +133,14 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
|
|||||||
PacketPlayerPositionAndLook positionAndLook = new PacketPlayerPositionAndLook();
|
PacketPlayerPositionAndLook positionAndLook = new PacketPlayerPositionAndLook();
|
||||||
|
|
||||||
positionAndLook.setX(0.0);
|
positionAndLook.setX(0.0);
|
||||||
positionAndLook.setY(2.0);
|
positionAndLook.setY(0.0);
|
||||||
positionAndLook.setZ(0.0);
|
positionAndLook.setZ(0.0);
|
||||||
positionAndLook.setYaw(90.0F);
|
positionAndLook.setYaw(90.0F);
|
||||||
positionAndLook.setPitch(0.0F);
|
positionAndLook.setPitch(0.0F);
|
||||||
positionAndLook.setTeleportId(ThreadLocalRandom.current().nextInt());
|
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(joinGame);
|
||||||
sendPacket(positionAndLook);
|
sendPacket(positionAndLook);
|
||||||
sendPacket(updateViewPos);
|
|
||||||
sendPacket(chunkData);
|
|
||||||
sendPacket(updateViewPos);
|
|
||||||
sendPacket(positionAndLook);
|
|
||||||
|
|
||||||
sendKeepAlive();
|
sendKeepAlive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,12 @@ public class ByteMessage extends ByteBuf {
|
|||||||
this.buf = buf;
|
this.buf = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] toByteArray(){
|
||||||
|
byte[] bytes = new byte[buf.readableBytes()];
|
||||||
|
buf.readBytes(bytes);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
/* Minecraft protocol methods */
|
/* Minecraft protocol methods */
|
||||||
|
|
||||||
public int readVarInt() {
|
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) {
|
public void writeCompoundTagArray(CompoundBinaryTag[] compoundTags) {
|
||||||
try {
|
try {
|
||||||
ByteBufOutputStream stream = new ByteBufOutputStream(buf);
|
ByteBufOutputStream stream = new ByteBufOutputStream(buf);
|
||||||
@ -1125,4 +1138,8 @@ public class ByteMessage extends ByteBuf {
|
|||||||
public boolean release(int decrement) {
|
public boolean release(int decrement) {
|
||||||
return buf.release(decrement);
|
return buf.release(decrement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ByteMessage create(){
|
||||||
|
return new ByteMessage(Unpooled.buffer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -17,6 +17,8 @@ public enum State {
|
|||||||
HANDSHAKING(0){
|
HANDSHAKING(0){
|
||||||
{
|
{
|
||||||
serverBound.register(Version.getMinimal(), 0x00, PacketHandshake::new);
|
serverBound.register(Version.getMinimal(), 0x00, PacketHandshake::new);
|
||||||
|
|
||||||
|
int[] i = new int[16 * 16 * 16];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
STATUS(1){
|
STATUS(1){
|
||||||
@ -36,12 +38,10 @@ public enum State {
|
|||||||
},
|
},
|
||||||
PLAY(3){
|
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, 0x24, PacketJoinGame::new);
|
||||||
clientBound.register(Version.V1_16_4, 0x34, PacketPlayerPositionAndLook::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);
|
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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,30 +5,42 @@ import io.netty.channel.nio.NioEventLoopGroup;
|
|||||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
import ru.nanit.limbo.LimboConfig;
|
import ru.nanit.limbo.LimboConfig;
|
||||||
import ru.nanit.limbo.connection.ClientChannelInitializer;
|
import ru.nanit.limbo.connection.ClientChannelInitializer;
|
||||||
|
import ru.nanit.limbo.connection.ClientConnection;
|
||||||
import ru.nanit.limbo.util.Logger;
|
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 {
|
public final class LimboServer {
|
||||||
|
|
||||||
private AtomicInteger playersCount;
|
private final Map<UUID, ClientConnection> connections = new ConcurrentHashMap<>();
|
||||||
|
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
|
||||||
public int getPlayersCount(){
|
public int getConnectionsCount(){
|
||||||
return playersCount.get();
|
return connections.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void incrementPlayers(){
|
public void addConnection(ClientConnection connection){
|
||||||
playersCount.incrementAndGet();
|
connections.put(connection.getUuid(), connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decrementPlayers(){
|
public void removeConnection(ClientConnection connection){
|
||||||
playersCount.decrementAndGet();
|
connections.remove(connection.getUuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() throws Exception {
|
public void start() throws Exception {
|
||||||
Logger.info("Starting server...");
|
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()
|
ServerBootstrap bootstrap = new ServerBootstrap()
|
||||||
.group(new NioEventLoopGroup(), new NioEventLoopGroup())
|
.group(new NioEventLoopGroup(), new NioEventLoopGroup())
|
||||||
@ -36,7 +48,12 @@ public final class LimboServer {
|
|||||||
.childHandler(new ClientChannelInitializer(this));
|
.childHandler(new ClientChannelInitializer(this));
|
||||||
|
|
||||||
bootstrap.bind(LimboConfig.getHost(), LimboConfig.getPort());
|
bootstrap.bind(LimboConfig.getHost(), LimboConfig.getPort());
|
||||||
|
|
||||||
Logger.info("Server started on %s:%d", LimboConfig.getHost(), LimboConfig.getPort());
|
Logger.info("Server started on %s:%d", LimboConfig.getHost(), LimboConfig.getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void broadcastKeepAlive(){
|
||||||
|
connections.values().forEach(ClientConnection::sendKeepAlive);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user