minecraft_25w45a_unobfuscated/net/minecraft/util/debug/TrackingDebugSynchronizer.java
2025-11-24 22:52:51 +03:00

305 lines
14 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
* org.jspecify.annotations.Nullable
*/
package net.minecraft.util.debug;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.function.BiConsumer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundDebugBlockValuePacket;
import net.minecraft.network.protocol.game.ClientboundDebugChunkValuePacket;
import net.minecraft.network.protocol.game.ClientboundDebugEntityValuePacket;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Unit;
import net.minecraft.util.debug.DebugPoiInfo;
import net.minecraft.util.debug.DebugSubscription;
import net.minecraft.util.debug.DebugSubscriptions;
import net.minecraft.util.debug.DebugValueSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import net.minecraft.world.entity.ai.village.poi.PoiRecord;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.ChunkPos;
import org.jspecify.annotations.Nullable;
public abstract class TrackingDebugSynchronizer<T> {
protected final DebugSubscription<T> subscription;
private final Set<UUID> subscribedPlayers = new ObjectOpenHashSet();
public TrackingDebugSynchronizer(DebugSubscription<T> subscription) {
this.subscription = subscription;
}
public final void tick(ServerLevel level) {
for (ServerPlayer player : level.players()) {
boolean wasSubscribed = this.subscribedPlayers.contains(player.getUUID());
boolean isSubscribed = player.debugSubscriptions().contains(this.subscription);
if (isSubscribed == wasSubscribed) continue;
if (isSubscribed) {
this.addSubscriber(player);
continue;
}
this.subscribedPlayers.remove(player.getUUID());
}
this.subscribedPlayers.removeIf(id -> level.getPlayerByUUID((UUID)id) == null);
if (!this.subscribedPlayers.isEmpty()) {
this.pollAndSendUpdates(level);
}
}
private void addSubscriber(ServerPlayer player) {
this.subscribedPlayers.add(player.getUUID());
player.getChunkTrackingView().forEach(chunkPos -> {
if (!player.connection.chunkSender.isPending(chunkPos.toLong())) {
this.startTrackingChunk(player, (ChunkPos)chunkPos);
}
});
player.level().getChunkSource().chunkMap.forEachEntityTrackedBy(player, entity -> this.startTrackingEntity(player, (Entity)entity));
}
protected final void sendToPlayersTrackingChunk(ServerLevel level, ChunkPos trackedChunk, Packet<? super ClientGamePacketListener> packet) {
ChunkMap chunkMap = level.getChunkSource().chunkMap;
for (UUID playerId : this.subscribedPlayers) {
ServerPlayer player;
Player player2 = level.getPlayerByUUID(playerId);
if (!(player2 instanceof ServerPlayer) || !chunkMap.isChunkTracked(player = (ServerPlayer)player2, trackedChunk.x, trackedChunk.z)) continue;
player.connection.send(packet);
}
}
protected final void sendToPlayersTrackingEntity(ServerLevel level, Entity trackedEntity, Packet<? super ClientGamePacketListener> packet) {
ChunkMap chunkMap = level.getChunkSource().chunkMap;
chunkMap.sendToTrackingPlayersFiltered(trackedEntity, packet, player -> this.subscribedPlayers.contains(player.getUUID()));
}
public final void startTrackingChunk(ServerPlayer player, ChunkPos chunkPos) {
if (this.subscribedPlayers.contains(player.getUUID())) {
this.sendInitialChunk(player, chunkPos);
}
}
public final void startTrackingEntity(ServerPlayer player, Entity entity) {
if (this.subscribedPlayers.contains(player.getUUID())) {
this.sendInitialEntity(player, entity);
}
}
protected void clear() {
}
protected void pollAndSendUpdates(ServerLevel level) {
}
protected void sendInitialChunk(ServerPlayer player, ChunkPos chunkPos) {
}
protected void sendInitialEntity(ServerPlayer player, Entity entity) {
}
public static class VillageSectionSynchronizer
extends TrackingDebugSynchronizer<Unit> {
public VillageSectionSynchronizer() {
super(DebugSubscriptions.VILLAGE_SECTIONS);
}
@Override
protected void sendInitialChunk(ServerPlayer player, ChunkPos chunkPos) {
ServerLevel level = player.level();
PoiManager poiManager = level.getPoiManager();
poiManager.getInChunk(t -> true, chunkPos, PoiManager.Occupancy.ANY).forEach(record -> {
SectionPos centerSection = SectionPos.of(record.getPos());
VillageSectionSynchronizer.forEachVillageSectionUpdate(level, centerSection, (sectionPos, isVillage) -> {
BlockPos sectionBlockPos = sectionPos.center();
player.connection.send(new ClientboundDebugBlockValuePacket(sectionBlockPos, this.subscription.packUpdate(isVillage != false ? Unit.INSTANCE : null)));
});
});
}
public void onPoiAdded(ServerLevel level, PoiRecord record) {
this.sendVillageSectionsPacket(level, record.getPos());
}
public void onPoiRemoved(ServerLevel level, BlockPos poiPos) {
this.sendVillageSectionsPacket(level, poiPos);
}
private void sendVillageSectionsPacket(ServerLevel level, BlockPos poiPos) {
VillageSectionSynchronizer.forEachVillageSectionUpdate(level, SectionPos.of(poiPos), (sectionPos, isVillage) -> {
BlockPos sectionBlockPos = sectionPos.center();
if (isVillage.booleanValue()) {
this.sendToPlayersTrackingChunk(level, new ChunkPos(sectionBlockPos), new ClientboundDebugBlockValuePacket(sectionBlockPos, this.subscription.packUpdate(Unit.INSTANCE)));
} else {
this.sendToPlayersTrackingChunk(level, new ChunkPos(sectionBlockPos), new ClientboundDebugBlockValuePacket(sectionBlockPos, this.subscription.emptyUpdate()));
}
});
}
private static void forEachVillageSectionUpdate(ServerLevel level, SectionPos centerSection, BiConsumer<SectionPos, Boolean> consumer) {
for (int offsetZ = -1; offsetZ <= 1; ++offsetZ) {
for (int offsetX = -1; offsetX <= 1; ++offsetX) {
for (int offsetY = -1; offsetY <= 1; ++offsetY) {
SectionPos sectionPos = centerSection.offset(offsetX, offsetY, offsetZ);
if (level.isVillage(sectionPos.center())) {
consumer.accept(sectionPos, true);
continue;
}
consumer.accept(sectionPos, false);
}
}
}
}
}
public static class PoiSynchronizer
extends TrackingDebugSynchronizer<DebugPoiInfo> {
public PoiSynchronizer() {
super(DebugSubscriptions.POIS);
}
@Override
protected void sendInitialChunk(ServerPlayer player, ChunkPos chunkPos) {
ServerLevel level = player.level();
PoiManager poiManager = level.getPoiManager();
poiManager.getInChunk(t -> true, chunkPos, PoiManager.Occupancy.ANY).forEach(record -> player.connection.send(new ClientboundDebugBlockValuePacket(record.getPos(), this.subscription.packUpdate(new DebugPoiInfo((PoiRecord)record)))));
}
public void onPoiAdded(ServerLevel level, PoiRecord record) {
this.sendToPlayersTrackingChunk(level, new ChunkPos(record.getPos()), new ClientboundDebugBlockValuePacket(record.getPos(), this.subscription.packUpdate(new DebugPoiInfo(record))));
}
public void onPoiRemoved(ServerLevel level, BlockPos poiPos) {
this.sendToPlayersTrackingChunk(level, new ChunkPos(poiPos), new ClientboundDebugBlockValuePacket(poiPos, this.subscription.emptyUpdate()));
}
public void onPoiTicketCountChanged(ServerLevel level, BlockPos poiPos) {
this.sendToPlayersTrackingChunk(level, new ChunkPos(poiPos), new ClientboundDebugBlockValuePacket(poiPos, this.subscription.packUpdate(level.getPoiManager().getDebugPoiInfo(poiPos))));
}
}
private static class ValueSource<T> {
private final DebugValueSource.ValueGetter<T> getter;
private @Nullable T lastSyncedValue;
private ValueSource(DebugValueSource.ValueGetter<T> getter) {
this.getter = getter;
}
public @Nullable DebugSubscription.Update<T> pollUpdate(DebugSubscription<T> subscription) {
T newValue = this.getter.get();
if (!Objects.equals(newValue, this.lastSyncedValue)) {
this.lastSyncedValue = newValue;
return subscription.packUpdate(newValue);
}
return null;
}
}
public static class SourceSynchronizer<T>
extends TrackingDebugSynchronizer<T> {
private final Map<ChunkPos, ValueSource<T>> chunkSources = new HashMap<ChunkPos, ValueSource<T>>();
private final Map<BlockPos, ValueSource<T>> blockEntitySources = new HashMap<BlockPos, ValueSource<T>>();
private final Map<UUID, ValueSource<T>> entitySources = new HashMap<UUID, ValueSource<T>>();
public SourceSynchronizer(DebugSubscription<T> subscription) {
super(subscription);
}
@Override
protected void clear() {
this.chunkSources.clear();
this.blockEntitySources.clear();
this.entitySources.clear();
}
@Override
protected void pollAndSendUpdates(ServerLevel level) {
DebugSubscription.Update<T> update;
for (Map.Entry<ChunkPos, ValueSource<T>> entry : this.chunkSources.entrySet()) {
update = entry.getValue().pollUpdate(this.subscription);
if (update == null) continue;
ChunkPos chunkPos = entry.getKey();
this.sendToPlayersTrackingChunk(level, chunkPos, new ClientboundDebugChunkValuePacket(chunkPos, update));
}
for (Map.Entry<Object, ValueSource<T>> entry : this.blockEntitySources.entrySet()) {
update = entry.getValue().pollUpdate(this.subscription);
if (update == null) continue;
BlockPos blockPos = (BlockPos)entry.getKey();
ChunkPos chunkPos = new ChunkPos(blockPos);
this.sendToPlayersTrackingChunk(level, chunkPos, new ClientboundDebugBlockValuePacket(blockPos, update));
}
for (Map.Entry<Object, ValueSource<T>> entry : this.entitySources.entrySet()) {
update = entry.getValue().pollUpdate(this.subscription);
if (update == null) continue;
Entity entity = Objects.requireNonNull(level.getEntity((UUID)entry.getKey()));
this.sendToPlayersTrackingEntity(level, entity, new ClientboundDebugEntityValuePacket(entity.getId(), update));
}
}
public void registerChunk(ChunkPos chunkPos, DebugValueSource.ValueGetter<T> getter) {
this.chunkSources.put(chunkPos, new ValueSource<T>(getter));
}
public void registerBlockEntity(BlockPos blockPos, DebugValueSource.ValueGetter<T> getter) {
this.blockEntitySources.put(blockPos, new ValueSource<T>(getter));
}
public void registerEntity(UUID entityId, DebugValueSource.ValueGetter<T> getter) {
this.entitySources.put(entityId, new ValueSource<T>(getter));
}
public void dropChunk(ChunkPos chunkPos) {
this.chunkSources.remove(chunkPos);
this.blockEntitySources.keySet().removeIf(chunkPos::contains);
}
public void dropBlockEntity(ServerLevel level, BlockPos blockPos) {
ValueSource<T> source = this.blockEntitySources.remove(blockPos);
if (source != null) {
ChunkPos chunkPos = new ChunkPos(blockPos);
this.sendToPlayersTrackingChunk(level, chunkPos, new ClientboundDebugBlockValuePacket(blockPos, this.subscription.emptyUpdate()));
}
}
public void dropEntity(Entity entity) {
this.entitySources.remove(entity.getUUID());
}
@Override
protected void sendInitialChunk(ServerPlayer player, ChunkPos chunkPos) {
ValueSource<T> chunkSource = this.chunkSources.get(chunkPos);
if (chunkSource != null && chunkSource.lastSyncedValue != null) {
player.connection.send(new ClientboundDebugChunkValuePacket(chunkPos, this.subscription.packUpdate(chunkSource.lastSyncedValue)));
}
for (Map.Entry<BlockPos, ValueSource<T>> entry : this.blockEntitySources.entrySet()) {
BlockPos blockPos;
Object lastValue = entry.getValue().lastSyncedValue;
if (lastValue == null || !chunkPos.contains(blockPos = entry.getKey())) continue;
player.connection.send(new ClientboundDebugBlockValuePacket(blockPos, this.subscription.packUpdate(lastValue)));
}
}
@Override
protected void sendInitialEntity(ServerPlayer player, Entity entity) {
ValueSource<T> source = this.entitySources.get(entity.getUUID());
if (source != null && source.lastSyncedValue != null) {
player.connection.send(new ClientboundDebugEntityValuePacket(entity.getId(), this.subscription.packUpdate(source.lastSyncedValue)));
}
}
}
}