Sfoglia il codice sorgente

Add Chunk data save and load events, closes #89 (#92)

* Created Chunk save and load event. Closes #89

* Fixed missing licence

* Update common/src/main/java/me/shedaniel/architectury/event/events/ChunkEvent.java

* Update common/src/main/java/me/shedaniel/architectury/event/events/ChunkEvent.java

* Update common/src/main/java/me/shedaniel/architectury/event/events/ChunkEvent.java

* Supply ServerLevel in ChunkEvent.LOAD, style cleanup

Signed-off-by: shedaniel <daniel@shedaniel.me>

* Add "Data" suffix to Chunk IO Events and mark level as nullable for load

* Update common/src/main/java/me/shedaniel/architectury/event/events/ChunkEvent.java

* Bump to 1.16

Co-authored-by: shedaniel <daniel@shedaniel.me>
Co-authored-by: Max <maxh2709@gmail.com>
canitzp 4 anni fa
parent
commit
582ededddd

+ 65 - 0
common/src/main/java/me/shedaniel/architectury/event/events/ChunkEvent.java

@@ -0,0 +1,65 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 architectury
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.event.events;
+
+import me.shedaniel.architectury.event.Event;
+import me.shedaniel.architectury.event.EventFactory;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.ProtoChunk;
+import org.jetbrains.annotations.Nullable;
+
+public interface ChunkEvent {
+    /**
+     * @see SaveData#save(ChunkAccess, ServerLevel, CompoundTag)
+     */
+    Event<SaveData> SAVE_DATA = EventFactory.createLoop();
+    /**
+     * @see LoadData#load(ChunkAccess, ServerLevel, CompoundTag)
+     */
+    Event<LoadData> LOAD_DATA = EventFactory.createLoop();
+    
+    interface SaveData {
+        /**
+         * Invoked when a chunk's data is saved, just before the data is written.
+         * Add your own data to the {@link CompoundTag} parameter to get your data saved as well.
+         * Equivalent to Forge's {@code ChunkDataEvent.Save}.
+         *
+         * @param chunk The chunk that is saved.
+         * @param level The level the chunk is in.
+         * @param nbt   The chunk data that is written to the save file.
+         */
+        void save(ChunkAccess chunk, ServerLevel level, CompoundTag nbt);
+    }
+    
+    interface LoadData {
+        /**
+         * Invoked just before a chunk's data is fully read.
+         * You can read out your own data from the {@link CompoundTag} parameter, when you have saved one before.
+         * Equivalent to Forge's {@code ChunkDataEvent.Load}.
+         *
+         * @param chunk The chunk that is loaded.
+         * @param level The level the chunk is in, may be {@code null}.
+         * @param nbt   The chunk data that was read from the save file.
+         */
+        void load(ChunkAccess chunk, @Nullable ServerLevel level, CompoundTag nbt);
+    }
+}

+ 51 - 0
fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinChunkMap.java

@@ -0,0 +1,51 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 architectury
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.mixin.fabric;
+
+import me.shedaniel.architectury.event.events.ChunkEvent;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.server.level.ChunkMap;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.ChunkStatus;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
+
+@Mixin(ChunkMap.class)
+public class MixinChunkMap {
+    @Shadow
+    @Final
+    private ServerLevel level;
+    
+    @Inject(
+            method = "save",
+            at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ChunkMap;write(Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/nbt/CompoundTag;)V", ordinal = 0),
+            locals = LocalCapture.CAPTURE_FAILHARD
+    )
+    private void save(ChunkAccess chunkAccess, CallbackInfoReturnable<Boolean> cir, ChunkPos pos, ChunkStatus chunkStatus, CompoundTag nbt) {
+        ChunkEvent.SAVE_DATA.invoker().save(chunkAccess, this.level, nbt);
+    }
+}

+ 49 - 0
fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinChunkSerializer.java

