diff --git a/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java b/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java index 4c0aa278..5b267266 100644 --- a/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java +++ b/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java @@ -27,17 +27,22 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.Deque; import java.util.UUID; +import java.util.concurrent.ConcurrentLinkedDeque; +import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.world.World; import net.minecraft.world.server.ServerWorld; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent; import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent; import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.event.world.ChunkEvent; import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -46,11 +51,21 @@ public class ForgeEventForwarder { private ForgeMod mod; private Collection eventListeners; + private Deque loadChunkEvents; + private Thread loadChunkEventProcessor; + public ForgeEventForwarder(ForgeMod mod) { this.mod = mod; this.eventListeners = new ArrayList<>(1); + + loadChunkEvents = new ConcurrentLinkedDeque<>(); MinecraftForge.EVENT_BUS.register(this); + + //see processLoadChunkEvents JavaDoc comment + loadChunkEventProcessor = new Thread(this::processLoadChunkEvents); + loadChunkEventProcessor.setDaemon(true); + loadChunkEventProcessor.start(); } public synchronized void addEventListener(ServerEventListener listener) { @@ -112,5 +127,66 @@ public synchronized void onPlayerLeave(PlayerLoggedOutEvent evt) { UUID uuid = evt.getPlayer().getUniqueID(); for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid); } + + @SubscribeEvent + public void onChunkLoad(ChunkEvent.Load evt) { + if (!(evt.getWorld() instanceof ServerWorld)) return; + + try { + UUID world = mod.getUUIDForWorld((ServerWorld) evt.getWorld()); + Vector2i chunk = new Vector2i(evt.getChunk().getPos().x, evt.getChunk().getPos().z); + + Logger.global.logInfo("Adding chunk: " + chunk); + + synchronized (loadChunkEvents) { + loadChunkEvents.add(new WorldChunk(world, chunk)); + loadChunkEvents.notify(); + } + } catch (IOException e) { + Logger.global.noFloodError("Failed to get the UUID for a world!", e); + } + } + + /** + * This is a workaround for forge not providing a way to detect if chunks are newly generated: + * Each time a chunk-load-event occurs, it is (asynchronously) tested if the chunk is already generated on the world files. + * If it is a new chunk it will likely not be saved to the disk right away. + */ + private void processLoadChunkEvents() { + while (!Thread.interrupted()) { + WorldChunk worldChunk; + if (mod.getPlugin().isLoaded() && (worldChunk = loadChunkEvents.poll()) != null) { + try { + World world = mod.getPlugin().getWorld(worldChunk.world); + if (world == null || world.isChunkGenerated(worldChunk.chunk)) continue; + + for (ServerEventListener listener : eventListeners) listener.onChunkFinishedGeneration(worldChunk.world, worldChunk.chunk); + + } catch (RuntimeException e) { + Logger.global.noFloodWarning("processLoadChunkEventsError", "Failed to test if a chunk is newly generated:" + e); + } + } else { + synchronized (loadChunkEvents) { + try { + loadChunkEvents.wait(10000); + } catch (InterruptedException e) { + break; + } + } + } + } + + Thread.currentThread().interrupt(); + } + + private class WorldChunk { + final UUID world; + final Vector2i chunk; + + public WorldChunk(UUID world, Vector2i chunk) { + this.world = world; + this.chunk = chunk; + } + } } 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 ca8a6f47..d6c22e83 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 @@ -214,6 +214,10 @@ public void onPlayerLeave(PlayerLoggedOutEvent evt) { public MinecraftServer getServer() { return this.serverInstance; } + + public Plugin getPlugin() { + return this.pluginInstance; + } @Override public Collection getOnlinePlayers() { diff --git a/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java b/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java index 4c0aa278..5b267266 100644 --- a/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java +++ b/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java @@ -27,17 +27,22 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.Deque; import java.util.UUID; +import java.util.concurrent.ConcurrentLinkedDeque; +import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.world.World; import net.minecraft.world.server.ServerWorld; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent; import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent; import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.event.world.ChunkEvent; import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -46,11 +51,21 @@ public class ForgeEventForwarder { private ForgeMod mod; private Collection eventListeners; + private Deque loadChunkEvents; + private Thread loadChunkEventProcessor; + public ForgeEventForwarder(ForgeMod mod) { this.mod = mod; this.eventListeners = new ArrayList<>(1); + + loadChunkEvents = new ConcurrentLinkedDeque<>(); MinecraftForge.EVENT_BUS.register(this); + + //see processLoadChunkEvents JavaDoc comment + loadChunkEventProcessor = new Thread(this::processLoadChunkEvents); + loadChunkEventProcessor.setDaemon(true); + loadChunkEventProcessor.start(); } public synchronized void addEventListener(ServerEventListener listener) { @@ -112,5 +127,66 @@ public synchronized void onPlayerLeave(PlayerLoggedOutEvent evt) { UUID uuid = evt.getPlayer().getUniqueID(); for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid); } + + @SubscribeEvent + public void onChunkLoad(ChunkEvent.Load evt) { + if (!(evt.getWorld() instanceof ServerWorld)) return; + + try { + UUID world = mod.getUUIDForWorld((ServerWorld) evt.getWorld()); + Vector2i chunk = new Vector2i(evt.getChunk().getPos().x, evt.getChunk().getPos().z); + + Logger.global.logInfo("Adding chunk: " + chunk); + + synchronized (loadChunkEvents) { + loadChunkEvents.add(new WorldChunk(world, chunk)); + loadChunkEvents.notify(); + } + } catch (IOException e) { + Logger.global.noFloodError("Failed to get the UUID for a world!", e); + } + } + + /** + * This is a workaround for forge not providing a way to detect if chunks are newly generated: + * Each time a chunk-load-event occurs, it is (asynchronously) tested if the chunk is already generated on the world files. + * If it is a new chunk it will likely not be saved to the disk right away. + */ + private void processLoadChunkEvents() { + while (!Thread.interrupted()) { + WorldChunk worldChunk; + if (mod.getPlugin().isLoaded() && (worldChunk = loadChunkEvents.poll()) != null) { + try { + World world = mod.getPlugin().getWorld(worldChunk.world); + if (world == null || world.isChunkGenerated(worldChunk.chunk)) continue; + + for (ServerEventListener listener : eventListeners) listener.onChunkFinishedGeneration(worldChunk.world, worldChunk.chunk); + + } catch (RuntimeException e) { + Logger.global.noFloodWarning("processLoadChunkEventsError", "Failed to test if a chunk is newly generated:" + e); + } + } else { + synchronized (loadChunkEvents) { + try { + loadChunkEvents.wait(10000); + } catch (InterruptedException e) { + break; + } + } + } + } + + Thread.currentThread().interrupt(); + } + + private class WorldChunk { + final UUID world; + final Vector2i chunk; + + public WorldChunk(UUID world, Vector2i chunk) { + this.world = world; + this.chunk = chunk; + } + } } 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 ee22bcfd..463b08cf 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 @@ -214,6 +214,10 @@ public void onPlayerLeave(PlayerLoggedOutEvent evt) { public MinecraftServer getServer() { return this.serverInstance; } + + public Plugin getPlugin() { + return this.pluginInstance; + } @Override public Collection getOnlinePlayers() { diff --git a/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java b/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java index 4c0aa278..5b267266 100644 --- a/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java +++ b/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java @@ -27,17 +27,22 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.Deque; import java.util.UUID; +import java.util.concurrent.ConcurrentLinkedDeque; +import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.world.World; import net.minecraft.world.server.ServerWorld; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent; import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent; import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.event.world.ChunkEvent; import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -46,11 +51,21 @@ public class ForgeEventForwarder { private ForgeMod mod; private Collection eventListeners; + private Deque loadChunkEvents; + private Thread loadChunkEventProcessor; + public ForgeEventForwarder(ForgeMod mod) { this.mod = mod; this.eventListeners = new ArrayList<>(1); + + loadChunkEvents = new ConcurrentLinkedDeque<>(); MinecraftForge.EVENT_BUS.register(this); + + //see processLoadChunkEvents JavaDoc comment + loadChunkEventProcessor = new Thread(this::processLoadChunkEvents); + loadChunkEventProcessor.setDaemon(true); + loadChunkEventProcessor.start(); } public synchronized void addEventListener(ServerEventListener listener) { @@ -112,5 +127,66 @@ public synchronized void onPlayerLeave(PlayerLoggedOutEvent evt) { UUID uuid = evt.getPlayer().getUniqueID(); for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid); } + + @SubscribeEvent + public void onChunkLoad(ChunkEvent.Load evt) { + if (!(evt.getWorld() instanceof ServerWorld)) return; + + try { + UUID world = mod.getUUIDForWorld((ServerWorld) evt.getWorld()); + Vector2i chunk = new Vector2i(evt.getChunk().getPos().x, evt.getChunk().getPos().z); + + Logger.global.logInfo("Adding chunk: " + chunk); + + synchronized (loadChunkEvents) { + loadChunkEvents.add(new WorldChunk(world, chunk)); + loadChunkEvents.notify(); + } + } catch (IOException e) { + Logger.global.noFloodError("Failed to get the UUID for a world!", e); + } + } + + /** + * This is a workaround for forge not providing a way to detect if chunks are newly generated: + * Each time a chunk-load-event occurs, it is (asynchronously) tested if the chunk is already generated on the world files. + * If it is a new chunk it will likely not be saved to the disk right away. + */ + private void processLoadChunkEvents() { + while (!Thread.interrupted()) { + WorldChunk worldChunk; + if (mod.getPlugin().isLoaded() && (worldChunk = loadChunkEvents.poll()) != null) { + try { + World world = mod.getPlugin().getWorld(worldChunk.world); + if (world == null || world.isChunkGenerated(worldChunk.chunk)) continue; + + for (ServerEventListener listener : eventListeners) listener.onChunkFinishedGeneration(worldChunk.world, worldChunk.chunk); + + } catch (RuntimeException e) { + Logger.global.noFloodWarning("processLoadChunkEventsError", "Failed to test if a chunk is newly generated:" + e); + } + } else { + synchronized (loadChunkEvents) { + try { + loadChunkEvents.wait(10000); + } catch (InterruptedException e) { + break; + } + } + } + } + + Thread.currentThread().interrupt(); + } + + private class WorldChunk { + final UUID world; + final Vector2i chunk; + + public WorldChunk(UUID world, Vector2i chunk) { + this.world = world; + this.chunk = chunk; + } + } } 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 c66ae57b..f6e5d3b2 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 @@ -97,7 +97,6 @@ public ForgeMod() { @SubscribeEvent public void onServerStarting(FMLServerStartingEvent event) { - this.worldUUIDs.clear(); this.serverInstance = event.getServer(); //register commands @@ -157,9 +156,7 @@ public UUID getUUIDForWorld(ServerWorld world) throws IOException { try { return worldUuidCache.get(world); } catch (RuntimeException e) { - Throwable cause = e.getCause(); - if (cause instanceof IOException) throw (IOException) cause; - else throw new IOException(cause); + throw new IOException(e); } } @@ -213,6 +210,10 @@ public void onPlayerLeave(PlayerLoggedOutEvent evt) { public MinecraftServer getServer() { return this.serverInstance; } + + public Plugin getPlugin() { + return this.pluginInstance; + } @Override public Collection getOnlinePlayers() {