mirror of
https://github.com/Minestom/Minestom.git
synced 2024-12-26 11:07:53 +01:00
Server process (#550)
This commit is contained in:
parent
0bd736d837
commit
5bbf4642e8
@ -21,6 +21,7 @@ import net.minestom.server.event.player.PlayerDeathEvent;
|
||||
import net.minestom.server.event.player.PlayerDisconnectEvent;
|
||||
import net.minestom.server.event.player.PlayerLoginEvent;
|
||||
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.InstanceContainer;
|
||||
import net.minestom.server.instance.InstanceManager;
|
||||
@ -133,7 +134,7 @@ public class PlayerInit {
|
||||
var eventHandler = MinecraftServer.getGlobalEventHandler();
|
||||
eventHandler.addChild(DEMO_NODE);
|
||||
|
||||
MinecraftServer.getUpdateManager().addTickMonitor(LAST_TICK::set);
|
||||
eventHandler.addListener(ServerTickMonitorEvent.class, event -> LAST_TICK.set(event.getTickMonitor()));
|
||||
|
||||
BenchmarkManager benchmarkManager = MinecraftServer.getBenchmarkManager();
|
||||
MinecraftServer.getSchedulerManager().buildTask(() -> {
|
||||
|
@ -4,17 +4,12 @@ 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.data.DataType;
|
||||
import net.minestom.server.data.SerializableData;
|
||||
import net.minestom.server.event.GlobalEventHandler;
|
||||
import net.minestom.server.exception.ExceptionManager;
|
||||
import net.minestom.server.extensions.Extension;
|
||||
import net.minestom.server.extensions.ExtensionManager;
|
||||
import net.minestom.server.fluid.Fluid;
|
||||
import net.minestom.server.gamedata.tags.TagManager;
|
||||
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;
|
||||
@ -25,10 +20,8 @@ import net.minestom.server.network.socket.Server;
|
||||
import net.minestom.server.ping.ResponseDataConsumer;
|
||||
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.TickSchedulerThread;
|
||||
import net.minestom.server.timer.SchedulerManager;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
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.DimensionTypeManager;
|
||||
import net.minestom.server.world.biomes.BiomeManager;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -75,40 +70,9 @@ public final class MinecraftServer {
|
||||
// Network monitoring
|
||||
private static int rateLimit = 300;
|
||||
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
|
||||
private static ConnectionManager connectionManager;
|
||||
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 volatile ServerProcess serverProcess;
|
||||
|
||||
private static int chunkViewDistance = Integer.getInteger("minestom.chunk-view-distance", 8);
|
||||
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 String brandName = "Minestom";
|
||||
private static Difficulty difficulty = Difficulty.NORMAL;
|
||||
private static TagManager tagManager;
|
||||
|
||||
public static MinecraftServer init() {
|
||||
if (minecraftServer != null) // don't init twice
|
||||
return 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();
|
||||
updateProcess();
|
||||
return new MinecraftServer();
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
|
||||
minecraftServer = new MinecraftServer();
|
||||
|
||||
return minecraftServer;
|
||||
@ApiStatus.Internal
|
||||
public static ServerProcess updateProcess() {
|
||||
ServerProcess process;
|
||||
try {
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the global event handler.
|
||||
* <p>
|
||||
* Used to register event callback at a global scale.
|
||||
*
|
||||
* @return the global event handler
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public static @UnknownNullability ServerProcess process() {
|
||||
return serverProcess;
|
||||
}
|
||||
|
||||
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() {
|
||||
checkInitStatus(packetListenerManager);
|
||||
return packetListenerManager;
|
||||
return serverProcess.packetListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling all registered instances.
|
||||
*
|
||||
* @return the instance manager
|
||||
*/
|
||||
public static InstanceManager getInstanceManager() {
|
||||
checkInitStatus(instanceManager);
|
||||
return instanceManager;
|
||||
return serverProcess.instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
checkInitStatus(blockManager);
|
||||
return blockManager;
|
||||
return serverProcess.block();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling commands.
|
||||
*
|
||||
* @return the command manager
|
||||
*/
|
||||
public static CommandManager getCommandManager() {
|
||||
checkInitStatus(commandManager);
|
||||
return commandManager;
|
||||
return serverProcess.command();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling recipes show to the clients.
|
||||
*
|
||||
* @return the recipe manager
|
||||
*/
|
||||
public static RecipeManager getRecipeManager() {
|
||||
checkInitStatus(recipeManager);
|
||||
return recipeManager;
|
||||
return serverProcess.recipe();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling storage.
|
||||
*
|
||||
* @return the storage manager
|
||||
*/
|
||||
@Deprecated
|
||||
public static StorageManager getStorageManager() {
|
||||
checkInitStatus(storageManager);
|
||||
return storageManager;
|
||||
return serverProcess.storage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling {@link DataType} used by {@link SerializableData}.
|
||||
*
|
||||
* @return the data manager
|
||||
*/
|
||||
@Deprecated
|
||||
public static DataManager getDataManager() {
|
||||
checkInitStatus(dataManager);
|
||||
return dataManager;
|
||||
return serverProcess.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling teams.
|
||||
*
|
||||
* @return the team manager
|
||||
*/
|
||||
public static TeamManager getTeamManager() {
|
||||
checkInitStatus(teamManager);
|
||||
return teamManager;
|
||||
return serverProcess.team();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling scheduled tasks.
|
||||
*
|
||||
* @return the scheduler manager
|
||||
*/
|
||||
public static SchedulerManager getSchedulerManager() {
|
||||
checkInitStatus(schedulerManager);
|
||||
return schedulerManager;
|
||||
return serverProcess.scheduler();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -357,68 +229,31 @@ public final class MinecraftServer {
|
||||
* @return the benchmark manager
|
||||
*/
|
||||
public static BenchmarkManager getBenchmarkManager() {
|
||||
checkInitStatus(benchmarkManager);
|
||||
return benchmarkManager;
|
||||
return serverProcess.benchmark();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exception manager for exception handling.
|
||||
*
|
||||
* @return the exception manager
|
||||
*/
|
||||
public static ExceptionManager getExceptionManager() {
|
||||
checkInitStatus(exceptionManager);
|
||||
return exceptionManager;
|
||||
return serverProcess.exception();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling server connections.
|
||||
*
|
||||
* @return the connection manager
|
||||
*/
|
||||
public static ConnectionManager getConnectionManager() {
|
||||
checkInitStatus(connectionManager);
|
||||
return connectionManager;
|
||||
return serverProcess.connection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the boss bar manager.
|
||||
*
|
||||
* @return the boss bar manager
|
||||
*/
|
||||
public static BossBarManager getBossBarManager() {
|
||||
checkInitStatus(bossBarManager);
|
||||
return bossBarManager;
|
||||
return serverProcess.bossBar();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
checkInitStatus(packetProcessor);
|
||||
return packetProcessor;
|
||||
return serverProcess.packetProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the server is up and running.
|
||||
*
|
||||
* @return true if the server is started
|
||||
*/
|
||||
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() {
|
||||
return stopping;
|
||||
return !isStarted();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -439,7 +274,7 @@ public final class MinecraftServer {
|
||||
*/
|
||||
@Deprecated
|
||||
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),
|
||||
"The chunk view distance must be between 2 and 32");
|
||||
MinecraftServer.chunkViewDistance = chunkViewDistance;
|
||||
@ -463,7 +298,7 @@ public final class MinecraftServer {
|
||||
*/
|
||||
@Deprecated
|
||||
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),
|
||||
"The entity view distance must be between 0 and 32");
|
||||
MinecraftServer.entityViewDistance = entityViewDistance;
|
||||
@ -487,7 +322,7 @@ public final class MinecraftServer {
|
||||
* @throws IllegalStateException if this is called after the server started
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -506,7 +341,7 @@ public final class MinecraftServer {
|
||||
* @param enabled true to enable, false to disable
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -518,73 +353,31 @@ public final class MinecraftServer {
|
||||
*/
|
||||
@Deprecated
|
||||
public static ResponseDataConsumer getResponseDataConsumer() {
|
||||
checkInitStatus(responseDataConsumer);
|
||||
return responseDataConsumer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling dimensions.
|
||||
*
|
||||
* @return the dimension manager
|
||||
*/
|
||||
public static DimensionTypeManager getDimensionTypeManager() {
|
||||
checkInitStatus(dimensionTypeManager);
|
||||
return dimensionTypeManager;
|
||||
return serverProcess.dimension();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling biomes.
|
||||
*
|
||||
* @return the biome manager
|
||||
*/
|
||||
public static BiomeManager getBiomeManager() {
|
||||
checkInitStatus(biomeManager);
|
||||
return biomeManager;
|
||||
return serverProcess.biome();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling advancements.
|
||||
*
|
||||
* @return the advancement manager
|
||||
*/
|
||||
public static AdvancementManager getAdvancementManager() {
|
||||
checkInitStatus(advancementManager);
|
||||
return advancementManager;
|
||||
return serverProcess.advancement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the manager handling {@link Extension}.
|
||||
*
|
||||
* @return the extension manager
|
||||
*/
|
||||
public static ExtensionManager getExtensionManager() {
|
||||
checkInitStatus(extensionManager);
|
||||
return extensionManager;
|
||||
return serverProcess.extension();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling tags.
|
||||
*
|
||||
* @return the tag manager
|
||||
*/
|
||||
public static TagManager getTagManager() {
|
||||
checkInitStatus(tagManager);
|
||||
return tagManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling the server ticks.
|
||||
*
|
||||
* @return the update manager
|
||||
*/
|
||||
public static UpdateManager getUpdateManager() {
|
||||
checkInitStatus(updateManager);
|
||||
return updateManager;
|
||||
return serverProcess.tag();
|
||||
}
|
||||
|
||||
public static Server getServer() {
|
||||
checkInitStatus(server);
|
||||
return server;
|
||||
return serverProcess.server();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -614,67 +407,15 @@ public final class MinecraftServer {
|
||||
* @throws IllegalStateException if called before {@link #init()} or if the server is already running
|
||||
*/
|
||||
public void start(@NotNull String address, int port) {
|
||||
Check.stateCondition(!initialized, "#start can only be called after #init");
|
||||
Check.stateCondition(started, "The server is already started");
|
||||
|
||||
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));
|
||||
serverProcess.start(new InetSocketAddress(address, port));
|
||||
new TickSchedulerThread(serverProcess).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops this server properly (saves if needed, kicking players, etc.)
|
||||
*/
|
||||
public static void stopCleanly() {
|
||||
if (stopping) return;
|
||||
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");*/
|
||||
serverProcess.stop();
|
||||
}
|
||||
|
||||
private static int getThreadCount(@NotNull String property, int count) {
|
||||
|
158
src/main/java/net/minestom/server/ServerProcess.java
Normal file
158
src/main/java/net/minestom/server/ServerProcess.java
Normal 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();
|
||||
}
|
324
src/main/java/net/minestom/server/ServerProcessImpl.java
Normal file
324
src/main/java/net/minestom/server/ServerProcessImpl.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -784,7 +784,7 @@ public class Entity implements Viewable, Tickable, Schedulable, TagHandler, Perm
|
||||
@ApiStatus.Internal
|
||||
protected void refreshCurrentChunk(Chunk 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);
|
||||
final Entity vehicle = this.vehicle;
|
||||
if (vehicle != null) vehicle.removePassenger(this);
|
||||
MinecraftServer.getUpdateManager().getThreadProvider().removeElement(this);
|
||||
MinecraftServer.process().dispatcher().removeElement(this);
|
||||
this.removed = true;
|
||||
Entity.ENTITY_BY_ID.remove(id);
|
||||
Entity.ENTITY_BY_UUID.remove(uuid);
|
||||
|
@ -7,6 +7,7 @@ import net.minestom.server.event.instance.InstanceTickEvent;
|
||||
import net.minestom.server.event.inventory.InventoryItemChangeEvent;
|
||||
import net.minestom.server.event.inventory.PlayerInventoryItemChangeEvent;
|
||||
import net.minestom.server.event.player.*;
|
||||
import net.minestom.server.event.server.ServerTickMonitorEvent;
|
||||
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<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<ServerTickMonitorEvent> SERVER_TICK_MONITOR_HANDLE = EventDispatcher.getHandle(ServerTickMonitorEvent.class);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ import com.google.gson.Gson;
|
||||
import net.minestom.dependencies.DependencyGetter;
|
||||
import net.minestom.dependencies.ResolvedDependency;
|
||||
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.EventNode;
|
||||
import net.minestom.server.ping.ResponseDataConsumer;
|
||||
@ -34,6 +34,8 @@ public class ExtensionManager {
|
||||
public final static String INDEV_RESOURCES_FOLDER = "minestom.extension.indevfolder.resources";
|
||||
private final static Gson GSON = new Gson();
|
||||
|
||||
private final ServerProcess serverProcess;
|
||||
|
||||
// LinkedHashMaps are HashMaps that preserve order
|
||||
private final Map<String, Extension> extensions = new LinkedHashMap<>();
|
||||
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 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;
|
||||
|
||||
public ExtensionManager() {
|
||||
public ExtensionManager(ServerProcess serverProcess) {
|
||||
this.serverProcess = serverProcess;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -219,7 +222,7 @@ public class ExtensionManager {
|
||||
discoveredExtension.createClassLoader();
|
||||
} catch (Exception e) {
|
||||
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", e);
|
||||
extensionIterator.remove();
|
||||
@ -239,7 +242,7 @@ public class ExtensionManager {
|
||||
} catch (Exception e) {
|
||||
discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.LOAD_FAILED;
|
||||
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));
|
||||
} catch (IllegalAccessException e) {
|
||||
// We made it accessible, should not occur
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
serverProcess.exception().handleException(e);
|
||||
} catch (NoSuchFieldException e) {
|
||||
// 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);
|
||||
@ -351,10 +354,10 @@ public class ExtensionManager {
|
||||
loggerField.setAccessible(true);
|
||||
loggerField.set(extension, eventNode);
|
||||
|
||||
MinecraftServer.getGlobalEventHandler().addChild(eventNode);
|
||||
serverProcess.eventHandler().addChild(eventNode);
|
||||
} catch (IllegalAccessException e) {
|
||||
// We made it accessible, should not occur
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
serverProcess.exception().handleException(e);
|
||||
} catch (NoSuchFieldException e) {
|
||||
// This should also not occur
|
||||
LOGGER.error("Main class '{}' in '{}' has no event node field.", mainClass, extensionName, e);
|
||||
@ -430,7 +433,7 @@ public class ExtensionManager {
|
||||
extensions.add(extension);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
serverProcess.exception().handleException(e);
|
||||
}
|
||||
}
|
||||
return extensions;
|
||||
@ -463,7 +466,7 @@ public class ExtensionManager {
|
||||
|
||||
return extension;
|
||||
} catch (IOException e) {
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
serverProcess.exception().handleException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -727,7 +730,7 @@ public class ExtensionManager {
|
||||
|
||||
// Remove event node
|
||||
EventNode<Event> eventNode = ext.getEventNode();
|
||||
MinecraftServer.getGlobalEventHandler().removeChild(eventNode);
|
||||
serverProcess.eventHandler().removeChild(eventNode);
|
||||
|
||||
ext.postTerminate();
|
||||
|
||||
|
@ -18,7 +18,7 @@ public final class MojangAuth {
|
||||
*/
|
||||
public static void init() {
|
||||
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;
|
||||
// Generate necessary fields...
|
||||
MojangAuth.keyPair = MojangCrypt.generateKeyPair();
|
||||
|
@ -5,7 +5,6 @@ import net.kyori.adventure.identity.Identity;
|
||||
import net.kyori.adventure.pointer.Pointers;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.Tickable;
|
||||
import net.minestom.server.UpdateManager;
|
||||
import net.minestom.server.adventure.audience.PacketGroupingAudience;
|
||||
import net.minestom.server.coordinate.Point;
|
||||
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.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.thread.ThreadDispatcher;
|
||||
import net.minestom.server.timer.Schedulable;
|
||||
import net.minestom.server.timer.Scheduler;
|
||||
import net.minestom.server.utils.PacketUtils;
|
||||
@ -50,13 +50,11 @@ import java.util.stream.Collectors;
|
||||
* <p>
|
||||
* WARNING: when making your own implementation registering the instance manually is required
|
||||
* with {@link InstanceManager#registerInstance(Instance)}, and
|
||||
* you need to be sure to signal the {@link UpdateManager} of the changes using
|
||||
* {@link UpdateManager#signalChunkLoad(Chunk)} and {@link UpdateManager#signalChunkUnload(Chunk)}.
|
||||
* you need to be sure to signal the {@link ThreadDispatcher} of every partition/element changes.
|
||||
*/
|
||||
public abstract class Instance implements Block.Getter, Block.Setter, Tickable, Schedulable, TagHandler, PacketGroupingAudience {
|
||||
|
||||
protected static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
|
||||
protected static final UpdateManager UPDATE_MANAGER = MinecraftServer.getUpdateManager();
|
||||
|
||||
private boolean registered;
|
||||
|
||||
|
@ -2,6 +2,7 @@ package net.minestom.server.instance;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.coordinate.Point;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Entity;
|
||||
@ -223,7 +224,8 @@ public class InstanceContainer extends Instance {
|
||||
// Clear cache
|
||||
this.chunks.remove(index);
|
||||
chunk.unload();
|
||||
UPDATE_MANAGER.signalChunkUnload(chunk);
|
||||
var dispatcher = MinecraftServer.process().dispatcher();
|
||||
dispatcher.deletePartition(chunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -525,6 +527,7 @@ public class InstanceContainer extends Instance {
|
||||
private void cacheChunk(@NotNull Chunk chunk) {
|
||||
final long index = ChunkUtils.getChunkIndex(chunk);
|
||||
this.chunks.put(index, chunk);
|
||||
UPDATE_MANAGER.signalChunkLoad(chunk);
|
||||
var dispatcher = MinecraftServer.process().dispatcher();
|
||||
dispatcher.createPartition(chunk);
|
||||
}
|
||||
}
|
@ -118,7 +118,8 @@ public final class InstanceManager {
|
||||
// Unregister
|
||||
instance.setRegistered(false);
|
||||
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) {
|
||||
instance.setRegistered(true);
|
||||
this.instances.add(instance);
|
||||
MinecraftServer.getUpdateManager().signalInstanceCreate(instance);
|
||||
var dispatcher = MinecraftServer.process().dispatcher();
|
||||
instance.getChunks().forEach(dispatcher::createPartition);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.minestom.server.listener.manager;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.ServerProcess;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.GlobalHandles;
|
||||
import net.minestom.server.event.player.PlayerPacketEvent;
|
||||
@ -21,11 +22,13 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
public final class PacketListenerManager {
|
||||
|
||||
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<>();
|
||||
|
||||
public PacketListenerManager() {
|
||||
public PacketListenerManager(ServerProcess serverProcess) {
|
||||
this.serverProcess = serverProcess;
|
||||
|
||||
setListener(ClientKeepAlivePacket.class, KeepAliveListener::listener);
|
||||
setListener(ClientChatMessagePacket.class, ChatMessageListener::listener);
|
||||
setListener(ClientClickWindowPacket.class, WindowListener::clickWindowListener);
|
||||
@ -79,7 +82,7 @@ public final class PacketListenerManager {
|
||||
// TODO remove legacy
|
||||
{
|
||||
final PacketController packetController = new PacketController();
|
||||
for (ClientPacketConsumer clientPacketConsumer : CONNECTION_MANAGER.getReceivePacketConsumers()) {
|
||||
for (ClientPacketConsumer clientPacketConsumer : serverProcess.connection().getReceivePacketConsumers()) {
|
||||
clientPacketConsumer.accept(player, packetController, packet);
|
||||
}
|
||||
|
||||
@ -113,7 +116,7 @@ public final class PacketListenerManager {
|
||||
* @return true if the packet is not cancelled, false otherwise
|
||||
*/
|
||||
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()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -2,10 +2,8 @@ package net.minestom.server.scoreboard;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.entity.LivingEntity;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.network.ConnectionManager;
|
||||
import net.minestom.server.utils.PacketUtils;
|
||||
import net.minestom.server.utils.UniqueIdUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -20,8 +18,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
*/
|
||||
public final class TeamManager {
|
||||
|
||||
private static final ConnectionManager CONNECTION_MANAGER = MinecraftServer.getConnectionManager();
|
||||
|
||||
/**
|
||||
* Represents all registered teams
|
||||
*/
|
||||
|
@ -13,8 +13,6 @@ import org.jline.terminal.TerminalBuilder;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MinestomTerminal {
|
||||
|
||||
private static final CommandManager COMMAND_MANAGER = MinecraftServer.getCommandManager();
|
||||
private static final String PROMPT = "> ";
|
||||
|
||||
private static volatile Terminal terminal;
|
||||
@ -37,7 +35,8 @@ public class MinestomTerminal {
|
||||
String command;
|
||||
try {
|
||||
command = reader.readLine(PROMPT);
|
||||
COMMAND_MANAGER.execute(COMMAND_MANAGER.getConsoleSender(), command);
|
||||
var commandManager = MinecraftServer.getCommandManager();
|
||||
commandManager.execute(commandManager.getConsoleSender(), command);
|
||||
} catch (UserInterruptException e) {
|
||||
// Handle Ctrl + C
|
||||
System.exit(0);
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
@ -12,7 +12,6 @@ import net.minestom.server.Viewable;
|
||||
import net.minestom.server.adventure.audience.PacketGroupingAudience;
|
||||
import net.minestom.server.entity.Entity;
|
||||
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.FramedPacket;
|
||||
import net.minestom.server.network.packet.server.SendablePacket;
|
||||
@ -50,7 +49,6 @@ import java.util.zip.Inflater;
|
||||
* Be sure to check the implementation code.
|
||||
*/
|
||||
public final class PacketUtils {
|
||||
private static final PacketListenerManager PACKET_LISTENER_MANAGER = MinecraftServer.getPacketListenerManager();
|
||||
private static final LocalCache<Deflater> LOCAL_DEFLATER = LocalCache.of(Deflater::new);
|
||||
|
||||
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,
|
||||
@NotNull Predicate<Player> predicate) {
|
||||
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
|
||||
final SendablePacket sendablePacket = GROUPED_PACKET ? new CachedPacket(packet) : packet;
|
||||
players.forEach(player -> {
|
||||
|
30
src/test/java/net/minestom/server/ServerProcessTest.java
Normal file
30
src/test/java/net/minestom/server/ServerProcessTest.java
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user