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.
This commit is contained in:
Blue (Lukas Rieger) 2020-09-14 11:25:25 +02:00
parent e8fc7028d0
commit 41921c680a
6 changed files with 241 additions and 4 deletions

View File

@ -27,17 +27,22 @@ package de.bluecolored.bluemap.forge;
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<ServerEventListener> eventListeners;
private Deque<WorldChunk> 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 class ForgeEventForwarder {
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;
}
}
}

View File

@ -214,6 +214,10 @@ public class ForgeMod implements ServerInterface {
public MinecraftServer getServer() {
return this.serverInstance;
}
public Plugin getPlugin() {
return this.pluginInstance;
}
@Override
public Collection<Player> getOnlinePlayers() {

View File

@ -27,17 +27,22 @@ package de.bluecolored.bluemap.forge;
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<ServerEventListener> eventListeners;
private Deque<WorldChunk> 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 class ForgeEventForwarder {
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;
}
}
}

View File

@ -214,6 +214,10 @@ public class ForgeMod implements ServerInterface {
public MinecraftServer getServer() {
return this.serverInstance;
}
public Plugin getPlugin() {
return this.pluginInstance;
}
@Override
public Collection<Player> getOnlinePlayers() {

View File

@ -27,17 +27,22 @@ package de.bluecolored.bluemap.forge;
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<ServerEventListener> eventListeners;
private Deque<WorldChunk> 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 class ForgeEventForwarder {
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;
}
}
}

View File

@ -97,7 +97,6 @@ public class ForgeMod implements ServerInterface {
@SubscribeEvent
public void onServerStarting(FMLServerStartingEvent event) {
this.worldUUIDs.clear();
this.serverInstance = event.getServer();
//register commands
@ -157,9 +156,7 @@ public class ForgeMod implements ServerInterface {
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 class ForgeMod implements ServerInterface {
public MinecraftServer getServer() {
return this.serverInstance;
}
public Plugin getPlugin() {
return this.pluginInstance;
}
@Override
public Collection<Player> getOnlinePlayers() {