ClientDebugSubscriber.java

net.minecraft.client.multiplayer.ClientDebugSubscriber

信息

  • 全限定名:net.minecraft.client.multiplayer.ClientDebugSubscriber
  • 类型:public class
  • 包:net.minecraft.client.multiplayer
  • 源码路径:src/main/java/net/minecraft/client/multiplayer/ClientDebugSubscriber.java
  • 起始行号:L29
  • 职责:

    TODO

字段/常量

  • connection

    • 类型: ClientPacketListener
    • 修饰符: private final
    • 源码定位: L30
    • 说明:

      TODO

  • debugScreenOverlay

    • 类型: DebugScreenOverlay
    • 修饰符: private final
    • 源码定位: L31
    • 说明:

      TODO

  • remoteSubscriptions

    • 类型: Set<DebugSubscription<?>>
    • 修饰符: private
    • 源码定位: L32
    • 说明:

      TODO

  • valuesBySubscription

    • 类型: Map<DebugSubscription<?>,ClientDebugSubscriber.ValueMaps<?>>
    • 修饰符: private final
    • 源码定位: L33
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.client.multiplayer.ClientDebugSubscriber.ValueMap

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

      TODO

  • net.minecraft.client.multiplayer.ClientDebugSubscriber.ValueMapType

    • 类型: interface
    • 修饰符: private
    • 源码定位: L276
    • 说明:

      TODO

  • net.minecraft.client.multiplayer.ClientDebugSubscriber.ValueMaps

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

      TODO

  • net.minecraft.client.multiplayer.ClientDebugSubscriber.ValueWrapper

    • 类型: record
    • 修饰符: private
    • 源码定位: L302
    • 说明:

      TODO

构造器

public ClientDebugSubscriber(ClientPacketListener connection, DebugScreenOverlay debugScreenOverlay) @ L35

  • 构造器名:ClientDebugSubscriber
  • 源码定位:L35
  • 修饰符:public

参数:

  • connection: ClientPacketListener
  • debugScreenOverlay: DebugScreenOverlay

说明:

TODO

方法

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

private static void addFlag(Set<DebugSubscription<?>> output, DebugSubscription<?> subscription, boolean flag) @ L40

  • 方法名:addFlag
  • 源码定位:L40
  • 返回类型:void
  • 修饰符:private static

参数:

  • output: Set<DebugSubscription<?>>
  • subscription: DebugSubscription<?>
  • flag: boolean

说明:

TODO

private Set<DebugSubscription<?>> requestedSubscriptions() @ L46

  • 方法名:requestedSubscriptions
  • 源码定位:L46
  • 返回类型:Set<DebugSubscription<?>>
  • 修饰符:private

参数:

说明:

TODO

public void clear() @ L70

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

参数:

说明:

TODO

public void tick(long gameTime) @ L75

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

参数:

  • gameTime: long

说明:

TODO

private void onSubscriptionsChanged(Set<DebugSubscription<?>> newSubscriptions) @ L89

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

参数:

  • newSubscriptions: Set<DebugSubscription<?>>

说明:

TODO

private void initializeSubscriptions(Set<DebugSubscription<?>> newSubscriptions) @ L95

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

参数:

  • newSubscriptions: Set<DebugSubscription<?>>

说明:

TODO

private <V> ClientDebugSubscriber.ValueMaps<V> getValueMaps(DebugSubscription<V> subscription) @ L101

  • 方法名:getValueMaps
  • 源码定位:L101
  • 返回类型: ClientDebugSubscriber.ValueMaps
  • 修饰符:private

参数:

  • subscription: DebugSubscription

说明:

TODO

private <K,V> ClientDebugSubscriber.ValueMap<K,V> getValueMap(DebugSubscription<V> subscription, ClientDebugSubscriber.ValueMapType<K,V> mapType) @ L105

  • 方法名:getValueMap
  • 源码定位:L105
  • 返回类型:<K,V> ClientDebugSubscriber.ValueMap<K,V>
  • 修饰符:private

参数:

  • subscription: DebugSubscription
  • mapType: ClientDebugSubscriber.ValueMapType<K,V>

说明:

TODO

private <K,V> V getValue(DebugSubscription<V> subscription, K key, ClientDebugSubscriber.ValueMapType<K,V> type) @ L112

  • 方法名:getValue
  • 源码定位:L112
  • 返回类型:<K,V> V
  • 修饰符:private

参数:

  • subscription: DebugSubscription
  • key: K
  • type: ClientDebugSubscriber.ValueMapType<K,V>

