mirror of
https://github.com/Nan1t/NanoLimbo.git
synced 2025-07-09 11:30:13 +02:00
Moved from Gitlab
This commit is contained in:
commit
ccedd3a160
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.idea
|
||||||
|
.gradle
|
||||||
|
gradle
|
||||||
|
gradlew
|
||||||
|
gradlew.bat
|
||||||
|
build
|
26
build.gradle
Normal file
26
build.gradle
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
group 'ru.nanit'
|
||||||
|
version '1.0'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||||
|
compile group: 'io.netty', name: 'netty-all', version: '4.1.54.Final'
|
||||||
|
compile group: 'net.kyori', name: 'adventure-nbt', version: '4.1.1'
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
manifest {
|
||||||
|
attributes("Main-Class": "ru.nanit.limbo.NanoLimbo")
|
||||||
|
}
|
||||||
|
|
||||||
|
from {
|
||||||
|
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
|
||||||
|
}
|
||||||
|
}
|
2
settings.gradle
Normal file
2
settings.gradle
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rootProject.name = 'NanoLimbo'
|
||||||
|
|
94
src/main/java/ru/nanit/limbo/LimboConfig.java
Normal file
94
src/main/java/ru/nanit/limbo/LimboConfig.java
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package ru.nanit.limbo;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public final class LimboConfig {
|
||||||
|
|
||||||
|
private static String host;
|
||||||
|
private static int port;
|
||||||
|
private static boolean onlineMode;
|
||||||
|
private static int maxPlayers;
|
||||||
|
private static IpForwardingType ipForwardingType;
|
||||||
|
private static long readTimeout;
|
||||||
|
private static PingData pingData;
|
||||||
|
|
||||||
|
public static void load(Path file) throws IOException {
|
||||||
|
if (!Files.exists(file)){
|
||||||
|
Files.copy(LimboConfig.class.getResourceAsStream("/settings.properties"), file);
|
||||||
|
}
|
||||||
|
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.load(Files.newInputStream(file));
|
||||||
|
|
||||||
|
host = properties.getProperty("host");
|
||||||
|
port = Integer.parseInt(properties.getProperty("port"));
|
||||||
|
onlineMode = Boolean.parseBoolean(properties.getProperty("online-mode"));
|
||||||
|
maxPlayers = Integer.parseInt(properties.getProperty("max-players"));
|
||||||
|
ipForwardingType = IpForwardingType.valueOf(properties.getProperty("ip-forwarding").toUpperCase());
|
||||||
|
readTimeout = Long.parseLong(properties.getProperty("read-timeout"));
|
||||||
|
pingData = new PingData();
|
||||||
|
|
||||||
|
pingData.setVersion(properties.getProperty("ping-version"));
|
||||||
|
pingData.setDescription(properties.getProperty("ping-description"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isOnlineMode() {
|
||||||
|
return onlineMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getMaxPlayers() {
|
||||||
|
return maxPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IpForwardingType getIpForwardingType() {
|
||||||
|
return ipForwardingType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getReadTimeout() {
|
||||||
|
return readTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PingData getPingData() {
|
||||||
|
return pingData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum IpForwardingType {
|
||||||
|
NONE,
|
||||||
|
LEGACY,
|
||||||
|
MODERN
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PingData {
|
||||||
|
|
||||||
|
private String version;
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(String version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
26
src/main/java/ru/nanit/limbo/NanoLimbo.java
Normal file
26
src/main/java/ru/nanit/limbo/NanoLimbo.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package ru.nanit.limbo;
|
||||||
|
|
||||||
|
import ru.nanit.limbo.server.LimboServer;
|
||||||
|
import ru.nanit.limbo.util.Logger;
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public final class NanoLimbo {
|
||||||
|
|
||||||
|
private LimboServer server;
|
||||||
|
|
||||||
|
public void start() throws Exception {
|
||||||
|
LimboConfig.load(Paths.get("./settings.properties"));
|
||||||
|
|
||||||
|
server = new LimboServer();
|
||||||
|
server.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args){
|
||||||
|
try {
|
||||||
|
new NanoLimbo().start();
|
||||||
|
} catch (Exception e){
|
||||||
|
Logger.error("Cannot start server: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package ru.nanit.limbo.connection;
|
||||||
|
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.ChannelPipeline;
|
||||||
|
import io.netty.handler.timeout.ReadTimeoutHandler;
|
||||||
|
import ru.nanit.limbo.LimboConfig;
|
||||||
|
import ru.nanit.limbo.protocol.pipeline.VarIntFrameDecoder;
|
||||||
|
import ru.nanit.limbo.protocol.pipeline.PacketDecoder;
|
||||||
|
import ru.nanit.limbo.protocol.pipeline.PacketEncoder;
|
||||||
|
import ru.nanit.limbo.protocol.pipeline.VarIntLengthEncoder;
|
||||||
|
import ru.nanit.limbo.server.LimboServer;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class ClientChannelInitializer extends ChannelInitializer<Channel> {
|
||||||
|
|
||||||
|
private final LimboServer server;
|
||||||
|
|
||||||
|
public ClientChannelInitializer(LimboServer server){
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initChannel(Channel channel) {
|
||||||
|
ChannelPipeline pipeline = channel.pipeline();
|
||||||
|
|
||||||
|
pipeline.addLast("timeout", new ReadTimeoutHandler(LimboConfig.getReadTimeout(), TimeUnit.MILLISECONDS));
|
||||||
|
pipeline.addLast("frame_decoder", new VarIntFrameDecoder());
|
||||||
|
pipeline.addLast("frame_encoder", new VarIntLengthEncoder());
|
||||||
|
pipeline.addLast("decoder", new PacketDecoder());
|
||||||
|
pipeline.addLast("encoder", new PacketEncoder());
|
||||||
|
pipeline.addLast("handler", new ClientConnection(channel, server));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
105
src/main/java/ru/nanit/limbo/connection/ClientConnection.java
Normal file
105
src/main/java/ru/nanit/limbo/connection/ClientConnection.java
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package ru.nanit.limbo.connection;
|
||||||
|
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelFutureListener;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
|
import ru.nanit.limbo.protocol.packets.login.*;
|
||||||
|
import ru.nanit.limbo.protocol.registry.Version;
|
||||||
|
import ru.nanit.limbo.protocol.pipeline.PacketDecoder;
|
||||||
|
import ru.nanit.limbo.protocol.pipeline.PacketEncoder;
|
||||||
|
import ru.nanit.limbo.protocol.packets.PacketHandshake;
|
||||||
|
import ru.nanit.limbo.protocol.packets.status.PacketStatusPing;
|
||||||
|
import ru.nanit.limbo.protocol.packets.status.PacketStatusRequest;
|
||||||
|
import ru.nanit.limbo.protocol.packets.status.PacketStatusResponse;
|
||||||
|
import ru.nanit.limbo.protocol.registry.State;
|
||||||
|
import ru.nanit.limbo.server.LimboServer;
|
||||||
|
import ru.nanit.limbo.util.UuidUtil;
|
||||||
|
|
||||||
|
public class ClientConnection extends ChannelInboundHandlerAdapter {
|
||||||
|
|
||||||
|
private final LimboServer server;
|
||||||
|
private final Channel channel;
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
public ClientConnection(Channel channel, LimboServer server){
|
||||||
|
this.channel = channel;
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||||
|
handlePacket(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlePacket(Object packet){
|
||||||
|
if (packet instanceof PacketHandshake){
|
||||||
|
PacketHandshake handshake = (PacketHandshake) packet;
|
||||||
|
State state = State.getById(handshake.getNextState());
|
||||||
|
updateStateAndVersion(state, handshake.getVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet instanceof PacketStatusRequest){
|
||||||
|
sendPacket(new PacketStatusResponse());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet instanceof PacketStatusPing){
|
||||||
|
sendPacketAndClose(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet instanceof PacketLoginStart){
|
||||||
|
this.username = ((PacketLoginStart) packet).getUsername();
|
||||||
|
|
||||||
|
// Limbo always in offline mode. Online mode set on proxy side
|
||||||
|
PacketLoginSuccess loginSuccess = new PacketLoginSuccess();
|
||||||
|
|
||||||
|
loginSuccess.setUuid(UuidUtil.getOfflineModeUuid(this.username));
|
||||||
|
loginSuccess.setUsername(this.username);
|
||||||
|
|
||||||
|
sendPacket(loginSuccess);
|
||||||
|
updateState(State.PLAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPacket(Object packet){
|
||||||
|
if (isConnected())
|
||||||
|
channel.writeAndFlush(packet, channel.voidPromise());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPacketAndClose(Object packet){
|
||||||
|
if (isConnected())
|
||||||
|
channel.writeAndFlush(packet).addListener(ChannelFutureListener.CLOSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect(){
|
||||||
|
if (channel.isActive()){
|
||||||
|
channel.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect(String reason){
|
||||||
|
PacketDisconnect packet = new PacketDisconnect();
|
||||||
|
packet.setReason(reason);
|
||||||
|
sendPacketAndClose(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnected(){
|
||||||
|
return channel.isActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateState(State state){
|
||||||
|
channel.pipeline().get(PacketDecoder.class).updateState(state);
|
||||||
|
channel.pipeline().get(PacketEncoder.class).updateState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateStateAndVersion(State state, Version version){
|
||||||
|
PacketDecoder decoder = channel.pipeline().get(PacketDecoder.class);
|
||||||
|
PacketEncoder encoder = channel.pipeline().get(PacketEncoder.class);
|
||||||
|
|
||||||
|
decoder.updateVersion(version);
|
||||||
|
decoder.updateState(state);
|
||||||
|
encoder.updateVersion(version);
|
||||||
|
encoder.updateState(state);
|
||||||
|
}
|
||||||
|
}
|
1107
src/main/java/ru/nanit/limbo/protocol/ByteMessage.java
Normal file
1107
src/main/java/ru/nanit/limbo/protocol/ByteMessage.java
Normal file
File diff suppressed because it is too large
Load Diff
8
src/main/java/ru/nanit/limbo/protocol/Direction.java
Normal file
8
src/main/java/ru/nanit/limbo/protocol/Direction.java
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package ru.nanit.limbo.protocol;
|
||||||
|
|
||||||
|
public enum Direction {
|
||||||
|
|
||||||
|
CLIENT,
|
||||||
|
SERVER
|
||||||
|
|
||||||
|
}
|
11
src/main/java/ru/nanit/limbo/protocol/Packet.java
Normal file
11
src/main/java/ru/nanit/limbo/protocol/Packet.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package ru.nanit.limbo.protocol;
|
||||||
|
|
||||||
|
import ru.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public interface Packet {
|
||||||
|
|
||||||
|
void encode(ByteMessage msg, Direction direction, Version version);
|
||||||
|
|
||||||
|
void decode(ByteMessage msg, Direction direction, Version version);
|
||||||
|
|
||||||
|
}
|
12
src/main/java/ru/nanit/limbo/protocol/PacketIn.java
Normal file
12
src/main/java/ru/nanit/limbo/protocol/PacketIn.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package ru.nanit.limbo.protocol;
|
||||||
|
|
||||||
|
import ru.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public interface PacketIn extends Packet {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void encode(ByteMessage msg, Direction direction, Version version) {
|
||||||
|
// Can be ignored for incoming packets
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
src/main/java/ru/nanit/limbo/protocol/PacketOut.java
Normal file
12
src/main/java/ru/nanit/limbo/protocol/PacketOut.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package ru.nanit.limbo.protocol;
|
||||||
|
|
||||||
|
import ru.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public interface PacketOut extends Packet {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void decode(ByteMessage msg, Direction direction, Version version) {
|
||||||
|
// Can be ignored for outgoing packets
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package ru.nanit.limbo.protocol.packets;
|
||||||
|
|
||||||
|
import ru.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ru.nanit.limbo.protocol.Packet;
|
||||||
|
import ru.nanit.limbo.protocol.Direction;
|
||||||
|
import ru.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketHandshake implements Packet {
|
||||||
|
|
||||||
|
private Version version;
|
||||||
|
private String host;
|
||||||
|
private int port;
|
||||||
|
private int nextState;
|
||||||
|
|
||||||
|
public Version getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(Version version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHost(String host) {
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPort(int port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNextState() {
|
||||||
|
return nextState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextState(int nextState) {
|
||||||
|
this.nextState = nextState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Direction direction, Version version) {
|
||||||
|
msg.writeVarInt(this.version.getProtocolNumber());
|
||||||
|
msg.writeString(host);
|
||||||
|
msg.writeShort(port);
|
||||||
|
msg.writeVarInt(nextState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decode(ByteMessage msg, Direction direction, Version version) {
|
||||||
|
this.version = Version.of(msg.readVarInt());
|
||||||
|
this.host = msg.readString();
|
||||||
|
this.port = msg.readUnsignedShort();
|
||||||
|
this.nextState = msg.readVarInt();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package ru.nanit.limbo.protocol.packets.login;
|
||||||
|
|
||||||
|
import ru.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ru.nanit.limbo.protocol.Direction;
|
||||||
|
import ru.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ru.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketDisconnect implements PacketOut {
|
||||||
|
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
public void setReason(String reason) {
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Direction direction, Version version) {
|
||||||
|
msg.writeString(String.format("{\"text\": \"%s\"}", reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package ru.nanit.limbo.protocol.packets.login;
|
||||||
|
|
||||||
|
import ru.nanit.limbo.protocol.*;
|
||||||
|
import ru.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketLoginStart implements PacketIn {
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decode(ByteMessage msg, Direction direction, Version version) {
|
||||||
|
this.username = msg.readString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package ru.nanit.limbo.protocol.packets.login;
|
||||||
|
|
||||||
|
import ru.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ru.nanit.limbo.protocol.Direction;
|
||||||
|
import ru.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ru.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class PacketLoginSuccess implements PacketOut {
|
||||||
|
|
||||||
|
private UUID uuid;
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
public void setUuid(UUID uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Direction direction, Version version) {
|
||||||
|
msg.writeUuid(uuid);
|
||||||
|
msg.writeString(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
package ru.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||||
|
import ru.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ru.nanit.limbo.protocol.Direction;
|
||||||
|
import ru.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ru.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PacketJoinGame implements PacketOut {
|
||||||
|
|
||||||
|
private int entityId;
|
||||||
|
private boolean isHardcore = false;
|
||||||
|
private int gameMode = 2;
|
||||||
|
private int previousGameMode = -1;
|
||||||
|
private int worldCount = 1;
|
||||||
|
private List<String> worldNames;
|
||||||
|
private CompoundBinaryTag dimensionCodec;
|
||||||
|
private CompoundBinaryTag dimension;
|
||||||
|
private String worldName;
|
||||||
|
private long hashedSeed;
|
||||||
|
private int maxPlayers;
|
||||||
|
private int viewDistance = 2;
|
||||||
|
private boolean reducedDebugInfo;
|
||||||
|
private boolean enableRespawnScreen;
|
||||||
|
private boolean isDebug;
|
||||||
|
private boolean isFlat;
|
||||||
|
|
||||||
|
public void setEntityId(int entityId) {
|
||||||
|
this.entityId = entityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHardcore(boolean hardcore) {
|
||||||
|
isHardcore = hardcore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGameMode(int gameMode) {
|
||||||
|
this.gameMode = gameMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreviousGameMode(int previousGameMode) {
|
||||||
|
this.previousGameMode = previousGameMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWorldCount(int worldCount) {
|
||||||
|
this.worldCount = worldCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWorldNames(List<String> worldNames) {
|
||||||
|
this.worldNames = worldNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDimensionCodec(CompoundBinaryTag dimensionCodec) {
|
||||||
|
this.dimensionCodec = dimensionCodec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDimension(CompoundBinaryTag dimension) {
|
||||||
|
this.dimension = dimension;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWorldName(String worldName) {
|
||||||
|
this.worldName = worldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHashedSeed(long hashedSeed) {
|
||||||
|
this.hashedSeed = hashedSeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxPlayers(int maxPlayers) {
|
||||||
|
this.maxPlayers = maxPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setViewDistance(int viewDistance) {
|
||||||
|
this.viewDistance = viewDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReducedDebugInfo(boolean reducedDebugInfo) {
|
||||||
|
this.reducedDebugInfo = reducedDebugInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnableRespawnScreen(boolean enableRespawnScreen) {
|
||||||
|
this.enableRespawnScreen = enableRespawnScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDebug(boolean debug) {
|
||||||
|
isDebug = debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlat(boolean flat) {
|
||||||
|
isFlat = flat;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Direction direction, Version version) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package ru.nanit.limbo.protocol.packets.status;
|
||||||
|
|
||||||
|
import ru.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ru.nanit.limbo.protocol.Packet;
|
||||||
|
import ru.nanit.limbo.protocol.Direction;
|
||||||
|
import ru.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketStatusPing implements Packet {
|
||||||
|
|
||||||
|
private long randomId;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Direction direction, Version version) {
|
||||||
|
msg.writeLong(randomId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decode(ByteMessage msg, Direction direction, Version version) {
|
||||||
|
this.randomId = msg.readLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package ru.nanit.limbo.protocol.packets.status;
|
||||||
|
|
||||||
|
import ru.nanit.limbo.protocol.*;
|
||||||
|
import ru.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketStatusRequest implements PacketIn {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decode(ByteMessage msg, Direction direction, Version version) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package ru.nanit.limbo.protocol.packets.status;
|
||||||
|
|
||||||
|
import ru.nanit.limbo.LimboConfig;
|
||||||
|
import ru.nanit.limbo.protocol.*;
|
||||||
|
import ru.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketStatusResponse implements PacketOut {
|
||||||
|
|
||||||
|
private static final String TEMPLATE = "{ \"version\": { \"name\": \"%s\", \"protocol\": %d }, \"players\": { \"max\": %d, \"online\": %d, \"sample\": [] }, \"description\": %s }";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Direction direction, Version version) {
|
||||||
|
String ver = LimboConfig.getPingData().getVersion();
|
||||||
|
String desc = LimboConfig.getPingData().getDescription();
|
||||||
|
String json = getResponseJson(ver, version.getProtocolNumber(), LimboConfig.getMaxPlayers(), 0, desc);
|
||||||
|
msg.writeString(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getResponseJson(String version, int protocol, int maxPlayers, int online, String description){
|
||||||
|
return String.format(TEMPLATE, version, protocol, maxPlayers, online, description);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package ru.nanit.limbo.protocol.pipeline;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||||
|
import ru.nanit.limbo.protocol.*;
|
||||||
|
import ru.nanit.limbo.protocol.registry.State;
|
||||||
|
import ru.nanit.limbo.protocol.registry.Version;
|
||||||
|
import ru.nanit.limbo.util.Logger;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PacketDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||||
|
|
||||||
|
private State.PacketVersionRegistry.PacketIdRegistry<?> mappings;
|
||||||
|
private Version version;
|
||||||
|
|
||||||
|
public PacketDecoder(){
|
||||||
|
updateVersion(Version.getMinimal());
|
||||||
|
updateState(State.HANDSHAKING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> out) throws Exception {
|
||||||
|
if (!ctx.channel().isActive() || mappings == null) return;
|
||||||
|
|
||||||
|
ByteMessage msg = new ByteMessage(buf);
|
||||||
|
int packetId = msg.readVarInt();
|
||||||
|
Packet packet = mappings.getPacket(packetId);
|
||||||
|
|
||||||
|
if (packet != null){
|
||||||
|
try {
|
||||||
|
packet.decode(msg, Direction.SERVER, mappings.getVersion());
|
||||||
|
} catch (Exception e){
|
||||||
|
Logger.warning("Cannot decode packet 0x%s: %s", Integer.toHexString(packetId), e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fireChannelRead(packet);
|
||||||
|
} else {
|
||||||
|
Logger.warning("Undefined incoming packet: 0x" + Integer.toHexString(packetId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateVersion(Version version){
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateState(State state){
|
||||||
|
this.mappings = state.serverBound.getRegistry(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package ru.nanit.limbo.protocol.pipeline;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.MessageToByteEncoder;
|
||||||
|
import ru.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ru.nanit.limbo.protocol.Packet;
|
||||||
|
import ru.nanit.limbo.protocol.Direction;
|
||||||
|
import ru.nanit.limbo.protocol.registry.Version;
|
||||||
|
import ru.nanit.limbo.protocol.registry.State;
|
||||||
|
import ru.nanit.limbo.util.Logger;
|
||||||
|
|
||||||
|
public class PacketEncoder extends MessageToByteEncoder<Packet> {
|
||||||
|
|
||||||
|
private State.PacketVersionRegistry.PacketIdRegistry<?> mappings;
|
||||||
|
private Version version;
|
||||||
|
|
||||||
|
public PacketEncoder(){
|
||||||
|
updateVersion(Version.getMinimal());
|
||||||
|
updateState(State.HANDSHAKING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void encode(ChannelHandlerContext ctx, Packet packet, ByteBuf out) throws Exception {
|
||||||
|
if (mappings == null) return;
|
||||||
|
|
||||||
|
ByteMessage msg = new ByteMessage(out);
|
||||||
|
int packetId = mappings.getPacketId(packet.getClass());
|
||||||
|
|
||||||
|
if (packetId == -1){
|
||||||
|
Logger.warning("Undefined packet class: %s", packet.getClass().getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.writeVarInt(packetId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
packet.encode(msg, Direction.CLIENT, version);
|
||||||
|
} catch (Exception e){
|
||||||
|
Logger.warning("Cannot encode packet 0x%s: %s", Integer.toHexString(packetId), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateVersion(Version version){
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateState(State state){
|
||||||
|
this.mappings = state.clientBound.getRegistry(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package ru.nanit.limbo.protocol.pipeline;
|
||||||
|
|
||||||
|
import io.netty.util.ByteProcessor;
|
||||||
|
|
||||||
|
public class VarIntByteDecoder implements ByteProcessor {
|
||||||
|
|
||||||
|
private int readVarInt;
|
||||||
|
private int bytesRead;
|
||||||
|
private DecodeResult result = DecodeResult.TOO_SHORT;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean process(byte k) {
|
||||||
|
readVarInt |= (k & 0x7F) << bytesRead++ * 7;
|
||||||
|
if (bytesRead > 3) {
|
||||||
|
result = DecodeResult.TOO_BIG;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((k & 0x80) != 128) {
|
||||||
|
result = DecodeResult.SUCCESS;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getReadVarint() {
|
||||||
|
return readVarInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBytesRead() {
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DecodeResult getResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum DecodeResult {
|
||||||
|
SUCCESS,
|
||||||
|
TOO_SHORT,
|
||||||
|
TOO_BIG
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package ru.nanit.limbo.protocol.pipeline;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
|
import ru.nanit.limbo.util.Logger;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class VarIntFrameDecoder extends ByteToMessageDecoder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
|
||||||
|
if (!ctx.channel().isActive()) {
|
||||||
|
in.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final VarIntByteDecoder reader = new VarIntByteDecoder();
|
||||||
|
|
||||||
|
int varintEnd = in.forEachByte(reader);
|
||||||
|
|
||||||
|
if (varintEnd == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader.getResult() == VarIntByteDecoder.DecodeResult.SUCCESS) {
|
||||||
|
int readVarint = reader.getReadVarint();
|
||||||
|
int bytesRead = reader.getBytesRead();
|
||||||
|
if (readVarint < 0) {
|
||||||
|
Logger.error("BAD_LENGTH_CACHED");
|
||||||
|
} else if (readVarint == 0) {
|
||||||
|
// skip over the empty packet and ignore it
|
||||||
|
in.readerIndex(varintEnd + 1);
|
||||||
|
} else {
|
||||||
|
int minimumRead = bytesRead + readVarint;
|
||||||
|
if (in.isReadable(minimumRead)) {
|
||||||
|
out.add(in.retainedSlice(varintEnd + 1, readVarint));
|
||||||
|
in.skipBytes(minimumRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (reader.getResult() == VarIntByteDecoder.DecodeResult.TOO_BIG) {
|
||||||
|
Logger.error("Too big");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package ru.nanit.limbo.protocol.pipeline;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandler;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.MessageToByteEncoder;
|
||||||
|
import ru.nanit.limbo.protocol.ByteMessage;
|
||||||
|
|
||||||
|
@ChannelHandler.Sharable
|
||||||
|
public class VarIntLengthEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void encode(ChannelHandlerContext ctx, ByteBuf buf, ByteBuf out) throws Exception {
|
||||||
|
ByteMessage msg = new ByteMessage(out);
|
||||||
|
msg.writeVarInt(buf.readableBytes());
|
||||||
|
msg.writeBytes(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception {
|
||||||
|
int anticipatedRequiredCapacity = 5 + msg.readableBytes();
|
||||||
|
return ctx.alloc().heapBuffer(anticipatedRequiredCapacity);
|
||||||
|
}
|
||||||
|
}
|
109
src/main/java/ru/nanit/limbo/protocol/registry/State.java
Normal file
109
src/main/java/ru/nanit/limbo/protocol/registry/State.java
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package ru.nanit.limbo.protocol.registry;
|
||||||
|
|
||||||
|
import ru.nanit.limbo.protocol.Packet;
|
||||||
|
import ru.nanit.limbo.protocol.packets.*;
|
||||||
|
import ru.nanit.limbo.protocol.packets.login.*;
|
||||||
|
import ru.nanit.limbo.protocol.packets.play.PacketJoinGame;
|
||||||
|
import ru.nanit.limbo.protocol.packets.status.PacketStatusPing;
|
||||||
|
import ru.nanit.limbo.protocol.packets.status.PacketStatusRequest;
|
||||||
|
import ru.nanit.limbo.protocol.packets.status.PacketStatusResponse;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public enum State {
|
||||||
|
|
||||||
|
HANDSHAKING(0){
|
||||||
|
{
|
||||||
|
serverBound.register(Version.getMinimal(), 0x00, PacketHandshake::new);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
STATUS(1){
|
||||||
|
{
|
||||||
|
clientBound.register(Version.getMinimal(), 0x00, PacketStatusResponse::new);
|
||||||
|
clientBound.register(Version.getMinimal(), 0x01, PacketStatusPing::new);
|
||||||
|
serverBound.register(Version.getMinimal(), 0x01, PacketStatusPing::new);
|
||||||
|
serverBound.register(Version.getMinimal(), 0x00, PacketStatusRequest::new);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
LOGIN(2){
|
||||||
|
{
|
||||||
|
clientBound.register(Version.getMinimal(), 0x00, PacketDisconnect::new);
|
||||||
|
clientBound.register(Version.getMinimal(), 0x02, PacketLoginSuccess::new);
|
||||||
|
serverBound.register(Version.getMinimal(), 0x00, PacketLoginStart::new);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PLAY(3){
|
||||||
|
{
|
||||||
|
clientBound.register(Version.V1_16_4, 0x24, PacketJoinGame::new);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final int stateId;
|
||||||
|
public final PacketVersionRegistry serverBound = new PacketVersionRegistry();
|
||||||
|
public final PacketVersionRegistry clientBound = new PacketVersionRegistry();
|
||||||
|
|
||||||
|
private static final Map<Integer, State> STATE_BY_ID = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (State registry : values()){
|
||||||
|
STATE_BY_ID.put(registry.stateId, registry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
State(int stateId){
|
||||||
|
this.stateId = stateId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static State getById(int stateId){
|
||||||
|
return STATE_BY_ID.get(stateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PacketVersionRegistry {
|
||||||
|
|
||||||
|
private final Map<Version, PacketIdRegistry<?>> MAPPINGS = new HashMap<>();
|
||||||
|
|
||||||
|
public PacketIdRegistry<?> getRegistry(Version version){
|
||||||
|
PacketIdRegistry<?> registry = MAPPINGS.get(version);
|
||||||
|
return registry != null ? registry : MAPPINGS.get(version.getClosest(MAPPINGS.keySet()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Packet> void register(Version version, int packetId, Supplier<T> supplier){
|
||||||
|
PacketIdRegistry<T> registry = (PacketIdRegistry<T>) MAPPINGS.computeIfAbsent(version, PacketIdRegistry::new);
|
||||||
|
registry.register(packetId, supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PacketIdRegistry<T extends Packet> {
|
||||||
|
|
||||||
|
private final Version version;
|
||||||
|
private final Map<Integer, Supplier<T>> packetsById = new HashMap<>();
|
||||||
|
private final Map<Class<?>, Integer> packetIdByClass = new HashMap<>();
|
||||||
|
|
||||||
|
public PacketIdRegistry(Version version){
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Version getVersion(){
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Packet getPacket(int packetId){
|
||||||
|
Supplier<T> supplier = packetsById.get(packetId);
|
||||||
|
return supplier == null ? null : supplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPacketId(Class<?> packetClass){
|
||||||
|
return packetIdByClass.getOrDefault(packetClass, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(int packetId, Supplier<T> supplier){
|
||||||
|
packetsById.put(packetId, supplier);
|
||||||
|
packetIdByClass.put(supplier.get().getClass(), packetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
77
src/main/java/ru/nanit/limbo/protocol/registry/Version.java
Normal file
77
src/main/java/ru/nanit/limbo/protocol/registry/Version.java
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package ru.nanit.limbo.protocol.registry;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public enum Version {
|
||||||
|
|
||||||
|
UNDEFINED(-1),
|
||||||
|
V1_9(107),
|
||||||
|
V1_9_1(108),
|
||||||
|
V1_9_2(109),
|
||||||
|
V1_9_4(110),
|
||||||
|
V1_10(210),
|
||||||
|
V1_11(315),
|
||||||
|
V1_11_1(316),
|
||||||
|
V1_12(335),
|
||||||
|
V1_12_1(338),
|
||||||
|
V1_12_2(340),
|
||||||
|
V1_13(393),
|
||||||
|
V1_13_1(401),
|
||||||
|
V1_13_2(404),
|
||||||
|
V1_14(477),
|
||||||
|
V1_14_1(480),
|
||||||
|
V1_14_2(485),
|
||||||
|
V1_14_3(490),
|
||||||
|
V1_14_4(498),
|
||||||
|
V1_15(573),
|
||||||
|
V1_15_1(575),
|
||||||
|
V1_15_2(578),
|
||||||
|
V1_16(735),
|
||||||
|
V1_16_1(736),
|
||||||
|
V1_16_2(751),
|
||||||
|
V1_16_3(753),
|
||||||
|
V1_16_4(754);
|
||||||
|
|
||||||
|
public static final Map<Integer, Version> VERSION_MAP;
|
||||||
|
|
||||||
|
static {
|
||||||
|
VERSION_MAP = new HashMap<>();
|
||||||
|
|
||||||
|
for (Version version : values()){
|
||||||
|
VERSION_MAP.put(version.getProtocolNumber(), version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Version getMinimal(){
|
||||||
|
return V1_9;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Version of(int protocolNumber){
|
||||||
|
return VERSION_MAP.getOrDefault(protocolNumber, UNDEFINED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int protocolNumber;
|
||||||
|
|
||||||
|
Version(int protocolNumber){
|
||||||
|
this.protocolNumber = protocolNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getProtocolNumber(){
|
||||||
|
return this.protocolNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Version getClosest(Collection<Version> available){
|
||||||
|
Version closest = getMinimal();
|
||||||
|
|
||||||
|
for (Version version : available){
|
||||||
|
if (version.protocolNumber > closest.protocolNumber && version.protocolNumber < this.protocolNumber){
|
||||||
|
closest = version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return closest;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
src/main/java/ru/nanit/limbo/server/LimboServer.java
Normal file
24
src/main/java/ru/nanit/limbo/server/LimboServer.java
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package ru.nanit.limbo.server;
|
||||||
|
|
||||||
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
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.util.Logger;
|
||||||
|
|
||||||
|
public final class LimboServer {
|
||||||
|
|
||||||
|
public void start() throws Exception {
|
||||||
|
Logger.info("Starting server...");
|
||||||
|
|
||||||
|
ServerBootstrap bootstrap = new ServerBootstrap()
|
||||||
|
.group(new NioEventLoopGroup(), new NioEventLoopGroup())
|
||||||
|
.channel(NioServerSocketChannel.class)
|
||||||
|
.childHandler(new ClientChannelInitializer(this));
|
||||||
|
|
||||||
|
bootstrap.bind(LimboConfig.getHost(), LimboConfig.getPort());
|
||||||
|
Logger.info("Server started on %s:%d", LimboConfig.getHost(), LimboConfig.getPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
63
src/main/java/ru/nanit/limbo/util/Logger.java
Normal file
63
src/main/java/ru/nanit/limbo/util/Logger.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package ru.nanit.limbo.util;
|
||||||
|
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
public final class Logger {
|
||||||
|
|
||||||
|
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("hh:mm:ss");
|
||||||
|
|
||||||
|
public static void info(Object msg, Object... args){
|
||||||
|
print(Level.INFO, msg, null, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void info(Object msg, Throwable t, Object... args){
|
||||||
|
print(Level.INFO, msg, t, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void warning(Object msg, Object... args){
|
||||||
|
print(Level.WARNING, msg, null, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void warning(Object msg, Throwable t, Object... args){
|
||||||
|
print(Level.WARNING, msg, t, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void error(Object msg, Object... args){
|
||||||
|
print(Level.ERROR, msg, null, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void error(Object msg, Throwable t, Object... args){
|
||||||
|
print(Level.ERROR, msg, t, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void print(Level level, Object msg, Throwable t, Object... args){
|
||||||
|
System.out.println(String.format("%s: %s", getPrefix(level), String.format(msg.toString(), args)));
|
||||||
|
if (t != null) t.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getPrefix(Level level){
|
||||||
|
return String.format("[%s] [%s]", getTime(), level.getDisplay());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getTime(){
|
||||||
|
return LocalTime.now().format(FORMATTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Level {
|
||||||
|
|
||||||
|
INFO ("INFO"),
|
||||||
|
WARNING("WARNING"),
|
||||||
|
ERROR("ERROR");
|
||||||
|
|
||||||
|
private final String display;
|
||||||
|
|
||||||
|
Level(String display){
|
||||||
|
this.display = display;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplay() {
|
||||||
|
return display;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
src/main/java/ru/nanit/limbo/util/UuidUtil.java
Normal file
13
src/main/java/ru/nanit/limbo/util/UuidUtil.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package ru.nanit.limbo.util;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class UuidUtil {
|
||||||
|
|
||||||
|
public static UUID getOfflineModeUuid(String username){
|
||||||
|
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username)
|
||||||
|
.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
src/main/resources/settings.properties
Normal file
16
src/main/resources/settings.properties
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#
|
||||||
|
# NanoLimbo configuration
|
||||||
|
#
|
||||||
|
|
||||||
|
host=localhost
|
||||||
|
port=65535
|
||||||
|
max-players=100
|
||||||
|
|
||||||
|
# Proxy forwarding support. Available types: NONE, LEGACY, MODERN
|
||||||
|
ip-forwarding=LEGACY
|
||||||
|
|
||||||
|
# Read timeout for connections in milliseconds
|
||||||
|
read-timeout=30000
|
||||||
|
|
||||||
|
ping-version=NanoLimbo
|
||||||
|
ping-description={"text": "NanoLimbo"}
|
Loading…
x
Reference in New Issue
Block a user