Server process (#550)

This commit is contained in:
TheMode 2022-01-05 09:01:21 +01:00
parent 0bd736d837
commit 5bbf4642e8
19 changed files with 652 additions and 594 deletions

View File

@ -21,6 +21,7 @@ import net.minestom.server.event.player.PlayerDeathEvent;
import net.minestom.server.event.player.PlayerDisconnectEvent; import net.minestom.server.event.player.PlayerDisconnectEvent;
import net.minestom.server.event.player.PlayerLoginEvent; import net.minestom.server.event.player.PlayerLoginEvent;
import net.minestom.server.event.player.PlayerSpawnEvent; import net.minestom.server.event.player.PlayerSpawnEvent;
import net.minestom.server.event.server.ServerTickMonitorEvent;
import net.minestom.server.instance.Instance; import net.minestom.server.instance.Instance;
import net.minestom.server.instance.InstanceContainer; import net.minestom.server.instance.InstanceContainer;
import net.minestom.server.instance.InstanceManager; import net.minestom.server.instance.InstanceManager;
@ -133,7 +134,7 @@ public class PlayerInit {
var eventHandler = MinecraftServer.getGlobalEventHandler(); var eventHandler = MinecraftServer.getGlobalEventHandler();
eventHandler.addChild(DEMO_NODE); eventHandler.addChild(DEMO_NODE);
MinecraftServer.getUpdateManager().addTickMonitor(LAST_TICK::set); eventHandler.addListener(ServerTickMonitorEvent.class, event -> LAST_TICK.set(event.getTickMonitor()));
BenchmarkManager benchmarkManager = MinecraftServer.getBenchmarkManager(); BenchmarkManager benchmarkManager = MinecraftServer.getBenchmarkManager();
MinecraftServer.getSchedulerManager().buildTask(() -> { MinecraftServer.getSchedulerManager().buildTask(() -> {

View File

@ -4,17 +4,12 @@ import net.minestom.server.advancements.AdvancementManager;
import net.minestom.server.adventure.bossbar.BossBarManager; import net.minestom.server.adventure.bossbar.BossBarManager;
import net.minestom.server.command.CommandManager; 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.SerializableData;
import net.minestom.server.event.GlobalEventHandler; import net.minestom.server.event.GlobalEventHandler;
import net.minestom.server.exception.ExceptionManager; import net.minestom.server.exception.ExceptionManager;
import net.minestom.server.extensions.Extension;
import net.minestom.server.extensions.ExtensionManager; import net.minestom.server.extensions.ExtensionManager;
import net.minestom.server.fluid.Fluid;
import net.minestom.server.gamedata.tags.TagManager; import net.minestom.server.gamedata.tags.TagManager;
import net.minestom.server.instance.InstanceManager; import net.minestom.server.instance.InstanceManager;
import net.minestom.server.instance.block.BlockManager; import net.minestom.server.instance.block.BlockManager;
import net.minestom.server.instance.block.rule.BlockPlacementRule;
import net.minestom.server.listener.manager.PacketListenerManager; import net.minestom.server.listener.manager.PacketListenerManager;
import net.minestom.server.monitoring.BenchmarkManager; import net.minestom.server.monitoring.BenchmarkManager;
import net.minestom.server.network.ConnectionManager; import net.minestom.server.network.ConnectionManager;
@ -25,10 +20,8 @@ import net.minestom.server.network.socket.Server;
import net.minestom.server.ping.ResponseDataConsumer; import net.minestom.server.ping.ResponseDataConsumer;
import net.minestom.server.recipe.RecipeManager; import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.scoreboard.TeamManager; import net.minestom.server.scoreboard.TeamManager;
import net.minestom.server.storage.StorageLocation;
import net.minestom.server.storage.StorageManager; import net.minestom.server.storage.StorageManager;
import net.minestom.server.terminal.MinestomTerminal; import net.minestom.server.thread.TickSchedulerThread;
import net.minestom.server.thread.MinestomThreadPool;
import net.minestom.server.timer.SchedulerManager; import net.minestom.server.timer.SchedulerManager;
import net.minestom.server.utils.MathUtils; import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.PacketUtils; import net.minestom.server.utils.PacketUtils;
@ -36,8 +29,10 @@ import net.minestom.server.utils.validate.Check;
import net.minestom.server.world.Difficulty; import net.minestom.server.world.Difficulty;
import net.minestom.server.world.DimensionTypeManager; import net.minestom.server.world.DimensionTypeManager;
import net.minestom.server.world.biomes.BiomeManager; import net.minestom.server.world.biomes.BiomeManager;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -75,40 +70,9 @@ public final class MinecraftServer {
// Network monitoring // Network monitoring
private static int rateLimit = 300; private static int rateLimit = 300;
private static int maxPacketSize = 30_000; private static int maxPacketSize = 30_000;
// Network
private static PacketListenerManager packetListenerManager;
private static PacketProcessor packetProcessor;
private static Server server;
private static ExceptionManager exceptionManager;
// In-Game Manager // In-Game Manager
private static ConnectionManager connectionManager; private static volatile ServerProcess serverProcess;
private static InstanceManager instanceManager;
private static BlockManager blockManager;
private static CommandManager commandManager;
private static RecipeManager recipeManager;
private static StorageManager storageManager;
private static DataManager dataManager;
private static TeamManager teamManager;
private static SchedulerManager schedulerManager;
private static BenchmarkManager benchmarkManager;
private static DimensionTypeManager dimensionTypeManager;
private static BiomeManager biomeManager;
private static AdvancementManager advancementManager;
private static BossBarManager bossBarManager;
private static ExtensionManager extensionManager;
private static final GlobalEventHandler GLOBAL_EVENT_HANDLER = new GlobalEventHandler();
private static UpdateManager updateManager;
private static MinecraftServer minecraftServer;
// Data
private static boolean initialized;
private static boolean started;
private static volatile boolean stopping;
private static int chunkViewDistance = Integer.getInteger("minestom.chunk-view-distance", 8); private static int chunkViewDistance = Integer.getInteger("minestom.chunk-view-distance", 8);
private static int entityViewDistance = Integer.getInteger("minestom.entity-view-distance", 5); private static int entityViewDistance = Integer.getInteger("minestom.entity-view-distance", 5);
@ -117,57 +81,22 @@ public final class MinecraftServer {
private static ResponseDataConsumer responseDataConsumer; private static ResponseDataConsumer responseDataConsumer;
private static String brandName = "Minestom"; private static String brandName = "Minestom";
private static Difficulty difficulty = Difficulty.NORMAL; private static Difficulty difficulty = Difficulty.NORMAL;
private static TagManager tagManager;
public static MinecraftServer init() { public static MinecraftServer init() {
if (minecraftServer != null) // don't init twice updateProcess();
return minecraftServer; return new MinecraftServer();
// Initialize the ExceptionManager at first
exceptionManager = new ExceptionManager();
extensionManager = new ExtensionManager();
// warmup/force-init registries
// without this line, registry types that are not loaded explicitly will have an internal empty registry in Registries
// That can happen with PotionType for instance, if no code tries to access a PotionType field
// TODO: automate (probably with code generation)
Fluid.values();
connectionManager = new ConnectionManager();
// Networking
packetProcessor = new PacketProcessor();
packetListenerManager = new PacketListenerManager();
instanceManager = new InstanceManager();
blockManager = new BlockManager();
commandManager = new CommandManager();
recipeManager = new RecipeManager();
storageManager = new StorageManager();
dataManager = new DataManager();
teamManager = new TeamManager();
schedulerManager = new SchedulerManager();
benchmarkManager = new BenchmarkManager();
dimensionTypeManager = new DimensionTypeManager();
biomeManager = new BiomeManager();
advancementManager = new AdvancementManager();
bossBarManager = new BossBarManager();
updateManager = new UpdateManager();
tagManager = new TagManager();
try {
server = new Server(packetProcessor);
} catch (IOException e) {
e.printStackTrace();
} }
initialized = true; @ApiStatus.Internal
public static ServerProcess updateProcess() {
minecraftServer = new MinecraftServer(); ServerProcess process;
try {
return minecraftServer; process = new ServerProcessImpl();
serverProcess = process;
} catch (IOException e) {
throw new RuntimeException(e);
}
return process;
} }
/** /**
@ -247,108 +176,51 @@ public final class MinecraftServer {
PacketUtils.broadcastPacket(new ServerDifficultyPacket(difficulty, true)); PacketUtils.broadcastPacket(new ServerDifficultyPacket(difficulty, true));
} }
/** @ApiStatus.Experimental
* Gets the global event handler. public static @UnknownNullability ServerProcess process() {
* <p> return serverProcess;
* Used to register event callback at a global scale. }
*
* @return the global event handler
*/
public static @NotNull GlobalEventHandler getGlobalEventHandler() { public static @NotNull GlobalEventHandler getGlobalEventHandler() {
return GLOBAL_EVENT_HANDLER; return serverProcess.eventHandler();
} }
/**
* Gets the manager handling all incoming packets
*
* @return the packet listener manager
*/
public static PacketListenerManager getPacketListenerManager() { public static PacketListenerManager getPacketListenerManager() {
checkInitStatus(packetListenerManager); return serverProcess.packetListener();
return packetListenerManager;
} }
/**
* Gets the manager handling all registered instances.
*
* @return the instance manager
*/
public static InstanceManager getInstanceManager() { public static InstanceManager getInstanceManager() {
checkInitStatus(instanceManager); return serverProcess.instance();
return instanceManager;
} }
/**
* Gets the manager handling {@link net.minestom.server.instance.block.BlockHandler block handlers}
* and {@link BlockPlacementRule placement rules}.
*
* @return the block manager
*/
public static BlockManager getBlockManager() { public static BlockManager getBlockManager() {
checkInitStatus(blockManager); return serverProcess.block();
return blockManager;
} }
/**
* Gets the manager handling commands.
*
* @return the command manager
*/
public static CommandManager getCommandManager() { public static CommandManager getCommandManager() {
checkInitStatus(commandManager); return serverProcess.command();
return commandManager;
} }
/**
* Gets the manager handling recipes show to the clients.
*
* @return the recipe manager
*/
public static RecipeManager getRecipeManager() { public static RecipeManager getRecipeManager() {
checkInitStatus(recipeManager); return serverProcess.recipe();
return recipeManager;
} }
/**
* Gets the manager handling storage.
*
* @return the storage manager
*/
@Deprecated @Deprecated
public static StorageManager getStorageManager() { public static StorageManager getStorageManager() {
checkInitStatus(storageManager); return serverProcess.storage();
return storageManager;
} }
/**
* Gets the manager handling {@link DataType} used by {@link SerializableData}.
*
* @return the data manager
*/
@Deprecated @Deprecated
public static DataManager getDataManager() { public static DataManager getDataManager() {
checkInitStatus(dataManager); return serverProcess.data();
return dataManager;
} }
/**
* Gets the manager handling teams.
*
* @return the team manager
*/
public static TeamManager getTeamManager() { public static TeamManager getTeamManager() {
checkInitStatus(teamManager); return serverProcess.team();
return teamManager;
} }
/**
* Gets the manager handling scheduled tasks.
*
* @return the scheduler manager
*/
public static SchedulerManager getSchedulerManager() { public static SchedulerManager getSchedulerManager() {
checkInitStatus(schedulerManager); return serverProcess.scheduler();
return schedulerManager;
} }
/** /**
@ -357,68 +229,31 @@ public final class MinecraftServer {
* @return the benchmark manager * @return the benchmark manager
*/ */
public static BenchmarkManager getBenchmarkManager() { public static BenchmarkManager getBenchmarkManager() {
checkInitStatus(benchmarkManager); return serverProcess.benchmark();
return benchmarkManager;
} }
/**
* Gets the exception manager for exception handling.
*
* @return the exception manager
*/
public static ExceptionManager getExceptionManager() { public static ExceptionManager getExceptionManager() {
checkInitStatus(exceptionManager); return serverProcess.exception();
return exceptionManager;
} }
/**
* Gets the manager handling server connections.
*
* @return the connection manager
*/
public static ConnectionManager getConnectionManager() { public static ConnectionManager getConnectionManager() {
checkInitStatus(connectionManager); return serverProcess.connection();
return connectionManager;
} }
/**
* Gets the boss bar manager.
*
* @return the boss bar manager
*/
public static BossBarManager getBossBarManager() { public static BossBarManager getBossBarManager() {
checkInitStatus(bossBarManager); return serverProcess.bossBar();
return bossBarManager;
} }
/**
* Gets the object handling the client packets processing.
* <p>
* Can be used if you want to convert a buffer to a client packet object.
*
* @return the packet processor
*/
public static PacketProcessor getPacketProcessor() { public static PacketProcessor getPacketProcessor() {
checkInitStatus(packetProcessor); return serverProcess.packetProcessor();
return packetProcessor;
} }
/**
* Gets if the server is up and running.
*
* @return true if the server is started
*/
public static boolean isStarted() { public static boolean isStarted() {
return started; return serverProcess.isAlive();
} }
/**
* Gets if the server is currently being shutdown using {@link #stopCleanly()}.
*
* @return true if the server is being stopped
*/
public static boolean isStopping() { public static boolean isStopping() {
return stopping; return !isStarted();
} }
/** /**
@ -439,7 +274,7 @@ public final class MinecraftServer {
*/ */
@Deprecated @Deprecated
public static void setChunkViewDistance(int chunkViewDistance) { public static void setChunkViewDistance(int chunkViewDistance) {
Check.stateCondition(started, "You cannot change the chunk view distance after the server has been started."); Check.stateCondition(serverProcess.isAlive(), "You cannot change the chunk view distance after the server has been started.");
Check.argCondition(!MathUtils.isBetween(chunkViewDistance, 2, 32), Check.argCondition(!MathUtils.isBetween(chunkViewDistance, 2, 32),
"The chunk view distance must be between 2 and 32"); "The chunk view distance must be between 2 and 32");
MinecraftServer.chunkViewDistance = chunkViewDistance; MinecraftServer.chunkViewDistance = chunkViewDistance;
@ -463,7 +298,7 @@ public final class MinecraftServer {
*/ */
@Deprecated @Deprecated
public static void setEntityViewDistance(int entityViewDistance) { public static void setEntityViewDistance(int entityViewDistance) {
Check.stateCondition(started, "You cannot change the entity view distance after the server has been started."); Check.stateCondition(serverProcess.isAlive(), "You cannot change the entity view distance after the server has been started.");
Check.argCondition(!MathUtils.isBetween(entityViewDistance, 0, 32), Check.argCondition(!MathUtils.isBetween(entityViewDistance, 0, 32),
"The entity view distance must be between 0 and 32"); "The entity view distance must be between 0 and 32");
MinecraftServer.entityViewDistance = entityViewDistance; MinecraftServer.entityViewDistance = entityViewDistance;
@ -487,7 +322,7 @@ public final class MinecraftServer {
* @throws IllegalStateException if this is called after the server started * @throws IllegalStateException if this is called after the server started
*/ */
public static void setCompressionThreshold(int compressionThreshold) { public static void setCompressionThreshold(int compressionThreshold) {
Check.stateCondition(started, "The compression threshold cannot be changed after the server has been started."); Check.stateCondition(serverProcess.isAlive(), "The compression threshold cannot be changed after the server has been started.");
MinecraftServer.compressionThreshold = compressionThreshold; MinecraftServer.compressionThreshold = compressionThreshold;
} }
@ -506,7 +341,7 @@ public final class MinecraftServer {
* @param enabled true to enable, false to disable * @param enabled true to enable, false to disable
*/ */
public static void setTerminalEnabled(boolean enabled) { public static void setTerminalEnabled(boolean enabled) {
Check.stateCondition(started, "Terminal settings may not be changed after starting the server."); Check.stateCondition(serverProcess.isAlive(), "Terminal settings may not be changed after starting the server.");
MinecraftServer.terminalEnabled = enabled; MinecraftServer.terminalEnabled = enabled;
} }
@ -518,73 +353,31 @@ public final class MinecraftServer {
*/ */
@Deprecated @Deprecated
public static ResponseDataConsumer getResponseDataConsumer() { public static ResponseDataConsumer getResponseDataConsumer() {
checkInitStatus(responseDataConsumer);
return responseDataConsumer; return responseDataConsumer;
} }
/**
* Gets the manager handling dimensions.
*
* @return the dimension manager
*/
public static DimensionTypeManager getDimensionTypeManager() { public static DimensionTypeManager getDimensionTypeManager() {
checkInitStatus(dimensionTypeManager); return serverProcess.dimension();
return dimensionTypeManager;
} }
/**
* Gets the manager handling biomes.
*
* @return the biome manager
*/
public static BiomeManager getBiomeManager() { public static BiomeManager getBiomeManager() {
checkInitStatus(biomeManager); return serverProcess.biome();
return biomeManager;
} }
/**
* Gets the manager handling advancements.
*
* @return the advancement manager
*/
public static AdvancementManager getAdvancementManager() { public static AdvancementManager getAdvancementManager() {
checkInitStatus(advancementManager); return serverProcess.advancement();
return advancementManager;
} }
/**
* Get the manager handling {@link Extension}.
*
* @return the extension manager
*/
public static ExtensionManager getExtensionManager() { public static ExtensionManager getExtensionManager() {
checkInitStatus(extensionManager); return serverProcess.extension();
return extensionManager;
} }
/**
* Gets the manager handling tags.
*
* @return the tag manager
*/
public static TagManager getTagManager() { public static TagManager getTagManager() {
checkInitStatus(tagManager); return serverProcess.tag();
return tagManager;
}
/**
* Gets the manager handling the server ticks.
*
* @return the update manager
*/
public static UpdateManager getUpdateManager() {
checkInitStatus(updateManager);
return updateManager;
} }
public static Server getServer() { public static Server getServer() {
checkInitStatus(server); return serverProcess.server();
return server;
} }
/** /**
@ -614,67 +407,15 @@ public final class MinecraftServer {
* @throws IllegalStateException if called before {@link #init()} or if the server is already running * @throws IllegalStateException if called before {@link #init()} or if the server is already running
*/ */
public void start(@NotNull String address, int port) { public void start(@NotNull String address, int port) {
Check.stateCondition(!initialized, "#start can only be called after #init"); serverProcess.start(new InetSocketAddress(address, port));
Check.stateCondition(started, "The server is already started"); new TickSchedulerThread(serverProcess).start();
extensionManager.start();
extensionManager.gotoPreInit();
MinecraftServer.started = true;
LOGGER.info("Starting Minestom server.");
updateManager.start();
extensionManager.gotoInit();
// Init server
try {
server.init(new InetSocketAddress(address, port));
} catch (IOException e) {
e.printStackTrace();
}
// Start server
server.start();
extensionManager.gotoPostInit();
LOGGER.info("Minestom server started successfully.");
if (terminalEnabled) {
MinestomTerminal.start();
}
// Stop the server on SIGINT
Runtime.getRuntime().addShutdownHook(new Thread(MinecraftServer::stopCleanly));
} }
/** /**
* Stops this server properly (saves if needed, kicking players, etc.) * Stops this server properly (saves if needed, kicking players, etc.)
*/ */
public static void stopCleanly() { public static void stopCleanly() {
if (stopping) return; serverProcess.stop();
stopping = true;
LOGGER.info("Stopping Minestom server.");
LOGGER.info("Unloading all extensions.");
extensionManager.shutdown();
updateManager.stop();
schedulerManager.shutdown();
connectionManager.shutdown();
server.stop();
storageManager.getLoadedLocations().forEach(StorageLocation::close);
LOGGER.info("Shutting down all thread pools.");
benchmarkManager.disable();
MinestomTerminal.stop();
MinestomThreadPool.shutdownAll();
LOGGER.info("Minestom server stopped successfully.");
}
private static void checkInitStatus(@Nullable Object object) {
/*Check.stateCondition(Objects.isNull(object),
"You cannot access the manager before MinecraftServer#init, " +
"if you are developing an extension be sure to retrieve them at least after Extension#preInitialize");*/
} }
private static int getThreadCount(@NotNull String property, int count) { private static int getThreadCount(@NotNull String property, int count) {

View File

@ -0,0 +1,158 @@
package net.minestom.server;
import net.minestom.server.advancements.AdvancementManager;
import net.minestom.server.adventure.bossbar.BossBarManager;
import net.minestom.server.command.CommandManager;
import net.minestom.server.data.DataManager;
import net.minestom.server.event.GlobalEventHandler;
import net.minestom.server.exception.ExceptionManager;
import net.minestom.server.extensions.ExtensionManager;
import net.minestom.server.gamedata.tags.TagManager;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.InstanceManager;
import net.minestom.server.instance.block.BlockManager;
import net.minestom.server.instance.block.rule.BlockPlacementRule;
import net.minestom.server.listener.manager.PacketListenerManager;
import net.minestom.server.monitoring.BenchmarkManager;
import net.minestom.server.network.ConnectionManager;
import net.minestom.server.network.PacketProcessor;
import net.minestom.server.network.socket.Server;
import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.scoreboard.TeamManager;
import net.minestom.server.storage.StorageManager;
import net.minestom.server.thread.ThreadDispatcher;
import net.minestom.server.timer.SchedulerManager;
import net.minestom.server.world.DimensionTypeManager;
import net.minestom.server.world.biomes.BiomeManager;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.net.SocketAddress;
@ApiStatus.Experimental
@ApiStatus.NonExtendable
public interface ServerProcess {
/**
* Handles incoming connections/players.
*/
@NotNull ConnectionManager connection();
/**
* Handles registered instances.
*/
@NotNull InstanceManager instance();
/**
* Handles {@link net.minestom.server.instance.block.BlockHandler block handlers}
* and {@link BlockPlacementRule placement rules}.
*/
@NotNull BlockManager block();
/**
* Handles registered commands.
*/
@NotNull CommandManager command();
/**
* Handles registered recipes shown to clients.
*/
@NotNull RecipeManager recipe();
/**
* Handles registered teams.
*/
@NotNull TeamManager team();
/**
* Gets the global event handler.
* <p>
* Used to register event callback at a global scale.
*/
@NotNull GlobalEventHandler eventHandler();
/**
* Main scheduler ticked at the server rate.
*/
@NotNull SchedulerManager scheduler();
@NotNull BenchmarkManager benchmark();
/**
* Handles registered dimensions.
*/
@NotNull DimensionTypeManager dimension();
/**
* Handles registered biomes.
*/
@NotNull BiomeManager biome();
/**
* Handles registered advancements.
*/
@NotNull AdvancementManager advancement();
/**
* Handles registered boss bars.
*/
@NotNull BossBarManager bossBar();
/**
* Loads and handle extensions.
*/
@NotNull ExtensionManager extension();
/**
* Handles registry tags.
*/
@NotNull TagManager tag();
/**
* Handles all thrown exceptions from the server.
*/
@NotNull ExceptionManager exception();
/**
* Handles incoming packets.
*/
@NotNull PacketListenerManager packetListener();
/**
* Gets the object handling the client packets processing.
* <p>
* Can be used if you want to convert a buffer to a client packet object.
*/
@NotNull PacketProcessor packetProcessor();
/**
* Exposed socket server.
*/
@NotNull Server server();
/**
* Dispatcher for tickable game objects.
*/
@NotNull ThreadDispatcher<Chunk> dispatcher();
/**
* Handles the server ticks.
*/
@NotNull Ticker ticker();
void start(@NotNull SocketAddress socketAddress);
void stop();
boolean isAlive();
@ApiStatus.NonExtendable
interface Ticker {
void tick(long nanoTime);
}
@Deprecated
@NotNull StorageManager storage();
@Deprecated
@NotNull DataManager data();
}

View File

@ -0,0 +1,324 @@
package net.minestom.server;
import net.minestom.server.acquirable.Acquirable;
import net.minestom.server.advancements.AdvancementManager;
import net.minestom.server.adventure.bossbar.BossBarManager;
import net.minestom.server.command.CommandManager;
import net.minestom.server.data.DataManager;
import net.minestom.server.event.GlobalEventHandler;
import net.minestom.server.event.GlobalHandles;
import net.minestom.server.event.server.ServerTickMonitorEvent;
import net.minestom.server.exception.ExceptionManager;
import net.minestom.server.extensions.ExtensionManager;
import net.minestom.server.gamedata.tags.TagManager;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.InstanceManager;
import net.minestom.server.instance.block.BlockManager;
import net.minestom.server.listener.manager.PacketListenerManager;
import net.minestom.server.monitoring.BenchmarkManager;
import net.minestom.server.monitoring.TickMonitor;
import net.minestom.server.network.ConnectionManager;
import net.minestom.server.network.PacketProcessor;
import net.minestom.server.network.socket.Server;
import net.minestom.server.network.socket.Worker;
import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.scoreboard.TeamManager;
import net.minestom.server.storage.StorageLocation;
import net.minestom.server.storage.StorageManager;
import net.minestom.server.terminal.MinestomTerminal;
import net.minestom.server.thread.MinestomThreadPool;
import net.minestom.server.thread.ThreadDispatcher;
import net.minestom.server.timer.SchedulerManager;
import net.minestom.server.utils.PacketUtils;
import net.minestom.server.world.DimensionTypeManager;
import net.minestom.server.world.biomes.BiomeManager;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.SocketAddress;
import java.util.concurrent.atomic.AtomicBoolean;
final class ServerProcessImpl implements ServerProcess {
private final static Logger LOGGER = LoggerFactory.getLogger(ServerProcessImpl.class);
private final ExceptionManager exception;
private final ExtensionManager extension;
private final ConnectionManager connection;
private final PacketProcessor packetProcessor;
private final PacketListenerManager packetListener;
private final InstanceManager instance;
private final BlockManager block;
private final CommandManager command;
private final RecipeManager recipe;
private final StorageManager storage;
private final DataManager data;
private final TeamManager team;
private final GlobalEventHandler eventHandler;
private final SchedulerManager scheduler;
private final BenchmarkManager benchmark;
private final DimensionTypeManager dimension;
private final BiomeManager biome;
private final AdvancementManager advancement;
private final BossBarManager bossBar;
private final TagManager tag;
private final Server server;
private final ThreadDispatcher<Chunk> dispatcher;
private final Ticker ticker;
private boolean terminalEnabled = System.getProperty("minestom.terminal.disabled") == null;
private final AtomicBoolean started = new AtomicBoolean();
private final AtomicBoolean stopped = new AtomicBoolean();
public ServerProcessImpl() throws IOException {
this.exception = new ExceptionManager();
this.extension = new ExtensionManager(this);
this.connection = new ConnectionManager();
this.packetProcessor = new PacketProcessor();
this.packetListener = new PacketListenerManager(this);
this.instance = new InstanceManager();
this.block = new BlockManager();
this.command = new CommandManager();
this.recipe = new RecipeManager();
this.storage = new StorageManager();
this.data = new DataManager();
this.team = new TeamManager();
this.eventHandler = new GlobalEventHandler();
this.scheduler = new SchedulerManager();
this.benchmark = new BenchmarkManager();
this.dimension = new DimensionTypeManager();
this.biome = new BiomeManager();
this.advancement = new AdvancementManager();
this.bossBar = new BossBarManager();
this.tag = new TagManager();
this.server = new Server(packetProcessor);
this.dispatcher = ThreadDispatcher.singleThread();
this.ticker = new TickerImpl();
}
@Override
public @NotNull ConnectionManager connection() {
return connection;
}
@Override
public @NotNull InstanceManager instance() {
return instance;
}
@Override
public @NotNull BlockManager block() {
return block;
}
@Override
public @NotNull CommandManager command() {
return command;
}
@Override
public @NotNull RecipeManager recipe() {
return recipe;
}
@Override
public @NotNull StorageManager storage() {
return storage;
}
@Override
public @NotNull DataManager data() {
return data;
}
@Override
public @NotNull TeamManager team() {
return team;
}
@Override
public @NotNull GlobalEventHandler eventHandler() {
return eventHandler;
}
@Override
public @NotNull SchedulerManager scheduler() {
return scheduler;
}
@Override
public @NotNull BenchmarkManager benchmark() {
return benchmark;
}
@Override
public @NotNull DimensionTypeManager dimension() {
return dimension;
}
@Override
public @NotNull BiomeManager biome() {
return biome;
}
@Override
public @NotNull AdvancementManager advancement() {
return advancement;
}
@Override
public @NotNull BossBarManager bossBar() {
return bossBar;
}
@Override
public @NotNull ExtensionManager extension() {
return extension;
}
@Override
public @NotNull TagManager tag() {
return tag;
}
@Override
public @NotNull ExceptionManager exception() {
return exception;
}
@Override
public @NotNull PacketListenerManager packetListener() {
return packetListener;
}
@Override
public @NotNull PacketProcessor packetProcessor() {
return packetProcessor;
}
@Override
public @NotNull Server server() {
return server;
}
@Override
public @NotNull ThreadDispatcher<Chunk> dispatcher() {
return dispatcher;
}
@Override
public @NotNull Ticker ticker() {
return ticker;
}
@Override
public void start(@NotNull SocketAddress socketAddress) {
if (!started.compareAndSet(false, true)) {
throw new IllegalStateException("Server already started");
}
extension.start();
extension.gotoPreInit();
LOGGER.info("Starting Minestom server.");
extension.gotoInit();
// Init server
try {
server.init(socketAddress);
} catch (IOException e) {
exception.handleException(e);
throw new RuntimeException(e);
}
// Start server
server.start();
extension.gotoPostInit();
LOGGER.info("Minestom server started successfully.");
if (terminalEnabled) {
MinestomTerminal.start();
}
// Stop the server on SIGINT
Runtime.getRuntime().addShutdownHook(new Thread(this::stop));
}
@Override
public void stop() {
if (!stopped.compareAndSet(false, true))
return;
LOGGER.info("Stopping Minestom server.");
LOGGER.info("Unloading all extensions.");
extension.shutdown();
scheduler.shutdown();
connection.shutdown();
server.stop();
storage.getLoadedLocations().forEach(StorageLocation::close);
LOGGER.info("Shutting down all thread pools.");
benchmark.disable();
MinestomTerminal.stop();
MinestomThreadPool.shutdownAll();
dispatcher.shutdown();
LOGGER.info("Minestom server stopped successfully.");
}
@Override
public boolean isAlive() {
return started.get() && !stopped.get();
}
private final class TickerImpl implements Ticker {
@Override
public void tick(long nanoTime) {
final long msTime = System.currentTimeMillis();
scheduler().processTick();
// Waiting players update (newly connected clients waiting to get into the server)
connection().updateWaitingPlayers();
// Keep Alive Handling
connection().handleKeepAlive(msTime);
// Server tick (chunks/entities)
serverTick(msTime);
// Flush all waiting packets
PacketUtils.flush();
server().workers().forEach(Worker::flush);
// Monitoring
{
final double acquisitionTimeMs = Acquirable.getAcquiringTime() / 1e6D;
final double tickTimeMs = (System.nanoTime() - nanoTime) / 1e6D;
final TickMonitor tickMonitor = new TickMonitor(tickTimeMs, acquisitionTimeMs);
GlobalHandles.SERVER_TICK_MONITOR_HANDLE.call(new ServerTickMonitorEvent(tickMonitor));
Acquirable.resetAcquiringTime();
}
}
private void serverTick(long tickStart) {
// Tick all instances
for (Instance instance : instance().getInstances()) {
try {
instance.tick(tickStart);
} catch (Exception e) {
exception().handleException(e);
}
}
// Tick all chunks (and entities inside)
dispatcher().updateAndAwait(tickStart);
// Clear removed entities & update threads
final long tickTime = System.currentTimeMillis() - tickStart;
dispatcher().refreshThreads(tickTime);
}
}
}

View File

@ -1,249 +0,0 @@
package net.minestom.server;
import net.minestom.server.acquirable.Acquirable;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.InstanceManager;
import net.minestom.server.monitoring.TickMonitor;
import net.minestom.server.network.ConnectionManager;
import net.minestom.server.network.socket.Worker;
import net.minestom.server.thread.MinestomThread;
import net.minestom.server.thread.ThreadDispatcher;
import net.minestom.server.timer.SchedulerManager;
import net.minestom.server.utils.PacketUtils;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.LockSupport;
import java.util.function.Consumer;
import java.util.function.LongConsumer;
/**
* Manager responsible for the server ticks.
* <p>
* The {@link ThreadDispatcher} manages the multi-thread aspect of chunk ticks.
*/
public final class UpdateManager {
private volatile boolean stopRequested;
// TODO make configurable
private final ThreadDispatcher<Chunk> threadDispatcher = ThreadDispatcher.singleThread();
private final Queue<LongConsumer> tickStartCallbacks = new ConcurrentLinkedQueue<>();
private final Queue<LongConsumer> tickEndCallbacks = new ConcurrentLinkedQueue<>();
private final List<Consumer<TickMonitor>> tickMonitors = new CopyOnWriteArrayList<>();
/**
* Should only be created in MinecraftServer.
*/
UpdateManager() {
}
/**
* Starts the server loop in the update thread.
*/
void start() {
new TickSchedulerThread().start();
}
/**
* Gets the current {@link ThreadDispatcher}.
*
* @return the current thread provider
*/
public @NotNull ThreadDispatcher<Chunk> getThreadProvider() {
return threadDispatcher;
}
/**
* Signals the {@link ThreadDispatcher} that an instance has been created.
* <p>
* WARNING: should be automatically done by the {@link InstanceManager}.
*
* @param instance the instance
*/
public void signalInstanceCreate(Instance instance) {
instance.getChunks().forEach(this::signalChunkLoad);
}
/**
* Signals the {@link ThreadDispatcher} that an instance has been deleted.
* <p>
* WARNING: should be automatically done by the {@link InstanceManager}.
*
* @param instance the instance
*/
public void signalInstanceDelete(Instance instance) {
instance.getChunks().forEach(this::signalChunkUnload);
}
/**
* Signals the {@link ThreadDispatcher} that a chunk has been loaded.
* <p>
* WARNING: should be automatically done by the {@link Instance} implementation.
*
* @param chunk the loaded chunk
*/
public void signalChunkLoad(@NotNull Chunk chunk) {
this.threadDispatcher.createPartition(chunk);
}
/**
* Signals the {@link ThreadDispatcher} that a chunk has been unloaded.
* <p>
* WARNING: should be automatically done by the {@link Instance} implementation.
*
* @param chunk the unloaded chunk
*/
public void signalChunkUnload(@NotNull Chunk chunk) {
this.threadDispatcher.deletePartition(chunk);
}
/**
* 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
*/
public void addTickStartCallback(@NotNull LongConsumer callback) {
this.tickStartCallbacks.add(callback);
}
/**
* Removes a tick start callback.
*
* @param callback the callback to remove
*/
public void removeTickStartCallback(@NotNull LongConsumer callback) {
this.tickStartCallbacks.remove(callback);
}
/**
* Adds a callback executed at the end of the next server tick.
* <p>
* The long in the consumer represents the duration (in ms) of the tick.
*
* @param callback the tick end callback
*/
public void addTickEndCallback(@NotNull LongConsumer callback) {
this.tickEndCallbacks.add(callback);
}
/**
* Removes a tick end callback.
*
* @param callback the callback to remove
*/
public void removeTickEndCallback(@NotNull LongConsumer callback) {
this.tickEndCallbacks.remove(callback);
}
public void addTickMonitor(@NotNull Consumer<TickMonitor> consumer) {
this.tickMonitors.add(consumer);
}
public void removeTickMonitor(@NotNull Consumer<TickMonitor> consumer) {
this.tickMonitors.remove(consumer);
}
/**
* Stops the server loop.
*/
public void stop() {
this.stopRequested = true;
}
private final class TickSchedulerThread extends MinestomThread {
private final ThreadDispatcher threadDispatcher = UpdateManager.this.threadDispatcher;
TickSchedulerThread() {
super(MinecraftServer.THREAD_NAME_TICK_SCHEDULER);
}
@Override
public void run() {
final ConnectionManager connectionManager = MinecraftServer.getConnectionManager();
final SchedulerManager schedulerManager = MinecraftServer.getSchedulerManager();
final List<Worker> workers = MinecraftServer.getServer().workers();
while (!stopRequested) {
try {
long currentTime = System.nanoTime();
final long tickStart = System.currentTimeMillis();
// Tick start callbacks
doTickCallback(tickStartCallbacks, tickStart);
schedulerManager.processTick();
// 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);
// Flush all waiting packets
PacketUtils.flush();
workers.forEach(Worker::flush);
// 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);
for (Consumer<TickMonitor> consumer : tickMonitors) {
consumer.accept(tickMonitor);
}
Acquirable.resetAcquiringTime();
}
// Disable thread until next tick
LockSupport.parkNanos((long) ((MinecraftServer.TICK_MS * 1e6) - tickTime));
} catch (Exception e) {
MinecraftServer.getExceptionManager().handleException(e);
}
}
this.threadDispatcher.shutdown();
}
/**
* 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) {
// Tick all instances
for (Instance instance : MinecraftServer.getInstanceManager().getInstances()) {
try {
instance.tick(tickStart);
} catch (Exception e) {
MinecraftServer.getExceptionManager().handleException(e);
}
}
// Tick all chunks (and entities inside)
this.threadDispatcher.updateAndAwait(tickStart);
// Clear removed entities & update threads
this.threadDispatcher.refreshThreads();
}
private void doTickCallback(Queue<LongConsumer> callbacks, long value) {
LongConsumer callback;
while ((callback = callbacks.poll()) != null) {
callback.accept(value);
}
}
}
}

View File

@ -784,7 +784,7 @@ public class Entity implements Viewable, Tickable, Schedulable, TagHandler, Perm
@ApiStatus.Internal @ApiStatus.Internal
protected void refreshCurrentChunk(Chunk currentChunk) { protected void refreshCurrentChunk(Chunk currentChunk) {
this.currentChunk = currentChunk; this.currentChunk = currentChunk;
MinecraftServer.getUpdateManager().getThreadProvider().updateElement(this, currentChunk); MinecraftServer.process().dispatcher().updateElement(this, currentChunk);
} }
/** /**
@ -1423,7 +1423,7 @@ public class Entity implements Viewable, Tickable, Schedulable, TagHandler, Perm
if (!passengers.isEmpty()) passengers.forEach(this::removePassenger); if (!passengers.isEmpty()) passengers.forEach(this::removePassenger);
final Entity vehicle = this.vehicle; final Entity vehicle = this.vehicle;
if (vehicle != null) vehicle.removePassenger(this); if (vehicle != null) vehicle.removePassenger(this);
MinecraftServer.getUpdateManager().getThreadProvider().removeElement(this); MinecraftServer.process().dispatcher().removeElement(this);
this.removed = true; this.removed = true;
Entity.ENTITY_BY_ID.remove(id); Entity.ENTITY_BY_ID.remove(id);
Entity.ENTITY_BY_UUID.remove(uuid); Entity.ENTITY_BY_UUID.remove(uuid);

View File

@ -7,6 +7,7 @@ import net.minestom.server.event.instance.InstanceTickEvent;
import net.minestom.server.event.inventory.InventoryItemChangeEvent; import net.minestom.server.event.inventory.InventoryItemChangeEvent;
import net.minestom.server.event.inventory.PlayerInventoryItemChangeEvent; import net.minestom.server.event.inventory.PlayerInventoryItemChangeEvent;
import net.minestom.server.event.player.*; import net.minestom.server.event.player.*;
import net.minestom.server.event.server.ServerTickMonitorEvent;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
/** /**
@ -25,4 +26,6 @@ public final class GlobalHandles {
public static final ListenerHandle<InstanceChunkLoadEvent> INSTANCE_CHUNK_LOAD = EventDispatcher.getHandle(InstanceChunkLoadEvent.class); public static final ListenerHandle<InstanceChunkLoadEvent> INSTANCE_CHUNK_LOAD = EventDispatcher.getHandle(InstanceChunkLoadEvent.class);
public static final ListenerHandle<InventoryItemChangeEvent> INVENTORY_ITEM_CHANGE_EVENT = EventDispatcher.getHandle(InventoryItemChangeEvent.class); public static final ListenerHandle<InventoryItemChangeEvent> INVENTORY_ITEM_CHANGE_EVENT = EventDispatcher.getHandle(InventoryItemChangeEvent.class);
public static final ListenerHandle<PlayerInventoryItemChangeEvent> PLAYER_INVENTORY_ITEM_CHANGE_EVENT = EventDispatcher.getHandle(PlayerInventoryItemChangeEvent.class); public static final ListenerHandle<PlayerInventoryItemChangeEvent> PLAYER_INVENTORY_ITEM_CHANGE_EVENT = EventDispatcher.getHandle(PlayerInventoryItemChangeEvent.class);
public static final ListenerHandle<ServerTickMonitorEvent> SERVER_TICK_MONITOR_HANDLE = EventDispatcher.getHandle(ServerTickMonitorEvent.class);
} }

View File

@ -0,0 +1,17 @@
package net.minestom.server.event.server;
import net.minestom.server.event.Event;
import net.minestom.server.monitoring.TickMonitor;
import org.jetbrains.annotations.NotNull;
public final class ServerTickMonitorEvent implements Event {
private final TickMonitor tickMonitor;
public ServerTickMonitorEvent(@NotNull TickMonitor tickMonitor) {
this.tickMonitor = tickMonitor;
}
public @NotNull TickMonitor getTickMonitor() {
return tickMonitor;
}
}

View File

@ -4,7 +4,7 @@ import com.google.gson.Gson;
import net.minestom.dependencies.DependencyGetter; import net.minestom.dependencies.DependencyGetter;
import net.minestom.dependencies.ResolvedDependency; import net.minestom.dependencies.ResolvedDependency;
import net.minestom.dependencies.maven.MavenRepository; import net.minestom.dependencies.maven.MavenRepository;
import net.minestom.server.MinecraftServer; import net.minestom.server.ServerProcess;
import net.minestom.server.event.Event; import net.minestom.server.event.Event;
import net.minestom.server.event.EventNode; import net.minestom.server.event.EventNode;
import net.minestom.server.ping.ResponseDataConsumer; import net.minestom.server.ping.ResponseDataConsumer;
@ -34,6 +34,8 @@ public class ExtensionManager {
public final static String INDEV_RESOURCES_FOLDER = "minestom.extension.indevfolder.resources"; public final static String INDEV_RESOURCES_FOLDER = "minestom.extension.indevfolder.resources";
private final static Gson GSON = new Gson(); private final static Gson GSON = new Gson();
private final ServerProcess serverProcess;
// LinkedHashMaps are HashMaps that preserve order // LinkedHashMaps are HashMaps that preserve order
private final Map<String, Extension> extensions = new LinkedHashMap<>(); private final Map<String, Extension> extensions = new LinkedHashMap<>();
private final Map<String, Extension> immutableExtensions = Collections.unmodifiableMap(extensions); private final Map<String, Extension> immutableExtensions = Collections.unmodifiableMap(extensions);
@ -42,10 +44,11 @@ public class ExtensionManager {
private final File dependenciesFolder = new File(extensionFolder, ".libs"); private final File dependenciesFolder = new File(extensionFolder, ".libs");
private Path extensionDataRoot = extensionFolder.toPath(); private Path extensionDataRoot = extensionFolder.toPath();
private enum State { DO_NOT_START, NOT_STARTED, STARTED, PRE_INIT, INIT, POST_INIT } private enum State {DO_NOT_START, NOT_STARTED, STARTED, PRE_INIT, INIT, POST_INIT}
private State state = State.NOT_STARTED; private State state = State.NOT_STARTED;
public ExtensionManager() { public ExtensionManager(ServerProcess serverProcess) {
this.serverProcess = serverProcess;
} }
/** /**
@ -219,7 +222,7 @@ public class ExtensionManager {
discoveredExtension.createClassLoader(); discoveredExtension.createClassLoader();
} catch (Exception e) { } catch (Exception e) {
discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.FAILED_TO_SETUP_CLASSLOADER; discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.FAILED_TO_SETUP_CLASSLOADER;
MinecraftServer.getExceptionManager().handleException(e); serverProcess.exception().handleException(e);
LOGGER.error("Failed to load extension {}", discoveredExtension.getName()); LOGGER.error("Failed to load extension {}", discoveredExtension.getName());
LOGGER.error("Failed to load extension", e); LOGGER.error("Failed to load extension", e);
extensionIterator.remove(); extensionIterator.remove();
@ -239,7 +242,7 @@ public class ExtensionManager {
} catch (Exception e) { } catch (Exception e) {
discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.LOAD_FAILED; discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.LOAD_FAILED;
LOGGER.error("Failed to load extension {}", discoveredExtension.getName()); LOGGER.error("Failed to load extension {}", discoveredExtension.getName());
MinecraftServer.getExceptionManager().handleException(e); serverProcess.exception().handleException(e);
} }
} }
} }
@ -338,7 +341,7 @@ public class ExtensionManager {
loggerField.set(extension, LoggerFactory.getLogger(extensionClass)); loggerField.set(extension, LoggerFactory.getLogger(extensionClass));
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
// We made it accessible, should not occur // We made it accessible, should not occur
MinecraftServer.getExceptionManager().handleException(e); serverProcess.exception().handleException(e);
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {
// This should also not occur (unless someone changed the logger in Extension superclass). // This should also not occur (unless someone changed the logger in Extension superclass).
LOGGER.error("Main class '{}' in '{}' has no logger field.", mainClass, extensionName, e); LOGGER.error("Main class '{}' in '{}' has no logger field.", mainClass, extensionName, e);
@ -351,10 +354,10 @@ public class ExtensionManager {
loggerField.setAccessible(true); loggerField.setAccessible(true);
loggerField.set(extension, eventNode); loggerField.set(extension, eventNode);
MinecraftServer.getGlobalEventHandler().addChild(eventNode); serverProcess.eventHandler().addChild(eventNode);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
// We made it accessible, should not occur // We made it accessible, should not occur
MinecraftServer.getExceptionManager().handleException(e); serverProcess.exception().handleException(e);
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {
// This should also not occur // This should also not occur
LOGGER.error("Main class '{}' in '{}' has no event node field.", mainClass, extensionName, e); LOGGER.error("Main class '{}' in '{}' has no event node field.", mainClass, extensionName, e);
@ -430,7 +433,7 @@ public class ExtensionManager {
extensions.add(extension); extensions.add(extension);
} }
} catch (IOException e) { } catch (IOException e) {
MinecraftServer.getExceptionManager().handleException(e); serverProcess.exception().handleException(e);
} }
} }
return extensions; return extensions;
@ -463,7 +466,7 @@ public class ExtensionManager {
return extension; return extension;
} catch (IOException e) { } catch (IOException e) {
MinecraftServer.getExceptionManager().handleException(e); serverProcess.exception().handleException(e);
return null; return null;
} }
} }
@ -727,7 +730,7 @@ public class ExtensionManager {
// Remove event node // Remove event node
EventNode<Event> eventNode = ext.getEventNode(); EventNode<Event> eventNode = ext.getEventNode();
MinecraftServer.getGlobalEventHandler().removeChild(eventNode); serverProcess.eventHandler().removeChild(eventNode);
ext.postTerminate(); ext.postTerminate();

View File

@ -18,7 +18,7 @@ public final class MojangAuth {
*/ */
public static void init() { public static void init() {
Check.stateCondition(enabled, "Mojang auth is already enabled!"); Check.stateCondition(enabled, "Mojang auth is already enabled!");
Check.stateCondition(MinecraftServer.isStarted(), "The server has already been started!"); Check.stateCondition(MinecraftServer.process().isAlive(), "The server has already been started!");
MojangAuth.enabled = true; MojangAuth.enabled = true;
// Generate necessary fields... // Generate necessary fields...
MojangAuth.keyPair = MojangCrypt.generateKeyPair(); MojangAuth.keyPair = MojangCrypt.generateKeyPair();

View File

@ -5,7 +5,6 @@ import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.pointer.Pointers; import net.kyori.adventure.pointer.Pointers;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.Tickable; import net.minestom.server.Tickable;
import net.minestom.server.UpdateManager;
import net.minestom.server.adventure.audience.PacketGroupingAudience; import net.minestom.server.adventure.audience.PacketGroupingAudience;
import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Point;
import net.minestom.server.data.Data; import net.minestom.server.data.Data;
@ -23,6 +22,7 @@ import net.minestom.server.network.packet.server.play.BlockActionPacket;
import net.minestom.server.network.packet.server.play.TimeUpdatePacket; import net.minestom.server.network.packet.server.play.TimeUpdatePacket;
import net.minestom.server.tag.Tag; import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagHandler; import net.minestom.server.tag.TagHandler;
import net.minestom.server.thread.ThreadDispatcher;
import net.minestom.server.timer.Schedulable; import net.minestom.server.timer.Schedulable;
import net.minestom.server.timer.Scheduler; import net.minestom.server.timer.Scheduler;
import net.minestom.server.utils.PacketUtils; import net.minestom.server.utils.PacketUtils;
@ -50,13 +50,11 @@ import java.util.stream.Collectors;
* <p> * <p>
* WARNING: when making your own implementation registering the instance manually is required * WARNING: when making your own implementation registering the instance manually is required
* with {@link InstanceManager#registerInstance(Instance)}, and * with {@link InstanceManager#registerInstance(Instance)}, and
* you need to be sure to signal the {@link UpdateManager} of the changes using * you need to be sure to signal the {@link ThreadDispatcher} of every partition/element changes.
* {@link UpdateManager#signalChunkLoad(Chunk)} and {@link UpdateManager#signalChunkUnload(Chunk)}.
*/ */
public abstract class Instance implements Block.Getter, Block.Setter, Tickable, Schedulable, TagHandler, PacketGroupingAudience { public abstract class Instance implements Block.Getter, Block.Setter, Tickable, Schedulable, TagHandler, PacketGroupingAudience {
protected static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager(); protected static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
protected static final UpdateManager UPDATE_MANAGER = MinecraftServer.getUpdateManager();
private boolean registered; private boolean registered;

View File

@ -2,6 +2,7 @@ package net.minestom.server.instance;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec; import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.Entity; import net.minestom.server.entity.Entity;
@ -223,7 +224,8 @@ public class InstanceContainer extends Instance {
// Clear cache // Clear cache
this.chunks.remove(index); this.chunks.remove(index);
chunk.unload(); chunk.unload();
UPDATE_MANAGER.signalChunkUnload(chunk); var dispatcher = MinecraftServer.process().dispatcher();
dispatcher.deletePartition(chunk);
} }
@Override @Override
@ -525,6 +527,7 @@ public class InstanceContainer extends Instance {
private void cacheChunk(@NotNull Chunk chunk) { private void cacheChunk(@NotNull Chunk chunk) {
final long index = ChunkUtils.getChunkIndex(chunk); final long index = ChunkUtils.getChunkIndex(chunk);
this.chunks.put(index, chunk); this.chunks.put(index, chunk);
UPDATE_MANAGER.signalChunkLoad(chunk); var dispatcher = MinecraftServer.process().dispatcher();
dispatcher.createPartition(chunk);
} }
} }

View File

@ -118,7 +118,8 @@ public final class InstanceManager {
// Unregister // Unregister
instance.setRegistered(false); instance.setRegistered(false);
this.instances.remove(instance); this.instances.remove(instance);
MinecraftServer.getUpdateManager().signalInstanceDelete(instance); var dispatcher = MinecraftServer.process().dispatcher();
instance.getChunks().forEach(dispatcher::deletePartition);
} }
} }
@ -155,6 +156,7 @@ public final class InstanceManager {
private void UNSAFE_registerInstance(@NotNull Instance instance) { private void UNSAFE_registerInstance(@NotNull Instance instance) {
instance.setRegistered(true); instance.setRegistered(true);
this.instances.add(instance); this.instances.add(instance);
MinecraftServer.getUpdateManager().signalInstanceCreate(instance); var dispatcher = MinecraftServer.process().dispatcher();
instance.getChunks().forEach(dispatcher::createPartition);
} }
} }

View File

@ -1,6 +1,7 @@
package net.minestom.server.listener.manager; package net.minestom.server.listener.manager;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.ServerProcess;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.event.GlobalHandles; import net.minestom.server.event.GlobalHandles;
import net.minestom.server.event.player.PlayerPacketEvent; import net.minestom.server.event.player.PlayerPacketEvent;
@ -21,11 +22,13 @@ import java.util.concurrent.ConcurrentHashMap;
public final class PacketListenerManager { public final class PacketListenerManager {
public final static Logger LOGGER = LoggerFactory.getLogger(PacketListenerManager.class); public final static Logger LOGGER = LoggerFactory.getLogger(PacketListenerManager.class);
private static final ConnectionManager CONNECTION_MANAGER = MinecraftServer.getConnectionManager(); private final ServerProcess serverProcess;
private final Map<Class<? extends ClientPacket>, PacketListenerConsumer> listeners = new ConcurrentHashMap<>(); private final Map<Class<? extends ClientPacket>, PacketListenerConsumer> listeners = new ConcurrentHashMap<>();
public PacketListenerManager() { public PacketListenerManager(ServerProcess serverProcess) {
this.serverProcess = serverProcess;
setListener(ClientKeepAlivePacket.class, KeepAliveListener::listener); setListener(ClientKeepAlivePacket.class, KeepAliveListener::listener);
setListener(ClientChatMessagePacket.class, ChatMessageListener::listener); setListener(ClientChatMessagePacket.class, ChatMessageListener::listener);
setListener(ClientClickWindowPacket.class, WindowListener::clickWindowListener); setListener(ClientClickWindowPacket.class, WindowListener::clickWindowListener);
@ -79,7 +82,7 @@ public final class PacketListenerManager {
// TODO remove legacy // TODO remove legacy
{ {
final PacketController packetController = new PacketController(); final PacketController packetController = new PacketController();
for (ClientPacketConsumer clientPacketConsumer : CONNECTION_MANAGER.getReceivePacketConsumers()) { for (ClientPacketConsumer clientPacketConsumer : serverProcess.connection().getReceivePacketConsumers()) {
clientPacketConsumer.accept(player, packetController, packet); clientPacketConsumer.accept(player, packetController, packet);
} }
@ -113,7 +116,7 @@ public final class PacketListenerManager {
* @return true if the packet is not cancelled, false otherwise * @return true if the packet is not cancelled, false otherwise
*/ */
public boolean processServerPacket(@NotNull ServerPacket packet, @NotNull Collection<Player> players) { public boolean processServerPacket(@NotNull ServerPacket packet, @NotNull Collection<Player> players) {
final List<ServerPacketConsumer> consumers = CONNECTION_MANAGER.getSendPacketConsumers(); final List<ServerPacketConsumer> consumers = serverProcess.connection().getSendPacketConsumers();
if (consumers.isEmpty()) { if (consumers.isEmpty()) {
return true; return true;
} }

View File

@ -2,10 +2,8 @@ package net.minestom.server.scoreboard;
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.LivingEntity; import net.minestom.server.entity.LivingEntity;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.network.ConnectionManager;
import net.minestom.server.utils.PacketUtils; import net.minestom.server.utils.PacketUtils;
import net.minestom.server.utils.UniqueIdUtils; import net.minestom.server.utils.UniqueIdUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -20,8 +18,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
*/ */
public final class TeamManager { public final class TeamManager {
private static final ConnectionManager CONNECTION_MANAGER = MinecraftServer.getConnectionManager();
/** /**
* Represents all registered teams * Represents all registered teams
*/ */

View File

@ -13,8 +13,6 @@ import org.jline.terminal.TerminalBuilder;
import java.io.IOException; import java.io.IOException;
public class MinestomTerminal { public class MinestomTerminal {
private static final CommandManager COMMAND_MANAGER = MinecraftServer.getCommandManager();
private static final String PROMPT = "> "; private static final String PROMPT = "> ";
private static volatile Terminal terminal; private static volatile Terminal terminal;
@ -37,7 +35,8 @@ public class MinestomTerminal {
String command; String command;
try { try {
command = reader.readLine(PROMPT); command = reader.readLine(PROMPT);
COMMAND_MANAGER.execute(COMMAND_MANAGER.getConsoleSender(), command); var commandManager = MinecraftServer.getCommandManager();
commandManager.execute(commandManager.getConsoleSender(), command);
} catch (UserInterruptException e) { } catch (UserInterruptException e) {
// Handle Ctrl + C // Handle Ctrl + C
System.exit(0); System.exit(0);

View File

@ -0,0 +1,31 @@
package net.minestom.server.thread;
import net.minestom.server.MinecraftServer;
import net.minestom.server.ServerProcess;
import org.jetbrains.annotations.ApiStatus;
import java.util.concurrent.locks.LockSupport;
@ApiStatus.Internal
public final class TickSchedulerThread extends MinestomThread {
private final ServerProcess serverProcess;
public TickSchedulerThread(ServerProcess serverProcess) {
super(MinecraftServer.THREAD_NAME_TICK_SCHEDULER);
this.serverProcess = serverProcess;
}
@Override
public void run() {
while (serverProcess.isAlive()) {
final long tickStart = System.nanoTime();
try {
serverProcess.ticker().tick(tickStart);
} catch (Exception e) {
serverProcess.exception().handleException(e);
}
final long tickTime = System.nanoTime() - tickStart;
LockSupport.parkNanos((long) ((MinecraftServer.TICK_MS * 1e6) - tickTime));
}
}
}

View File

@ -12,7 +12,6 @@ import net.minestom.server.Viewable;
import net.minestom.server.adventure.audience.PacketGroupingAudience; import net.minestom.server.adventure.audience.PacketGroupingAudience;
import net.minestom.server.entity.Entity; 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.network.packet.server.CachedPacket; import net.minestom.server.network.packet.server.CachedPacket;
import net.minestom.server.network.packet.server.FramedPacket; import net.minestom.server.network.packet.server.FramedPacket;
import net.minestom.server.network.packet.server.SendablePacket; import net.minestom.server.network.packet.server.SendablePacket;
@ -50,7 +49,6 @@ import java.util.zip.Inflater;
* Be sure to check the implementation code. * Be sure to check the implementation code.
*/ */
public final class PacketUtils { public final class PacketUtils {
private static final PacketListenerManager PACKET_LISTENER_MANAGER = MinecraftServer.getPacketListenerManager();
private static final LocalCache<Deflater> LOCAL_DEFLATER = LocalCache.of(Deflater::new); private static final LocalCache<Deflater> LOCAL_DEFLATER = LocalCache.of(Deflater::new);
public static final boolean GROUPED_PACKET = getBoolean("minestom.grouped-packet", true); public static final boolean GROUPED_PACKET = getBoolean("minestom.grouped-packet", true);
@ -118,7 +116,7 @@ public final class PacketUtils {
public static void sendGroupedPacket(@NotNull Collection<Player> players, @NotNull ServerPacket packet, public static void sendGroupedPacket(@NotNull Collection<Player> players, @NotNull ServerPacket packet,
@NotNull Predicate<Player> predicate) { @NotNull Predicate<Player> predicate) {
if (players.isEmpty()) return; if (players.isEmpty()) return;
if (!PACKET_LISTENER_MANAGER.processServerPacket(packet, players)) return; if (!MinecraftServer.getPacketListenerManager().processServerPacket(packet, players)) return;
// work out if the packet needs to be sent individually due to server-side translating // work out if the packet needs to be sent individually due to server-side translating
final SendablePacket sendablePacket = GROUPED_PACKET ? new CachedPacket(packet) : packet; final SendablePacket sendablePacket = GROUPED_PACKET ? new CachedPacket(packet) : packet;
players.forEach(player -> { players.forEach(player -> {

View File

@ -0,0 +1,30 @@
package net.minestom.server;
import org.junit.jupiter.api.Test;
import java.net.InetSocketAddress;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class ServerProcessTest {
@Test
public void init() {
AtomicReference<ServerProcess> process = new AtomicReference<>();
assertDoesNotThrow(() -> process.set(MinecraftServer.updateProcess()));
assertDoesNotThrow(() -> process.get().start(new InetSocketAddress("localhost", 25565)));
assertThrows(Exception.class, () -> process.get().start(new InetSocketAddress("localhost", 25566)));
assertDoesNotThrow(() -> process.get().stop());
}
@Test
public void tick() {
var process = MinecraftServer.updateProcess();
process.start(new InetSocketAddress("localhost", 25567));
var ticker = process.ticker();
assertDoesNotThrow(() -> ticker.tick(System.currentTimeMillis()));
assertDoesNotThrow(process::stop);
}
}