说明:

TODO

public DebugValueAccess createDebugValueAccess(Level level) @ L117

  • 方法名:createDebugValueAccess
  • 源码定位:L117
  • 返回类型:DebugValueAccess
  • 修饰符:public

参数:

  • level: Level

说明:

TODO

public <T> void updateChunk(long gameTime, ChunkPos chunkPos, DebugSubscription.Update<T> update) @ L174

  • 方法名:updateChunk
  • 源码定位:L174
  • 返回类型: void
  • 修饰符:public

参数:

  • gameTime: long
  • chunkPos: ChunkPos
  • update: DebugSubscription.Update

说明:

TODO

public <T> void updateBlock(long gameTime, BlockPos blockPos, DebugSubscription.Update<T> update) @ L178

  • 方法名:updateBlock
  • 源码定位:L178
  • 返回类型: void
  • 修饰符:public

参数:

  • gameTime: long
  • blockPos: BlockPos
  • update: DebugSubscription.Update

说明:

TODO

public <T> void updateEntity(long gameTime, Entity entity, DebugSubscription.Update<T> update) @ L182

  • 方法名:updateEntity
  • 源码定位:L182
  • 返回类型: void
  • 修饰符:public

参数:

  • gameTime: long
  • entity: Entity
  • update: DebugSubscription.Update

说明:

TODO

public <T> void pushEvent(long gameTime, DebugSubscription.Event<T> event) @ L186

  • 方法名:pushEvent
  • 源码定位:L186
  • 返回类型: void
  • 修饰符:public

参数:

  • gameTime: long
  • event: DebugSubscription.Event

说明:

TODO

private <K,V> void updateMap(long gameTime, K key, DebugSubscription.Update<V> update, ClientDebugSubscriber.ValueMapType<K,V> type) @ L193

  • 方法名:updateMap
  • 源码定位:L193
  • 返回类型:<K,V> void
  • 修饰符:private

参数:

  • gameTime: long
  • key: K
  • update: DebugSubscription.Update
  • type: ClientDebugSubscriber.ValueMapType<K,V>

说明:

TODO

private <K,V> void forEachValue(DebugSubscription<V> subscription, ClientDebugSubscriber.ValueMapType<K,V> type, BiConsumer<K,V> consumer) @ L200

  • 方法名:forEachValue
  • 源码定位:L200
  • 返回类型:<K,V> void
  • 修饰符:private

参数:

  • subscription: DebugSubscription
  • type: ClientDebugSubscriber.ValueMapType<K,V>
  • consumer: BiConsumer<K,V>

说明:

TODO

public void dropLevel() @ L207

  • 方法名:dropLevel
  • 源码定位:L207
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public void dropChunk(ChunkPos chunkPos) @ L212

  • 方法名:dropChunk
  • 源码定位:L212
  • 返回类型:void
  • 修饰符:public

参数:

  • chunkPos: ChunkPos

说明:

TODO

public void dropEntity(Entity entity) @ L220

  • 方法名:dropEntity
  • 源码定位:L220
  • 返回类型:void
  • 修饰符:public

参数:

  • entity: Entity

说明:

TODO

private static <T> ClientDebugSubscriber.ValueMapType<UUID,T> entities() @ L228

  • 方法名:entities
  • 源码定位:L228
  • 返回类型: ClientDebugSubscriber.ValueMapType<UUID,T>
  • 修饰符:private static

参数:

说明:

TODO

private static <T> ClientDebugSubscriber.ValueMapType<BlockPos,T> blocks() @ L232

  • 方法名:blocks
  • 源码定位:L232
  • 返回类型: ClientDebugSubscriber.ValueMapType<BlockPos,T>
  • 修饰符:private static

参数:

说明:

TODO

private static <T> ClientDebugSubscriber.ValueMapType<ChunkPos,T> chunks() @ L236

  • 方法名:chunks
  • 源码定位:L236
  • 返回类型: ClientDebugSubscriber.ValueMapType<ChunkPos,T>
  • 修饰符:private static

参数:

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
public class ClientDebugSubscriber {
    private final ClientPacketListener connection;
    private final DebugScreenOverlay debugScreenOverlay;
    private Set<DebugSubscription<?>> remoteSubscriptions = Set.of();
    private final Map<DebugSubscription<?>, ClientDebugSubscriber.ValueMaps<?>> valuesBySubscription = new HashMap<>();
 
    public ClientDebugSubscriber(ClientPacketListener connection, DebugScreenOverlay debugScreenOverlay) {
        this.debugScreenOverlay = debugScreenOverlay;
        this.connection = connection;
    }
 
