TrackingDebugSynchronizer.java

net.minecraft.util.debug.TrackingDebugSynchronizer

信息

  • 全限定名:net.minecraft.util.debug.TrackingDebugSynchronizer
  • 类型:public abstract class
  • 包:net.minecraft.util.debug
  • 源码路径:src/main/java/net/minecraft/util/debug/TrackingDebugSynchronizer.java
  • 起始行号:L28
  • 职责:

    TODO

字段/常量

  • subscription

    • 类型: DebugSubscription<T>
    • 修饰符: protected final
    • 源码定位: L29
    • 说明:

      TODO

  • subscribedPlayers

    • 类型: Set<UUID>
    • 修饰符: private final
    • 源码定位: L30
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.util.debug.TrackingDebugSynchronizer.PoiSynchronizer

    • 类型: class
    • 修饰符: public static
    • 源码定位: L104
    • 说明:

      TODO

  • net.minecraft.util.debug.TrackingDebugSynchronizer.PoiSynchronizer.->

    • 类型: record
    • 修饰符: package-private
    • 源码定位: L115
    • 说明:

      TODO

  • net.minecraft.util.debug.TrackingDebugSynchronizer.PoiSynchronizer.)

    • 类型: record
    • 修饰符: public
    • 源码定位: L120
    • 说明:

      TODO

  • net.minecraft.util.debug.TrackingDebugSynchronizer.PoiSynchronizer.)..

    • 类型: record
    • 修饰符: package-private
    • 源码定位: L123
    • 说明:

      TODO

  • net.minecraft.util.debug.TrackingDebugSynchronizer.SourceSynchronizer

    • 类型: class
    • 修饰符: public static
    • 源码定位: L141
    • 说明:

      TODO

  • net.minecraft.util.debug.TrackingDebugSynchronizer.ValueSource

    • 类型: class
    • 修饰符: private static
    • 源码定位: L241
    • 说明:

      TODO

  • net.minecraft.util.debug.TrackingDebugSynchronizer.VillageSectionSynchronizer

    • 类型: class
    • 修饰符: public static
    • 源码定位: L260
    • 说明:

      TODO

  • net.minecraft.util.debug.TrackingDebugSynchronizer.VillageSectionSynchronizer.->

    • 类型: record
    • 修饰符: package-private
    • 源码定位: L271
    • 说明:

      TODO

  • net.minecraft.util.debug.TrackingDebugSynchronizer.VillageSectionSynchronizer.->..

    • 类型: record
    • 修饰符: package-private
    • 源码定位: L272
    • 说明:

      TODO

  • net.minecraft.util.debug.TrackingDebugSynchronizer.VillageSectionSynchronizer.)

    • 类型: record
    • 修饰符: public
    • 源码定位: L286
    • 说明:

      TODO

  • net.minecraft.util.debug.TrackingDebugSynchronizer.VillageSectionSynchronizer.)..

    • 类型: record
    • 修饰符: package-private
    • 源码定位: L287
    • 说明:

      TODO

构造器

public TrackingDebugSynchronizer(DebugSubscription<T> subscription) @ L32

  • 构造器名:TrackingDebugSynchronizer
  • 源码定位:L32
  • 修饰符:public

参数:

  • subscription: DebugSubscription

说明:

TODO

方法

下面的方法块按源码顺序生成。

public final void tick(ServerLevel level) @ L36

  • 方法名:tick
  • 源码定位:L36
  • 返回类型:void
  • 修饰符:public final

参数:

  • level: ServerLevel

说明:

TODO

private void addSubscriber(ServerPlayer player) @ L55

  • 方法名:addSubscriber
  • 源码定位:L55
  • 返回类型:void
  • 修饰符:private

参数:

  • player: ServerPlayer

说明:

TODO

protected final void sendToPlayersTrackingChunk(ServerLevel level, ChunkPos trackedChunk, Packet<?super ClientGamePacketListener> packet) @ L65

  • 方法名:sendToPlayersTrackingChunk
  • 源码定位:L65
  • 返回类型:void
  • 修饰符:protected final

参数:

  • level: ServerLevel
  • trackedChunk: ChunkPos
  • packet: Packet<?super ClientGamePacketListener>

说明:

TODO

protected final void sendToPlayersTrackingEntity(ServerLevel level, Entity trackedEntity, Packet<?super ClientGamePacketListener> packet) @ L75

  • 方法名:sendToPlayersTrackingEntity
  • 源码定位:L75
  • 返回类型:void
  • 修饰符:protected final

