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>
This commit is contained in:
canitzp
2021-05-27 01:03:20 +02:00
committed by GitHub
parent d737e8e2b7
commit 582ededddd
10 changed files with 309 additions and 1 deletions

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

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

View File

@@ -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 {
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

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

View File

@@ -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

View File

@@ -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) {