diff --git a/TerraForgedAPI/src/main/java/com/terraforged/api/chunk/column/DecoratorContext.java b/TerraForgedAPI/src/main/java/com/terraforged/api/chunk/column/DecoratorContext.java index c258a67..2181348 100644 --- a/TerraForgedAPI/src/main/java/com/terraforged/api/chunk/column/DecoratorContext.java +++ b/TerraForgedAPI/src/main/java/com/terraforged/api/chunk/column/DecoratorContext.java @@ -28,6 +28,7 @@ package com.terraforged.api.chunk.column; import com.terraforged.api.chunk.ChunkContext; import com.terraforged.core.cell.Cell; import com.terraforged.core.world.climate.Climate; +import com.terraforged.core.world.geology.DepthBuffer; import com.terraforged.core.world.heightmap.Levels; import com.terraforged.core.world.terrain.Terrain; import com.terraforged.core.world.terrain.Terrains; @@ -40,6 +41,7 @@ public class DecoratorContext extends ChunkContext { public final Levels levels; public final Climate climate; public final Terrains terrains; + public final DepthBuffer depthBuffer = new DepthBuffer(); public final BlockPos.Mutable pos = new BlockPos.Mutable(); public Biome biome; diff --git a/TerraForgedApp/src/main/java/com/terraforged/app/Cache.java b/TerraForgedApp/src/main/java/com/terraforged/app/Cache.java index 2ca735b..7a091c0 100644 --- a/TerraForgedApp/src/main/java/com/terraforged/app/Cache.java +++ b/TerraForgedApp/src/main/java/com/terraforged/app/Cache.java @@ -56,7 +56,7 @@ public class Cache { this.context = new GeneratorContext(terrain, settings); this.renderer = RegionGenerator.builder() .factory(new WorldGeneratorFactory(context)) - .pool(ThreadPool.getCommon()) + .pool(ThreadPool.getPool()) .size(3, 2) .build(); } diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/region/Region.java b/TerraForgedCore/src/main/java/com/terraforged/core/region/Region.java index af2595d..b4a145f 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/region/Region.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/region/Region.java @@ -30,15 +30,18 @@ import com.terraforged.core.cell.Extent; import com.terraforged.core.filter.Filterable; import com.terraforged.core.region.chunk.ChunkReader; import com.terraforged.core.region.chunk.ChunkWriter; +import com.terraforged.core.util.concurrent.Disposable; import com.terraforged.core.world.decorator.Decorator; import com.terraforged.core.world.heightmap.Heightmap; import com.terraforged.core.world.rivermap.RiverRegionList; import com.terraforged.core.world.terrain.Terrain; +import me.dags.noise.util.NoiseUtil; import java.util.Collection; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -public class Region implements Extent { +public class Region implements Extent, Disposable { private final int regionX; private final int regionZ; @@ -51,8 +54,15 @@ public class Region implements Extent { private final Size chunkSize; private final GenCell[] blocks; private final GenChunk[] chunks; + private final int disposableChunks; + private final Disposable.Listener disposalListener; + private final AtomicInteger disposedChunks = new AtomicInteger(); public Region(int regionX, int regionZ, int size, int borderChunks) { + this(regionX, regionZ, size, borderChunks, region -> {}); + } + + public Region(int regionX, int regionZ, int size, int borderChunks, Disposable.Listener disposalListener) { this.regionX = regionX; this.regionZ = regionZ; this.chunkX = regionX << size; @@ -62,10 +72,25 @@ public class Region implements Extent { this.border = borderChunks; this.chunkSize = Size.chunks(size, borderChunks); this.blockSize = Size.blocks(size, borderChunks); + this.disposalListener = disposalListener; + this.disposableChunks = chunkSize.size * chunkSize.size; this.blocks = new GenCell[blockSize.total * blockSize.total]; this.chunks = new GenChunk[chunkSize.total * chunkSize.total]; } + @Override + public void dispose() { + int disposed = disposedChunks.incrementAndGet(); + if (disposed < disposableChunks) { + return; + } + disposalListener.onDispose(this); + } + + public long getRegionId() { + return NoiseUtil.seed(getRegionX(), getRegionZ()); + } + public int getRegionX() { return regionX; } @@ -333,6 +358,11 @@ public class Region implements Extent { return blockZ; } + @Override + public void dispose() { + Region.this.dispose(); + } + @Override public Cell getCell(int blockX, int blockZ) { int relX = regionBlockX + (blockX & 15); diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionCache.java b/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionCache.java index e309965..4a2a3e5 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionCache.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionCache.java @@ -26,6 +26,7 @@ package com.terraforged.core.region; import com.terraforged.core.region.chunk.ChunkReader; +import com.terraforged.core.util.concurrent.Disposable; import com.terraforged.core.util.concurrent.cache.Cache; import com.terraforged.core.util.concurrent.cache.CacheEntry; import com.terraforged.core.world.heightmap.RegionExtent; @@ -34,32 +35,37 @@ import me.dags.noise.util.NoiseUtil; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -public class RegionCache implements RegionExtent { +public class RegionCache implements RegionExtent, Disposable.Listener { private final boolean queuing; - private final RegionGenerator renderer; + private final RegionGenerator generator; private final Cache> cache; - public RegionCache(boolean queueNeighbours, RegionGenerator renderer) { - this.renderer = renderer; + public RegionCache(boolean queueNeighbours, RegionGenerator generator) { this.queuing = queueNeighbours; - this.cache = new Cache<>(80, 60, TimeUnit.SECONDS); + this.generator = new RegionGenerator(generator, this); + this.cache = new Cache<>(60, 30, TimeUnit.SECONDS); + } + + @Override + public void onDispose(Region region) { + cache.remove(region.getRegionId()); } @Override public int chunkToRegion(int coord) { - return renderer.chunkToRegion(coord); + return generator.chunkToRegion(coord); } @Override public CompletableFuture getRegionAsync(int regionX, int regionZ) { - return renderer.generate(regionX, regionZ); + return generator.generate(regionX, regionZ); } @Override public ChunkReader getChunk(int chunkX, int chunkZ) { - int regionX = renderer.chunkToRegion(chunkX); - int regionZ = renderer.chunkToRegion(chunkZ); + int regionX = generator.chunkToRegion(chunkX); + int regionZ = generator.chunkToRegion(chunkZ); Region region = getRegion(regionX, regionZ); return region.getChunk(chunkX, chunkZ); } @@ -77,7 +83,7 @@ public class RegionCache implements RegionExtent { public CacheEntry queueRegion(int regionX, int regionZ) { long id = NoiseUtil.seed(regionX, regionZ); - return cache.computeIfAbsent(id, l -> renderer.generateCached(regionX, regionZ)); + return cache.computeIfAbsent(id, l -> generator.generateCached(regionX, regionZ)); } private void queueNeighbours(int regionX, int regionZ) { diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionFactory.java b/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionFactory.java index 23eca7c..83c927e 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionFactory.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionFactory.java @@ -1,6 +1,8 @@ package com.terraforged.core.region; +import com.terraforged.core.util.concurrent.Disposable; + public interface RegionFactory { - Region create(int regionX, int regionZ, int size, int borderChunks); + Region create(int regionX, int regionZ, int size, int borderChunks, Disposable.Listener listener); } diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionGenerator.java b/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionGenerator.java index 7de02f6..dc8407c 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionGenerator.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionGenerator.java @@ -26,6 +26,7 @@ package com.terraforged.core.region; import com.terraforged.core.region.legacy.LegacyRegion; +import com.terraforged.core.util.concurrent.Disposable; import com.terraforged.core.util.concurrent.ThreadPool; import com.terraforged.core.util.concurrent.cache.CacheEntry; import com.terraforged.core.world.WorldGenerator; @@ -42,6 +43,7 @@ public class RegionGenerator implements RegionExtent { private final RegionFactory regions; private final ThreadPool threadPool; private final ThreadLocal genPool; + private final Disposable.Listener disposalListener; private RegionGenerator(Builder builder) { this.factor = builder.factor; @@ -49,6 +51,16 @@ public class RegionGenerator implements RegionExtent { this.threadPool = builder.threadPool; this.regions = builder.regionFactory; this.genPool = ThreadLocal.withInitial(builder.factory); + this.disposalListener = region -> {}; + } + + protected RegionGenerator(RegionGenerator from, Disposable.Listener listener) { + this.factor = from.factor; + this.border = from.border; + this.threadPool = from.threadPool; + this.regions = from.regions; + this.genPool = from.genPool; + this.disposalListener = listener; } public RegionCache toCache() { @@ -88,7 +100,7 @@ public class RegionGenerator implements RegionExtent { public Region generateRegion(int regionX, int regionZ) { WorldGenerator generator = genPool.get(); - Region region = regions.create(regionX, regionZ, factor, border); + Region region = regions.create(regionX, regionZ, factor, border, disposalListener); RiverRegionList rivers = generator.getHeightmap().getRiverMap().getRivers(region); region.generateBase(generator.getHeightmap()); region.generateRivers(generator.getHeightmap(), rivers); @@ -103,7 +115,7 @@ public class RegionGenerator implements RegionExtent { public Region generateRegion(float centerX, float centerZ, float zoom, boolean filter) { WorldGenerator generator = genPool.get(); - Region region = regions.create(0, 0, factor, border); + Region region = regions.create(0, 0, factor, border, disposalListener); region.generateZoom(generator.getHeightmap(), centerX, centerZ, zoom); postProcess(region, generator, centerX, centerZ, zoom, filter); return region; diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/region/chunk/ChunkHolder.java b/TerraForgedCore/src/main/java/com/terraforged/core/region/chunk/ChunkHolder.java index 26d70ae..8d417d7 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/region/chunk/ChunkHolder.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/region/chunk/ChunkHolder.java @@ -26,8 +26,9 @@ package com.terraforged.core.region.chunk; import com.terraforged.core.cell.Extent; +import com.terraforged.core.util.concurrent.Disposable; -public interface ChunkHolder extends Extent { +public interface ChunkHolder extends Extent, Disposable { int getChunkX(); diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/region/legacy/LegacyRegion.java b/TerraForgedCore/src/main/java/com/terraforged/core/region/legacy/LegacyRegion.java index e681dc2..34d77dc 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/region/legacy/LegacyRegion.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/region/legacy/LegacyRegion.java @@ -1,6 +1,7 @@ package com.terraforged.core.region.legacy; import com.terraforged.core.region.Region; +import com.terraforged.core.util.concurrent.Disposable; /** * This is here to provide compatibility for versions 0.0.2 and below which contained a @@ -9,8 +10,8 @@ import com.terraforged.core.region.Region; */ public class LegacyRegion extends Region { - public LegacyRegion(int regionX, int regionZ, int size, int borderChunks) { - super(regionX, regionZ, size, borderChunks); + public LegacyRegion(int regionX, int regionZ, int size, int borderChunks, Disposable.Listener listener) { + super(regionX, regionZ, size, borderChunks, listener); } /** diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/Disposable.java b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/Disposable.java new file mode 100644 index 0000000..3907b7d --- /dev/null +++ b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/Disposable.java @@ -0,0 +1,11 @@ +package com.terraforged.core.util.concurrent; + +public interface Disposable { + + void dispose(); + + interface Listener { + + void onDispose(T t); + } +} diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/ThreadPool.java b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/ThreadPool.java index f732624..afc6f2b 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/ThreadPool.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/ThreadPool.java @@ -33,41 +33,19 @@ import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Future; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; public class ThreadPool implements Executor { - public static final int DEFAULT_POOL_SIZE = defaultPoolSize(); + private static final ThreadPool instance = new ThreadPool("TerraForged", defaultPoolSize()); - private static final Object lock = new Object(); - private static final ForkJoinPool defaultPool = ThreadPool.createPool(2, "TFCore"); - - private static ThreadPool instance = new ThreadPool(defaultPoolSize()); - - private final int poolSize; private final ExecutorService service; - private ThreadPool() { - this.service = defaultPool; - this.poolSize = -1; - } - - public ThreadPool(int size) { - this.poolSize = size; - this.service = ThreadPool.createExecutor(size, "TerraForged"); - } - - private ThreadPool(ForkJoinPool pool) { - this.service = pool; - this.poolSize = pool.getPoolSize(); + private ThreadPool(String name, int size) { + this.service = ThreadPool.createPool(size, name); } public void shutdown() { - if (poolSize > 0) { - service.shutdown(); - } + service.shutdown(); } @Override @@ -87,51 +65,16 @@ public class ThreadPool implements Executor { return new AsyncBatcher(service, size); } - public static ThreadPool getCurrent() { - synchronized (lock) { - return instance; - } + public static ThreadPool getPool() { + return instance; } - public static ThreadPool getFixed(int size) { - synchronized (lock) { - if (instance.poolSize != size) { - instance.shutdown(); - instance = new ThreadPool(size); - } - return instance; - } - } - - public static ThreadPool getFixed() { - synchronized (lock) { - if (instance.poolSize == -1) { - instance = new ThreadPool(ThreadPool.DEFAULT_POOL_SIZE); - } - return instance; - } - } - - public static ThreadPool getCommon() { - synchronized (lock) { - if (instance.poolSize != -1) { - instance.shutdown(); - instance = new ThreadPool(); - } - return instance; - } - } - - public static ForkJoinPool getDefaultPool() { - return defaultPool; + public static ThreadPool create(int size) { + return new ThreadPool("Pool", Math.max(1, size)); } public static void shutdownCurrent() { - synchronized (lock) { - instance.shutdown(); - // replace with the common pool - instance = new ThreadPool(); - } + instance.shutdown(); } private static int defaultPoolSize() { @@ -139,19 +82,6 @@ public class ThreadPool implements Executor { return Math.max(1, (int) ((threads / 3F) * 2F)); } - public static ExecutorService createExecutor(int size, String name) { - ThreadPoolExecutor service = new ThreadPoolExecutor( - size, - size, - 30, - TimeUnit.SECONDS, - new LinkedBlockingQueue<>(), - new WorkerFactory(name) - ); - service.allowCoreThreadTimeOut(true); - return service; - } - public static ForkJoinPool createPool(int size, String name) { return new ForkJoinPool(size, new WorkerFactory.ForkJoin(name), null, true); } diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/Cache.java b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/Cache.java index 07b9180..c6a5ba1 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/Cache.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/Cache.java @@ -4,7 +4,6 @@ import com.terraforged.core.util.concurrent.ThreadPool; import java.util.concurrent.TimeUnit; import java.util.function.LongFunction; -import java.util.function.Supplier; public class Cache implements Runnable { @@ -20,8 +19,8 @@ public class Cache implements Runnable { this.map = new SynchronizedLongMap<>(100); } - public V computeIfAbsent(long key, Supplier supplier) { - return computeIfAbsent(key, o -> supplier.get()); + public void remove(long key) { + map.remove(key); } public V computeIfAbsent(long key, LongFunction func) { @@ -34,7 +33,7 @@ public class Cache implements Runnable { long now = System.currentTimeMillis(); if (now - timestamp > intervalMS) { timestamp = now; - ThreadPool.getDefaultPool().execute(this); + ThreadPool.getPool().execute(this); } } diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/geology/DepthBuffer.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/geology/DepthBuffer.java new file mode 100644 index 0000000..3188790 --- /dev/null +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/geology/DepthBuffer.java @@ -0,0 +1,31 @@ +package com.terraforged.core.world.geology; + +public class DepthBuffer { + + private float sum; + private float[] buffer; + + public void init(int size) { + sum = 0F; + if (buffer == null || buffer.length < size) { + buffer = new float[size]; + } + } + + public float getSum() { + return sum; + } + + public float get(int index) { + return buffer[index]; + } + + public float getDepth(int index) { + return buffer[index] / sum; + } + + public void set(int index, float value) { + sum += value; + buffer[index] = value; + } +} diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/geology/Strata.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/geology/Strata.java index ee646b2..436d3f8 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/geology/Strata.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/geology/Strata.java @@ -44,14 +44,19 @@ public class Strata { this.heightMod = heightMod; } - public boolean downwards(final int x, final int y, final int z, Stratum.Visitor visitor) { - DepthBuffer depthBuffer = new DepthBuffer(strata, x, z); + public boolean downwards(final int x, final int y, final int z, final Stratum.Visitor visitor) { + DepthBuffer buffer = new DepthBuffer(); + initBuffer(buffer, x, z); + return downwards(x, y, z, buffer, visitor); + } + + public boolean downwards(final int x, final int y, final int z, final DepthBuffer buffer, Stratum.Visitor visitor) { + initBuffer(buffer, x, z); int py = y; T last = null; - float sum = depthBuffer.sum; for (int i = 0; i < strata.size(); i++) { - float depth = depthBuffer.buffer[i] / sum; + float depth = buffer.get(i); int height = NoiseUtil.round(depth * y); T value = strata.get(i).getValue(); last = value; @@ -76,45 +81,15 @@ public class Strata { return true; } - public boolean upwards(int x, int y, int z, Stratum.Visitor visitor) { - DepthBuffer depthBuffer = new DepthBuffer(strata, x, z); - int py = 0; - float sum = depthBuffer.sum; - for (int i = strata.size() - 1; i > 0; i--) { - float depth = depthBuffer.buffer[i] / sum; - int height = NoiseUtil.round(depth * y); - T value = strata.get(i).getValue(); - for (int dy = 0; dy < height; dy++) { - boolean cont = visitor.visit(py, value); - if (!cont) { - return false; - } - if (++py > y) { - return false; - } - } - } - return true; - } - private int getYOffset(int x, int z) { return (int) (64 * heightMod.getValue(x, z)); } - private static class DepthBuffer { - - private final float sum; - private final float[] buffer; - - private DepthBuffer(List> strata, int x, int z) { - buffer = new float[strata.size()]; - float sum = 0F; - for (int i = 0; i < strata.size(); i++) { - float depth = strata.get(i).getDepth(x, z); - sum += depth; - buffer[i] = depth; - } - this.sum = sum; + private void initBuffer(DepthBuffer buffer, int x, int z) { + buffer.init(strata.size()); + for (int i = 0; i < strata.size(); i++) { + float depth = strata.get(i).getDepth(x, z); + buffer.set(i, depth); } } diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverMap.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverMap.java index 6238623..80f90f1 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverMap.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverMap.java @@ -151,6 +151,6 @@ public class RiverMap { } private CacheEntry generateRegion(int rx, int rz) { - return CacheEntry.supplyAsync(() -> new RiverRegion(rx, rz, heightmap, context, riverMapConfig), ThreadPool.getDefaultPool()); + return CacheEntry.supplyAsync(() -> new RiverRegion(rx, rz, heightmap, context, riverMapConfig), ThreadPool.getPool()); } } diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/FastChunk.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/FastChunk.java index e242c11..64a688b 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/FastChunk.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/FastChunk.java @@ -55,7 +55,7 @@ public class FastChunk implements ChunkDelegate { section.unlock(); return replaced; } - return Blocks.AIR.getDefaultState(); + return Blocks.VOID_AIR.getDefaultState(); } public void setBiomes(BiomeContainer biomes) { diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java index c1413b6..6f1efad 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java @@ -216,6 +216,9 @@ public class TerraChunkGenerator extends ObfHelperChunkGenerator { + geology.getGeology(context.biome).getStrata(x, z).downwards(x, top, z, context.depthBuffer, (py, state) -> { context.pos.setPos(x, py, z); buffer.getDelegate().setBlockState(context.pos, state, false); return true; diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/Preview.java b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/Preview.java index 29599f8..906b567 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/Preview.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/gui/preview/Preview.java @@ -165,7 +165,7 @@ public class Preview extends Button { RegionGenerator renderer = RegionGenerator.builder() .factory(new WorldGeneratorFactory(context)) - .pool(ThreadPool.getCommon()) + .pool(ThreadPool.getPool()) .size(FACTOR, 0) .build(); diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/server/ServerPropertiesFix.java b/TerraForgedMod/src/main/java/com/terraforged/mod/server/ServerPropertiesFix.java new file mode 100644 index 0000000..0bae446 --- /dev/null +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/server/ServerPropertiesFix.java @@ -0,0 +1,51 @@ +package com.terraforged.mod.server; + +import net.minecraft.server.ServerPropertiesProvider; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.dedicated.PropertyManager; +import net.minecraft.server.dedicated.ServerProperties; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLDedicatedServerSetupEvent; + +import java.lang.reflect.Field; +import java.util.Optional; +import java.util.Properties; + +@Mod.EventBusSubscriber(value = Dist.DEDICATED_SERVER, bus = Mod.EventBusSubscriber.Bus.MOD) +public class ServerPropertiesFix { + + @SubscribeEvent + public static void setup(FMLDedicatedServerSetupEvent event) { + DedicatedServer server = event.getServerSupplier().get(); + get(server, DedicatedServer.class, ServerPropertiesProvider.class).ifPresent(provider -> provider.func_219033_a(props -> { + return get(props, PropertyManager.class, Properties.class).flatMap(properties -> { + String world = properties.getProperty("mod-level-type"); + if (world != null && !world.isEmpty()) { + properties.setProperty("level-type", world); + return Optional.of(new ServerProperties(properties)); + } + return Optional.empty(); + }).orElse(props); + })); + } + + private static Optional get(Object owner, Class target, Class type) { + for (Field field : target.getDeclaredFields()) { + if (field.getType() == type) { + try { + field.setAccessible(true); + Object value = field.get(owner); + if (value != null) { + return Optional.of(type.cast(value)); + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + break; + } + } + } + return Optional.empty(); + } +}