参数:

  • level: ServerLevel
  • trackedEntity: Entity
  • packet: Packet<?super ClientGamePacketListener>

说明:

TODO

public final void startTrackingChunk(ServerPlayer player, ChunkPos chunkPos) @ L80

  • 方法名:startTrackingChunk
  • 源码定位:L80
  • 返回类型:void
  • 修饰符:public final

参数:

  • player: ServerPlayer
  • chunkPos: ChunkPos

说明:

TODO

public final void startTrackingEntity(ServerPlayer player, Entity entity) @ L86

  • 方法名:startTrackingEntity
  • 源码定位:L86
  • 返回类型:void
  • 修饰符:public final

参数:

  • player: ServerPlayer
  • entity: Entity

说明:

TODO

protected void clear() @ L92

  • 方法名:clear
  • 源码定位:L92
  • 返回类型:void
  • 修饰符:protected

参数:

说明:

TODO

protected void pollAndSendUpdates(ServerLevel level) @ L95

  • 方法名:pollAndSendUpdates
  • 源码定位:L95
  • 返回类型:void
  • 修饰符:protected

参数:

  • level: ServerLevel

说明:

TODO

protected void sendInitialChunk(ServerPlayer player, ChunkPos chunkPos) @ L98

  • 方法名:sendInitialChunk
  • 源码定位:L98
  • 返回类型:void
  • 修饰符:protected

参数:

  • player: ServerPlayer
  • chunkPos: ChunkPos

说明:

TODO

protected void sendInitialEntity(ServerPlayer player, Entity entity) @ L101

  • 方法名:sendInitialEntity
  • 源码定位:L101
  • 返回类型:void
  • 修饰符:protected

参数:

  • player: ServerPlayer
  • entity: Entity

说明:

TODO