    private static void addFlag(Set<DebugSubscription<?>> output, DebugSubscription<?> subscription, boolean flag) {
        if (flag) {
            output.add(subscription);
        }
    }
 
    private Set<DebugSubscription<?>> requestedSubscriptions() {
        Set<DebugSubscription<?>> subscriptions = new ReferenceOpenHashSet<>();
        addFlag(subscriptions, RemoteDebugSampleType.TICK_TIME.subscription(), this.debugScreenOverlay.showFpsCharts());
        if (SharedConstants.DEBUG_ENABLED) {
            addFlag(subscriptions, DebugSubscriptions.BEES, SharedConstants.DEBUG_BEES);
            addFlag(subscriptions, DebugSubscriptions.BEE_HIVES, SharedConstants.DEBUG_BEES);
            addFlag(subscriptions, DebugSubscriptions.BRAINS, SharedConstants.DEBUG_BRAIN);
            addFlag(subscriptions, DebugSubscriptions.BREEZES, SharedConstants.DEBUG_BREEZE_MOB);
            addFlag(subscriptions, DebugSubscriptions.ENTITY_BLOCK_INTERSECTIONS, SharedConstants.DEBUG_ENTITY_BLOCK_INTERSECTION);
            addFlag(subscriptions, DebugSubscriptions.ENTITY_PATHS, SharedConstants.DEBUG_PATHFINDING);
            addFlag(subscriptions, DebugSubscriptions.GAME_EVENTS, SharedConstants.DEBUG_GAME_EVENT_LISTENERS);
            addFlag(subscriptions, DebugSubscriptions.GAME_EVENT_LISTENERS, SharedConstants.DEBUG_GAME_EVENT_LISTENERS);
            addFlag(subscriptions, DebugSubscriptions.GOAL_SELECTORS, SharedConstants.DEBUG_GOAL_SELECTOR || SharedConstants.DEBUG_BEES);
            addFlag(subscriptions, DebugSubscriptions.NEIGHBOR_UPDATES, SharedConstants.DEBUG_NEIGHBORSUPDATE);
            addFlag(subscriptions, DebugSubscriptions.POIS, SharedConstants.DEBUG_POI);
            addFlag(subscriptions, DebugSubscriptions.RAIDS, SharedConstants.DEBUG_RAIDS);
            addFlag(subscriptions, DebugSubscriptions.REDSTONE_WIRE_ORIENTATIONS, SharedConstants.DEBUG_EXPERIMENTAL_REDSTONEWIRE_UPDATE_ORDER);
            addFlag(subscriptions, DebugSubscriptions.STRUCTURES, SharedConstants.DEBUG_STRUCTURES);
            addFlag(subscriptions, DebugSubscriptions.VILLAGE_SECTIONS, SharedConstants.DEBUG_VILLAGE_SECTIONS);
        }
 
        return subscriptions;
    }
 
    public void clear() {
        this.remoteSubscriptions = Set.of();
        this.dropLevel();
    }
 
    public void tick(long gameTime) {
        Set<DebugSubscription<?>> newSubscriptions = this.requestedSubscriptions();
        if (!newSubscriptions.equals(this.remoteSubscriptions)) {
            this.remoteSubscriptions = newSubscriptions;
            this.onSubscriptionsChanged(newSubscriptions);
        }
 
        this.valuesBySubscription.forEach((subscription, valueMaps) -> {
            if (subscription.expireAfterTicks() != 0) {
                valueMaps.purgeExpired(gameTime);
            }
        });
    }
 
    private void onSubscriptionsChanged(Set<DebugSubscription<?>> newSubscriptions) {
        this.valuesBySubscription.keySet().retainAll(newSubscriptions);
        this.initializeSubscriptions(newSubscriptions);
        this.connection.send(new ServerboundDebugSubscriptionRequestPacket(newSubscriptions));
    }
 
    private void initializeSubscriptions(Set<DebugSubscription<?>> newSubscriptions) {
        for (DebugSubscription<?> subscription : newSubscriptions) {
            this.valuesBySubscription.computeIfAbsent(subscription, s -> new ClientDebugSubscriber.ValueMaps());
        }
    }
 
    private <V> ClientDebugSubscriber.@Nullable ValueMaps<V> getValueMaps(DebugSubscription<V> subscription) {
        return (ClientDebugSubscriber.ValueMaps<V>)this.valuesBySubscription.get(subscription);
    }
 
