ClientboundCommandsPacket.java

net.minecraft.network.protocol.game.ClientboundCommandsPacket

信息

  • 全限定名:net.minecraft.network.protocol.game.ClientboundCommandsPacket
  • 类型:public class
  • 包:net.minecraft.network.protocol.game
  • 源码路径:src/main/java/net/minecraft/network/protocol/game/ClientboundCommandsPacket.java
  • 起始行号:L31
  • 实现:Packet
  • 职责:

    TODO

字段/常量

  • STREAM_CODEC

    • 类型: StreamCodec<FriendlyByteBuf,ClientboundCommandsPacket>
    • 修饰符: public static final
    • 源码定位: L32
    • 说明:

      TODO

  • MASK_TYPE

    • 类型: byte
    • 修饰符: private static final
    • 源码定位: L35
    • 说明:

      TODO

  • FLAG_EXECUTABLE

    • 类型: byte
    • 修饰符: private static final
    • 源码定位: L36
    • 说明:

      TODO

  • FLAG_REDIRECT

    • 类型: byte
    • 修饰符: private static final
    • 源码定位: L37
    • 说明:

      TODO

  • FLAG_CUSTOM_SUGGESTIONS

    • 类型: byte
    • 修饰符: private static final
    • 源码定位: L38
    • 说明:

      TODO

  • FLAG_RESTRICTED

    • 类型: byte
    • 修饰符: private static final
    • 源码定位: L39
    • 说明:

      TODO

  • TYPE_ROOT

    • 类型: byte
    • 修饰符: private static final
    • 源码定位: L40
    • 说明:

      TODO

  • TYPE_LITERAL

    • 类型: byte
    • 修饰符: private static final
    • 源码定位: L41
    • 说明:

      TODO

  • TYPE_ARGUMENT

    • 类型: byte
    • 修饰符: private static final
    • 源码定位: L42
    • 说明:

      TODO

  • rootIndex

    • 类型: int
    • 修饰符: private final
    • 源码定位: L43
    • 说明:

      TODO

  • entries

    • 类型: List<ClientboundCommandsPacket.Entry>
    • 修饰符: private final
    • 源码定位: L44
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.network.protocol.game.ClientboundCommandsPacket.ArgumentNodeStub

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

      TODO

  • net.minecraft.network.protocol.game.ClientboundCommandsPacket.Entry

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

      TODO

  • net.minecraft.network.protocol.game.ClientboundCommandsPacket.LiteralNodeStub

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

      TODO

  • net.minecraft.network.protocol.game.ClientboundCommandsPacket.NodeBuilder

    • 类型: interface
    • 修饰符: public
    • 源码定位: L269
    • 说明:

      TODO

  • net.minecraft.network.protocol.game.ClientboundCommandsPacket.NodeInspector

    • 类型: interface
    • 修饰符: public
    • 源码定位: L277
    • 说明:

      TODO

  • net.minecraft.network.protocol.game.ClientboundCommandsPacket.NodeResolver

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

      TODO

  • net.minecraft.network.protocol.game.ClientboundCommandsPacket.NodeStub

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

      TODO

构造器

public ClientboundCommandsPacket(RootCommandNode<S> root, ClientboundCommandsPacket.NodeInspector<S> inspector) @ L46

  • 构造器名:ClientboundCommandsPacket
  • 源码定位:L46
  • 修饰符:public

参数:

  • root: RootCommandNode
  • inspector: ClientboundCommandsPacket.NodeInspector

说明:

TODO

private ClientboundCommandsPacket(FriendlyByteBuf input) @ L52

  • 构造器名:ClientboundCommandsPacket
  • 源码定位:L52
  • 修饰符:private

参数:

  • input: FriendlyByteBuf

说明:

TODO

方法

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

private void write(FriendlyByteBuf output) @ L58

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

参数:

  • output: FriendlyByteBuf

说明:

TODO

private static void validateEntries(List<ClientboundCommandsPacket.Entry> entries, BiPredicate<ClientboundCommandsPacket.Entry,IntSet> validator) @ L63

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