代码

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) {
                if (isSubscribed) {
                    this.addSubscriber(player);
                } else {
                    this.subscribedPlayers.remove(player.getUUID());
                }
            }
        }
 
        this.subscribedPlayers.removeIf(id -> level.getPlayerByUUID(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.pack())) {
                this.startTrackingChunk(player, chunkPos);
            }
        });
        player.level().getChunkSource().chunkMap.forEachEntityTrackedBy(player, entity -> this.startTrackingEntity(player, entity));
    }
 
    protected final void sendToPlayersTrackingChunk(ServerLevel level, ChunkPos trackedChunk, Packet<? super ClientGamePacketListener> packet) {
        ChunkMap chunkMap = level.getChunkSource().chunkMap;
 
        for (UUID playerId : this.subscribedPlayers) {
            if (level.getPlayerByUUID(playerId) instanceof ServerPlayer player && chunkMap.isChunkTracked(player, trackedChunk.x(), trackedChunk.z())) {
                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 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(record))))
                );
        }
 
        public void onPoiAdded(ServerLevel level, PoiRecord record) {
            this.sendToPlayersTrackingChunk(
                level,
                ChunkPos.containing(record.getPos()),
                new ClientboundDebugBlockValuePacket(record.getPos(), this.subscription.packUpdate(new DebugPoiInfo(record)))
            );
        }
 
        public void onPoiRemoved(ServerLevel level, BlockPos poiPos) {
            this.sendToPlayersTrackingChunk(level, ChunkPos.containing(poiPos), new ClientboundDebugBlockValuePacket(poiPos, this.subscription.emptyUpdate()));
        }
 
        public void onPoiTicketCountChanged(ServerLevel level, BlockPos poiPos) {
            this.sendToPlayersTrackingChunk(
                level,
                ChunkPos.containing(poiPos),
                new ClientboundDebugBlockValuePacket(poiPos, this.subscription.packUpdate(level.getPoiManager().getDebugPoiInfo(poiPos)))
            );
        }
    }
 
    public static class SourceSynchronizer<T> extends TrackingDebugSynchronizer<T> {
        private final Map<ChunkPos, TrackingDebugSynchronizer.ValueSource<T>> chunkSources = new HashMap<>();
        private final Map<BlockPos, TrackingDebugSynchronizer.ValueSource<T>> blockEntitySources = new HashMap<>();
        private final Map<UUID, TrackingDebugSynchronizer.ValueSource<T>> entitySources = new HashMap<>();
 
        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) {
            for (Entry<ChunkPos, TrackingDebugSynchronizer.ValueSource<T>> entry : this.chunkSources.entrySet()) {
                DebugSubscription.Update<T> update = entry.getValue().pollUpdate(this.subscription);
                if (update != null) {
                    ChunkPos chunkPos = entry.getKey();
                    this.sendToPlayersTrackingChunk(level, chunkPos, new ClientboundDebugChunkValuePacket(chunkPos, update));
                }
            }
 
            for (Entry<BlockPos, TrackingDebugSynchronizer.ValueSource<T>> entryx : this.blockEntitySources.entrySet()) {
                DebugSubscription.Update<T> update = entryx.getValue().pollUpdate(this.subscription);
                if (update != null) {
                    BlockPos blockPos = entryx.getKey();
                    ChunkPos chunkPos = ChunkPos.containing(blockPos);
                    this.sendToPlayersTrackingChunk(level, chunkPos, new ClientboundDebugBlockValuePacket(blockPos, update));
                }
            }
 
            for (Entry<UUID, TrackingDebugSynchronizer.ValueSource<T>> entryxx : this.entitySources.entrySet()) {
                DebugSubscription.Update<T> update = entryxx.getValue().pollUpdate(this.subscription);
                if (update != null) {
                    Entity entity = Objects.requireNonNull(level.getEntity(entryxx.getKey()));
                    this.sendToPlayersTrackingEntity(level, entity, new ClientboundDebugEntityValuePacket(entity.getId(), update));
                }
            }
        }
 
        public void registerChunk(ChunkPos chunkPos, DebugValueSource.ValueGetter<T> getter) {
            this.chunkSources.put(chunkPos, new TrackingDebugSynchronizer.ValueSource<>(getter));
        }
 
        public void registerBlockEntity(BlockPos blockPos, DebugValueSource.ValueGetter<T> getter) {
            this.blockEntitySources.put(blockPos, new TrackingDebugSynchronizer.ValueSource<>(getter));
        }
 
        public void registerEntity(UUID entityId, DebugValueSource.ValueGetter<T> getter) {
            this.entitySources.put(entityId, new TrackingDebugSynchronizer.ValueSource<>(getter));
        }
 
        public void dropChunk(ChunkPos chunkPos) {
            this.chunkSources.remove(chunkPos);
            this.blockEntitySources.keySet().removeIf(chunkPos::contains);
        }
 
        public void dropBlockEntity(ServerLevel level, BlockPos blockPos) {
            TrackingDebugSynchronizer.ValueSource<T> source = this.blockEntitySources.remove(blockPos);
            if (source != null) {
                ChunkPos chunkPos = ChunkPos.containing(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) {
            TrackingDebugSynchronizer.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 (Entry<BlockPos, TrackingDebugSynchronizer.ValueSource<T>> entry : this.blockEntitySources.entrySet()) {
                T lastValue = entry.getValue().lastSyncedValue;
                if (lastValue != null) {
                    BlockPos blockPos = entry.getKey();
                    if (chunkPos.contains(blockPos)) {
                        player.connection.send(new ClientboundDebugBlockValuePacket(blockPos, this.subscription.packUpdate(lastValue)));
                    }
                }
            }
        }
 
        @Override
        protected void sendInitialEntity(ServerPlayer player, Entity entity) {
            TrackingDebugSynchronizer.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)));
            }
        }
    }
 
    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 DebugSubscription.@Nullable Update<T> pollUpdate(DebugSubscription<T> subscription) {
            T newValue = this.getter.get();
            if (!Objects.equals(newValue, this.lastSyncedValue)) {
                this.lastSyncedValue = newValue;
                return subscription.packUpdate(newValue);
            } else {
                return null;
            }
        }
    }
 
    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());
                        forEachVillageSectionUpdate(
                            level,
                            centerSection,
                            (sectionPos, isVillage) -> {
                                BlockPos sectionBlockPos = sectionPos.center();
                                player.connection
                                    .send(new ClientboundDebugBlockValuePacket(sectionBlockPos, this.subscription.packUpdate(isVillage ? 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) {
            forEachVillageSectionUpdate(
                level,
                SectionPos.of(poiPos),
                (sectionPos, isVillage) -> {
                    BlockPos sectionBlockPos = sectionPos.center();
                    if (isVillage) {
                        this.sendToPlayersTrackingChunk(
                            level,
                            ChunkPos.containing(sectionBlockPos),
                            new ClientboundDebugBlockValuePacket(sectionBlockPos, this.subscription.packUpdate(Unit.INSTANCE))
                        );
                    } else {
                        this.sendToPlayersTrackingChunk(
                            level, ChunkPos.containing(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);
                        } else {
                            consumer.accept(sectionPos, false);
                        }
                    }
                }
            }
        }
    }
}

引用的其他类