Merge pull request #3567 from kosma/fabric-1.18-onchunkgenerate

Fabric 1.18: Fix onchunkgenerate trigger
This commit is contained in:
mikeprimm 2021-12-16 14:35:44 -06:00 committed by GitHub
commit ec369e96f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 97 additions and 172 deletions

View File

@ -2,8 +2,6 @@ package org.dynmap.fabric_1_18;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
@ -20,8 +18,6 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ChunkHolder;
import net.minecraft.server.world.ServerChunkManager;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.IdList;
@ -46,11 +42,10 @@ import org.dynmap.fabric_1_18.command.DmarkerCommand;
import org.dynmap.fabric_1_18.command.DynmapCommand;
import org.dynmap.fabric_1_18.command.DynmapExpCommand;
import org.dynmap.fabric_1_18.event.BlockEvents;
import org.dynmap.fabric_1_18.event.ChunkDataEvents;
import org.dynmap.fabric_1_18.event.CustomServerChunkEvents;
import org.dynmap.fabric_1_18.event.CustomServerLifecycleEvents;
import org.dynmap.fabric_1_18.event.PlayerEvents;
import org.dynmap.fabric_1_18.mixin.BiomeEffectsAccessor;
import org.dynmap.fabric_1_18.mixin.ThreadedAnvilChunkStorageAccessor;
import org.dynmap.fabric_1_18.permissions.FilePermissions;
import org.dynmap.fabric_1_18.permissions.OpPermissions;
import org.dynmap.fabric_1_18.permissions.PermissionProvider;
@ -126,32 +121,6 @@ public class DynmapPlugin {
public static DynmapBlockState[] stateByID;
private Map<String, LongOpenHashSet> knownloadedchunks = new HashMap<String, LongOpenHashSet>();
private void addKnownChunk(FabricWorld fw, ChunkPos pos) {
LongOpenHashSet cset = knownloadedchunks.get(fw.getName());
if (cset == null) {
cset = new LongOpenHashSet();
knownloadedchunks.put(fw.getName(), cset);
}
cset.add(pos.toLong());
}
private void removeKnownChunk(FabricWorld fw, ChunkPos pos) {
LongOpenHashSet cset = knownloadedchunks.get(fw.getName());
if (cset != null) {
cset.remove(pos.toLong());
}
}
private boolean checkIfKnownChunk(FabricWorld fw, ChunkPos pos) {
LongOpenHashSet cset = knownloadedchunks.get(fw.getName());
if (cset != null) {
return cset.contains(pos.toLong());
}
return false;
}
/**
* Initialize block states (org.dynmap.blockstate.DynmapBlockState)
*/
@ -658,69 +627,25 @@ public class DynmapPlugin {
}
}
public void handleChunkLoad(ServerWorld world, WorldChunk chunk) {
public void handleChunkGenerate(ServerWorld world, Chunk chunk) {
if (!onchunkgenerate) return;
if ((chunk != null) && (chunk.getStatus() == ChunkStatus.FULL)) {
FabricWorld fw = getWorld(world, false);
if (fw != null) {
addKnownChunk(fw, chunk.getPos());
FabricWorld fw = getWorld(world, false);
ChunkPos chunkPos = chunk.getPos();
int yMax = 0;
ChunkSection[] sections = chunk.getSectionArray();
for (int i = 0; i < sections.length; i++) {
if ((sections[i] != null) && (!sections[i].isEmpty())) {
yMax = 16 * (i + 1);
}
}
}
public void handleChunkUnload(ServerWorld world, WorldChunk chunk) {
if (!onchunkgenerate) return;
if ((chunk != null) && (chunk.getStatus() == ChunkStatus.FULL)) {
FabricWorld fw = getWorld(world, false);
ChunkPos cp = chunk.getPos();
if (fw != null) {
if (!checkIfKnownChunk(fw, cp)) {
int ymax = 0;
ChunkSection[] sections = chunk.getSectionArray();
for (int i = 0; i < sections.length; i++) {
if ((sections[i] != null) && (!sections[i].isEmpty())) {
ymax = 16 * (i + 1);
}
}
int x = cp.x << 4;
int z = cp.z << 4;
// If not empty AND not initial scan
if (ymax > 0) {
Log.info("New generated chunk detected at " + cp + " for " + fw.getName());
mapManager.touchVolume(fw.getName(), x, 0, z, x + 15, ymax, z + 16, "chunkgenerate");
}
}
removeKnownChunk(fw, cp);
}
}
}
public void handleChunkDataSave(ServerWorld world, Chunk chunk) {
if (!onchunkgenerate) return;
if ((chunk != null) && (chunk.getStatus() == ChunkStatus.FULL)) {
FabricWorld fw = getWorld(world, false);
ChunkPos cp = chunk.getPos();
if (fw != null) {
if (!checkIfKnownChunk(fw, cp)) {
int ymax = 0;
ChunkSection[] sections = chunk.getSectionArray();
for (int i = 0; i < sections.length; i++) {
if ((sections[i] != null) && (!sections[i].isEmpty())) {
ymax = 16 * (i + 1);
}
}
int x = cp.x << 4;
int z = cp.z << 4;
// If not empty AND not initial scan
if (ymax > 0) {
mapManager.touchVolume(fw.getName(), x, 0, z, x + 15, ymax, z + 16, "chunkgenerate");
}
addKnownChunk(fw, cp);
}
}
if (yMax > 0) {
mapManager.touchVolume(fw.getName(),
chunkPos.getStartX(), 0, chunkPos.getStartZ(),
chunkPos.getEndX(), yMax, chunkPos.getEndZ(),
"chunkgenerate");
//Log.info("New generated chunk detected at %s[%s]".formatted(fw.getName(), chunkPos.getStartPos()));
}
}
@ -755,39 +680,17 @@ public class DynmapPlugin {
onblockchange_with_id = core.isTrigger("blockupdate-with-id");
if (onblockchange_with_id)
onblockchange = true;
if ((worldTracker == null) && (onblockchange || onchunkpopulate || onchunkgenerate)) {
if (worldTracker == null)
worldTracker = new WorldTracker();
ServerWorldEvents.LOAD.register((server, world) -> worldTracker.handleWorldLoad(server, world));
ServerWorldEvents.UNLOAD.register((server, world) -> worldTracker.handleWorldUnload(server, world));
ServerChunkEvents.CHUNK_LOAD.register((world, chunk) -> worldTracker.handleChunkLoad(world, chunk));
ServerChunkEvents.CHUNK_UNLOAD.register((world, chunk) -> worldTracker.handleChunkUnload(world, chunk));
ChunkDataEvents.SAVE.register((world, chunk) -> worldTracker.handleChunkDataSave(world, chunk));
if (onchunkpopulate || onchunkgenerate) {
CustomServerChunkEvents.CHUNK_GENERATE.register((world, chunk) -> worldTracker.handleChunkGenerate(world, chunk));
}
if (onblockchange) {
BlockEvents.BLOCK_EVENT.register((world, pos) -> worldTracker.handleBlockEvent(world, pos));
}
// Prime the known full chunks
if (onchunkgenerate && (server.getWorlds() != null)) {
for (ServerWorld world : server.getWorlds()) {
FabricWorld fw = getWorld(world);
if (fw == null) continue;
ServerChunkManager chunkManager = world.getChunkManager();
Long2ObjectLinkedOpenHashMap<ChunkHolder> chunks = ((ThreadedAnvilChunkStorageAccessor) chunkManager.threadedAnvilChunkStorage).getChunkHolders();
for (Map.Entry<Long, ChunkHolder> k : chunks.long2ObjectEntrySet()) {
ChunkHolder ch = k.getValue();
Chunk c = null;
try {
c = ch.getSavingFuture().getNow(null);
} catch (Exception x) {
}
if (c == null) continue;
ChunkStatus cs = c.getStatus();
ChunkPos pos = ch.getPos();
if (cs == ChunkStatus.FULL) { // Cooked?
// Add it as known
addKnownChunk(fw, pos);
}
}
}
}
ServerWorldEvents.LOAD.register((server, world) -> worldTracker.handleWorldLoad(server, world));
ServerWorldEvents.UNLOAD.register((server, world) -> worldTracker.handleWorldUnload(server, world));
}
FabricWorld getWorldByName(String name) {
@ -890,5 +793,4 @@ public class DynmapPlugin {
}
}
}
}
}

View File

@ -0,0 +1,5 @@
package org.dynmap.fabric_1_18.access;
public interface ProtoChunkAccessor {
boolean getTouchedByWorldGen();
}

View File

@ -1,24 +0,0 @@
package org.dynmap.fabric_1_18.event;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.chunk.Chunk;
public class ChunkDataEvents {
private ChunkDataEvents() {
}
public static Event<Save> SAVE = EventFactory.createArrayBacked(Save.class,
(listeners) -> (world, chunk) -> {
for (Save callback : listeners) {
callback.onSave(world, chunk);
}
}
);
@FunctionalInterface
public interface Save {
void onSave(ServerWorld world, Chunk chunk);
}
}

View File

@ -0,0 +1,21 @@
package org.dynmap.fabric_1_18.event;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.chunk.Chunk;
public class CustomServerChunkEvents {
public static Event<ChunkGenerate> CHUNK_GENERATE = EventFactory.createArrayBacked(ChunkGenerate.class,
(listeners) -> (world, chunk) -> {
for (ChunkGenerate callback : listeners) {
callback.onChunkGenerate(world, chunk);
}
}
);
@FunctionalInterface
public interface ChunkGenerate {
void onChunkGenerate(ServerWorld world, Chunk chunk);
}
}

View File

@ -0,0 +1,30 @@
package org.dynmap.fabric_1_18.mixin;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.chunk.ProtoChunk;
import org.dynmap.fabric_1_18.access.ProtoChunkAccessor;
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;
@Mixin(ProtoChunk.class)
public class ProtoChunkMixin implements ProtoChunkAccessor {
private boolean touchedByWorldGen = false;
@Inject(
method = "setBlockState",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/chunk/ChunkSection;setBlockState(IIILnet/minecraft/block/BlockState;)Lnet/minecraft/block/BlockState;"
)
)
public void setBlockState(BlockPos pos, BlockState state, boolean moved, CallbackInfoReturnable<BlockState> info) {
touchedByWorldGen = true;
}
public boolean getTouchedByWorldGen() {
return touchedByWorldGen;
}
}

View File

@ -1,13 +0,0 @@
package org.dynmap.fabric_1_18.mixin;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import net.minecraft.server.world.ChunkHolder;
import net.minecraft.server.world.ThreadedAnvilChunkStorage;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(ThreadedAnvilChunkStorage.class)
public interface ThreadedAnvilChunkStorageAccessor {
@Accessor
Long2ObjectLinkedOpenHashMap<ChunkHolder> getChunkHolders();
}

View File

@ -1,9 +1,11 @@
package org.dynmap.fabric_1_18.mixin;
import net.minecraft.server.world.ChunkHolder;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.server.world.ThreadedAnvilChunkStorage;
import net.minecraft.world.chunk.Chunk;
import org.dynmap.fabric_1_18.event.ChunkDataEvents;
import org.dynmap.fabric_1_18.access.ProtoChunkAccessor;
import org.dynmap.fabric_1_18.event.CustomServerChunkEvents;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@ -11,16 +13,20 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ThreadedAnvilChunkStorage.class)
@Mixin(value = ThreadedAnvilChunkStorage.class, priority = 666 /* fire before Fabric API CHUNK_LOAD event */)
public abstract class ThreadedAnvilChunkStorageMixin {
@Shadow
@Final
private ServerWorld world;
@Shadow
ServerWorld world;
@Inject(method = "save(Lnet/minecraft/world/chunk/Chunk;)Z", at = @At("RETURN"))
private void save(Chunk chunk, CallbackInfoReturnable<Boolean> info) {
if (info.getReturnValueZ()) {
ChunkDataEvents.SAVE.invoker().onSave(this.world, chunk);
@Inject(
/* Same place as fabric-lifecycle-events-v1 event CHUNK_LOAD (we will fire before it) */
method = "method_17227",
at = @At("TAIL")
)
private void onChunkGenerate(ChunkHolder chunkHolder, Chunk protoChunk, CallbackInfoReturnable<Chunk> callbackInfoReturnable) {
if (((ProtoChunkAccessor)protoChunk).getTouchedByWorldGen()) {
CustomServerChunkEvents.CHUNK_GENERATE.invoker().onChunkGenerate(this.world, callbackInfoReturnable.getReturnValue());
}
}
}
}

View File

@ -301,8 +301,6 @@ compass-mode: newnorth
render-triggers:
- blockupdate
#- blockupdate-with-id
#- lightingupdate
- chunkpopulate
- chunkgenerate
#- none

View File

@ -7,9 +7,9 @@
"BiomeEffectsAccessor",
"MinecraftServerMixin",
"PlayerManagerMixin",
"ProtoChunkMixin",
"ServerPlayerEntityMixin",
"ServerPlayNetworkHandlerMixin",
"ThreadedAnvilChunkStorageAccessor",
"ThreadedAnvilChunkStorageMixin",
"WorldChunkMixin"
],