参数:

  • entries: List<ClientboundCommandsPacket.Entry>
  • validator: BiPredicate<ClientboundCommandsPacket.Entry,IntSet>

说明:

TODO

private static void validateEntries(List<ClientboundCommandsPacket.Entry> entries) @ L74

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

参数:

  • entries: List<ClientboundCommandsPacket.Entry>

说明:

TODO

private static <S> Object2IntMap<CommandNode<S>> enumerateNodes(RootCommandNode<S> root) @ L79

  • 方法名:enumerateNodes
  • 源码定位:L79
  • 返回类型: Object2IntMap<CommandNode>
  • 修饰符:private static

参数:

  • root: RootCommandNode

说明:

TODO

private static <S> List<ClientboundCommandsPacket.Entry> createEntries(Object2IntMap<CommandNode<S>> nodeToId, ClientboundCommandsPacket.NodeInspector<S> inspector) @ L99

  • 方法名:createEntries
  • 源码定位:L99
  • 返回类型: List<ClientboundCommandsPacket.Entry>
  • 修饰符:private static

参数:

  • nodeToId: Object2IntMap<CommandNode>
  • inspector: ClientboundCommandsPacket.NodeInspector

说明:

TODO

private static ClientboundCommandsPacket.Entry readNode(FriendlyByteBuf input) @ L112

  • 方法名:readNode
  • 源码定位:L112
  • 返回类型:ClientboundCommandsPacket.Entry
  • 修饰符:private static

参数:

  • input: FriendlyByteBuf

说明:

TODO

private static ClientboundCommandsPacket.NodeStub read(FriendlyByteBuf input, byte flags) @ L120

  • 方法名:read
  • 源码定位:L120
  • 返回类型:ClientboundCommandsPacket.NodeStub
  • 修饰符:private static

参数:

  • input: FriendlyByteBuf
  • flags: byte

说明:

TODO

private static <S> ClientboundCommandsPacket.Entry createEntry(CommandNode<S> node, ClientboundCommandsPacket.NodeInspector<S> inspector, Object2IntMap<CommandNode<S>> ids) @ L141

  • 方法名:createEntry
  • 源码定位:L141
  • 返回类型: ClientboundCommandsPacket.Entry
  • 修饰符:private static

参数:

  • node: CommandNode
  • inspector: ClientboundCommandsPacket.NodeInspector
  • ids: Object2IntMap<CommandNode>

说明:

TODO

public PacketType<ClientboundCommandsPacket> type() @ L187

  • 方法名:type
  • 源码定位:L187
  • 返回类型:PacketType
  • 修饰符:public

参数:

说明:

TODO

public void handle(ClientGamePacketListener listener) @ L192

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

参数:

  • listener: ClientGamePacketListener

说明:

TODO

public <S> RootCommandNode<S> getRoot(CommandBuildContext context, ClientboundCommandsPacket.NodeBuilder<S> builder) @ L196

  • 方法名:getRoot
  • 源码定位:L196
  • 返回类型: RootCommandNode
  • 修饰符:public

参数:

  • context: CommandBuildContext
  • builder: ClientboundCommandsPacket.NodeBuilder

说明:

TODO

代码

public class ClientboundCommandsPacket implements Packet<ClientGamePacketListener> {
    public static final StreamCodec<FriendlyByteBuf, ClientboundCommandsPacket> STREAM_CODEC = Packet.codec(
        ClientboundCommandsPacket::write, ClientboundCommandsPacket::new
    );
    private static final byte MASK_TYPE = 3;
    private static final byte FLAG_EXECUTABLE = 4;
    private static final byte FLAG_REDIRECT = 8;
    private static final byte FLAG_CUSTOM_SUGGESTIONS = 16;
    private static final byte FLAG_RESTRICTED = 32;
    private static final byte TYPE_ROOT = 0;
    private static final byte TYPE_LITERAL = 1;
    private static final byte TYPE_ARGUMENT = 2;
    private final int rootIndex;
    private final List<ClientboundCommandsPacket.Entry> entries;
 
