Some Netty optimizations

This commit is contained in:
Nanit 2020-11-29 15:00:17 +02:00
parent b4b40d36e0
commit 7ba831d1a6
10 changed files with 905 additions and 849 deletions

3
.gitignore vendored
View File

@ -3,4 +3,5 @@
/gradle/
gradlew
gradlew.bat
build/
build/
settings.yml

1348
LICENSE

File diff suppressed because it is too large Load Diff

132
README.md
View File

@ -1,66 +1,66 @@
## NanoLimbo
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.
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.
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:
* High performance. The server not saves and not cached any useless (for limbo) data.
* Support for **BungeeCord** and **Velocity** info forwarding.
* All necessary data are configurable.
* Lightweight. App size is around **1.5 MB.**
![](https://i.imgur.com/sT8p1Gz.png)
### Protocol support
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.
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.
### Commands
There are no commands. To close server just run `Ctrl+C` in the terminal. It will be closed correctly.
### Installation
The installation process is simple.
1. Download the latest version of program **[here](https://github.com/Nan1t/NanoLimbo/releases)**
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:
`java -jar <FileName>.jar`
4. The server will create `settings.yml` file. It's a server configuration.
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.
This will disable some useless for production information in the console.
### About player info forwarding
The server supports player info forwarding from the proxy. There are two type of info forwarding:
* LEGACY - The BungeeCord IP forwarding
* 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 **Velocity** with modern info forwarding, set this type and paste secret key from Velocity
config into `secret` field.
### Contributing
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.
### Building
The project uses `Gradle` to build and minimize output .jar file.
Run `shadowJar` task to build minimized executable jar file.
### Contacts
If you have any question or suggestion, join to [Discord server](https://discord.gg/4VGP3Gv)
## NanoLimbo
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.
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.
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:
* High performance. The server not saves and not cached any useless (for limbo) data.
* Support for **BungeeCord** and **Velocity** info forwarding.
* All necessary data are configurable.
* Lightweight. App size is around **1.5 MB.**
![](https://i.imgur.com/sT8p1Gz.png)
### Protocol support
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.
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.
### Commands
There are no commands. To close server just run `Ctrl+C` in the terminal. It will be closed correctly.
### Installation
The installation process is simple.
1. Download the latest version of program **[here](https://github.com/Nan1t/NanoLimbo/releases)**
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:
`java -jar <FileName>.jar`
4. The server will create `settings.yml` file. It's a server configuration.
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.
This will disable some useless for production information in the console.
### About player info forwarding
The server supports player info forwarding from the proxy. There are two type of info forwarding:
* LEGACY - The BungeeCord IP forwarding
* 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 **Velocity** with modern info forwarding, set this type and paste secret key from Velocity
config into `secret` field.
### Contributing
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.
### Building
The project uses `Gradle` to build and minimize output .jar file.
Run `shadowJar` task to build minimized executable jar file.
### Contacts
If you have any question or suggestion, join to [Discord server](https://discord.gg/4VGP3Gv)

View File

@ -14,7 +14,7 @@ dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile files('libs/napi-configurate-yaml-1.0.jar')
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'
}

View File

@ -182,23 +182,23 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
return;
}
sendPacket(PACKET_LOGIN_SUCCESS);
writePacket(PACKET_LOGIN_SUCCESS);
updateState(State.PLAY);
server.getConnections().addConnection(this);
sendPacket(PACKET_JOIN_GAME);
sendPacket(PACKET_PLAYER_ABILITIES);
sendPacket(PACKET_PLAYER_POS);
sendPacket(PACKET_PLAYER_INFO);
sendKeepAlive();
writePacket(PACKET_JOIN_GAME);
writePacket(PACKET_PLAYER_ABILITIES);
writePacket(PACKET_PLAYER_POS);
writePacket(PACKET_PLAYER_INFO);
if (PACKET_BOSS_BAR != null)
sendPacket(PACKET_BOSS_BAR);
writePacket(PACKET_BOSS_BAR);
if (PACKET_JOIN_MESSAGE != null)
sendPacket(PACKET_JOIN_MESSAGE);
writePacket(PACKET_JOIN_MESSAGE);
sendKeepAlive();
}
public void disconnectLogin(String reason){
@ -227,6 +227,14 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
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(){
return channel.isActive();
}

View File

@ -1,25 +1,25 @@
package ru.nanit.limbo.connection;
import java.util.UUID;
public class GameProfile {
private UUID uuid;
private String username;
public UUID getUuid() {
return uuid;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
package ru.nanit.limbo.connection;
import java.util.UUID;
public class GameProfile {
private UUID uuid;
private String username;
public UUID getUuid() {
return uuid;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}

View File

@ -1,31 +1,32 @@
package ru.nanit.limbo.protocol;
public class PreRenderedPacket implements PacketOut {
private final PacketOut packet;
private byte[] message;
public PreRenderedPacket(PacketOut packet){
this.packet = packet;
}
public PacketOut getWrappedPacket(){
return packet;
}
public PreRenderedPacket render(){
ByteMessage renderedMessage = ByteMessage.create();
packet.encode(renderedMessage);
this.message = renderedMessage.toByteArray();
return this;
}
@Override
public void encode(ByteMessage msg) {
msg.writeBytes(message);
}
public static PreRenderedPacket of(PacketOut packet){
return new PreRenderedPacket(packet).render();
}
}
package ru.nanit.limbo.protocol;
public class PreRenderedPacket implements PacketOut {
private final PacketOut packet;
private byte[] message;
public PreRenderedPacket(PacketOut packet){
this.packet = packet;
}
public PacketOut getWrappedPacket(){
return packet;
}
public PreRenderedPacket render(){
ByteMessage renderedMessage = ByteMessage.create();
packet.encode(renderedMessage);
this.message = renderedMessage.toByteArray();
renderedMessage.release();
return this;
}
@Override
public void encode(ByteMessage msg) {
msg.writeBytes(message);
}
public static PreRenderedPacket of(PacketOut packet){
return new PreRenderedPacket(packet).render();
}
}

View File

@ -1,31 +1,31 @@
package ru.nanit.limbo.protocol.packets.play;
import ru.nanit.limbo.protocol.ByteMessage;
import ru.nanit.limbo.protocol.PacketOut;
public class PacketPlayerAbilities implements PacketOut {
private int flags = 0x02;
private float flyingSpeed = 0.0F;
private float fieldOfView = 0.1F;
public void setFlags(int flags) {
this.flags = flags;
}
public void setFlyingSpeed(float flyingSpeed) {
this.flyingSpeed = flyingSpeed;
}
public void setFieldOfView(float fieldOfView) {
this.fieldOfView = fieldOfView;
}
@Override
public void encode(ByteMessage msg) {
msg.writeByte(flags);
msg.writeFloat(flyingSpeed);
msg.writeFloat(fieldOfView);
}
}
package ru.nanit.limbo.protocol.packets.play;
import ru.nanit.limbo.protocol.ByteMessage;
import ru.nanit.limbo.protocol.PacketOut;
public class PacketPlayerAbilities implements PacketOut {
private int flags = 0x02;
private float flyingSpeed = 0.0F;
private float fieldOfView = 0.1F;
public void setFlags(int flags) {
this.flags = flags;
}
public void setFlyingSpeed(float flyingSpeed) {
this.flyingSpeed = flyingSpeed;
}
public void setFieldOfView(float fieldOfView) {
this.fieldOfView = fieldOfView;
}
@Override
public void encode(ByteMessage msg) {
msg.writeByte(flags);
msg.writeFloat(flyingSpeed);
msg.writeFloat(fieldOfView);
}
}

View File

@ -1,8 +1,15 @@
package ru.nanit.limbo.server;
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.socket.nio.NioServerSocketChannel;
import io.netty.util.ResourceLeakDetector;
import napi.configurate.serializing.NodeSerializers;
import ru.nanit.limbo.configuration.LimboConfig;
import ru.nanit.limbo.configuration.SocketAddressSerializer;
@ -14,8 +21,7 @@ import ru.nanit.limbo.world.DimensionRegistry;
import java.net.SocketAddress;
import java.nio.file.Paths;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public final class LimboServer {
@ -23,6 +29,10 @@ public final class LimboServer {
private LimboConfig config;
private Connections connections;
private DimensionRegistry dimensionRegistry;
private ScheduledFuture<?> keepAliveTask;
private EventLoopGroup bossGroup;
private EventLoopGroup workerGroup;
public LimboConfig getConfig(){
return config;
@ -39,6 +49,8 @@ public final class LimboServer {
public void start() throws Exception {
Logger.info("Starting server...");
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED);
NodeSerializers.register(SocketAddress.class, new SocketAddressSerializer());
NodeSerializers.register(InfoForwarding.class, new InfoForwarding.Serializer());
NodeSerializers.register(PingData.class, new PingData.Serializer());
@ -56,21 +68,55 @@ public final class LimboServer {
ClientConnection.preInitPackets(this);
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(this::broadcastKeepAlive, 0L, 5L, TimeUnit.SECONDS);
startBootstrap();
new ServerBootstrap()
.group(new NioEventLoopGroup(), new NioEventLoopGroup())
.channel(NioServerSocketChannel.class)
.childHandler(new ClientChannelInitializer(this))
.localAddress(config.getAddress())
.bind();
keepAliveTask = workerGroup.scheduleAtFixedRate(this::broadcastKeepAlive, 0L, 5L, TimeUnit.SECONDS);
Runtime.getRuntime().addShutdownHook(new Thread(this::stop, "NanoLimbo shutdown thread"));
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(){
connections.getAllConnections().forEach(ClientConnection::sendKeepAlive);
}
private void stop(){
if (keepAliveTask != null){
keepAliveTask.cancel(true);
}
if (bossGroup != null){
bossGroup.shutdownGracefully();
}
if (workerGroup != null){
workerGroup.shutdownGracefully();
}
}
}

View File

@ -19,7 +19,7 @@ public final class Logger {
}
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){