All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.nukkit.network.protocol.AvailableCommandsPacket Maven / Gradle / Ivy

There is a newer version: 1.20.40-r1
Show newest version
package cn.nukkit.network.protocol;

import cn.nukkit.api.PowerNukkitDifference;
import cn.nukkit.command.data.*;
import cn.nukkit.network.protocol.types.CommandEnumConstraintData;
import cn.nukkit.utils.BinaryStream;
import com.nukkitx.network.util.Preconditions;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import lombok.ToString;

import java.util.*;
import java.util.function.ObjIntConsumer;

import static cn.nukkit.utils.Utils.dynamic;

/**
 * @author MagicDroidX (Nukkit Project)
 */
@PowerNukkitDifference(since = "1.4.0.0-PN", info = "Made the arg type constants dynamic because they can change in Minecraft updates")
@ToString
public class AvailableCommandsPacket extends DataPacket {

    public static final byte NETWORK_ID = ProtocolInfo.AVAILABLE_COMMANDS_PACKET;

    private static final ObjIntConsumer WRITE_BYTE = (s, v) -> s.putByte((byte) v);
    private static final ObjIntConsumer WRITE_SHORT = BinaryStream::putLShort;
    private static final ObjIntConsumer WRITE_INT = BinaryStream::putLInt;

    public static final int ARG_FLAG_VALID = 0x100000;
    public static final int ARG_FLAG_ENUM = 0x200000;
    public static final int ARG_FLAG_POSTFIX = 0x1000000;
    public static final int ARG_FLAG_SOFT_ENUM = 0x4000000;

    public static final int ARG_TYPE_INT = dynamic(1);
    public static final int ARG_TYPE_FLOAT = dynamic(3);
    public static final int ARG_TYPE_VALUE = dynamic(4);
    public static final int ARG_TYPE_WILDCARD_INT = dynamic(5);
    public static final int ARG_TYPE_OPERATOR = dynamic(6);
    public static final int ARG_TYPE_COMPARE_OPERATOR = dynamic(7);
    public static final int ARG_TYPE_TARGET = dynamic(8);
    public static final int ARG_TYPE_WILDCARD_TARGET = dynamic(10);

    public static final int ARG_TYPE_FILE_PATH = dynamic(17);

    public static final int ARG_TYPE_FULL_INTEGER_RANGE = dynamic(23);

    public static final int ARG_TYPE_EQUIPMENT_SLOT = dynamic(37);
    public static final int ARG_TYPE_STRING = dynamic(39);
    public static final int ARG_TYPE_BLOCK_POSITION = dynamic(47);
    public static final int ARG_TYPE_POSITION = dynamic(48);

    public static final int ARG_TYPE_MESSAGE = dynamic(51);
    public static final int ARG_TYPE_RAWTEXT = dynamic(53);
    public static final int ARG_TYPE_JSON = dynamic(57);
    public static final int ARG_TYPE_BLOCK_STATES = dynamic(67);
    public static final int ARG_TYPE_COMMAND = dynamic(70);

    public Map commands;
    @Deprecated
    public final Map> softEnums = new HashMap<>();
    public final List constraints = new ObjectArrayList<>();

    @Override
    public byte pid() {
        return NETWORK_ID;
    }

    @Override
    public void decode() {
        //non
    }