    public <S> ClientboundCommandsPacket(RootCommandNode<S> root, ClientboundCommandsPacket.NodeInspector<S> inspector) {
        Object2IntMap<CommandNode<S>> nodeToId = enumerateNodes(root);
        this.entries = createEntries(nodeToId, inspector);
        this.rootIndex = nodeToId.getInt(root);
    }
 
    private ClientboundCommandsPacket(FriendlyByteBuf input) {
        this.entries = input.readList(ClientboundCommandsPacket::readNode);
        this.rootIndex = input.readVarInt();
        validateEntries(this.entries);
    }
 
    private void write(FriendlyByteBuf output) {
        output.writeCollection(this.entries, (buffer, entry) -> entry.write(buffer));
        output.writeVarInt(this.rootIndex);
    }
 
    private static void validateEntries(List<ClientboundCommandsPacket.Entry> entries, BiPredicate<ClientboundCommandsPacket.Entry, IntSet> validator) {
        IntSet elementsToCheck = new IntOpenHashSet(IntSets.fromTo(0, entries.size()));
 
        while (!elementsToCheck.isEmpty()) {
            boolean worked = elementsToCheck.removeIf(index -> validator.test(entries.get(index), elementsToCheck));
            if (!worked) {
                throw new IllegalStateException("Server sent an impossible command tree");
            }
        }
    }
 
    private static void validateEntries(List<ClientboundCommandsPacket.Entry> entries) {
        validateEntries(entries, ClientboundCommandsPacket.Entry::canBuild);
        validateEntries(entries, ClientboundCommandsPacket.Entry::canResolve);
    }
 
    private static <S> Object2IntMap<CommandNode<S>> enumerateNodes(RootCommandNode<S> root) {
        Object2IntMap<CommandNode<S>> nodeToId = new Object2IntOpenHashMap<>();
        Queue<CommandNode<S>> queue = new ArrayDeque<>();
        queue.add(root);
 
        CommandNode<S> node;
        while ((node = queue.poll()) != null) {
            if (!nodeToId.containsKey(node)) {
                int id = nodeToId.size();
                nodeToId.put(node, id);
                queue.addAll(node.getChildren());
                if (node.getRedirect() != null) {
                    queue.add(node.getRedirect());
                }
            }
        }
 
        return nodeToId;
    }
 
    private static <S> List<ClientboundCommandsPacket.Entry> createEntries(
        Object2IntMap<CommandNode<S>> nodeToId, ClientboundCommandsPacket.NodeInspector<S> inspector
    ) {
        ObjectArrayList<ClientboundCommandsPacket.Entry> result = new ObjectArrayList<>(nodeToId.size());
        result.size(nodeToId.size());
 
        for (Object2IntMap.Entry<CommandNode<S>> entry : Object2IntMaps.fastIterable(nodeToId)) {
            result.set(entry.getIntValue(), createEntry(entry.getKey(), inspector, nodeToId));
        }
 
        return result;
    }
 
    private static ClientboundCommandsPacket.Entry readNode(FriendlyByteBuf input) {
        byte flags = input.readByte();
        int[] children = input.readVarIntArray();
        int redirect = (flags & 8) != 0 ? input.readVarInt() : 0;
        ClientboundCommandsPacket.NodeStub stub = read(input, flags);
        return new ClientboundCommandsPacket.Entry(stub, flags, redirect, children);
    }
 
    private static ClientboundCommandsPacket.@Nullable NodeStub read(FriendlyByteBuf input, byte flags) {
        int type = flags & 3;
        if (type == 2) {
            String name = input.readUtf();
            int id = input.readVarInt();
            ArgumentTypeInfo<?, ?> argumentType = BuiltInRegistries.COMMAND_ARGUMENT_TYPE.byId(id);
            if (argumentType == null) {
                return null;
            } else {
                ArgumentTypeInfo.Template<?> argument = argumentType.deserializeFromNetwork(input);
                Identifier suggestionId = (flags & 16) != 0 ? input.readIdentifier() : null;
                return new ClientboundCommandsPacket.ArgumentNodeStub(name, argument, suggestionId);
            }
        } else if (type == 1) {
            String id = input.readUtf();
            return new ClientboundCommandsPacket.LiteralNodeStub(id);
        } else {
            return null;
        }
    }
 
