diff --git a/src/main/java/ru/betterend/BetterEnd.java b/src/main/java/ru/betterend/BetterEnd.java index 628f2618..2cf9e6c9 100644 --- a/src/main/java/ru/betterend/BetterEnd.java +++ b/src/main/java/ru/betterend/BetterEnd.java @@ -32,6 +32,7 @@ import ru.betterend.registry.EndSounds; import ru.betterend.registry.EndStructures; import ru.betterend.registry.EndTags; import ru.betterend.util.BonemealUtil; +import ru.betterend.util.DataFixerUtil; import ru.betterend.util.Logger; import ru.betterend.world.generator.BetterEndBiomeSource; import ru.betterend.world.generator.GeneratorOptions; @@ -65,6 +66,7 @@ public class BetterEnd implements ModInitializer { Integrations.register(); BonemealUtil.init(); GeneratorOptions.init(); + DataFixerUtil.init(); if (hasGuideBook()) { GuideBookItem.register(); diff --git a/src/main/java/ru/betterend/mixin/common/ServerWorldMixin.java b/src/main/java/ru/betterend/mixin/common/ServerWorldMixin.java new file mode 100644 index 00000000..4d2a5df9 --- /dev/null +++ b/src/main/java/ru/betterend/mixin/common/ServerWorldMixin.java @@ -0,0 +1,69 @@ +package ru.betterend.mixin.common; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.Executor; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.metadata.ModMetadata; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.WorldGenerationProgressListener; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.World; +import net.minecraft.world.dimension.DimensionType; +import net.minecraft.world.gen.Spawner; +import net.minecraft.world.gen.chunk.ChunkGenerator; +import net.minecraft.world.level.ServerWorldProperties; +import net.minecraft.world.level.storage.LevelStorage; +import ru.betterend.BetterEnd; +import ru.betterend.util.DataFixerUtil; + +@Mixin(ServerWorld.class) +public class ServerWorldMixin { + @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() + "/betterend_data.nbt"); + ModMetadata meta = FabricLoader.getInstance().getModContainer(BetterEnd.MOD_ID).get().getMetadata(); + String version = meta.getVersion().toString(); + boolean fix = false; + if (version.equals("${version}")) { + version = "development"; + } + if (beData.exists()) { + CompoundTag root; + try { + root = NbtIo.read(beData); + } + catch (IOException e) { + BetterEnd.LOGGER.error("World data loading failed", e); + return; + } + String dataVersion = root.getString("version"); + fix = !dataVersion.equals(version); + } + else { + CompoundTag root = new CompoundTag(); + root.putString("version", version); + try { + NbtIo.write(root, beData); + } + catch (IOException e) { + BetterEnd.LOGGER.error("World data saving failed", e); + } + fix = true; + } + + if (fix) { + DataFixerUtil.fixData(beData.getParentFile()); + } + } +} diff --git a/src/main/java/ru/betterend/util/DataFixerUtil.java b/src/main/java/ru/betterend/util/DataFixerUtil.java new file mode 100644 index 00000000..99085b14 --- /dev/null +++ b/src/main/java/ru/betterend/util/DataFixerUtil.java @@ -0,0 +1,90 @@ +package ru.betterend.util; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.storage.RegionFile; + +public class DataFixerUtil { + private static final Map REPLACEMENT = Maps.newHashMap(); + + public static void init() { + addFix("minecraft:stone", "minecraft:glowstone"); + } + + public static void fixData(File dir) { + List regions = getAllRegions(dir, null); + regions.forEach((file) -> { + try { + System.out.println("Fixing " + file); + boolean[] changed = new boolean[1]; + RegionFile region = new RegionFile(file, file.getParentFile(), true); + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { + ChunkPos pos = new ChunkPos(x, z); + changed[0] = false; + if (region.hasChunk(pos)) { + DataInputStream input = region.getChunkInputStream(pos); + CompoundTag root = NbtIo.read(input); + input.close(); + ListTag sections = root.getCompound("Level").getList("Sections", 10); + sections.forEach((tag) -> { + ListTag palette = ((CompoundTag) tag).getList("Palette", 10); + palette.forEach((blockTag) -> { + CompoundTag blockTagCompound = ((CompoundTag) blockTag); + String name = blockTagCompound.getString("Name"); + String replace = REPLACEMENT.get(name); + if (replace != null) { + blockTagCompound.putString("Name", replace); + changed[0] = true; + } + }); + }); + if (changed[0]) { + System.out.println("Write!"); + DataOutputStream output = region.getChunkOutputStream(pos); + NbtIo.write(root, output); + output.close(); + } + } + } + } + region.close(); + } + catch (Exception e) { + e.printStackTrace(); + } + }); + } + + private static void addFix(String result, String... names) { + for (String name: names) { + REPLACEMENT.put(name, result); + } + } + + private static List getAllRegions(File dir, List list) { + if (list == null) { + list = Lists.newArrayList(); + } + for (File file: dir.listFiles()) { + if (file.isDirectory()) { + getAllRegions(file, list); + } + else if (file.isFile() && file.getName().endsWith(".mca")) { + list.add(file); + } + } + return list; + } +} diff --git a/src/main/resources/assets/betterend/models/item/end_stone_stalactite.json b/src/main/resources/assets/betterend/models/item/end_stone_stalactite.json new file mode 100644 index 00000000..0ea40081 --- /dev/null +++ b/src/main/resources/assets/betterend/models/item/end_stone_stalactite.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "betterend:item/end_stone_stalactite" + } +} diff --git a/src/main/resources/assets/betterend/models/item/end_stone_stalactite_cavemoss.json b/src/main/resources/assets/betterend/models/item/end_stone_stalactite_cavemoss.json new file mode 100644 index 00000000..642d4c00 --- /dev/null +++ b/src/main/resources/assets/betterend/models/item/end_stone_stalactite_cavemoss.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "betterend:item/end_stone_stalactite_cavemoss" + } +} diff --git a/src/main/resources/assets/betterend/textures/item/end_stone_stalactite.png b/src/main/resources/assets/betterend/textures/item/end_stone_stalactite.png new file mode 100644 index 00000000..994dd767 Binary files /dev/null and b/src/main/resources/assets/betterend/textures/item/end_stone_stalactite.png differ diff --git a/src/main/resources/assets/betterend/textures/item/end_stone_stalactite_cavemoss.png b/src/main/resources/assets/betterend/textures/item/end_stone_stalactite_cavemoss.png new file mode 100644 index 00000000..6d3cdf8b Binary files /dev/null and b/src/main/resources/assets/betterend/textures/item/end_stone_stalactite_cavemoss.png differ diff --git a/src/main/resources/betterend.mixins.common.json b/src/main/resources/betterend.mixins.common.json index 625615ac..82e04940 100644 --- a/src/main/resources/betterend.mixins.common.json +++ b/src/main/resources/betterend.mixins.common.json @@ -29,6 +29,7 @@ "LivingEntityMixin", "BoneMealItemMixin", "PlayerEntityMixin", + "ServerWorldMixin", "SlimeEntityMixin", "AnvilBlockMixin", "BrewingAccessor",