From 41921c680a56c390a3f64454593f3bb6866eadb3 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Mon, 14 Sep 2020 11:25:25 +0200 Subject: [PATCH] Add a trigger to render tiles for newly generated chunks on forge This uses a workaround that is checking for each chunk-load-event, if that chunk is already present and generated on the world files. The chunk will most likely not be present on the worldfiles right after being generated, because it is usually not saved to disk right away. --- .../bluemap/forge/ForgeEventForwarder.java | 76 +++++++++++++++++++ .../bluecolored/bluemap/forge/ForgeMod.java | 4 + .../bluemap/forge/ForgeEventForwarder.java | 76 +++++++++++++++++++ .../bluecolored/bluemap/forge/ForgeMod.java | 4 + .../bluemap/forge/ForgeEventForwarder.java | 76 +++++++++++++++++++ .../bluecolored/bluemap/forge/ForgeMod.java | 9 ++- 6 files changed, 241 insertions(+), 4 deletions(-) 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() {