    private static <S> ClientboundCommandsPacket.Entry createEntry(
        CommandNode<S> node, ClientboundCommandsPacket.NodeInspector<S> inspector, Object2IntMap<CommandNode<S>> ids
    ) {
        int flags = 0;
        int redirect;
        if (node.getRedirect() != null) {
            flags |= 8;
            redirect = ids.getInt(node.getRedirect());
        } else {
            redirect = 0;
        }
 
        if (inspector.isExecutable(node)) {
            flags |= 4;
        }
 
        if (inspector.isRestricted(node)) {
            flags |= 32;
        }
 
        ClientboundCommandsPacket.NodeStub nodeStub;
        switch (node) {
            case RootCommandNode<S> ignored:
                flags |= 0;
                nodeStub = null;
                break;
            case ArgumentCommandNode<S, ?> arg:
                Identifier suggestionId = inspector.suggestionId(arg);
                nodeStub = new ClientboundCommandsPacket.ArgumentNodeStub(arg.getName(), ArgumentTypeInfos.unpack(arg.getType()), suggestionId);
                flags |= 2;
                if (suggestionId != null) {
                    flags |= 16;
                }
                break;
            case LiteralCommandNode<S> literal:
                nodeStub = new ClientboundCommandsPacket.LiteralNodeStub(literal.getLiteral());
                flags |= 1;
                break;
            default:
                throw new UnsupportedOperationException("Unknown node type " + node);
        }
 
        int[] childrenIds = node.getChildren().stream().mapToInt(ids::getInt).toArray();
        return new ClientboundCommandsPacket.Entry(nodeStub, flags, redirect, childrenIds);
    }
 
    @Override
    public PacketType<ClientboundCommandsPacket> type() {
        return GamePacketTypes.CLIENTBOUND_COMMANDS;
    }
 
    public void handle(ClientGamePacketListener listener) {
        listener.handleCommands(this);
    }
 
    public <S> RootCommandNode<S> getRoot(CommandBuildContext context, ClientboundCommandsPacket.NodeBuilder<S> builder) {
        return (RootCommandNode<S>)new ClientboundCommandsPacket.NodeResolver<>(context, builder, this.entries).resolve(this.rootIndex);
    }
 
    private record ArgumentNodeStub(String id, ArgumentTypeInfo.Template<?> argumentType, @Nullable Identifier suggestionId)
        implements ClientboundCommandsPacket.NodeStub {
        @Override
        public <S> ArgumentBuilder<S, ?> build(CommandBuildContext context, ClientboundCommandsPacket.NodeBuilder<S> builder) {
            ArgumentType<?> type = this.argumentType.instantiate(context);
            return builder.createArgument(this.id, type, this.suggestionId);
        }
 
        @Override
        public void write(FriendlyByteBuf output) {
            output.writeUtf(this.id);
            serializeCap(output, this.argumentType);
            if (this.suggestionId != null) {
                output.writeIdentifier(this.suggestionId);
            }
        }
 
        private static <A extends ArgumentType<?>> void serializeCap(FriendlyByteBuf output, ArgumentTypeInfo.Template<A> argumentType) {
            serializeCap(output, argumentType.type(), argumentType);
        }
 
        private static <A extends ArgumentType<?>, T extends ArgumentTypeInfo.Template<A>> void serializeCap(
            FriendlyByteBuf output, ArgumentTypeInfo<A, T> info, ArgumentTypeInfo.Template<A> argumentType
        ) {
            output.writeVarInt(BuiltInRegistries.COMMAND_ARGUMENT_TYPE.getId(info));
            info.serializeToNetwork((T)argumentType, output);
        }
    }
 
