mirror of
https://github.com/Minestom/Minestom.git
synced 2024-10-02 16:38:16 +02:00
Merge pull request #107 from LeoDog896/master
Better KeepAlive and Connection handling
This commit is contained in:
commit
2e9f964d33
@ -6,7 +6,6 @@ import net.minestom.server.command.CommandManager;
|
|||||||
import net.minestom.server.data.DataManager;
|
import net.minestom.server.data.DataManager;
|
||||||
import net.minestom.server.data.DataType;
|
import net.minestom.server.data.DataType;
|
||||||
import net.minestom.server.data.SerializableData;
|
import net.minestom.server.data.SerializableData;
|
||||||
import net.minestom.server.entity.EntityManager;
|
|
||||||
import net.minestom.server.entity.EntityType;
|
import net.minestom.server.entity.EntityType;
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.event.GlobalEventHandler;
|
import net.minestom.server.event.GlobalEventHandler;
|
||||||
@ -56,7 +55,6 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main server class used to start the server and retrieve all the managers.
|
* The main server class used to start the server and retrieve all the managers.
|
||||||
@ -105,7 +103,6 @@ public final class MinecraftServer {
|
|||||||
private static ConnectionManager connectionManager;
|
private static ConnectionManager connectionManager;
|
||||||
private static InstanceManager instanceManager;
|
private static InstanceManager instanceManager;
|
||||||
private static BlockManager blockManager;
|
private static BlockManager blockManager;
|
||||||
private static EntityManager entityManager;
|
|
||||||
private static CommandManager commandManager;
|
private static CommandManager commandManager;
|
||||||
private static RecipeManager recipeManager;
|
private static RecipeManager recipeManager;
|
||||||
private static StorageManager storageManager;
|
private static StorageManager storageManager;
|
||||||
@ -167,7 +164,6 @@ public final class MinecraftServer {
|
|||||||
|
|
||||||
instanceManager = new InstanceManager();
|
instanceManager = new InstanceManager();
|
||||||
blockManager = new BlockManager();
|
blockManager = new BlockManager();
|
||||||
entityManager = new EntityManager();
|
|
||||||
commandManager = new CommandManager();
|
commandManager = new CommandManager();
|
||||||
recipeManager = new RecipeManager();
|
recipeManager = new RecipeManager();
|
||||||
storageManager = new StorageManager();
|
storageManager = new StorageManager();
|
||||||
@ -335,16 +331,6 @@ public final class MinecraftServer {
|
|||||||
return blockManager;
|
return blockManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the manager handling waiting players.
|
|
||||||
*
|
|
||||||
* @return the entity manager
|
|
||||||
*/
|
|
||||||
public static EntityManager getEntityManager() {
|
|
||||||
checkInitStatus(entityManager);
|
|
||||||
return entityManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the manager handling commands.
|
* Gets the manager handling commands.
|
||||||
*
|
*
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
package net.minestom.server;
|
package net.minestom.server;
|
||||||
|
|
||||||
import com.google.common.collect.Queues;
|
import com.google.common.collect.Queues;
|
||||||
import net.minestom.server.entity.EntityManager;
|
|
||||||
import net.minestom.server.instance.Instance;
|
import net.minestom.server.instance.Instance;
|
||||||
import net.minestom.server.instance.InstanceManager;
|
import net.minestom.server.instance.InstanceManager;
|
||||||
|
import net.minestom.server.network.ConnectionManager;
|
||||||
import net.minestom.server.thread.PerInstanceThreadProvider;
|
import net.minestom.server.thread.PerInstanceThreadProvider;
|
||||||
import net.minestom.server.thread.ThreadProvider;
|
import net.minestom.server.thread.ThreadProvider;
|
||||||
import net.minestom.server.utils.validate.Check;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -50,7 +49,7 @@ public final class UpdateManager {
|
|||||||
* Starts the server loop in the update thread.
|
* Starts the server loop in the update thread.
|
||||||
*/
|
*/
|
||||||
protected void start() {
|
protected void start() {
|
||||||
final EntityManager entityManager = MinecraftServer.getEntityManager();
|
final ConnectionManager connectionManager = MinecraftServer.getConnectionManager();
|
||||||
|
|
||||||
updateExecutionService.scheduleAtFixedRate(() -> {
|
updateExecutionService.scheduleAtFixedRate(() -> {
|
||||||
try {
|
try {
|
||||||
@ -66,10 +65,10 @@ public final class UpdateManager {
|
|||||||
doTickCallback(tickStartCallbacks, tickStart);
|
doTickCallback(tickStartCallbacks, tickStart);
|
||||||
|
|
||||||
// Waiting players update (newly connected clients waiting to get into the server)
|
// Waiting players update (newly connected clients waiting to get into the server)
|
||||||
entityManager.updateWaitingPlayers();
|
connectionManager.updateWaitingPlayers();
|
||||||
|
|
||||||
// Keep Alive Handling
|
// Keep Alive Handling
|
||||||
entityManager.handleKeepAlive(tickStart);
|
connectionManager.handleKeepAlive(tickStart);
|
||||||
|
|
||||||
// Server tick (chunks/entities)
|
// Server tick (chunks/entities)
|
||||||
serverTick(tickStart);
|
serverTick(tickStart);
|
||||||
|
@ -1,82 +0,0 @@
|
|||||||
package net.minestom.server.entity;
|
|
||||||
|
|
||||||
import com.google.common.collect.Queues;
|
|
||||||
import net.minestom.server.MinecraftServer;
|
|
||||||
import net.minestom.server.chat.ChatColor;
|
|
||||||
import net.minestom.server.chat.ColoredText;
|
|
||||||
import net.minestom.server.event.player.PlayerLoginEvent;
|
|
||||||
import net.minestom.server.instance.Instance;
|
|
||||||
import net.minestom.server.network.ConnectionManager;
|
|
||||||
import net.minestom.server.network.packet.server.play.KeepAlivePacket;
|
|
||||||
import net.minestom.server.network.player.PlayerConnection;
|
|
||||||
import net.minestom.server.utils.validate.Check;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Queue;
|
|
||||||
|
|
||||||
public final class EntityManager {
|
|
||||||
|
|
||||||
private static final ConnectionManager CONNECTION_MANAGER = MinecraftServer.getConnectionManager();
|
|
||||||
|
|
||||||
private static final long KEEP_ALIVE_DELAY = 10_000;
|
|
||||||
private static final long KEEP_ALIVE_KICK = 30_000;
|
|
||||||
private static final ColoredText TIMEOUT_TEXT = ColoredText.of(ChatColor.RED + "Timeout");
|
|
||||||
|
|
||||||
private final Queue<Player> waitingPlayers = Queues.newConcurrentLinkedQueue();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connects waiting players.
|
|
||||||
*/
|
|
||||||
public void updateWaitingPlayers() {
|
|
||||||
// Connect waiting players
|
|
||||||
waitingPlayersTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates keep alive by checking the last keep alive packet and send a new one if needed.
|
|
||||||
*
|
|
||||||
* @param tickStart the time of the update in milliseconds, forwarded to the packet
|
|
||||||
*/
|
|
||||||
public void handleKeepAlive(long tickStart) {
|
|
||||||
final KeepAlivePacket keepAlivePacket = new KeepAlivePacket(tickStart);
|
|
||||||
for (Player player : CONNECTION_MANAGER.getOnlinePlayers()) {
|
|
||||||
final long lastKeepAlive = tickStart - player.getLastKeepAlive();
|
|
||||||
if (lastKeepAlive > KEEP_ALIVE_DELAY && player.didAnswerKeepAlive()) {
|
|
||||||
final PlayerConnection playerConnection = player.getPlayerConnection();
|
|
||||||
player.refreshKeepAlive(tickStart);
|
|
||||||
playerConnection.sendPacket(keepAlivePacket);
|
|
||||||
} else if (lastKeepAlive >= KEEP_ALIVE_KICK) {
|
|
||||||
player.kick(TIMEOUT_TEXT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds connected clients after the handshake (used to free the networking threads).
|
|
||||||
*/
|
|
||||||
private void waitingPlayersTick() {
|
|
||||||
Player waitingPlayer;
|
|
||||||
while ((waitingPlayer = waitingPlayers.poll()) != null) {
|
|
||||||
|
|
||||||
PlayerLoginEvent loginEvent = new PlayerLoginEvent(waitingPlayer);
|
|
||||||
waitingPlayer.callEvent(PlayerLoginEvent.class, loginEvent);
|
|
||||||
final Instance spawningInstance = loginEvent.getSpawningInstance();
|
|
||||||
|
|
||||||
Check.notNull(spawningInstance, "You need to specify a spawning instance in the PlayerLoginEvent");
|
|
||||||
|
|
||||||
waitingPlayer.init(spawningInstance);
|
|
||||||
|
|
||||||
// Spawn the player at Player#getRespawnPoint during the next instance tick
|
|
||||||
spawningInstance.scheduleNextTick(waitingPlayer::setInstance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a player into the waiting list, to be handled during the next server tick.
|
|
||||||
*
|
|
||||||
* @param player the {@link Player player} to add into the waiting list
|
|
||||||
*/
|
|
||||||
public void addWaitingPlayer(@NotNull Player player) {
|
|
||||||
this.waitingPlayers.add(player);
|
|
||||||
}
|
|
||||||
}
|
|
@ -229,10 +229,11 @@ public class Player extends LivingEntity implements CommandSender {
|
|||||||
* Init the player and spawn him.
|
* Init the player and spawn him.
|
||||||
* <p>
|
* <p>
|
||||||
* WARNING: executed in the main update thread
|
* WARNING: executed in the main update thread
|
||||||
|
* UNSAFE: Only meant to be used when a netty player connects through the server.
|
||||||
*
|
*
|
||||||
* @param spawnInstance the player spawn instance (defined in {@link PlayerLoginEvent})
|
* @param spawnInstance the player spawn instance (defined in {@link PlayerLoginEvent})
|
||||||
*/
|
*/
|
||||||
protected void init(@NotNull Instance spawnInstance) {
|
public void UNSAFE_init(@NotNull Instance spawnInstance) {
|
||||||
this.dimensionType = spawnInstance.getDimensionType();
|
this.dimensionType = spawnInstance.getDimensionType();
|
||||||
|
|
||||||
JoinGamePacket joinGamePacket = new JoinGamePacket();
|
JoinGamePacket joinGamePacket = new JoinGamePacket();
|
||||||
|
@ -8,23 +8,28 @@ import net.minestom.server.chat.JsonMessage;
|
|||||||
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.event.player.AsyncPlayerPreLoginEvent;
|
import net.minestom.server.event.player.AsyncPlayerPreLoginEvent;
|
||||||
|
import net.minestom.server.event.player.PlayerLoginEvent;
|
||||||
|
import net.minestom.server.instance.Instance;
|
||||||
import net.minestom.server.listener.manager.ClientPacketConsumer;
|
import net.minestom.server.listener.manager.ClientPacketConsumer;
|
||||||
import net.minestom.server.listener.manager.ServerPacketConsumer;
|
import net.minestom.server.listener.manager.ServerPacketConsumer;
|
||||||
import net.minestom.server.network.packet.client.login.LoginStartPacket;
|
import net.minestom.server.network.packet.client.login.LoginStartPacket;
|
||||||
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.ChatMessagePacket;
|
import net.minestom.server.network.packet.server.play.ChatMessagePacket;
|
||||||
import net.minestom.server.network.packet.server.play.DisconnectPacket;
|
import net.minestom.server.network.packet.server.play.DisconnectPacket;
|
||||||
|
import net.minestom.server.network.packet.server.play.KeepAlivePacket;
|
||||||
import net.minestom.server.network.player.NettyPlayerConnection;
|
import net.minestom.server.network.player.NettyPlayerConnection;
|
||||||
import net.minestom.server.network.player.PlayerConnection;
|
import net.minestom.server.network.player.PlayerConnection;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketUtils;
|
||||||
import net.minestom.server.utils.async.AsyncUtils;
|
import net.minestom.server.utils.async.AsyncUtils;
|
||||||
import net.minestom.server.utils.callback.validator.PlayerValidator;
|
import net.minestom.server.utils.callback.validator.PlayerValidator;
|
||||||
|
import net.minestom.server.utils.validate.Check;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@ -34,6 +39,11 @@ import java.util.function.Consumer;
|
|||||||
*/
|
*/
|
||||||
public final class ConnectionManager {
|
public final class ConnectionManager {
|
||||||
|
|
||||||
|
private static final long KEEP_ALIVE_DELAY = 10_000;
|
||||||
|
private static final long KEEP_ALIVE_KICK = 30_000;
|
||||||
|
private static final ColoredText TIMEOUT_TEXT = ColoredText.of(ChatColor.RED + "Timeout");
|
||||||
|
|
||||||
|
private final Queue<Player> waitingPlayers = new ConcurrentLinkedQueue<>();
|
||||||
private final Set<Player> players = new CopyOnWriteArraySet<>();
|
private final Set<Player> players = new CopyOnWriteArraySet<>();
|
||||||
private final Map<PlayerConnection, Player> connectionPlayerMap = new ConcurrentHashMap<>();
|
private final Map<PlayerConnection, Player> connectionPlayerMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@ -408,7 +418,7 @@ public final class ConnectionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the player to the waiting list
|
// Add the player to the waiting list
|
||||||
MinecraftServer.getEntityManager().addWaitingPlayer(player);
|
addWaitingPlayer(player);
|
||||||
|
|
||||||
if (register) {
|
if (register) {
|
||||||
registerPlayer(player);
|
registerPlayer(player);
|
||||||
@ -451,4 +461,61 @@ public final class ConnectionManager {
|
|||||||
this.players.clear();
|
this.players.clear();
|
||||||
this.connectionPlayerMap.clear();
|
this.connectionPlayerMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects waiting players.
|
||||||
|
*/
|
||||||
|
public void updateWaitingPlayers() {
|
||||||
|
// Connect waiting players
|
||||||
|
waitingPlayersTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates keep alive by checking the last keep alive packet and send a new one if needed.
|
||||||
|
*
|
||||||
|
* @param tickStart the time of the update in milliseconds, forwarded to the packet
|
||||||
|
*/
|
||||||
|
public void handleKeepAlive(long tickStart) {
|
||||||
|
final KeepAlivePacket keepAlivePacket = new KeepAlivePacket(tickStart);
|
||||||
|
for (Player player : getOnlinePlayers()) {
|
||||||
|
final long lastKeepAlive = tickStart - player.getLastKeepAlive();
|
||||||
|
// Occasionally a packet may be dropped, this is very useful for networks that experience lag / packet loss.
|
||||||
|
if (lastKeepAlive >= KEEP_ALIVE_KICK) {
|
||||||
|
player.kick(TIMEOUT_TEXT);
|
||||||
|
} else if (lastKeepAlive > KEEP_ALIVE_DELAY) {
|
||||||
|
final PlayerConnection playerConnection = player.getPlayerConnection();
|
||||||
|
player.refreshKeepAlive(tickStart);
|
||||||
|
playerConnection.sendPacket(keepAlivePacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds connected clients after the handshake (used to free the networking threads).
|
||||||
|
*/
|
||||||
|
private void waitingPlayersTick() {
|
||||||
|
Player waitingPlayer;
|
||||||
|
while ((waitingPlayer = waitingPlayers.poll()) != null) {
|
||||||
|
|
||||||
|
PlayerLoginEvent loginEvent = new PlayerLoginEvent(waitingPlayer);
|
||||||
|
waitingPlayer.callEvent(PlayerLoginEvent.class, loginEvent);
|
||||||
|
final Instance spawningInstance = loginEvent.getSpawningInstance();
|
||||||
|
|
||||||
|
Check.notNull(spawningInstance, "You need to specify a spawning instance in the PlayerLoginEvent");
|
||||||
|
|
||||||
|
waitingPlayer.UNSAFE_init(spawningInstance);
|
||||||
|
|
||||||
|
// Spawn the player at Player#getRespawnPoint during the next instance tick
|
||||||
|
spawningInstance.scheduleNextTick(waitingPlayer::setInstance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a player into the waiting list, to be handled during the next server tick.
|
||||||
|
*
|
||||||
|
* @param player the {@link Player player} to add into the waiting list
|
||||||
|
*/
|
||||||
|
public void addWaitingPlayer(@NotNull Player player) {
|
||||||
|
this.waitingPlayers.add(player);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user