    private <K, V> ClientDebugSubscriber.@Nullable ValueMap<K, V> getValueMap(
        DebugSubscription<V> subscription, ClientDebugSubscriber.ValueMapType<K, V> mapType
    ) {
        ClientDebugSubscriber.ValueMaps<V> maps = this.getValueMaps(subscription);
        return maps != null ? mapType.get(maps) : null;
    }
 
    private <K, V> @Nullable V getValue(DebugSubscription<V> subscription, K key, ClientDebugSubscriber.ValueMapType<K, V> type) {
        ClientDebugSubscriber.ValueMap<K, V> values = this.getValueMap(subscription, type);
        return values != null ? values.getValue(key) : null;
    }
 
    public DebugValueAccess createDebugValueAccess(Level level) {
        return new DebugValueAccess() {
            {
                Objects.requireNonNull(ClientDebugSubscriber.this);
            }
 
            @Override
            public <T> void forEachChunk(DebugSubscription<T> subscription, BiConsumer<ChunkPos, T> consumer) {
                ClientDebugSubscriber.this.forEachValue(subscription, ClientDebugSubscriber.chunks(), consumer);
            }
 
            @Override
            public <T> @Nullable T getChunkValue(DebugSubscription<T> subscription, ChunkPos chunkPos) {
                return ClientDebugSubscriber.this.getValue(subscription, chunkPos, ClientDebugSubscriber.chunks());
            }
 
            @Override
            public <T> void forEachBlock(DebugSubscription<T> subscription, BiConsumer<BlockPos, T> consumer) {
                ClientDebugSubscriber.this.forEachValue(subscription, ClientDebugSubscriber.blocks(), consumer);
            }
 
            @Override
            public <T> @Nullable T getBlockValue(DebugSubscription<T> subscription, BlockPos blockPos) {
                return ClientDebugSubscriber.this.getValue(subscription, blockPos, ClientDebugSubscriber.blocks());
            }
 
            @Override
            public <T> void forEachEntity(DebugSubscription<T> subscription, BiConsumer<Entity, T> consumer) {
                ClientDebugSubscriber.this.forEachValue(subscription, ClientDebugSubscriber.entities(), (entityId, value) -> {
                    Entity entity = level.getEntity(entityId);
                    if (entity != null) {
                        consumer.accept(entity, value);
                    }
                });
            }
 
            @Override
            public <T> @Nullable T getEntityValue(DebugSubscription<T> subscription, Entity entity) {
                return ClientDebugSubscriber.this.getValue(subscription, entity.getUUID(), ClientDebugSubscriber.entities());
            }
 
            @Override
            public <T> void forEachEvent(DebugSubscription<T> subscription, DebugValueAccess.EventVisitor<T> visitor) {
                ClientDebugSubscriber.ValueMaps<T> values = ClientDebugSubscriber.this.getValueMaps(subscription);
                if (values != null) {
                    long gameTime = level.getGameTime();
 
                    for (ClientDebugSubscriber.ValueWrapper<T> event : values.events) {
                        int remainingTicks = (int)(event.expiresAfterTime() - gameTime);
                        int totalLifetime = subscription.expireAfterTicks();
                        visitor.accept(event.value(), remainingTicks, totalLifetime);
                    }
                }
            }
        };
    }
 
    public <T> void updateChunk(long gameTime, ChunkPos chunkPos, DebugSubscription.Update<T> update) {
        this.updateMap(gameTime, chunkPos, update, chunks());
    }
 
    public <T> void updateBlock(long gameTime, BlockPos blockPos, DebugSubscription.Update<T> update) {
        this.updateMap(gameTime, blockPos, update, blocks());
    }
 
    public <T> void updateEntity(long gameTime, Entity entity, DebugSubscription.Update<T> update) {
        this.updateMap(gameTime, entity.getUUID(), update, entities());
    }
 
    public <T> void pushEvent(long gameTime, DebugSubscription.Event<T> event) {
        ClientDebugSubscriber.ValueMaps<T> values = this.getValueMaps(event.subscription());
        if (values != null) {
            values.events.add(new ClientDebugSubscriber.ValueWrapper<>(event.value(), gameTime + event.subscription().expireAfterTicks()));
        }
    }
 
    private <K, V> void updateMap(long gameTime, K key, DebugSubscription.Update<V> update, ClientDebugSubscriber.ValueMapType<K, V> type) {
        ClientDebugSubscriber.ValueMap<K, V> values = this.getValueMap(update.subscription(), type);
        if (values != null) {
            values.apply(gameTime, key, update);
        }
    }
 