    private record Entry(ClientboundCommandsPacket.@Nullable NodeStub stub, int flags, int redirect, int[] children) {
        public void write(FriendlyByteBuf output) {
            output.writeByte(this.flags);
            output.writeVarIntArray(this.children);
            if ((this.flags & 8) != 0) {
                output.writeVarInt(this.redirect);
            }
 
            if (this.stub != null) {
                this.stub.write(output);
            }
        }
 
        public boolean canBuild(IntSet unbuiltNodes) {
            return (this.flags & 8) != 0 ? !unbuiltNodes.contains(this.redirect) : true;
        }
 
        public boolean canResolve(IntSet unresolvedNodes) {
            for (int child : this.children) {
                if (unresolvedNodes.contains(child)) {
                    return false;
                }
            }
 
            return true;
        }
    }
 
    private record LiteralNodeStub(String id) implements ClientboundCommandsPacket.NodeStub {
        @Override
        public <S> ArgumentBuilder<S, ?> build(CommandBuildContext context, ClientboundCommandsPacket.NodeBuilder<S> builder) {
            return builder.createLiteral(this.id);
        }
 
        @Override
        public void write(FriendlyByteBuf output) {
            output.writeUtf(this.id);
        }
    }
 
    public interface NodeBuilder<S> {
        ArgumentBuilder<S, ?> createLiteral(String id);
 
        ArgumentBuilder<S, ?> createArgument(String id, ArgumentType<?> argumentType, @Nullable Identifier suggestionId);
 
        ArgumentBuilder<S, ?> configure(ArgumentBuilder<S, ?> input, boolean executable, boolean restricted);
    }
 
    public interface NodeInspector<S> {
        @Nullable Identifier suggestionId(ArgumentCommandNode<S, ?> node);
 
        boolean isExecutable(CommandNode<S> node);
 
        boolean isRestricted(CommandNode<S> node);
    }
 
    private static class NodeResolver<S> {
        private final CommandBuildContext context;
        private final ClientboundCommandsPacket.NodeBuilder<S> builder;
        private final List<ClientboundCommandsPacket.Entry> entries;
        private final List<CommandNode<S>> nodes;
 
        private NodeResolver(CommandBuildContext context, ClientboundCommandsPacket.NodeBuilder<S> builder, List<ClientboundCommandsPacket.Entry> entries) {
            this.context = context;
            this.builder = builder;
            this.entries = entries;
            ObjectArrayList<CommandNode<S>> nodes = new ObjectArrayList<>();
            nodes.size(entries.size());
            this.nodes = nodes;
        }
 
        public CommandNode<S> resolve(int index) {
            CommandNode<S> currentNode = this.nodes.get(index);
            if (currentNode != null) {
                return currentNode;
            } else {
                ClientboundCommandsPacket.Entry entry = this.entries.get(index);
                CommandNode<S> result;
                if (entry.stub == null) {
                    result = new RootCommandNode<>();
                } else {
                    ArgumentBuilder<S, ?> resultBuilder = entry.stub.build(this.context, this.builder);
                    if ((entry.flags & 8) != 0) {
                        resultBuilder.redirect(this.resolve(entry.redirect));
                    }
 
                    boolean isExecutable = (entry.flags & 4) != 0;
                    boolean isRestricted = (entry.flags & 32) != 0;
                    result = this.builder.configure(resultBuilder, isExecutable, isRestricted).build();
                }
 
                this.nodes.set(index, result);
 
                for (int childId : entry.children) {
                    CommandNode<S> child = this.resolve(childId);
                    if (!(child instanceof RootCommandNode)) {
                        result.addChild(child);
                    }
                }
 
                return result;
            }
        }
    }
 
    private interface NodeStub {
        <S> ArgumentBuilder<S, ?> build(CommandBuildContext context, ClientboundCommandsPacket.NodeBuilder<S> builder);
 
        void write(FriendlyByteBuf output);
    }
}

引用的其他类