diff --git a/src/main/java/ru/betterend/mixin/common/ServerWorldMixin.java b/src/main/java/ru/betterend/mixin/common/ServerWorldMixin.java index 0aab95ac..a48eca97 100644 --- a/src/main/java/ru/betterend/mixin/common/ServerWorldMixin.java +++ b/src/main/java/ru/betterend/mixin/common/ServerWorldMixin.java @@ -2,6 +2,7 @@ package ru.betterend.mixin.common; import java.io.File; import java.util.List; +import java.util.Locale; import java.util.concurrent.Executor; import org.spongepowered.asm.mixin.Mixin; @@ -32,21 +33,39 @@ import ru.betterend.world.generator.GeneratorOptions; @Mixin(ServerWorld.class) public class ServerWorldMixin { + private static final int DEV_VERSION = be_getVersionInt("63.63.63"); + private static final int FIX_VERSION = DEV_VERSION; + private static String lastWorld = null; + @Inject(method = "*", at = @At("TAIL")) private void be_onServerWorldInit(MinecraftServer server, Executor workerExecutor, LevelStorage.Session session, ServerWorldProperties properties, RegistryKey registryKey, DimensionType dimensionType, WorldGenerationProgressListener worldGenerationProgressListener, ChunkGenerator chunkGenerator, boolean debugWorld, long l, List list, boolean bl, CallbackInfo info) { - File beData = new File(FabricLoader.getInstance().getGameDir().getParent().toString(), "saves/" + properties.getLevelName() + "/data/betterend_data.nbt"); + if (lastWorld != null && lastWorld.equals(session.getDirectoryName())) { + return; + } + + lastWorld = session.getDirectoryName(); + + @SuppressWarnings("resource") + ServerWorld world = (ServerWorld) (Object) this; + File dir = session.getWorldDirectory(world.getRegistryKey()); + if (!new File(dir, "level.dat").exists()) { + dir = dir.getParentFile(); + } + File data = new File(dir, "data/betterend_data.nbt"); + ModMetadata meta = FabricLoader.getInstance().getModContainer(BetterEnd.MOD_ID).get().getMetadata(); - String version = BetterEnd.isDevEnvironment() ? "development" : meta.getVersion().toString(); + int version = BetterEnd.isDevEnvironment() ? DEV_VERSION : be_getVersionInt(meta.getVersion().toString()); - WorldDataUtil.load(beData); + WorldDataUtil.load(data); CompoundTag root = WorldDataUtil.getRootTag(); - String dataVersion = root.getString("version"); + int dataVersion = be_getVersionInt(root.getString("version")); GeneratorOptions.setPortalPos(NbtHelper.toBlockPos(root.getCompound("portal"))); - boolean fix = !dataVersion.equals(version); - if (fix) { - DataFixerUtil.fixData(beData.getParentFile()); - root.putString("version", version); + if (dataVersion < version) { + if (version < FIX_VERSION) { + DataFixerUtil.fixData(data.getParentFile()); + } + root.putString("version", be_getVersionString(version)); WorldDataUtil.saveFile(); } } @@ -60,4 +79,24 @@ public class ServerWorldMixin { } } } + + private static int be_getVersionInt(String version) { + if (version.isEmpty()) { + return 0; + } + try { + String[] values = version.split("\\."); + return Integer.parseInt(values[0]) << 12 | Integer.parseInt(values[1]) << 6 | Integer.parseInt(values[1]); + } + catch (Exception e) { + return 0; + } + } + + private static String be_getVersionString(int version) { + int a = (version >> 12) & 63; + int b = (version >> 6) & 63; + int c = version & 63; + return String.format(Locale.ROOT, "%d.%d.%d", a, b, c); + } } diff --git a/src/main/java/ru/betterend/registry/EndFeatures.java b/src/main/java/ru/betterend/registry/EndFeatures.java index ca213e77..5adb90c9 100644 --- a/src/main/java/ru/betterend/registry/EndFeatures.java +++ b/src/main/java/ru/betterend/registry/EndFeatures.java @@ -59,6 +59,7 @@ import ru.betterend.world.features.terrain.SulphuricCaveFeature; import ru.betterend.world.features.terrain.SulphuricLakeFeature; import ru.betterend.world.features.terrain.SurfaceVentFeature; import ru.betterend.world.features.terrain.caves.RoundCaveFeature; +import ru.betterend.world.features.terrain.caves.TunelCaveFeature; import ru.betterend.world.features.trees.DragonTreeFeature; import ru.betterend.world.features.trees.GiganticAmaranitaFeature; import ru.betterend.world.features.trees.HelixTreeFeature; @@ -198,6 +199,7 @@ public class EndFeatures { public static final EndFeature OBSIDIAN_PILLAR_BASEMENT = EndFeature.makeChansedFeature("obsidian_pillar_basement", new ObsidianPillarBasementFeature(), 8); public static final EndFeature OBSIDIAN_BOULDER = EndFeature.makeChansedFeature("obsidian_boulder", new ObsidianBoulderFeature(), 10); public static final EndFeature FALLEN_PILLAR = EndFeature.makeChansedFeature("fallen_pillar", new FallenPillarFeature(), 20); + public static final EndFeature TUNEL_CAVE = EndFeature.makeRawGenFeature("tunel_cave", new TunelCaveFeature(), 2); // Ores // public static final EndFeature THALLASIUM_ORE = EndFeature.makeOreFeature("thallasium_ore", EndBlocks.THALLASIUM.ore, 12, 6, 0, 16, 128); @@ -254,6 +256,7 @@ public class EndFeatures { if (EndBiomes.getBiome(id).hasCaves()) { addFeature(ROUND_CAVE, features); + addFeature(TUNEL_CAVE, features); } EndBiome endBiome = EndBiomes.getBiome(id); @@ -271,6 +274,7 @@ public class EndFeatures { if (def.hasCaves()) { def.addFeature(ROUND_CAVE); + def.addFeature(TUNEL_CAVE); } } diff --git a/src/main/java/ru/betterend/util/SplineHelper.java b/src/main/java/ru/betterend/util/SplineHelper.java index b15ac46b..b5e15f59 100644 --- a/src/main/java/ru/betterend/util/SplineHelper.java +++ b/src/main/java/ru/betterend/util/SplineHelper.java @@ -33,6 +33,29 @@ public class SplineHelper { return spline; } + public static List smoothSpline(List spline, int segmentPoints) { + List result = Lists.newArrayList(); + Vector3f start = spline.get(0); + for (int i = 1; i < spline.size(); i++) { + Vector3f end = spline.get(i); + for (int j = 0; j < segmentPoints; j++) { + float delta = (float) j / segmentPoints; + delta = 0.5F - 0.5F * MathHelper.cos(delta * 3.14159F); + result.add(lerp(start, end, delta)); + } + start = end; + } + result.add(start); + return result; + } + + private static Vector3f lerp(Vector3f start, Vector3f end, float delta) { + float x = MathHelper.lerp(delta, start.getX(), end.getX()); + float y = MathHelper.lerp(delta, start.getY(), end.getY()); + float z = MathHelper.lerp(delta, start.getZ(), end.getZ()); + return new Vector3f(x, y, z); + } + public static void offsetParts(List spline, Random random, float dx, float dy, float dz) { int count = spline.size(); for (int i = 1; i < count; i++) { diff --git a/src/main/java/ru/betterend/util/WorldDataUtil.java b/src/main/java/ru/betterend/util/WorldDataUtil.java index cb6b1816..e881ba09 100644 --- a/src/main/java/ru/betterend/util/WorldDataUtil.java +++ b/src/main/java/ru/betterend/util/WorldDataUtil.java @@ -15,7 +15,7 @@ public class WorldDataUtil { saveFile = file; if (file.exists()) { try { - root = NbtIo.read(file); + root = NbtIo.readCompressed(file); } catch (IOException e) { BetterEnd.LOGGER.error("World data loading failed", e); @@ -48,7 +48,7 @@ public class WorldDataUtil { public static void saveFile() { try { - NbtIo.write(root, saveFile); + NbtIo.writeCompressed(root, saveFile); } catch (IOException e) { BetterEnd.LOGGER.error("World data saving failed", e); diff --git a/src/main/java/ru/betterend/util/sdf/SDF.java b/src/main/java/ru/betterend/util/sdf/SDF.java index c5db2106..74aff385 100644 --- a/src/main/java/ru/betterend/util/sdf/SDF.java +++ b/src/main/java/ru/betterend/util/sdf/SDF.java @@ -273,4 +273,38 @@ public abstract class SDF { world.setBlock(info.getPos(), info.getState()); }); } + + public Set getPositions(ServerWorldAccess world, BlockPos start) { + Set blocks = Sets.newHashSet(); + Set ends = Sets.newHashSet(); + Set add = Sets.newHashSet(); + ends.add(new BlockPos(0, 0, 0)); + boolean run = true; + + Mutable bPos = new Mutable(); + + while (run) { + for (BlockPos center: ends) { + for (Direction dir: Direction.values()) { + bPos.set(center).move(dir); + BlockPos wpos = bPos.add(start); + BlockState state = world.getBlockState(wpos); + if (!blocks.contains(wpos) && canReplace.apply(state)) { + if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) { + add.add(bPos.toImmutable()); + } + } + } + } + + ends.forEach((end) -> blocks.add(end.add(start))); + ends.clear(); + ends.addAll(add); + add.clear(); + + run &= !ends.isEmpty(); + } + + return blocks; + } } diff --git a/src/main/java/ru/betterend/world/features/terrain/caves/TunelCaveFeature.java b/src/main/java/ru/betterend/world/features/terrain/caves/TunelCaveFeature.java new file mode 100644 index 00000000..72826f16 --- /dev/null +++ b/src/main/java/ru/betterend/world/features/terrain/caves/TunelCaveFeature.java @@ -0,0 +1,54 @@ +package ru.betterend.world.features.terrain.caves; + +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.function.Function; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.Material; +import net.minecraft.client.util.math.Vector3f; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.StructureWorldAccess; +import ru.betterend.registry.EndTags; +import ru.betterend.util.BlocksHelper; +import ru.betterend.util.SplineHelper; +import ru.betterend.util.sdf.SDF; + +public class TunelCaveFeature extends EndCaveFeature { + private static final Function REPLACE; + + @Override + protected Set generate(StructureWorldAccess world, BlockPos center, int radius, Random random) { + //OpenSimplexNoise noise = new OpenSimplexNoise(MHelper.getSeed(534, center.getX(), center.getZ())); + float rad = radius * 0.15F; + int min = MathHelper.ceil(rad) - 15; + int max = 31 - MathHelper.floor(rad); + List spline = SplineHelper.makeSpline(0, 0, 0, 0, 0, 0, radius / 3); + spline = SplineHelper.smoothSpline(spline, 5); + SplineHelper.offsetParts(spline, random, 5, radius * 0.4F, 5); + for (Vector3f vec: spline) { + float x = MathHelper.clamp(vec.getX(), min, max); + float y = MathHelper.clamp(vec.getY(), -radius, radius); + float z = MathHelper.clamp(vec.getZ(), min, max); + vec.set(x, y, z); + } + SDF sdf = SplineHelper.buildSDF(spline, rad, rad, (vec) -> Blocks.AIR.getDefaultState()); + Set positions = sdf.setReplaceFunction(REPLACE).getPositions(world, center); + for (BlockPos p: positions) { + BlocksHelper.setWithoutUpdate(world, p, CAVE_AIR); + } + return positions; + } + + static { + REPLACE = (state) -> { + return state.isIn(EndTags.GEN_TERRAIN) + || state.getMaterial().isReplaceable() + || state.getMaterial().equals(Material.PLANT) + || state.getMaterial().equals(Material.LEAVES); + }; + } +}