mirror of
https://github.com/Nan1t/NanoLimbo.git
synced 2025-07-09 03:30:12 +02:00
Some Netty optimizations
This commit is contained in:
parent
b4b40d36e0
commit
7ba831d1a6
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@
|
|||||||
/gradle/
|
/gradle/
|
||||||
gradlew
|
gradlew
|
||||||
gradlew.bat
|
gradlew.bat
|
||||||
build/
|
build/
|
||||||
|
settings.yml
|
132
README.md
132
README.md
@ -1,66 +1,66 @@
|
|||||||
## NanoLimbo
|
## NanoLimbo
|
||||||
|
|
||||||
This is lightweight minecraft limbo server, written on Java with Netty.
|
This is lightweight minecraft limbo server, written on Java with Netty.
|
||||||
The main goal of the project is maximum simplicity with a minimum number of sent and processed packets.
|
The main goal of the project is maximum simplicity with a minimum number of sent and processed packets.
|
||||||
This limbo is empty, there are no ability to set schematic building since
|
This limbo is empty, there are no ability to set schematic building since
|
||||||
this is not necessary. You can send useful information in chat or BossBar.
|
this is not necessary. You can send useful information in chat or BossBar.
|
||||||
|
|
||||||
No plugins, no logs. The server is fully clear. It only able keep a lot of players while the main server is down.
|
No plugins, no logs. The server is fully clear. It only able keep a lot of players while the main server is down.
|
||||||
|
|
||||||
The general features:
|
The general features:
|
||||||
* High performance. The server not saves and not cached any useless (for limbo) data.
|
* High performance. The server not saves and not cached any useless (for limbo) data.
|
||||||
* Support for **BungeeCord** and **Velocity** info forwarding.
|
* Support for **BungeeCord** and **Velocity** info forwarding.
|
||||||
* All necessary data are configurable.
|
* All necessary data are configurable.
|
||||||
* Lightweight. App size is around **1.5 MB.**
|
* Lightweight. App size is around **1.5 MB.**
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Protocol support
|
### Protocol support
|
||||||
|
|
||||||
The server written based on `1.16.4` minecraft protocol. You can use `ViaVersion`
|
The server written based on `1.16.4` minecraft protocol. You can use `ViaVersion`
|
||||||
or another protocol hack on your **proxy server** to achieve compatibility with other versions.
|
or another protocol hack on your **proxy server** to achieve compatibility with other versions.
|
||||||
However, compatibility with other versions is in the plans, but not the nearest ones, since there
|
However, compatibility with other versions is in the plans, but not the nearest ones, since there
|
||||||
is a way to do this using a plugin that is installed on most networks.
|
is a way to do this using a plugin that is installed on most networks.
|
||||||
|
|
||||||
### Commands
|
### Commands
|
||||||
|
|
||||||
There are no commands. To close server just run `Ctrl+C` in the terminal. It will be closed correctly.
|
There are no commands. To close server just run `Ctrl+C` in the terminal. It will be closed correctly.
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
The installation process is simple.
|
The installation process is simple.
|
||||||
|
|
||||||
1. Download the latest version of program **[here](https://github.com/Nan1t/NanoLimbo/releases)**
|
1. Download the latest version of program **[here](https://github.com/Nan1t/NanoLimbo/releases)**
|
||||||
2. Put jar file in the folder you want.
|
2. Put jar file in the folder you want.
|
||||||
3. Create a start script as you did it for Bukkit/Spigot/etc. with command like this:
|
3. Create a start script as you did it for Bukkit/Spigot/etc. with command like this:
|
||||||
`java -jar <FileName>.jar`
|
`java -jar <FileName>.jar`
|
||||||
4. The server will create `settings.yml` file. It's a server configuration.
|
4. The server will create `settings.yml` file. It's a server configuration.
|
||||||
5. Configure it as you want and restart server.
|
5. Configure it as you want and restart server.
|
||||||
|
|
||||||
I recommend you to set parameter `debugLevel` to `0` when you finish testing server and run it into production.
|
I recommend you to set parameter `debugLevel` to `0` when you finish testing server and run it into production.
|
||||||
This will disable some useless for production information in the console.
|
This will disable some useless for production information in the console.
|
||||||
|
|
||||||
### About player info forwarding
|
### About player info forwarding
|
||||||
|
|
||||||
The server supports player info forwarding from the proxy. There are two type of info forwarding:
|
The server supports player info forwarding from the proxy. There are two type of info forwarding:
|
||||||
|
|
||||||
* LEGACY - The BungeeCord IP forwarding
|
* LEGACY - The BungeeCord IP forwarding
|
||||||
* MODERN - Velocity native info forwarding type
|
* MODERN - Velocity native info forwarding type
|
||||||
|
|
||||||
If you use **BungeeCord**, or Velocity with LEGACY forwarding, just set this type in the config.
|
If you use **BungeeCord**, or Velocity with LEGACY forwarding, just set this type in the config.
|
||||||
If you use **Velocity** with modern info forwarding, set this type and paste secret key from Velocity
|
If you use **Velocity** with modern info forwarding, set this type and paste secret key from Velocity
|
||||||
config into `secret` field.
|
config into `secret` field.
|
||||||
|
|
||||||
### Contributing
|
### Contributing
|
||||||
|
|
||||||
You can create pull request, if you found some bug, optimization ability, or you wanna add some functional,
|
You can create pull request, if you found some bug, optimization ability, or you wanna add some functional,
|
||||||
which will not significantly load the server.
|
which will not significantly load the server.
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
The project uses `Gradle` to build and minimize output .jar file.
|
The project uses `Gradle` to build and minimize output .jar file.
|
||||||
Run `shadowJar` task to build minimized executable jar file.
|
Run `shadowJar` task to build minimized executable jar file.
|
||||||
|
|
||||||
### Contacts
|
### Contacts
|
||||||
|
|
||||||
If you have any question or suggestion, join to [Discord server](https://discord.gg/4VGP3Gv)
|
If you have any question or suggestion, join to [Discord server](https://discord.gg/4VGP3Gv)
|
||||||
|
@ -14,7 +14,7 @@ dependencies {
|
|||||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||||
compile files('libs/napi-configurate-yaml-1.0.jar')
|
compile files('libs/napi-configurate-yaml-1.0.jar')
|
||||||
compile group: 'org.yaml', name: 'snakeyaml', version: '1.27'
|
compile group: 'org.yaml', name: 'snakeyaml', version: '1.27'
|
||||||
compile group: 'io.netty', name: 'netty-handler', version: '4.1.54.Final'
|
compile group: 'io.netty', name: 'netty-all', version: '4.1.54.Final'
|
||||||
compile group: 'net.kyori', name: 'adventure-nbt', version: '4.1.1'
|
compile group: 'net.kyori', name: 'adventure-nbt', version: '4.1.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,23 +182,23 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendPacket(PACKET_LOGIN_SUCCESS);
|
writePacket(PACKET_LOGIN_SUCCESS);
|
||||||
updateState(State.PLAY);
|
updateState(State.PLAY);
|
||||||
|
|
||||||
server.getConnections().addConnection(this);
|
server.getConnections().addConnection(this);
|
||||||
|
|
||||||
sendPacket(PACKET_JOIN_GAME);
|
writePacket(PACKET_JOIN_GAME);
|
||||||
sendPacket(PACKET_PLAYER_ABILITIES);
|
writePacket(PACKET_PLAYER_ABILITIES);
|
||||||
sendPacket(PACKET_PLAYER_POS);
|
writePacket(PACKET_PLAYER_POS);
|
||||||
sendPacket(PACKET_PLAYER_INFO);
|
writePacket(PACKET_PLAYER_INFO);
|
||||||
|
|
||||||
sendKeepAlive();
|
|
||||||
|
|
||||||
if (PACKET_BOSS_BAR != null)
|
if (PACKET_BOSS_BAR != null)
|
||||||
sendPacket(PACKET_BOSS_BAR);
|
writePacket(PACKET_BOSS_BAR);
|
||||||
|
|
||||||
if (PACKET_JOIN_MESSAGE != null)
|
if (PACKET_JOIN_MESSAGE != null)
|
||||||
sendPacket(PACKET_JOIN_MESSAGE);
|
writePacket(PACKET_JOIN_MESSAGE);
|
||||||
|
|
||||||
|
sendKeepAlive();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disconnectLogin(String reason){
|
public void disconnectLogin(String reason){
|
||||||
@ -227,6 +227,14 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
|
|||||||
channel.writeAndFlush(packet).addListener(ChannelFutureListener.CLOSE);
|
channel.writeAndFlush(packet).addListener(ChannelFutureListener.CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void writePacket(Object packet){
|
||||||
|
if (isConnected()) channel.write(packet, channel.voidPromise());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flushPackets(){
|
||||||
|
if (isConnected()) channel.flush();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isConnected(){
|
public boolean isConnected(){
|
||||||
return channel.isActive();
|
return channel.isActive();
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
package ru.nanit.limbo.connection;
|
package ru.nanit.limbo.connection;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class GameProfile {
|
public class GameProfile {
|
||||||
|
|
||||||
private UUID uuid;
|
private UUID uuid;
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
public UUID getUuid() {
|
public UUID getUuid() {
|
||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUuid(UUID uuid) {
|
public void setUuid(UUID uuid) {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUsername(String username) {
|
public void setUsername(String username) {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,32 @@
|
|||||||
package ru.nanit.limbo.protocol;
|
package ru.nanit.limbo.protocol;
|
||||||
|
|
||||||
public class PreRenderedPacket implements PacketOut {
|
public class PreRenderedPacket implements PacketOut {
|
||||||
|
|
||||||
private final PacketOut packet;
|
private final PacketOut packet;
|
||||||
private byte[] message;
|
private byte[] message;
|
||||||
|
|
||||||
public PreRenderedPacket(PacketOut packet){
|
public PreRenderedPacket(PacketOut packet){
|
||||||
this.packet = packet;
|
this.packet = packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PacketOut getWrappedPacket(){
|
public PacketOut getWrappedPacket(){
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PreRenderedPacket render(){
|
public PreRenderedPacket render(){
|
||||||
ByteMessage renderedMessage = ByteMessage.create();
|
ByteMessage renderedMessage = ByteMessage.create();
|
||||||
packet.encode(renderedMessage);
|
packet.encode(renderedMessage);
|
||||||
this.message = renderedMessage.toByteArray();
|
this.message = renderedMessage.toByteArray();
|
||||||
return this;
|
renderedMessage.release();
|
||||||
}
|
return this;
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
@Override
|
||||||
msg.writeBytes(message);
|
public void encode(ByteMessage msg) {
|
||||||
}
|
msg.writeBytes(message);
|
||||||
|
}
|
||||||
public static PreRenderedPacket of(PacketOut packet){
|
|
||||||
return new PreRenderedPacket(packet).render();
|
public static PreRenderedPacket of(PacketOut packet){
|
||||||
}
|
return new PreRenderedPacket(packet).render();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.play;
|
package ru.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
import ru.nanit.limbo.protocol.ByteMessage;
|
||||||
import ru.nanit.limbo.protocol.PacketOut;
|
import ru.nanit.limbo.protocol.PacketOut;
|
||||||
|
|
||||||
public class PacketPlayerAbilities implements PacketOut {
|
public class PacketPlayerAbilities implements PacketOut {
|
||||||
|
|
||||||
private int flags = 0x02;
|
private int flags = 0x02;
|
||||||
private float flyingSpeed = 0.0F;
|
private float flyingSpeed = 0.0F;
|
||||||
private float fieldOfView = 0.1F;
|
private float fieldOfView = 0.1F;
|
||||||
|
|
||||||
public void setFlags(int flags) {
|
public void setFlags(int flags) {
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFlyingSpeed(float flyingSpeed) {
|
public void setFlyingSpeed(float flyingSpeed) {
|
||||||
this.flyingSpeed = flyingSpeed;
|
this.flyingSpeed = flyingSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFieldOfView(float fieldOfView) {
|
public void setFieldOfView(float fieldOfView) {
|
||||||
this.fieldOfView = fieldOfView;
|
this.fieldOfView = fieldOfView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encode(ByteMessage msg) {
|
public void encode(ByteMessage msg) {
|
||||||
msg.writeByte(flags);
|
msg.writeByte(flags);
|
||||||
msg.writeFloat(flyingSpeed);
|
msg.writeFloat(flyingSpeed);
|
||||||
msg.writeFloat(fieldOfView);
|
msg.writeFloat(fieldOfView);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
package ru.nanit.limbo.server;
|
package ru.nanit.limbo.server;
|
||||||
|
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
import io.netty.channel.ChannelOption;
|
||||||
|
import io.netty.channel.EventLoopGroup;
|
||||||
|
import io.netty.channel.ServerChannel;
|
||||||
|
import io.netty.channel.epoll.Epoll;
|
||||||
|
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||||
|
import io.netty.channel.epoll.EpollServerSocketChannel;
|
||||||
import io.netty.channel.nio.NioEventLoopGroup;
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
|
import io.netty.util.ResourceLeakDetector;
|
||||||
import napi.configurate.serializing.NodeSerializers;
|
import napi.configurate.serializing.NodeSerializers;
|
||||||
import ru.nanit.limbo.configuration.LimboConfig;
|
import ru.nanit.limbo.configuration.LimboConfig;
|
||||||
import ru.nanit.limbo.configuration.SocketAddressSerializer;
|
import ru.nanit.limbo.configuration.SocketAddressSerializer;
|
||||||
@ -14,8 +21,7 @@ import ru.nanit.limbo.world.DimensionRegistry;
|
|||||||
|
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public final class LimboServer {
|
public final class LimboServer {
|
||||||
@ -23,6 +29,10 @@ public final class LimboServer {
|
|||||||
private LimboConfig config;
|
private LimboConfig config;
|
||||||
private Connections connections;
|
private Connections connections;
|
||||||
private DimensionRegistry dimensionRegistry;
|
private DimensionRegistry dimensionRegistry;
|
||||||
|
private ScheduledFuture<?> keepAliveTask;
|
||||||
|
|
||||||
|
private EventLoopGroup bossGroup;
|
||||||
|
private EventLoopGroup workerGroup;
|
||||||
|
|
||||||
public LimboConfig getConfig(){
|
public LimboConfig getConfig(){
|
||||||
return config;
|
return config;
|
||||||
@ -39,6 +49,8 @@ public final class LimboServer {
|
|||||||
public void start() throws Exception {
|
public void start() throws Exception {
|
||||||
Logger.info("Starting server...");
|
Logger.info("Starting server...");
|
||||||
|
|
||||||
|
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED);
|
||||||
|
|
||||||
NodeSerializers.register(SocketAddress.class, new SocketAddressSerializer());
|
NodeSerializers.register(SocketAddress.class, new SocketAddressSerializer());
|
||||||
NodeSerializers.register(InfoForwarding.class, new InfoForwarding.Serializer());
|
NodeSerializers.register(InfoForwarding.class, new InfoForwarding.Serializer());
|
||||||
NodeSerializers.register(PingData.class, new PingData.Serializer());
|
NodeSerializers.register(PingData.class, new PingData.Serializer());
|
||||||
@ -56,21 +68,55 @@ public final class LimboServer {
|
|||||||
|
|
||||||
ClientConnection.preInitPackets(this);
|
ClientConnection.preInitPackets(this);
|
||||||
|
|
||||||
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
|
startBootstrap();
|
||||||
executor.scheduleAtFixedRate(this::broadcastKeepAlive, 0L, 5L, TimeUnit.SECONDS);
|
|
||||||
|
|
||||||
new ServerBootstrap()
|
keepAliveTask = workerGroup.scheduleAtFixedRate(this::broadcastKeepAlive, 0L, 5L, TimeUnit.SECONDS);
|
||||||
.group(new NioEventLoopGroup(), new NioEventLoopGroup())
|
|
||||||
.channel(NioServerSocketChannel.class)
|
Runtime.getRuntime().addShutdownHook(new Thread(this::stop, "NanoLimbo shutdown thread"));
|
||||||
.childHandler(new ClientChannelInitializer(this))
|
|
||||||
.localAddress(config.getAddress())
|
|
||||||
.bind();
|
|
||||||
|
|
||||||
Logger.info("Server started on %s", config.getAddress());
|
Logger.info("Server started on %s", config.getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startBootstrap(){
|
||||||
|
Class<? extends ServerChannel> channelClass;
|
||||||
|
|
||||||
|
if (Epoll.isAvailable()){
|
||||||
|
bossGroup = new EpollEventLoopGroup(1);
|
||||||
|
workerGroup = new EpollEventLoopGroup(4);
|
||||||
|
channelClass = EpollServerSocketChannel.class;
|
||||||
|
Logger.debug("Using Epoll transport type");
|
||||||
|
} else {
|
||||||
|
bossGroup = new NioEventLoopGroup(1);
|
||||||
|
workerGroup = new NioEventLoopGroup(4);
|
||||||
|
channelClass = NioServerSocketChannel.class;
|
||||||
|
Logger.debug("Using Java NIO transport type");
|
||||||
|
}
|
||||||
|
|
||||||
|
new ServerBootstrap()
|
||||||
|
.group(bossGroup, workerGroup)
|
||||||
|
.channel(channelClass)
|
||||||
|
.childHandler(new ClientChannelInitializer(this))
|
||||||
|
.childOption(ChannelOption.TCP_NODELAY, true)
|
||||||
|
.localAddress(config.getAddress())
|
||||||
|
.bind();
|
||||||
|
}
|
||||||
|
|
||||||
private void broadcastKeepAlive(){
|
private void broadcastKeepAlive(){
|
||||||
connections.getAllConnections().forEach(ClientConnection::sendKeepAlive);
|
connections.getAllConnections().forEach(ClientConnection::sendKeepAlive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void stop(){
|
||||||
|
if (keepAliveTask != null){
|
||||||
|
keepAliveTask.cancel(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bossGroup != null){
|
||||||
|
bossGroup.shutdownGracefully();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workerGroup != null){
|
||||||
|
workerGroup.shutdownGracefully();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ public final class Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void debug(Object msg, Object... args){
|
public static void debug(Object msg, Object... args){
|
||||||
print(Level.INFO, msg, null, args);
|
print(Level.DEBUG, msg, null, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void warning(Object msg, Object... args){
|
public static void warning(Object msg, Object... args){
|
||||||
|
Loading…
x
Reference in New Issue
Block a user