From e62c5d5be3ce01074def13b82ff5b9fa0bff808e Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Fri, 25 Sep 2020 18:24:07 +0200 Subject: [PATCH] Add debug command to save the world and flush scheduled changed chunks Also change the status command messages a little --- .../bluemap/common/plugin/Plugin.java | 13 ++++ .../common/plugin/commands/CommandHelper.java | 24 ++++++- .../common/plugin/commands/Commands.java | 62 +++++++++++++++++-- .../serverinterface/ServerInterface.java | 13 ++++ .../bluecolored/bluemap/fabric/FabricMod.java | 33 ++++++++++ .../bluecolored/bluemap/fabric/FabricMod.java | 33 ++++++++++ .../bluecolored/bluemap/fabric/FabricMod.java | 33 ++++++++++ .../bluecolored/bluemap/forge/ForgeMod.java | 33 ++++++++++ .../bluecolored/bluemap/forge/ForgeMod.java | 33 ++++++++++ .../bluecolored/bluemap/forge/ForgeMod.java | 33 ++++++++++ .../bluemap/bukkit/BukkitPlugin.java | 21 +++++++ .../bluemap/bukkit/EventForwarder.java | 6 +- .../bluemap/sponge/SpongePlugin.java | 24 +++++++ 13 files changed, 350 insertions(+), 11 deletions(-) diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java index 0f9866b4..e48b4f5a 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java @@ -372,6 +372,15 @@ public MapUpdateHandler getUpdateHandler() { return updateHandler; } + public boolean flushWorldUpdates(UUID worldUUID) throws IOException { + if (serverInterface.persistWorldChanges(worldUUID)) { + updateHandler.onWorldSaveToDisk(worldUUID); + return true; + } + + return false; + } + public WebServer getWebServer() { return webServer; } @@ -380,4 +389,8 @@ public boolean isLoaded() { return loaded; } + public String getImplementationType() { + return implementationType; + } + } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java index 64484053..1ad8f825 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java @@ -42,6 +42,7 @@ import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.plugin.text.TextColor; +import de.bluecolored.bluemap.common.plugin.text.TextFormat; import de.bluecolored.bluemap.core.render.hires.HiresModelManager; import de.bluecolored.bluemap.core.world.World; @@ -62,12 +63,29 @@ public List createStatusMessage(){ lines.add(Text.of(TextColor.BLUE, "Tile-Updates:")); if (renderer.isRunning()) { - lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", Text.of(TextColor.GREEN, "running").setHoverText(Text.of("click to pause rendering")).setClickCommand("/bluemap pause"), TextColor.GRAY, "!")); + lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", + Text.of(TextColor.GREEN, "running") + .setHoverText(Text.of("click to pause rendering")) + .setClickCommand("/bluemap pause"), + TextColor.GRAY, "!")); } else { - lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", Text.of(TextColor.RED, "paused").setHoverText(Text.of("click to resume rendering")).setClickCommand("/bluemap resume"), TextColor.GRAY, "!")); + lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", + Text.of(TextColor.RED, "paused") + .setHoverText(Text.of("click to resume rendering")) + .setClickCommand("/bluemap resume"), + TextColor.GRAY, "!")); } - lines.add(Text.of(TextColor.WHITE, " Scheduled tile-updates: ", Text.of(TextColor.GOLD, renderer.getQueueSize()).setHoverText(Text.of("tiles waiting for a free render-thread")), TextColor.GRAY, " + " , Text.of(TextColor.GRAY, plugin.getUpdateHandler().getUpdateBufferCount()).setHoverText(Text.of("tiles waiting for world-save")))); + lines.add(Text.of( + TextColor.WHITE, " Scheduled tile-updates: ", + TextColor.GOLD, renderer.getQueueSize()).setHoverText( + Text.of( + TextColor.WHITE, "Tiles waiting for a free render-thread: ", TextColor.GOLD, renderer.getQueueSize(), + TextColor.WHITE, "\n\nChunks marked as changed: ", TextColor.GOLD, plugin.getUpdateHandler().getUpdateBufferCount(), + TextColor.GRAY, TextFormat.ITALIC, "\n(Changed chunks will be rendered as soon as they are saved back to the world-files)" + ) + ) + ); RenderTask[] tasks = renderer.getRenderTasks(); if (tasks.length > 0) { diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java index fe33c927..2c8f405e 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java @@ -119,6 +119,12 @@ public void init() { .then(argument("y", DoubleArgumentType.doubleArg()) .then(argument("z", DoubleArgumentType.doubleArg()) .executes(this::debugBlockCommand)))))) + + .then(literal("flush") + .executes(this::debugFlushCommand) + + .then(argument("world", StringArgumentType.string()).suggests(new WorldSuggestionProvider<>(plugin)) + .executes(this::debugFlushCommand))) .then(literal("cache") .executes(this::debugClearCacheCommand)) @@ -377,6 +383,47 @@ public int debugClearCacheCommand(CommandContext context) throws CommandSynta return 1; } + + public int debugFlushCommand(CommandContext context) throws CommandSyntaxException { + CommandSource source = commandSourceInterface.apply(context.getSource()); + + // parse arguments + Optional worldName = getOptionalArgument(context, "world", String.class); + + final World world; + if (worldName.isPresent()) { + world = parseWorld(worldName.get()).orElse(null); + + if (world == null) { + source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.worldHelperHover(), " with this name: ", TextColor.WHITE, worldName.get())); + return 0; + } + } else { + world = source.getWorld().orElse(null); + + if (world == null) { + source.sendMessage(Text.of(TextColor.RED, "Can't detect a location from this command-source, you'll have to define a world!")); + return 0; + } + } + + new Thread(() -> { + source.sendMessage(Text.of(TextColor.GOLD, "Saving world and flushing changes...")); + try { + if (plugin.flushWorldUpdates(world.getUUID())) { + source.sendMessage(Text.of(TextColor.GREEN, "Successfully saved and flushed all changes.")); + } else { + source.sendMessage(Text.of(TextColor.RED, "This operation is not supported by this implementation (" + plugin.getImplementationType() + ")")); + } + } catch (IOException ex) { + source.sendMessage(Text.of(TextColor.RED, "There was an unexpected exception trying to save the world. Please check the console for more details...")); + Logger.global.logError("Unexpected exception trying to save the world!", ex); + } + }).start(); + + return 1; + } + public int debugBlockCommand(CommandContext context) throws CommandSyntaxException { final CommandSource source = commandSourceInterface.apply(context.getSource()); @@ -515,10 +562,17 @@ public int renderCommand(CommandContext context) { // execute render new Thread(() -> { - if (world != null) { - helper.createWorldRenderTask(source, world, center, radius); - } else { - helper.createMapRenderTask(source, map, center, radius); + try { + if (world != null) { + plugin.getServerInterface().persistWorldChanges(world.getUUID()); + helper.createWorldRenderTask(source, world, center, radius); + } else { + plugin.getServerInterface().persistWorldChanges(map.getWorld().getUUID()); + helper.createMapRenderTask(source, map, center, radius); + } + } catch (IOException ex) { + source.sendMessage(Text.of(TextColor.RED, "There was an unexpected exception trying to save the world. Please check the console for more details...")); + Logger.global.logError("Unexpected exception trying to save the world!", ex); } }).start(); diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java index 92ca74c6..6e18abd0 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java @@ -63,6 +63,19 @@ default String getWorldName(UUID worldUUID) { return null; } + /** + * Attempts to persist all changes that have been made in a world to disk. + * + * @param worldUUID The {@link UUID} of the world to be persisted. + * @return true if the changes have been successfully persisted, false if this operation is not supported by the implementation + * + * @throws IOException if something went wrong trying to persist the changes + * @throws IllegalArgumentException if there is no world with this UUID + */ + default boolean persistWorldChanges(UUID worldUUID) throws IOException, IllegalArgumentException { + return false; + } + /** * Returns the Folder containing the configurations for the plugin */ diff --git a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java index 30b10354..95333919 100644 --- a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java +++ b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java @@ -33,7 +33,9 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; import org.apache.logging.log4j.LogManager; @@ -164,6 +166,37 @@ private UUID loadUUIDForWorld(ServerWorld world) throws IOException { File dimensionDir = world.getDimension().getType().getSaveDirectory(world.getSaveHandler().getWorldDir()); return getUUIDForWorld(dimensionDir.getCanonicalFile()); } + + @Override + public boolean persistWorldChanges(UUID worldUUID) throws IOException, IllegalArgumentException { + final CompletableFuture taskResult = new CompletableFuture<>(); + + serverInstance.execute(() -> { + try { + for (ServerWorld world : serverInstance.getWorlds()) { + if (getUUIDForWorld(world).equals(worldUUID)) { + world.save(null, true, false); + } + } + + taskResult.complete(true); + } catch (Exception e) { + taskResult.completeExceptionally(e); + } + }); + + try { + return taskResult.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException(e); + } catch (ExecutionException e) { + Throwable t = e.getCause(); + if (t instanceof IOException) throw (IOException) t; + if (t instanceof IllegalArgumentException) throw (IllegalArgumentException) t; + throw new IOException(t); + } + } @Override public File getConfigFolder() { diff --git a/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java b/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java index e925ec90..a9b49116 100644 --- a/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java +++ b/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java @@ -33,7 +33,9 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; import org.apache.logging.log4j.LogManager; @@ -169,6 +171,37 @@ private UUID loadUUIDForWorld(ServerWorld world) throws IOException { File dimensionDir = dimensionFolder.getCanonicalFile(); return getUUIDForWorld(dimensionDir); } + + @Override + public boolean persistWorldChanges(UUID worldUUID) throws IOException, IllegalArgumentException { + final CompletableFuture taskResult = new CompletableFuture<>(); + + serverInstance.execute(() -> { + try { + for (ServerWorld world : serverInstance.getWorlds()) { + if (getUUIDForWorld(world).equals(worldUUID)) { + world.save(null, true, false); + } + } + + taskResult.complete(true); + } catch (Exception e) { + taskResult.completeExceptionally(e); + } + }); + + try { + return taskResult.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException(e); + } catch (ExecutionException e) { + Throwable t = e.getCause(); + if (t instanceof IOException) throw (IOException) t; + if (t instanceof IllegalArgumentException) throw (IllegalArgumentException) t; + throw new IOException(t); + } + } @Override public File getConfigFolder() { diff --git a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java index bb05b8f2..7918afea 100644 --- a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java +++ b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java @@ -33,7 +33,9 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; import org.apache.logging.log4j.LogManager; @@ -169,6 +171,37 @@ private UUID loadUUIDForWorld(ServerWorld world) throws IOException { File dimensionDir = dimensionFolder.getCanonicalFile(); return getUUIDForWorld(dimensionDir); } + + @Override + public boolean persistWorldChanges(UUID worldUUID) throws IOException, IllegalArgumentException { + final CompletableFuture taskResult = new CompletableFuture<>(); + + serverInstance.execute(() -> { + try { + for (ServerWorld world : serverInstance.getWorlds()) { + if (getUUIDForWorld(world).equals(worldUUID)) { + world.save(null, true, false); + } + } + + taskResult.complete(true); + } catch (Exception e) { + taskResult.completeExceptionally(e); + } + }); + + try { + return taskResult.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException(e); + } catch (ExecutionException e) { + Throwable t = e.getCause(); + if (t instanceof IOException) throw (IOException) t; + if (t instanceof IllegalArgumentException) throw (IllegalArgumentException) t; + throw new IOException(t); + } + } @Override public File getConfigFolder() { diff --git a/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java b/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java index f9998aed..ad375bde 100644 --- a/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java +++ b/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java @@ -33,7 +33,9 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; import org.apache.logging.log4j.LogManager; @@ -185,6 +187,37 @@ private File getFolderForWorld(ServerWorld world) throws IOException { return worldFolder.getCanonicalFile(); } + + @Override + public boolean persistWorldChanges(UUID worldUUID) throws IOException, IllegalArgumentException { + final CompletableFuture taskResult = new CompletableFuture<>(); + + serverInstance.execute(() -> { + try { + for (ServerWorld world : serverInstance.getWorlds()) { + if (getUUIDForWorld(world).equals(worldUUID)) { + world.save(null, true, false); + } + } + + taskResult.complete(true); + } catch (Exception e) { + taskResult.completeExceptionally(e); + } + }); + + try { + return taskResult.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException(e); + } catch (ExecutionException e) { + Throwable t = e.getCause(); + if (t instanceof IOException) throw (IOException) t; + if (t instanceof IllegalArgumentException) throw (IllegalArgumentException) t; + throw new IOException(t); + } + } @Override public File getConfigFolder() { diff --git a/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java b/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java index c6b13922..466dfe37 100644 --- a/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java +++ b/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java @@ -33,7 +33,9 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; import org.apache.logging.log4j.LogManager; @@ -185,6 +187,37 @@ private File getFolderForWorld(ServerWorld world) throws IOException { return worldFolder.getCanonicalFile(); } + + @Override + public boolean persistWorldChanges(UUID worldUUID) throws IOException, IllegalArgumentException { + final CompletableFuture taskResult = new CompletableFuture<>(); + + serverInstance.execute(() -> { + try { + for (ServerWorld world : serverInstance.getWorlds()) { + if (getUUIDForWorld(world).equals(worldUUID)) { + world.save(null, true, false); + } + } + + taskResult.complete(true); + } catch (Exception e) { + taskResult.completeExceptionally(e); + } + }); + + try { + return taskResult.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException(e); + } catch (ExecutionException e) { + Throwable t = e.getCause(); + if (t instanceof IOException) throw (IOException) t; + if (t instanceof IllegalArgumentException) throw (IllegalArgumentException) t; + throw new IOException(t); + } + } @Override public File getConfigFolder() { diff --git a/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java b/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java index 7a6125b3..71e6828f 100644 --- a/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java +++ b/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java @@ -33,7 +33,9 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; import org.apache.logging.log4j.LogManager; @@ -183,6 +185,37 @@ private File getFolderForWorld(ServerWorld world) throws IOException { File dimensionFolder = DimensionType.func_236031_a_(world.func_234923_W_(), worldFolder); return dimensionFolder.getCanonicalFile(); } + + @Override + public boolean persistWorldChanges(UUID worldUUID) throws IOException, IllegalArgumentException { + final CompletableFuture taskResult = new CompletableFuture<>(); + + serverInstance.execute(() -> { + try { + for (ServerWorld world : serverInstance.getWorlds()) { + if (getUUIDForWorld(world).equals(worldUUID)) { + world.save(null, true, false); + } + } + + taskResult.complete(true); + } catch (Exception e) { + taskResult.completeExceptionally(e); + } + }); + + try { + return taskResult.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException(e); + } catch (ExecutionException e) { + Throwable t = e.getCause(); + if (t instanceof IOException) throw (IOException) t; + if (t instanceof IllegalArgumentException) throw (IllegalArgumentException) t; + throw new IOException(t); + } + } @Override public File getConfigFolder() { diff --git a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java index abb37fb3..8013011f 100644 --- a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java +++ b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java @@ -259,6 +259,27 @@ public Optional getPlayer(UUID uuid) { return Optional.ofNullable(onlinePlayerMap.get(uuid)); } + @Override + public boolean persistWorldChanges(UUID worldUUID) throws IOException, IllegalArgumentException { + try { + return Bukkit.getScheduler().callSyncMethod(this, () -> { + World world = Bukkit.getWorld(worldUUID); + if (world == null) throw new IllegalArgumentException("There is no world with this uuid: " + worldUUID); + world.save(); + + return true; + }).get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException(e); + } catch (ExecutionException e) { + Throwable t = e.getCause(); + if (t instanceof IOException) throw (IOException) t; + if (t instanceof IllegalArgumentException) throw (IllegalArgumentException) t; + throw new IOException(t); + } + } + /** * Only update some of the online players each tick to minimize performance impact on the server-thread. * Only call this method on the server-thread. diff --git a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java index 2288d6eb..c3a46ca8 100644 --- a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java +++ b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java @@ -72,10 +72,8 @@ public synchronized void removeAllListeners() { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public synchronized void onChunkSaveToDisk(ChunkUnloadEvent evt) { - if (evt.isSaveChunk()) { - Vector2i chunkPos = new Vector2i(evt.getChunk().getX(), evt.getChunk().getZ()); - for (ServerEventListener listener : listeners) listener.onChunkSaveToDisk(evt.getWorld().getUID(), chunkPos); - } + Vector2i chunkPos = new Vector2i(evt.getChunk().getX(), evt.getChunk().getZ()); + for (ServerEventListener listener : listeners) listener.onChunkSaveToDisk(evt.getWorld().getUID(), chunkPos); } /* Use ChunkSaveToDisk as it is the preferred event to use and more reliable on the chunk actually saved to disk diff --git a/implementations/sponge-7.2.0/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java b/implementations/sponge-7.2.0/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java index c43f8f7e..7f7f4afc 100644 --- a/implementations/sponge-7.2.0/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java +++ b/implementations/sponge-7.2.0/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java @@ -35,6 +35,7 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; import javax.inject.Inject; @@ -84,6 +85,7 @@ public class SpongePlugin implements ServerInterface { private SpongeCommands commands; private SpongeExecutorService asyncExecutor; + private SpongeExecutorService syncExecutor; private int playerUpdateIndex = 0; private Map onlinePlayerMap; @@ -110,6 +112,7 @@ public SpongePlugin(org.slf4j.Logger logger) { @Listener public void onServerStart(GameStartingServerEvent evt) { asyncExecutor = Sponge.getScheduler().createAsyncExecutor(this); + syncExecutor = Sponge.getScheduler().createSyncExecutor(this); //save all world properties to generate level_sponge.dat files for (WorldProperties properties : Sponge.getServer().getAllWorldProperties()) { @@ -237,6 +240,27 @@ public boolean isMetricsEnabled(boolean configValue) { return Sponge.getMetricsConfigManager().getGlobalCollectionState().asBoolean(); } + @Override + public boolean persistWorldChanges(UUID worldUUID) throws IOException, IllegalArgumentException { + try { + return syncExecutor.submit(() -> { + World world = Sponge.getServer().getWorld(worldUUID).orElse(null); + if (world == null) throw new IllegalArgumentException("There is no world with this uuid: " + worldUUID); + world.save(); + + return true; + }).get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException(e); + } catch (ExecutionException e) { + Throwable t = e.getCause(); + if (t instanceof IOException) throw (IOException) t; + if (t instanceof IllegalArgumentException) throw (IllegalArgumentException) t; + throw new IOException(t); + } + } + /** * Only update some of the online players each tick to minimize performance impact on the server-thread. * Only call this method on the server-thread.