Cleanup UpdateManager

This commit is contained in:
themode 2020-11-12 00:18:36 +01:00
parent 46bf69c9e2
commit 041bc721c8
2 changed files with 90 additions and 66 deletions

View File

@ -1,23 +1,19 @@
package net.minestom.server; package net.minestom.server;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.entity.EntityManager; import net.minestom.server.entity.EntityManager;
import net.minestom.server.entity.Player;
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.network.packet.server.play.KeepAlivePacket;
import net.minestom.server.thread.PerGroupChunkProvider; import net.minestom.server.thread.PerGroupChunkProvider;
import net.minestom.server.thread.ThreadProvider; import net.minestom.server.thread.ThreadProvider;
import net.minestom.server.utils.thread.MinestomThread; import net.minestom.server.utils.thread.MinestomThread;
import net.minestom.server.utils.validate.Check; import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.function.DoubleConsumer; import java.util.function.LongConsumer;
/** /**
* Manager responsible for the server ticks. * Manager responsible for the server ticks.
@ -27,37 +23,30 @@ import java.util.function.DoubleConsumer;
*/ */
public final class UpdateManager { public final class UpdateManager {
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 ExecutorService mainUpdate = new MinestomThread(1, MinecraftServer.THREAD_NAME_MAIN_UPDATE); private final ExecutorService mainUpdate = new MinestomThread(1, MinecraftServer.THREAD_NAME_MAIN_UPDATE);
private boolean stopRequested; private boolean stopRequested;
private ThreadProvider threadProvider; private ThreadProvider threadProvider;
private final ConcurrentLinkedQueue<Runnable> tickStartCallbacks = new ConcurrentLinkedQueue<>(); private final ConcurrentLinkedQueue<LongConsumer> tickStartCallbacks = new ConcurrentLinkedQueue<>();
private final ConcurrentLinkedQueue<DoubleConsumer> tickEndCallbacks = new ConcurrentLinkedQueue<>(); private final ConcurrentLinkedQueue<LongConsumer> tickEndCallbacks = new ConcurrentLinkedQueue<>();
{ {
// DEFAULT THREAD PROVIDER // DEFAULT THREAD PROVIDER
//threadProvider = new PerInstanceThreadProvider();
threadProvider = new PerGroupChunkProvider(); threadProvider = new PerGroupChunkProvider();
} }
/** /**
* Should only be created in MinecraftServer * Should only be created in MinecraftServer.
*/ */
protected UpdateManager() { protected UpdateManager() {
} }
/** /**
* Starts the server loop in the update thread * Starts the server loop in the update thread.
*/ */
protected void start() { protected void start() {
mainUpdate.execute(() -> { mainUpdate.execute(() -> {
final ConnectionManager connectionManager = MinecraftServer.getConnectionManager();
final EntityManager entityManager = MinecraftServer.getEntityManager(); final EntityManager entityManager = MinecraftServer.getEntityManager();
final long tickDistance = MinecraftServer.TICK_MS * 1000000; final long tickDistance = MinecraftServer.TICK_MS * 1000000;
@ -67,53 +56,19 @@ public final class UpdateManager {
final long tickStart = System.currentTimeMillis(); final long tickStart = System.currentTimeMillis();
// Tick start callbacks // Tick start callbacks
if (!tickStartCallbacks.isEmpty()) { doTickCallback(tickStartCallbacks, tickStart);
Runnable callback;
while ((callback = tickStartCallbacks.poll()) != null) {
callback.run();
}
}
List<Future<?>> futures;
// Server tick (instance/chunk/entity)
// Synchronize with the update manager instance, like the signal for chunk load/unload
synchronized (this) {
futures = threadProvider.update(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(); entityManager.updateWaitingPlayers();
// Keep Alive Handling // Keep Alive Handling
final KeepAlivePacket keepAlivePacket = new KeepAlivePacket(tickStart); entityManager.handleKeepAlive(tickStart);
for (Player player : connectionManager.getOnlinePlayers()) {
final long lastKeepAlive = tickStart - player.getLastKeepAlive();
if (lastKeepAlive > KEEP_ALIVE_DELAY && player.didAnswerKeepAlive()) {
player.refreshKeepAlive(tickStart);
player.getPlayerConnection().sendPacket(keepAlivePacket);
} else if (lastKeepAlive >= KEEP_ALIVE_KICK) {
player.kick(TIMEOUT_TEXT);
}
}
for (final Future<?> future : futures) {
try {
future.get();
} catch (Throwable e) {
e.printStackTrace();
}
}
// Server tick (chunks/entities)
serverTick(tickStart);
// Tick end callbacks // Tick end callbacks
if (!tickEndCallbacks.isEmpty()) { doTickCallback(tickEndCallbacks, (System.nanoTime() - currentTime) / 1000000);
final double tickEnd = (System.nanoTime() - currentTime) / 1000000D;
DoubleConsumer callback;
while ((callback = tickEndCallbacks.poll()) != null) {
callback.accept(tickEnd);
}
}
// Sleep until next tick // Sleep until next tick
final long sleepTime = Math.max(1, (tickDistance - (System.nanoTime() - currentTime)) / 1000000); final long sleepTime = Math.max(1, (tickDistance - (System.nanoTime() - currentTime)) / 1000000);
@ -128,6 +83,44 @@ public final class UpdateManager {
}); });
} }
/**
* Executes a server tick and returns only once all the futures are completed.
*
* @param tickStart the time of the tick in milliseconds
*/
private void serverTick(long tickStart) {
List<Future<?>> futures;
// Server tick (instance/chunk/entity)
// Synchronize with the update manager instance, like the signal for chunk load/unload
synchronized (this) {
futures = threadProvider.update(tickStart);
}
for (final Future<?> future : futures) {
try {
future.get();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
/**
* Used to execute tick-related callbacks.
*
* @param callbacks the callbacks to execute
* @param value the value to give to the consumers
*/
private void doTickCallback(ConcurrentLinkedQueue<LongConsumer> callbacks, long value) {
if (!callbacks.isEmpty()) {
LongConsumer callback;
while ((callback = callbacks.poll()) != null) {
callback.accept(value);
}
}
}
/** /**
* Gets the current {@link ThreadProvider}. * Gets the current {@link ThreadProvider}.
* *
@ -206,10 +199,12 @@ public final class UpdateManager {
/** /**
* Adds a callback executed at the start of the next server tick. * Adds a callback executed at the start of the next server tick.
* <p>
* The long in the consumer represents the starting time (in ms) of the tick.
* *
* @param callback the tick start callback * @param callback the tick start callback
*/ */
public void addTickStartCallback(Runnable callback) { public void addTickStartCallback(@NotNull LongConsumer callback) {
this.tickStartCallbacks.add(callback); this.tickStartCallbacks.add(callback);
} }
@ -218,18 +213,18 @@ public final class UpdateManager {
* *
* @param callback the callback to remove * @param callback the callback to remove
*/ */
public void removeTickStartCallback(Runnable callback) { public void removeTickStartCallback(@NotNull LongConsumer callback) {
this.tickStartCallbacks.remove(callback); this.tickStartCallbacks.remove(callback);
} }
/** /**
* Adds a callback executed at the end of the next server tick. * Adds a callback executed at the end of the next server tick.
* <p> * <p>
* The double in the consumer represents the duration (in ms) of the tick. * The long in the consumer represents the duration (in ms) of the tick.
* *
* @param callback the tick end callback * @param callback the tick end callback
*/ */
public void addTickEndCallback(DoubleConsumer callback) { public void addTickEndCallback(@NotNull LongConsumer callback) {
this.tickEndCallbacks.add(callback); this.tickEndCallbacks.add(callback);
} }
@ -238,12 +233,12 @@ public final class UpdateManager {
* *
* @param callback the callback to remove * @param callback the callback to remove
*/ */
public void removeTickEndCallback(DoubleConsumer callback) { public void removeTickEndCallback(@NotNull LongConsumer callback) {
this.tickEndCallbacks.remove(callback); this.tickEndCallbacks.remove(callback);
} }
/** /**
* Stops the server loop * Stops the server loop.
*/ */
public void stop() { public void stop() {
stopRequested = true; stopRequested = true;

View File

@ -1,10 +1,15 @@
package net.minestom.server.entity; package net.minestom.server.entity;
import net.minestom.server.MinecraftServer; 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.event.player.PlayerLoginEvent;
import net.minestom.server.event.player.PlayerPreLoginEvent; import net.minestom.server.event.player.PlayerPreLoginEvent;
import net.minestom.server.instance.Instance; 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.utils.validate.Check; import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
@ -12,10 +17,16 @@ import java.util.function.Consumer;
public final class EntityManager { 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 ConcurrentLinkedQueue<Player> waitingPlayers = new ConcurrentLinkedQueue<>(); private final ConcurrentLinkedQueue<Player> waitingPlayers = new ConcurrentLinkedQueue<>();
/** /**
* Connect waiting players * Connects waiting players.
*/ */
public void updateWaitingPlayers() { public void updateWaitingPlayers() {
// Connect waiting players // Connect waiting players
@ -23,7 +34,25 @@ public final class EntityManager {
} }
/** /**
* Add connected clients after the handshake (used to free the networking threads) * 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()) {
player.refreshKeepAlive(tickStart);
player.getPlayerConnection().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() { private void waitingPlayersTick() {
Player waitingPlayer; Player waitingPlayer;
@ -41,14 +70,14 @@ public final class EntityManager {
} }
/** /**
* Call the player initialization callbacks and the event {@link PlayerPreLoginEvent}. * Calls the player initialization callbacks and the event {@link PlayerPreLoginEvent}.
* If the {@link Player} hasn't been kicked, add him to the waiting list. * If the {@link Player} hasn't been kicked, add him to the waiting list.
* <p> * <p>
* Can be considered as a pre-init thing. * Can be considered as a pre-init thing.
* *
* @param player the {@link Player} to add * @param player the {@link Player} to add
*/ */
public void addWaitingPlayer(Player player) { public void addWaitingPlayer(@NotNull Player player) {
// Init player (register events) // Init player (register events)
for (Consumer<Player> playerInitialization : MinecraftServer.getConnectionManager().getPlayerInitializations()) { for (Consumer<Player> playerInitialization : MinecraftServer.getConnectionManager().getPlayerInitializations()) {