diff --git a/src/main/java/fr/themode/demo/Main.java b/src/main/java/fr/themode/demo/Main.java index e5435b80b..4b41b0491 100644 --- a/src/main/java/fr/themode/demo/Main.java +++ b/src/main/java/fr/themode/demo/Main.java @@ -15,6 +15,7 @@ import net.minestom.server.item.Material; import net.minestom.server.network.packet.server.play.DeclareRecipesPacket; import net.minestom.server.recipe.RecipeManager; import net.minestom.server.recipe.ShapelessRecipe; +import net.minestom.server.timer.TaskRunnable; import net.minestom.server.utils.time.TimeUnit; import net.minestom.server.utils.time.UpdateOption; @@ -50,6 +51,13 @@ public class Main { MinecraftServer.getBenchmarkManager().enable(new UpdateOption(10 * 1000, TimeUnit.MILLISECOND)); + MinecraftServer.getSchedulerManager().addShutdownTask(new TaskRunnable() { + @Override + public void run() { + System.out.println("Good night"); + } + }); + PlayerInit.init(); minecraftServer.start("localhost", 55555, PlayerInit.getResponseDataConsumer()); diff --git a/src/main/java/fr/themode/demo/commands/SimpleCommand.java b/src/main/java/fr/themode/demo/commands/SimpleCommand.java index 9e0127f94..81bbf19e2 100644 --- a/src/main/java/fr/themode/demo/commands/SimpleCommand.java +++ b/src/main/java/fr/themode/demo/commands/SimpleCommand.java @@ -1,5 +1,6 @@ package fr.themode.demo.commands; +import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandProcessor; import net.minestom.server.entity.EntityCreature; import net.minestom.server.entity.Player; @@ -26,6 +27,8 @@ public class SimpleCommand implements CommandProcessor { creature.setPathTo(player.getPosition()); } + MinecraftServer.stopCleanly(); + return true; } diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index b4e6eaac9..cee0eea43 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -187,4 +187,13 @@ public class MinecraftServer { nettyServer.start(address, port); } + /** + * Stops this server properly (saves if needed, kicking players, etc.) + */ + public static void stopCleanly() { + updateManager.stop(); + nettyServer.stop(); + schedulerManager.shutdown(); + } + } diff --git a/src/main/java/net/minestom/server/UpdateManager.java b/src/main/java/net/minestom/server/UpdateManager.java index 8f2673d30..3deba4bdf 100644 --- a/src/main/java/net/minestom/server/UpdateManager.java +++ b/src/main/java/net/minestom/server/UpdateManager.java @@ -13,6 +13,7 @@ import java.util.concurrent.ExecutorService; public class UpdateManager { private ExecutorService mainUpdate = new MinestomThread(MinecraftServer.THREAD_COUNT_MAIN_UPDATE, MinecraftServer.THREAD_NAME_MAIN_UPDATE); + private boolean stopRequested; public void start() { @@ -25,7 +26,7 @@ public class UpdateManager { final long tickDistance = MinecraftServer.TICK_MS * 1000000; long currentTime; - while (true) { + while (!stopRequested) { currentTime = System.nanoTime(); // Keep Alive Handling @@ -63,4 +64,7 @@ public class UpdateManager { }); } + public void stop() { + stopRequested = true; + } } diff --git a/src/main/java/net/minestom/server/network/netty/NettyServer.java b/src/main/java/net/minestom/server/network/netty/NettyServer.java index 101961212..b652a4cfd 100644 --- a/src/main/java/net/minestom/server/network/netty/NettyServer.java +++ b/src/main/java/net/minestom/server/network/netty/NettyServer.java @@ -1,6 +1,7 @@ package net.minestom.server.network.netty; import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; @@ -62,4 +63,12 @@ public class NettyServer { public int getPort() { return port; } + + public void stop() { + try { + group.shutdownGracefully().sync(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } } diff --git a/src/main/java/net/minestom/server/timer/SchedulerManager.java b/src/main/java/net/minestom/server/timer/SchedulerManager.java index f30c44b1c..0e5ca04ad 100644 --- a/src/main/java/net/minestom/server/timer/SchedulerManager.java +++ b/src/main/java/net/minestom/server/timer/SchedulerManager.java @@ -13,8 +13,10 @@ import java.util.concurrent.atomic.AtomicInteger; public class SchedulerManager { private static final AtomicInteger COUNTER = new AtomicInteger(); + private static final AtomicInteger SHUTDOWN_COUNTER = new AtomicInteger(); private static ExecutorService batchesPool = new MinestomThread(MinecraftServer.THREAD_COUNT_SCHEDULER, MinecraftServer.THREAD_NAME_SCHEDULER); private List tasks = new CopyOnWriteArrayList<>(); + private List shutdownTasks = new CopyOnWriteArrayList<>(); public int addTask(TaskRunnable runnable, UpdateOption updateOption, int maxCallCount) { int id = COUNTER.incrementAndGet(); @@ -34,6 +36,29 @@ public class SchedulerManager { return addTask(runnable, updateOption, 1); } + /** + * Adds a task to run when the server shutdowns + * @param runnable the task to perform + * @return + */ + public int addShutdownTask(TaskRunnable runnable) { + int id = SHUTDOWN_COUNTER.incrementAndGet(); + runnable.setId(id); + + Task task = new Task(runnable, null, 1); + this.shutdownTasks.add(task); + + return id; + } + + public void shutdown() { + batchesPool.execute(() -> { + for (Task task : shutdownTasks) { + task.getRunnable().run(); + } + }); + } + public void removeTask(int taskId) { this.tasks.removeIf(task -> task.getId() == taskId); }