Misc network improvement

This commit is contained in:
themode 2022-03-20 03:22:38 +01:00
parent 54fd65eded
commit 71b6e8df90
5 changed files with 48 additions and 104 deletions

View File

@ -2,14 +2,12 @@ package net.minestom.server.network;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.event.EventDispatcher; import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.player.AsyncPlayerPreLoginEvent; import net.minestom.server.event.player.AsyncPlayerPreLoginEvent;
import net.minestom.server.event.player.PlayerLoginEvent; import net.minestom.server.event.player.PlayerLoginEvent;
import net.minestom.server.instance.Instance; import net.minestom.server.instance.Instance;
import net.minestom.server.network.packet.server.login.LoginSuccessPacket; import net.minestom.server.network.packet.server.login.LoginSuccessPacket;
import net.minestom.server.network.packet.server.play.DisconnectPacket;
import net.minestom.server.network.packet.server.play.KeepAlivePacket; import net.minestom.server.network.packet.server.play.KeepAlivePacket;
import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.utils.StringUtils; import net.minestom.server.utils.StringUtils;
@ -45,8 +43,6 @@ public final class ConnectionManager {
// The player provider to have your own Player implementation // The player provider to have your own Player implementation
private volatile PlayerProvider playerProvider = Player::new; private volatile PlayerProvider playerProvider = Player::new;
private Component shutdownText = Component.text("The server is shutting down.", NamedTextColor.RED);
/** /**
* Gets the {@link Player} linked to a {@link PlayerConnection}. * Gets the {@link Player} linked to a {@link PlayerConnection}.
* *
@ -167,25 +163,6 @@ public final class ConnectionManager {
return playerProvider; return playerProvider;
} }
/**
* Gets the kick reason when the server is shutdown using {@link MinecraftServer#stopCleanly()}.
*
* @return the kick reason in case on a shutdown
*/
public @NotNull Component getShutdownText() {
return shutdownText;
}
/**
* Changes the kick reason in case of a shutdown.
*
* @param shutdownText the new shutdown kick reason
* @see #getShutdownText()
*/
public void setShutdownText(@NotNull Component shutdownText) {
this.shutdownText = shutdownText;
}
public synchronized void registerPlayer(@NotNull Player player) { public synchronized void registerPlayer(@NotNull Player player) {
this.players.add(player); this.players.add(player);
this.connectionPlayerMap.put(player.getPlayerConnection(), player); this.connectionPlayerMap.put(player.getPlayerConnection(), player);
@ -220,12 +197,8 @@ public final class ConnectionManager {
// Call pre login event // Call pre login event
AsyncPlayerPreLoginEvent asyncPlayerPreLoginEvent = new AsyncPlayerPreLoginEvent(player); AsyncPlayerPreLoginEvent asyncPlayerPreLoginEvent = new AsyncPlayerPreLoginEvent(player);
EventDispatcher.call(asyncPlayerPreLoginEvent); EventDispatcher.call(asyncPlayerPreLoginEvent);
// Close the player channel if he has been disconnected (kick) if (!player.isOnline())
if (!player.isOnline()) { return; // Player has been kicked
playerConnection.flush();
//playerConnection.disconnect();
return;
}
// Change UUID/Username based on the event // Change UUID/Username based on the event
{ {
final String eventUsername = asyncPlayerPreLoginEvent.getUsername(); final String eventUsername = asyncPlayerPreLoginEvent.getUsername();
@ -265,14 +238,6 @@ public final class ConnectionManager {
* Shutdowns the connection manager by kicking all the currently connected players. * Shutdowns the connection manager by kicking all the currently connected players.
*/ */
public synchronized void shutdown() { public synchronized void shutdown() {
DisconnectPacket disconnectPacket = new DisconnectPacket(shutdownText);
for (Player player : getOnlinePlayers()) {
final PlayerConnection playerConnection = player.getPlayerConnection();
playerConnection.sendPacket(disconnectPacket);
playerConnection.flush();
player.remove();
playerConnection.disconnect();
}
this.players.clear(); this.players.clear();
this.connectionPlayerMap.clear(); this.connectionPlayerMap.clear();
} }

View File

@ -1,6 +1,5 @@
package net.minestom.server.network.player; package net.minestom.server.network.player;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.entity.fakeplayer.FakePlayer; import net.minestom.server.entity.fakeplayer.FakePlayer;
import net.minestom.server.entity.fakeplayer.FakePlayerController; import net.minestom.server.entity.fakeplayer.FakePlayerController;
@ -27,13 +26,6 @@ public class FakePlayerConnection extends PlayerConnection {
return new InetSocketAddress(0); return new InetSocketAddress(0);
} }
@Override
public void disconnect() {
super.disconnect();
if (getFakePlayer().getOption().isRegistered())
MinecraftServer.getConnectionManager().removePlayer(this);
}
public FakePlayer getFakePlayer() { public FakePlayer getFakePlayer() {
return (FakePlayer) getPlayer(); return (FakePlayer) getPlayer();
} }

View File

@ -3,6 +3,7 @@ package net.minestom.server.network.player;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.listener.manager.PacketListenerManager; import net.minestom.server.listener.manager.PacketListenerManager;
import net.minestom.server.network.ConnectionState; import net.minestom.server.network.ConnectionState;
@ -95,15 +96,6 @@ public abstract class PlayerConnection {
sendPackets(List.of(packets)); sendPackets(List.of(packets));
} }
/**
* Flush waiting data to the connection.
* <p>
* Might not do anything depending on the implementation.
*/
public void flush() {
// Empty
}
/** /**
* Gets the remote address of the client. * Gets the remote address of the client.
* *
@ -148,6 +140,11 @@ public abstract class PlayerConnection {
*/ */
public void disconnect() { public void disconnect() {
this.online = false; this.online = false;
MinecraftServer.getConnectionManager().removePlayer(this);
final Player player = getPlayer();
if (player != null && !player.isRemoved()) {
player.scheduleNextTick(Entity::remove);
}
} }
/** /**

View File

@ -182,11 +182,6 @@ public class PlayerSocketConnection extends PlayerConnection {
write(buffer, buffer.position(), buffer.remaining()); write(buffer, buffer.position(), buffer.remaining());
} }
@Override
public void flush() {
this.workerQueue.relaxedOffer(this::flushSync);
}
@Override @Override
public @NotNull SocketAddress getRemoteAddress() { public @NotNull SocketAddress getRemoteAddress() {
return remoteAddress; return remoteAddress;
@ -377,7 +372,6 @@ public class PlayerSocketConnection extends PlayerConnection {
} }
var buffer = PacketUtils.createFramedPacket(serverPacket, compressed); var buffer = PacketUtils.createFramedPacket(serverPacket, compressed);
writeBufferSync0(buffer, 0, buffer.limit()); writeBufferSync0(buffer, 0, buffer.limit());
if (player == null) flushSync(); // Player is probably not logged yet
} }
private void writeBufferSync(@NotNull ByteBuffer buffer, int index, int length) { private void writeBufferSync(@NotNull ByteBuffer buffer, int index, int length) {
@ -418,35 +412,26 @@ public class PlayerSocketConnection extends PlayerConnection {
} }
} }
public void flushSync() { public void flushSync() throws IOException {
if (!channel.isConnected()) throw new ClosedChannelException();
if (waitingBuffers.isEmpty() && tickBuffer.getPlain().writeChannel(channel))
return; // Fast exit if the tick buffer can be reused
try { try {
if (!channel.isConnected()) throw new ClosedChannelException(); updateLocalBuffer();
try { } catch (OutOfMemoryError e) {
if (waitingBuffers.isEmpty() && tickBuffer.getPlain().writeChannel(channel)) this.waitingBuffers.clear();
return; // Fast exit if the tick buffer can be reused System.gc(); // Explicit gc forcing buffers to be collected
throw new ClosedChannelException();
}
try { // Write as much as possible from the waiting list
updateLocalBuffer(); Iterator<BinaryBuffer> iterator = waitingBuffers.iterator();
} catch (OutOfMemoryError e) { while (iterator.hasNext()) {
this.waitingBuffers.clear(); BinaryBuffer waitingBuffer = iterator.next();
System.gc(); // Explicit gc forcing buffers to be collected if (!waitingBuffer.writeChannel(channel)) break;
throw new ClosedChannelException(); iterator.remove();
} PooledBuffers.add(waitingBuffer);
// Write as much as possible from the waiting list
Iterator<BinaryBuffer> iterator = waitingBuffers.iterator();
while (iterator.hasNext()) {
BinaryBuffer waitingBuffer = iterator.next();
if (!waitingBuffer.writeChannel(channel)) break;
iterator.remove();
PooledBuffers.add(waitingBuffer);
}
} catch (IOException e) { // Couldn't write to the socket
MinecraftServer.getExceptionManager().handleException(e);
throw new ClosedChannelException();
}
} catch (ClosedChannelException e) {
disconnect();
} }
} }

View File

@ -1,8 +1,6 @@
package net.minestom.server.network.socket; package net.minestom.server.network.socket;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Player;
import net.minestom.server.network.player.PlayerSocketConnection; import net.minestom.server.network.player.PlayerSocketConnection;
import net.minestom.server.thread.MinestomThread; import net.minestom.server.thread.MinestomThread;
import net.minestom.server.utils.binary.BinaryBuffer; import net.minestom.server.utils.binary.BinaryBuffer;
@ -49,17 +47,27 @@ public final class Worker extends MinestomThread {
MinecraftServer.getExceptionManager().handleException(e); MinecraftServer.getExceptionManager().handleException(e);
} }
// Flush all connections if needed // Flush all connections if needed
try { for (PlayerSocketConnection connection : connectionMap.values()) {
connectionMap.values().forEach(PlayerSocketConnection::flushSync); try {
} catch (Exception e) { connection.flushSync();
MinecraftServer.getExceptionManager().handleException(e); } catch (Exception e) {
connection.disconnect();
}
} }
// Wait for an event // Wait for an event
this.selector.select(key -> { this.selector.select(key -> {
final SocketChannel channel = (SocketChannel) key.channel(); final SocketChannel channel = (SocketChannel) key.channel();
if (!channel.isOpen()) return; if (!channel.isOpen()) return;
if (!key.isReadable()) return; if (!key.isReadable()) return;
PlayerSocketConnection connection = connectionMap.get(channel); final PlayerSocketConnection connection = connectionMap.get(channel);
if (connection == null) {
try {
channel.close();
} catch (IOException e) {
// Empty
}
return;
}
try { try {
BinaryBuffer readBuffer = BinaryBuffer.wrap(PooledBuffers.packetBuffer()); BinaryBuffer readBuffer = BinaryBuffer.wrap(PooledBuffers.packetBuffer());
// Consume last incomplete packet // Consume last incomplete packet
@ -84,17 +92,14 @@ public final class Worker extends MinestomThread {
public void disconnect(PlayerSocketConnection connection, SocketChannel channel) { public void disconnect(PlayerSocketConnection connection, SocketChannel channel) {
assert !connection.isOnline(); assert !connection.isOnline();
assert Thread.currentThread() == this; assert Thread.currentThread() == this;
connection.flushSync(); this.connectionMap.remove(channel);
try { if (channel.isOpen()) {
channel.close(); try {
this.connectionMap.remove(channel); connection.flushSync();
MinecraftServer.getConnectionManager().removePlayer(connection); channel.close();
Player player = connection.getPlayer(); } catch (IOException e) {
if (player != null && !player.isRemoved()) { // Socket operation may fail if the socket is already closed
player.scheduleNextTick(Entity::remove);
} }
} catch (IOException e) {
e.printStackTrace();
} }
} }