2020-04-24 03:25:58 +02:00
|
|
|
package net.minestom.server;
|
|
|
|
|
2021-04-25 19:08:04 +02:00
|
|
|
import net.minestom.server.acquirable.Acquirable;
|
2021-04-09 18:22:29 +02:00
|
|
|
import net.minestom.server.instance.Chunk;
|
2020-07-03 16:44:21 +02:00
|
|
|
import net.minestom.server.instance.Instance;
|
2020-10-13 04:31:03 +02:00
|
|
|
import net.minestom.server.instance.InstanceManager;
|
2021-04-11 00:09:01 +02:00
|
|
|
import net.minestom.server.monitoring.TickMonitor;
|
2021-01-18 01:26:20 +01:00
|
|
|
import net.minestom.server.network.ConnectionManager;
|
2021-04-09 05:40:40 +02:00
|
|
|
import net.minestom.server.network.player.NettyPlayerConnection;
|
2021-04-26 16:27:50 +02:00
|
|
|
import net.minestom.server.thread.SingleThreadProvider;
|
2020-07-03 16:44:21 +02:00
|
|
|
import net.minestom.server.thread.ThreadProvider;
|
2021-04-09 05:40:40 +02:00
|
|
|
import net.minestom.server.utils.async.AsyncUtils;
|
2020-11-12 00:18:36 +01:00
|
|
|
import org.jetbrains.annotations.NotNull;
|
2020-04-17 01:16:02 +02:00
|
|
|
|
2020-08-19 01:24:51 +02:00
|
|
|
import java.util.List;
|
2020-12-08 09:36:19 +01:00
|
|
|
import java.util.Queue;
|
2021-07-11 16:56:27 +02:00
|
|
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
2021-07-08 15:40:27 +02:00
|
|
|
import java.util.concurrent.locks.LockSupport;
|
2021-04-11 00:09:01 +02:00
|
|
|
import java.util.function.Consumer;
|
2020-11-12 00:18:36 +01:00
|
|
|
import java.util.function.LongConsumer;
|
2020-04-17 01:16:02 +02:00
|
|
|
|
2020-10-13 05:40:23 +02:00
|
|
|
/**
|
2020-10-13 12:49:29 +02:00
|
|
|
* Manager responsible for the server ticks.
|
2020-10-13 05:40:23 +02:00
|
|
|
* <p>
|
2021-04-17 02:50:33 +02:00
|
|
|
* The {@link ThreadProvider} manages the multi-thread aspect of chunk ticks.
|
2020-10-13 05:40:23 +02:00
|
|
|
*/
|
2020-06-02 14:43:31 +02:00
|
|
|
public final class UpdateManager {
|
2020-04-17 01:16:02 +02:00
|
|
|
|
2020-12-02 20:55:05 +01:00
|
|
|
private volatile boolean stopRequested;
|
2020-04-17 01:16:02 +02:00
|
|
|
|
2021-04-26 16:27:50 +02:00
|
|
|
// TODO make configurable
|
|
|
|
private ThreadProvider threadProvider = new SingleThreadProvider();
|
2020-08-02 11:37:39 +02:00
|
|
|
|
2021-06-20 22:03:14 +02:00
|
|
|
private final Queue<LongConsumer> tickStartCallbacks = new ConcurrentLinkedQueue<>();
|
|
|
|
private final Queue<LongConsumer> tickEndCallbacks = new ConcurrentLinkedQueue<>();
|
2021-04-11 00:09:01 +02:00
|
|
|
private final List<Consumer<TickMonitor>> tickMonitors = new CopyOnWriteArrayList<>();
|
2020-08-10 23:31:12 +02:00
|
|
|
|
2020-06-02 14:43:31 +02:00
|
|
|
/**
|
2020-11-12 00:18:36 +01:00
|
|
|
* Should only be created in MinecraftServer.
|
2020-06-02 14:43:31 +02:00
|
|
|
*/
|
|
|
|
protected UpdateManager() {
|
|
|
|
}
|
2020-04-17 01:16:02 +02:00
|
|
|
|
2020-08-07 09:14:50 +02:00
|
|
|
/**
|
2020-11-12 00:18:36 +01:00
|
|
|
* Starts the server loop in the update thread.
|
2020-08-07 09:14:50 +02:00
|
|
|
*/
|
|
|
|
protected void start() {
|
2021-01-18 01:26:20 +01:00
|
|
|
final ConnectionManager connectionManager = MinecraftServer.getConnectionManager();
|
2020-04-17 01:16:02 +02:00
|
|
|
|
2021-07-08 15:40:27 +02:00
|
|
|
new Thread(() -> {
|
|
|
|
while (!stopRequested) {
|
|
|
|
try {
|
|
|
|
long currentTime = System.nanoTime();
|
|
|
|
final long tickStart = System.currentTimeMillis();
|
|
|
|
|
|
|
|
// Tick start callbacks
|
|
|
|
doTickCallback(tickStartCallbacks, tickStart);
|
|
|
|
|
|
|
|
// Waiting players update (newly connected clients waiting to get into the server)
|
|
|
|
connectionManager.updateWaitingPlayers();
|
|
|
|
|
|
|
|
// Keep Alive Handling
|
|
|
|
connectionManager.handleKeepAlive(tickStart);
|
|
|
|
|
|
|
|
// Server tick (chunks/entities)
|
|
|
|
serverTick(tickStart);
|
|
|
|
|
|
|
|
// the time that the tick took in nanoseconds
|
|
|
|
final long tickTime = System.nanoTime() - currentTime;
|
|
|
|
|
|
|
|
// Tick end callbacks
|
|
|
|
doTickCallback(tickEndCallbacks, tickTime);
|
|
|
|
|
|
|
|
// Monitoring
|
|
|
|
if (!tickMonitors.isEmpty()) {
|
|
|
|
final double acquisitionTimeMs = Acquirable.getAcquiringTime() / 1e6D;
|
|
|
|
final double tickTimeMs = tickTime / 1e6D;
|
|
|
|
final TickMonitor tickMonitor = new TickMonitor(tickTimeMs, acquisitionTimeMs);
|
|
|
|
this.tickMonitors.forEach(consumer -> consumer.accept(tickMonitor));
|
|
|
|
Acquirable.resetAcquiringTime();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Flush all waiting packets
|
|
|
|
AsyncUtils.runAsync(() -> connectionManager.getOnlinePlayers().parallelStream()
|
|
|
|
.filter(player -> player.getPlayerConnection() instanceof NettyPlayerConnection)
|
|
|
|
.map(player -> (NettyPlayerConnection) player.getPlayerConnection())
|
|
|
|
.forEach(NettyPlayerConnection::flush));
|
|
|
|
|
|
|
|
// Disable thread until next tick
|
|
|
|
LockSupport.parkNanos((long) ((MinecraftServer.TICK_MS * 1e6) - tickTime));
|
|
|
|
} catch (Exception e) {
|
|
|
|
MinecraftServer.getExceptionManager().handleException(e);
|
2020-12-28 12:27:25 +01:00
|
|
|
}
|
|
|
|
}
|
2021-07-08 15:40:27 +02:00
|
|
|
}, MinecraftServer.THREAD_NAME_TICK_SCHEDULER).start();
|
2020-04-17 01:16:02 +02:00
|
|
|
}
|
|
|
|
|
2020-11-12 00:18:36 +01:00
|
|
|
/**
|
|
|
|
* 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) {
|
2021-04-14 20:32:02 +02:00
|
|
|
// Tick all instances
|
2021-07-11 16:56:27 +02:00
|
|
|
MinecraftServer.getInstanceManager().getInstances().forEach(instance -> {
|
|
|
|
try {
|
|
|
|
instance.tick(tickStart);
|
|
|
|
} catch (Exception e) {
|
|
|
|
MinecraftServer.getExceptionManager().handleException(e);
|
|
|
|
}
|
|
|
|
});
|
2021-04-18 00:00:02 +02:00
|
|
|
// Tick all chunks (and entities inside)
|
2021-07-01 00:34:58 +02:00
|
|
|
this.threadProvider.updateAndAwait(tickStart);
|
2021-04-15 01:44:08 +02:00
|
|
|
|
|
|
|
// Clear removed entities & update threads
|
2021-04-18 11:53:38 +02:00
|
|
|
long tickTime = System.currentTimeMillis() - tickStart;
|
|
|
|
this.threadProvider.refreshThreads(tickTime);
|
2020-11-12 00:18:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Used to execute tick-related callbacks.
|
|
|
|
*
|
|
|
|
* @param callbacks the callbacks to execute
|
|
|
|
* @param value the value to give to the consumers
|
|
|
|
*/
|
2020-12-08 09:36:19 +01:00
|
|
|
private void doTickCallback(Queue<LongConsumer> callbacks, long value) {
|
2020-11-12 00:18:36 +01:00
|
|
|
if (!callbacks.isEmpty()) {
|
|
|
|
LongConsumer callback;
|
|
|
|
while ((callback = callbacks.poll()) != null) {
|
|
|
|
callback.accept(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-03 16:44:21 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the current {@link ThreadProvider}.
|
2020-07-03 16:44:21 +02:00
|
|
|
*
|
|
|
|
* @return the current thread provider
|
|
|
|
*/
|
2021-04-15 01:44:08 +02:00
|
|
|
public @NotNull ThreadProvider getThreadProvider() {
|
2020-07-03 16:44:21 +02:00
|
|
|
return threadProvider;
|
|
|
|
}
|
|
|
|
|
2020-08-06 16:28:04 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Signals the {@link ThreadProvider} that an instance has been created.
|
2020-10-13 04:31:03 +02:00
|
|
|
* <p>
|
|
|
|
* WARNING: should be automatically done by the {@link InstanceManager}.
|
2020-10-13 04:10:37 +02:00
|
|
|
*
|
|
|
|
* @param instance the instance
|
|
|
|
*/
|
2021-04-15 01:44:08 +02:00
|
|
|
public void signalInstanceCreate(Instance instance) {
|
2020-10-13 04:10:37 +02:00
|
|
|
this.threadProvider.onInstanceCreate(instance);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Signals the {@link ThreadProvider} that an instance has been deleted.
|
2020-10-13 04:31:03 +02:00
|
|
|
* <p>
|
|
|
|
* WARNING: should be automatically done by the {@link InstanceManager}.
|
2020-10-13 04:10:37 +02:00
|
|
|
*
|
|
|
|
* @param instance the instance
|
|
|
|
*/
|
2021-04-15 01:44:08 +02:00
|
|
|
public void signalInstanceDelete(Instance instance) {
|
2020-10-13 04:10:37 +02:00
|
|
|
this.threadProvider.onInstanceDelete(instance);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Signals the {@link ThreadProvider} that a chunk has been loaded.
|
2020-10-13 04:31:03 +02:00
|
|
|
* <p>
|
|
|
|
* WARNING: should be automatically done by the {@link Instance} implementation.
|
2020-08-06 16:28:04 +02:00
|
|
|
*
|
2021-04-14 22:48:13 +02:00
|
|
|
* @param chunk the loaded chunk
|
2020-08-06 16:28:04 +02:00
|
|
|
*/
|
2021-04-15 01:44:08 +02:00
|
|
|
public void signalChunkLoad(@NotNull Chunk chunk) {
|
2021-04-14 21:29:37 +02:00
|
|
|
this.threadProvider.onChunkLoad(chunk);
|
2020-08-06 16:28:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Signals the {@link ThreadProvider} that a chunk has been unloaded.
|
2020-10-13 04:31:03 +02:00
|
|
|
* <p>
|
|
|
|
* WARNING: should be automatically done by the {@link Instance} implementation.
|
2020-08-06 16:28:04 +02:00
|
|
|
*
|
2021-04-14 22:48:13 +02:00
|
|
|
* @param chunk the unloaded chunk
|
2020-08-06 16:28:04 +02:00
|
|
|
*/
|
2021-04-15 01:44:08 +02:00
|
|
|
public void signalChunkUnload(@NotNull Chunk chunk) {
|
2021-04-14 21:29:37 +02:00
|
|
|
this.threadProvider.onChunkUnload(chunk);
|
2020-08-06 16:28:04 +02:00
|
|
|
}
|
|
|
|
|
2020-10-22 15:57:27 +02:00
|
|
|
/**
|
|
|
|
* Adds a callback executed at the start of the next server tick.
|
2020-11-12 00:18:36 +01:00
|
|
|
* <p>
|
|
|
|
* The long in the consumer represents the starting time (in ms) of the tick.
|
2020-10-22 15:57:27 +02:00
|
|
|
*
|
|
|
|
* @param callback the tick start callback
|
|
|
|
*/
|
2020-11-12 00:18:36 +01:00
|
|
|
public void addTickStartCallback(@NotNull LongConsumer callback) {
|
2020-10-22 15:57:27 +02:00
|
|
|
this.tickStartCallbacks.add(callback);
|
2020-08-11 01:41:14 +02:00
|
|
|
}
|
|
|
|
|
2020-10-22 15:57:27 +02:00
|
|
|
/**
|
|
|
|
* Removes a tick start callback.
|
|
|
|
*
|
|
|
|
* @param callback the callback to remove
|
|
|
|
*/
|
2020-11-12 00:18:36 +01:00
|
|
|
public void removeTickStartCallback(@NotNull LongConsumer callback) {
|
2020-10-22 15:57:27 +02:00
|
|
|
this.tickStartCallbacks.remove(callback);
|
2020-08-11 01:41:14 +02:00
|
|
|
}
|
|
|
|
|
2020-10-22 15:57:27 +02:00
|
|
|
/**
|
|
|
|
* Adds a callback executed at the end of the next server tick.
|
|
|
|
* <p>
|
2020-11-12 00:18:36 +01:00
|
|
|
* The long in the consumer represents the duration (in ms) of the tick.
|
2020-10-22 15:57:27 +02:00
|
|
|
*
|
|
|
|
* @param callback the tick end callback
|
|
|
|
*/
|
2020-11-12 00:18:36 +01:00
|
|
|
public void addTickEndCallback(@NotNull LongConsumer callback) {
|
2020-10-22 15:57:27 +02:00
|
|
|
this.tickEndCallbacks.add(callback);
|
2020-08-10 23:31:12 +02:00
|
|
|
}
|
|
|
|
|
2020-10-22 15:57:27 +02:00
|
|
|
/**
|
|
|
|
* Removes a tick end callback.
|
|
|
|
*
|
|
|
|
* @param callback the callback to remove
|
|
|
|
*/
|
2020-11-12 00:18:36 +01:00
|
|
|
public void removeTickEndCallback(@NotNull LongConsumer callback) {
|
2020-10-22 15:57:27 +02:00
|
|
|
this.tickEndCallbacks.remove(callback);
|
2020-08-10 23:31:12 +02:00
|
|
|
}
|
|
|
|
|
2021-04-11 00:09:01 +02:00
|
|
|
public void addTickMonitor(@NotNull Consumer<TickMonitor> consumer) {
|
|
|
|
this.tickMonitors.add(consumer);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void removeTickMonitor(@NotNull Consumer<TickMonitor> consumer) {
|
|
|
|
this.tickMonitors.remove(consumer);
|
|
|
|
}
|
|
|
|
|
2020-08-07 09:14:50 +02:00
|
|
|
/**
|
2020-11-12 00:18:36 +01:00
|
|
|
* Stops the server loop.
|
2020-08-07 09:14:50 +02:00
|
|
|
*/
|
2020-04-28 19:22:54 +02:00
|
|
|
public void stop() {
|
2021-04-15 04:06:55 +02:00
|
|
|
this.stopRequested = true;
|
|
|
|
this.threadProvider.shutdown();
|
2020-04-28 19:22:54 +02:00
|
|
|
}
|
2020-04-17 01:16:02 +02:00
|
|
|
}
|