@@ -0,0 +1,49 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 architectury
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.mixin.fabric;
+
+import me.shedaniel.architectury.event.events.ChunkEvent;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.ListTag;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.entity.ai.village.poi.PoiManager;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.biome.BiomeSource;
+import net.minecraft.world.level.chunk.*;
+import net.minecraft.world.level.chunk.storage.ChunkSerializer;
+import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
+import net.minecraft.world.level.lighting.LevelLightEngine;
+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.CallbackInfoReturnable;
+import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
+
+@Mixin(ChunkSerializer.class)
+public class MixinChunkSerializer {
+    @Inject(method = "read", at = @At("RETURN"), locals = LocalCapture.CAPTURE_FAILHARD)
+    private static void load(ServerLevel serverLevel, StructureManager structureManager, PoiManager poiManager, ChunkPos chunkPos, CompoundTag compoundTag,
+                             CallbackInfoReturnable<ProtoChunk> cir, ChunkGenerator chunkGenerator, BiomeSource biomeSource, CompoundTag compoundTagLevelData,
+                             ChunkBiomeContainer chunkBiomeContainer, UpgradeData upgradeData, ProtoTickList<?> protoTickList, ProtoTickList<?> protoTickList2,
+                             boolean bl, ListTag listTag, int i, LevelChunkSection[] levelChunkSections, boolean bl2, ChunkSource chunkSource,
+                             LevelLightEngine levelLightEngine, long l, ChunkStatus.ChunkType chunkType, ChunkAccess chunk) {
+        ChunkEvent.LOAD_DATA.invoker().load(chunk, serverLevel, compoundTag);
+    }
+}

+ 2 - 0
fabric/src/main/resources/architectury.mixins.json

@@ -26,6 +26,8 @@
     "MixinBlockItem",
     "MixinBucketItem",
     "MixinCatSpawner",
+    "MixinChunkMap",
+    "MixinChunkSerializer",
     "MixinCollisionContext",
     "MixinCommands",
     "MixinDedicatedServer",

+ 24 - 0
forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplCommon.java

@@ -31,6 +31,7 @@ import net.minecraft.world.InteractionResult;
 import net.minecraft.world.InteractionResultHolder;
 import net.minecraft.world.item.ItemStack;
 import net.minecraft.world.level.Level;
+import net.minecraft.world.level.LevelAccessor;
 import net.minecraftforge.event.CommandEvent;
 import net.minecraftforge.event.RegisterCommandsEvent;
 import net.minecraftforge.event.ServerChatEvent;
@@ -49,6 +50,7 @@ import net.minecraftforge.event.entity.player.PlayerEvent.*;
 import net.minecraftforge.event.world.BlockEvent.BreakEvent;
 import net.minecraftforge.event.world.BlockEvent.EntityPlaceEvent;
 import net.minecraftforge.event.world.BlockEvent.FarmlandTrampleEvent;
+import net.minecraftforge.event.world.ChunkDataEvent;
 import net.minecraftforge.event.world.ExplosionEvent.Detonate;
 import net.minecraftforge.event.world.ExplosionEvent.Start;
 import net.minecraftforge.event.world.WorldEvent;
@@ -371,6 +373,28 @@ public class EventHandlerImplCommon {
         }
     }
     
+    @SubscribeEvent(priority = EventPriority.HIGH)
+    public static void event(ChunkDataEvent.Save event) {
+        if (event.getWorld() instanceof ServerLevel) {
+            ChunkEvent.SAVE_DATA.invoker().save(event.getChunk(), (ServerLevel) event.getWorld(), event.getData());
+        }
+    }
+    
+    @SubscribeEvent(priority = EventPriority.HIGH)
+    public static void event(ChunkDataEvent.Load event) {
+        LevelAccessor level = event.getChunk().getWorldForge();
+        if (!(level instanceof ServerLevel)) {
+            level = ((WorldEventAttachment) event).architectury$getAttachedLevel();
+        }
+        ChunkEvent.LOAD_DATA.invoker().load(event.getChunk(), level instanceof ServerLevel ? (ServerLevel) level : null, event.getData());
+    }
+    
+    public interface WorldEventAttachment {
+        LevelAccessor architectury$getAttachedLevel();
+        
+        void architectury$attachLevel(LevelAccessor level);
+    }
+    
     public static class ModBasedEventHandler {
         
     }

+ 65 - 0
forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinChunkSerializer.java