    @Override
    public void encode() {
        this.reset();
        Set enumValuesSet = new ObjectOpenHashSet<>();
        Set postfixSet = new ObjectOpenHashSet<>();
        Set enumsSet = new ObjectOpenHashSet<>();
        Set softEnumsSet = new ObjectOpenHashSet<>();

        // Get all enum values
        for (var entry : commands.entrySet()) {
            var data = entry.getValue().versions.get(0);
            if (data.aliases != null) {
                enumValuesSet.addAll(data.aliases.getValues());
                enumsSet.add(data.aliases);
            }

            for (CommandParameter[] overload : data.overloads.values().stream().map(o -> o.input.parameters).toList()) {
                for (CommandParameter parameter : overload) {
                    CommandEnum commandEnumData = parameter.enumData;
                    if (commandEnumData != null) {
                        if (commandEnumData.isSoft()) {
                            softEnumsSet.add(commandEnumData);
                        } else {
                            enumValuesSet.addAll(commandEnumData.getValues());
                            enumsSet.add(commandEnumData);
                        }
                    }

                    String postfix = parameter.postFix;
                    if (postfix != null) {
                        postfixSet.add(postfix);
                    }
                }
            }
        }

        // Add Constraint Enums
        // Not need it for now
        /*for(CommandEnumData enumData : packet.getConstraints().stream().map(CommandEnumConstraintData::getEnumData).collect(Collectors.toList())) {
            if (enumData.isSoft()) {
                softEnumsSet.add(enumData);
            } else {
                enumsSet.add(enumData);
            }
            enumValuesSet.addAll(Arrays.asList(enumData.getValues()));
        }*/

        List enumValues = new ObjectArrayList<>(enumValuesSet);
        List postFixes = new ObjectArrayList<>(postfixSet);
        List enums = new ObjectArrayList<>(enumsSet);
        List softEnums = new ObjectArrayList<>(softEnumsSet);

        putUnsignedVarInt(enumValues.size());
        for (var enumValue : enumValues) {
            this.putString(enumValue);
        }
        putUnsignedVarInt(postFixes.size());
        for (var postFix : postFixes) {
            this.putString(postFix);
        }

        this.writeEnums(enumValues, enums);

        this.putUnsignedVarInt(commands.size());
        for (var entry : commands.entrySet()) {
            this.writeCommand(entry, enums, softEnums, postFixes);
        }

        this.putUnsignedVarInt(softEnums.size());
        for (var softEnum : softEnums) {
            this.writeCommandEnum(softEnum);
        }

        // Constraints
        // Not need it for now
        /*helper.writeArray(buffer, packet.getConstraints(), (buf, constraint) -> {
            helper.writeCommandEnumConstraints(buf, constraint, enums, enumValues);
        });*/

        this.putUnsignedVarInt(0);
    }

    private void writeEnums(List values, List enums) {
        // Determine width of enum index
        ObjIntConsumer indexWriter;
        int valuesSize = values.size();
        if (valuesSize < 0x100) {//256
            indexWriter = WRITE_BYTE;
        } else if (valuesSize < 0x10000) {//65536
            indexWriter = WRITE_SHORT;
        } else {
            indexWriter = WRITE_INT;
        }
        // Write enums
        putUnsignedVarInt(enums.size());
        for (CommandEnum commandEnum : enums) {
            this.putString(commandEnum.getName());

            this.putUnsignedVarInt(commandEnum.getValues().size());
            for (String value : commandEnum.getValues()) {
                int index = values.indexOf(value);
                Preconditions.checkArgument(index > -1, "Invalid enum value detected: " + value);
                indexWriter.accept(this, index);
            }
        }
    }

    private void writeCommand(Map.Entry commandEntry, List enums, List softEnums, List postFixes) {
        var commandData = commandEntry.getValue().versions.get(0);
        this.putString(commandEntry.getKey());
        this.putString(commandData.description);
        int flags = 0;
        for (CommandData.Flag flag : commandData.flags) {
            flags |= 1 << flag.ordinal();
        }
        this.putLShort(flags);
        this.putByte((byte) commandData.permission);

        CommandEnum aliases = commandData.aliases;
        this.putLInt(commandData.aliases == null ? -1 : enums.indexOf(commandData.aliases));

        Collection overloads = commandData.overloads.values();
        this.putUnsignedVarInt(overloads.size());
        for (CommandOverload overload : overloads) {
            this.putUnsignedVarInt(overload.input.parameters.length);
            for (CommandParameter param : overload.input.parameters) {
                this.writeParameter(param, enums, softEnums, postFixes);
            }
        }
    }

    private void writeParameter(CommandParameter param, List enums, List softEnums, List postFixes) {
        this.putString(param.name);

        int index;
        if (param.postFix != null) {
            index = postFixes.indexOf(param.postFix) | ARG_FLAG_POSTFIX;
        } else if (param.enumData != null) {
            if (param.enumData.isSoft()) {
                index = softEnums.indexOf(param.enumData) | ARG_FLAG_SOFT_ENUM | ARG_FLAG_VALID;
            } else {
                index = enums.indexOf(param.enumData) | ARG_FLAG_ENUM | ARG_FLAG_VALID;
            }
        } else if (param.type != null) {
            index = param.type.getId() | ARG_FLAG_VALID;
        } else {
            throw new IllegalStateException("No param type specified: " + param);
        }

        this.putLInt(index);
        this.putBoolean(param.optional);

        byte options = 0;
        if (param.paramOptions != null) {
            for (CommandParamOption option : param.paramOptions) {
                options |= 1 << option.ordinal();
            }
        }
        this.putByte(options);
    }

    private void writeCommandEnum(CommandEnum enumData) {
        Preconditions.checkNotNull(enumData, "enumData");

        this.putString(enumData.getName());

        List values = enumData.getValues();
        this.putUnsignedVarInt(values.size());
        for (String value : values) {
            this.putString(value);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy