Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ChunkWatchEvent #464

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021, 2022 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 dev.architectury.event.events.common;

import dev.architectury.event.Event;
import dev.architectury.event.EventFactory;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.LevelChunk;

public interface ChunkWatchEvent {
/**
* This event is fired whenever a {@link ServerPlayer} begins watching a chunk and the chunk is queued up for
* sending to the client.
* <p>
* This event must NOT be used to send additional chunk-related data to the client as the client will not be aware
* of the chunk yet when this event fires. {@link ChunkWatchEvent#SENT} should be used for this purpose instead
*/
Event<ChunkListener> WATCH = EventFactory.createLoop();

/**
* This event is fired whenever a chunk being watched by a {@link ServerPlayer} is transmitted to their client.
* <p>
* This event may be used to send additional chunk-related data to the client.
*/
Event<ChunkListener> SENT = EventFactory.createLoop();

/**
* This event is fired whenever a {@link ServerPlayer} stops watching a chunk. The chunk this event fires for
* may never have actually been known to the client if the chunk goes out of range before being sent due to
* slow pacing of chunk sync on slow connections or to slow clients.
*/
Event<ChunkPosListener> UNWATCH = EventFactory.createLoop();

@FunctionalInterface
interface ChunkListener {
void listen(LevelChunk chunk, ServerLevel level, ServerPlayer player);
}

@FunctionalInterface
interface ChunkPosListener {
void listen(ChunkPos chunkPos, ServerLevel level, ServerPlayer player);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,21 @@
package dev.architectury.mixin.fabric;

import dev.architectury.event.events.common.ChunkEvent;
import dev.architectury.event.events.common.ChunkWatchEvent;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
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.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

Expand All @@ -48,4 +52,14 @@ public class MixinChunkMap {
private void save(ChunkAccess chunkAccess, CallbackInfoReturnable<Boolean> cir, ChunkPos pos, ChunkStatus chunkStatus, CompoundTag nbt) {
ChunkEvent.SAVE_DATA.invoker().save(chunkAccess, this.level, nbt);
}

@Inject(method = "markChunkPendingToSend(Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/world/level/chunk/LevelChunk;)V", at = @At("TAIL"))
private static void watch(ServerPlayer player, LevelChunk chunk, CallbackInfo ci) {
ChunkWatchEvent.WATCH.invoker().listen(chunk, player.serverLevel(), player);
}

@Inject(method = "dropChunk(Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/world/level/ChunkPos;)V", at = @At("HEAD"))
private static void unwatch(ServerPlayer player, ChunkPos chunkPos, CallbackInfo ci) {
ChunkWatchEvent.UNWATCH.invoker().listen(chunkPos, player.serverLevel(), player);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021, 2022 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 dev.architectury.mixin.fabric;

import dev.architectury.event.events.common.ChunkWatchEvent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.network.PlayerChunkSender;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.level.chunk.LevelChunk;
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;

@Mixin(PlayerChunkSender.class)
public class MixinPlayerChunkSender {
@Inject(method = "sendChunk", at = @At("TAIL"))
private static void send(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk, CallbackInfo ci) {
ChunkWatchEvent.SENT.invoker().listen(chunk, level, packetListener.player);
}
}
1 change: 1 addition & 0 deletions fabric/src/main/resources/architectury.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"MixinPhantomSpawner",
"MixinPlayer",
"MixinPlayerAdvancements",
"MixinPlayerChunkSender",
"MixinPlayerList",
"MixinResultSlot",
"MixinServerLevel",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021, 2022 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 dev.architectury.mixin.forge.minecraftforge;

import dev.architectury.event.events.common.ChunkWatchEvent;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.LevelChunk;
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;

@Mixin(ChunkMap.class)
public abstract class MixinChunkMap {
@Inject(method = "markChunkPendingToSend(Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/world/level/chunk/LevelChunk;)V", at = @At("TAIL"))
private static void watch(ServerPlayer player, LevelChunk chunk, CallbackInfo ci) {
ChunkWatchEvent.WATCH.invoker().listen(chunk, player.serverLevel(), player);
}

@Inject(method = "dropChunk(Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/world/level/ChunkPos;)V", at = @At("HEAD"))
private static void unwatch(ServerPlayer player, ChunkPos chunkPos, CallbackInfo ci) {
ChunkWatchEvent.UNWATCH.invoker().listen(chunkPos, player.serverLevel(), player);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021, 2022 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 dev.architectury.mixin.forge.minecraftforge;

import dev.architectury.event.events.common.ChunkWatchEvent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.network.PlayerChunkSender;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.level.chunk.LevelChunk;
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;

@Mixin(PlayerChunkSender.class)
public abstract class MixinPlayerChunkSender {
@Inject(method = "sendChunk", at = @At("TAIL"))
private static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk, CallbackInfo ci) {
ChunkWatchEvent.SENT.invoker().listen(chunk, level, packetListener.player);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"client": [
],
"mixins": [
"minecraftforge.MixinChunkMap",
"minecraftforge.MixinPlayerChunkSender",
"minecraftforge.MixinChunkSerializer",
"minecraftforge.MixinEntitySpawnExtension"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
package dev.architectury.neoforge;

import dev.architectury.event.EventHandler;
import dev.architectury.event.events.common.ChunkWatchEvent;
import dev.architectury.registry.level.biome.forge.BiomeModificationsImpl;
import dev.architectury.utils.ArchitecturyConstants;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.Mod;

@Mod(ArchitecturyConstants.MOD_ID)
Expand All @@ -30,4 +32,22 @@ public ArchitecturyNeoForge() {
EventHandler.init();
BiomeModificationsImpl.init();
}

@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.FORGE)
private static class ForgeBusSubscriber {
@SubscribeEvent
private static void event(net.neoforged.neoforge.event.level.ChunkWatchEvent.Watch event) {
ChunkWatchEvent.WATCH.invoker().listen(event.getChunk(), event.getLevel(), event.getPlayer());
}

@SubscribeEvent
private static void event(net.neoforged.neoforge.event.level.ChunkWatchEvent.Sent event) {
ChunkWatchEvent.SENT.invoker().listen(event.getChunk(), event.getLevel(), event.getPlayer());
}

@SubscribeEvent
private static void event(net.neoforged.neoforge.event.level.ChunkWatchEvent.UnWatch event) {
ChunkWatchEvent.UNWATCH.invoker().listen(event.getPos(), event.getLevel(), event.getPlayer());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,15 @@ public static void debugEvents() {
ChunkEvent.SAVE_DATA.register((chunk, level, nbt) -> {
// TestMod.SINK.accept("Chunk saved at x=" + chunk.getPos().x + ", z=" + chunk.getPos().z + " in dimension '" + level.dimension().location() + "'");
});
ChunkWatchEvent.WATCH.register((chunk, level, player) -> {
// TestMod.SINK.accept("Chunk at x=%d, z=%d in dimension '%s' being watched by %s", chunk.getPos().x, chunk.getPos().z, level.dimension().location(), player.getScoreboardName());
});
ChunkWatchEvent.SENT.register((chunk, level, player) -> {
// TestMod.SINK.accept("Chunk at x=%d, z=%d in dimension '%s' sent to %s", chunk.getPos().x, chunk.getPos().z, level.dimension().location(), player.getScoreboardName());
});
ChunkWatchEvent.UNWATCH.register((chunkPos, level, player) -> {
// TestMod.SINK.accept("Chunk at x=%d, z=%d in dimension '%s' abandoned by %s", chunkPos.x, chunkPos.z, level.dimension().location(), player.getScoreboardName());
});
}

public static String toShortString(Vec3i pos) {
Expand Down
Loading