@@ -0,0 +1,65 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 architectury
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.mixin.forge;
+
+import me.shedaniel.architectury.event.forge.EventHandlerImplCommon;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.entity.ai.village.poi.PoiManager;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.chunk.ProtoChunk;
+import net.minecraft.world.level.chunk.storage.ChunkSerializer;
+import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
+import net.minecraftforge.event.world.ChunkDataEvent;
+import net.minecraftforge.eventbus.api.Event;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.ModifyArg;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+import java.lang.ref.WeakReference;
+
+@Mixin(ChunkSerializer.class)
+public class MixinChunkSerializer {
+    @Unique
+    private static ThreadLocal<WeakReference<ServerLevel>> level = new ThreadLocal<>();
+    
+    @Inject(method = "read", at = @At("HEAD"))
+    private static void read(ServerLevel worldIn, StructureManager templateManagerIn, PoiManager poiManager, ChunkPos pos, CompoundTag compound, CallbackInfoReturnable<ProtoChunk> cir) {
+        level.set(new WeakReference<>(worldIn));
+    }
+    
+    @ModifyArg(method = "read", at = @At(value = "INVOKE",
+            ordinal = 1,
+            target = "Lnet/minecraftforge/eventbus/api/IEventBus;post(Lnet/minecraftforge/eventbus/api/Event;)Z"),
+            index = 0)
+    private static Event modifyProtoChunkLevel(Event event) {
+        // We should get this PRed to Forge
+        WeakReference<ServerLevel> levelRef = level.get();
+        if (levelRef != null && event instanceof ChunkDataEvent.Load) {
+            ChunkDataEvent.Load load = (ChunkDataEvent.Load) event;
+            ((EventHandlerImplCommon.WorldEventAttachment) load).architectury$attachLevel(levelRef.get());
+        }
+        level.set(null);
+        return event;
+    }
+}

+ 44 - 0
forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinWorldEvent.java

@@ -0,0 +1,44 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 architectury
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.mixin.forge;
+
+import me.shedaniel.architectury.event.forge.EventHandlerImplCommon;
+import net.minecraft.world.level.LevelAccessor;
+import net.minecraftforge.event.world.WorldEvent;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+
+import java.lang.ref.WeakReference;
+
+@Mixin(WorldEvent.class)
+public class MixinWorldEvent implements EventHandlerImplCommon.WorldEventAttachment {
+    @Unique
+    private WeakReference<LevelAccessor> level;
+    
+    @Override
+    public LevelAccessor architectury$getAttachedLevel() {
+        return level == null ? null : level.get();
+    }
+    
+    @Override
+    public void architectury$attachLevel(LevelAccessor level) {
+        this.level = new WeakReference<>(level);
+    }
+}

+ 2 - 0
forge/src/main/resources/architectury.mixins.json

@@ -15,9 +15,11 @@
     "GameRulesAccessor$IntegerValueSimple",
     "MixinBlockEntity",
     "MixinBlockEntityExtension",
+    "MixinChunkSerializer",
     "MixinClientLevel",
     "MixinItemExtension",
     "MixinRegistryEntry",
+    "MixinWorldEvent",
     "MobSpawnSettingsBuilderAccessor"
   ],
   "injectors": {

+ 1 - 1
gradle.properties

@@ -6,7 +6,7 @@ supported_version=1.16.4/5
 
 archives_base_name=architectury
 archives_base_name_snapshot=architectury-snapshot
-base_version=1.15
+base_version=1.16
 maven_group=me.shedaniel
 
 fabric_loader_version=0.11.1

+ 6 - 0
testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java

@@ -217,6 +217,12 @@ public class DebugEvents {
         LightningEvent.STRIKE.register((bolt, level, pos, toStrike) -> {
             SINK.accept(bolt.getScoreboardName() + " struck at " + toShortString(pos) + logSide(level));
         });
+        ChunkEvent.LOAD_DATA.register((chunk, level, nbt) -> {
+            SINK.accept("Chunk loaded at x=" + chunk.getPos().x + ", z=" + chunk.getPos().z + " in dimension '" + level.dimension().location() + "'");
+        });
+        ChunkEvent.SAVE_DATA.register((chunk, level, nbt) -> {
+            SINK.accept("Chunk saved at x=" + chunk.getPos().x + ", z=" + chunk.getPos().z + " in dimension '" + level.dimension().location() + "'");
+        });
     }
     
     public static String toShortString(Vec3i pos) {