    private <K, V> void forEachValue(DebugSubscription<V> subscription, ClientDebugSubscriber.ValueMapType<K, V> type, BiConsumer<K, V> consumer) {
        ClientDebugSubscriber.ValueMap<K, V> values = this.getValueMap(subscription, type);
        if (values != null) {
            values.forEach(consumer);
        }
    }
 
    public void dropLevel() {
        this.valuesBySubscription.clear();
        this.initializeSubscriptions(this.remoteSubscriptions);
    }
 
    public void dropChunk(ChunkPos chunkPos) {
        if (!this.valuesBySubscription.isEmpty()) {
            for (ClientDebugSubscriber.ValueMaps<?> values : this.valuesBySubscription.values()) {
                values.dropChunkAndBlocks(chunkPos);
            }
        }
    }
 
    public void dropEntity(Entity entity) {
        if (!this.valuesBySubscription.isEmpty()) {
            for (ClientDebugSubscriber.ValueMaps<?> values : this.valuesBySubscription.values()) {
                values.entityValues.removeKey(entity.getUUID());
            }
        }
    }
 
    private static <T> ClientDebugSubscriber.ValueMapType<UUID, T> entities() {
        return v -> v.entityValues;
    }
 
    private static <T> ClientDebugSubscriber.ValueMapType<BlockPos, T> blocks() {
        return v -> v.blockValues;
    }
 
    private static <T> ClientDebugSubscriber.ValueMapType<ChunkPos, T> chunks() {
        return v -> v.chunkValues;
    }
 
    @OnlyIn(Dist.CLIENT)
    private static class ValueMap<K, V> {
        private final Map<K, ClientDebugSubscriber.ValueWrapper<V>> values = new HashMap<>();
 
        public void removeValues(Predicate<ClientDebugSubscriber.ValueWrapper<V>> predicate) {
            this.values.values().removeIf(predicate);
        }
 
        public void removeKey(K key) {
            this.values.remove(key);
        }
 
        public void removeKeys(Predicate<K> predicate) {
            this.values.keySet().removeIf(predicate);
        }
 
        public @Nullable V getValue(K key) {
            ClientDebugSubscriber.ValueWrapper<V> result = this.values.get(key);
            return result != null ? result.value() : null;
        }
 
        public void apply(long gameTime, K key, DebugSubscription.Update<V> update) {
            if (update.value().isPresent()) {
                this.values.put(key, new ClientDebugSubscriber.ValueWrapper<>(update.value().get(), gameTime + update.subscription().expireAfterTicks()));
            } else {
                this.values.remove(key);
            }
        }
 
        public void forEach(BiConsumer<K, V> output) {
            this.values.forEach((k, v) -> output.accept((K)k, v.value()));
        }
    }
 
    @FunctionalInterface
    @OnlyIn(Dist.CLIENT)
    private interface ValueMapType<K, V> {
        ClientDebugSubscriber.ValueMap<K, V> get(ClientDebugSubscriber.ValueMaps<V> maps);
    }
 
    @OnlyIn(Dist.CLIENT)
    private static class ValueMaps<V> {
        private final ClientDebugSubscriber.ValueMap<ChunkPos, V> chunkValues = new ClientDebugSubscriber.ValueMap<>();
        private final ClientDebugSubscriber.ValueMap<BlockPos, V> blockValues = new ClientDebugSubscriber.ValueMap<>();
        private final ClientDebugSubscriber.ValueMap<UUID, V> entityValues = new ClientDebugSubscriber.ValueMap<>();
        private final List<ClientDebugSubscriber.ValueWrapper<V>> events = new ArrayList<>();
 
        public void purgeExpired(long gameTime) {
            Predicate<ClientDebugSubscriber.ValueWrapper<V>> expiredPredicate = v -> v.hasExpired(gameTime);
            this.chunkValues.removeValues(expiredPredicate);
            this.blockValues.removeValues(expiredPredicate);
            this.entityValues.removeValues(expiredPredicate);
            this.events.removeIf(expiredPredicate);
        }
 
        public void dropChunkAndBlocks(ChunkPos chunkPos) {
            this.chunkValues.removeKey(chunkPos);
            this.blockValues.removeKeys(chunkPos::contains);
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    private record ValueWrapper<T>(T value, long expiresAfterTime) {
        private static final long NO_EXPIRY = -1L;
 
        public boolean hasExpired(long gameTime) {
            return this.expiresAfterTime == -1L ? false : gameTime >= this.expiresAfterTime;
        }
    }
}

引用的其他类