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.utils.BinaryStream;
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;
    public final Map> softEnums = new HashMap<>();

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

    @Override
    public void decode() {

    }

    @Override
    public void encode() {
        this.reset();

        LinkedHashSet enumValuesSet = new LinkedHashSet<>();
        LinkedHashSet postFixesSet = new LinkedHashSet<>();
        LinkedHashSet enumsSet = new LinkedHashSet<>();

        commands.forEach((name, data) -> {
            CommandData cmdData = data.versions.get(0);

            if (cmdData.aliases != null) {
                enumsSet.add(cmdData.aliases);

                enumValuesSet.addAll(cmdData.aliases.getValues());
            }

            for (CommandOverload overload : cmdData.overloads.values()) {
                for (CommandParameter parameter : overload.input.parameters) {
                    if (parameter.enumData != null) {
                        enumsSet.add(parameter.enumData);

                        enumValuesSet.addAll(parameter.enumData.getValues());
                    }

                    if (parameter.postFix != null) {
                        postFixesSet.add(parameter.postFix);
                    }
                }
            }
        });

        List enumValues = new ArrayList<>(enumValuesSet);
        List enums = new ArrayList<>(enumsSet);
        List postFixes = new ArrayList<>(postFixesSet);

        this.putUnsignedVarInt(enumValues.size());
        enumValues.forEach(this::putString);

        this.putUnsignedVarInt(postFixes.size());
        postFixes.forEach(this::putString);

        ObjIntConsumer indexWriter;
        if (enumValues.size() < 256) {
            indexWriter = WRITE_BYTE;
        } else if (enumValues.size() < 65536) {
            indexWriter = WRITE_SHORT;
        } else {
            indexWriter = WRITE_INT;
        }

        this.putUnsignedVarInt(enums.size());
        enums.forEach((cmdEnum) -> {
            putString(cmdEnum.getName());

            List values = cmdEnum.getValues();
            putUnsignedVarInt(values.size());

            for (String val : values) {
                int i = enumValues.indexOf(val);

                if (i < 0) {
                    throw new IllegalStateException("Enum value '" + val + "' not found");
                }

                indexWriter.accept(this, i);
            }
        });

        putUnsignedVarInt(commands.size());

        commands.forEach((name, cmdData) -> {
            CommandData data = cmdData.versions.get(0);

            putString(name);
            putString(data.description);

            int flags = 0;
            for (CommandData.Flag flag : data.flags) {
                flags |= 1 << flag.ordinal();
            }
            putLShort(flags);

            putByte((byte) data.permission);

            putLInt(data.aliases == null ? -1 : enums.indexOf(data.aliases));

            putUnsignedVarInt(data.overloads.size());
            for (CommandOverload overload : data.overloads.values()) {
                putUnsignedVarInt(overload.input.parameters.length);

                for (CommandParameter parameter : overload.input.parameters) {
                    putString(parameter.name);

                    int type = 0;
                    if (parameter.postFix != null) {
                        int i = postFixes.indexOf(parameter.postFix);
                        if (i < 0) {
                            throw new IllegalStateException("Postfix '" + parameter.postFix + "' isn't in postfix array");
                        }
                        type = ARG_FLAG_POSTFIX | i;
                    } else {
                        type |= ARG_FLAG_VALID;
                        if (parameter.enumData != null) {
                            type |= ARG_FLAG_ENUM | enums.indexOf(parameter.enumData);
                        } else {
                            type |= parameter.type.getId();
                        }
                    }

                    putLInt(type);
                    putBoolean(parameter.optional);
//                    putByte(parameter.options); // TODO: 19/03/2019 Bit flags. Only first bit is used for GameRules.
                    byte options = 0;

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

        this.putUnsignedVarInt(softEnums.size());

        softEnums.forEach((name, values) -> {
            this.putString(name);
            this.putUnsignedVarInt(values.size());
            values.forEach(this::putString);
        });

        this.putUnsignedVarInt(0);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy