cssc: more parsers (nbt and chat component), fixes

This commit is contained in:
Bixilon 2020-12-25 18:13:11 +01:00
parent 73f95a4cc0
commit 04c12f8409
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
23 changed files with 498 additions and 82 deletions

View File

@ -86,7 +86,6 @@ public abstract class CommandNode {
}
String nextArgument = stringReader.readUnquotedString();
if (this.literalChildren.containsKey(nextArgument)) {
stringReader.skip(nextArgument.length());
this.literalChildren.get(nextArgument).isSyntaxCorrect(connection, stringReader);
return;
}

View File

@ -13,9 +13,17 @@
package de.bixilon.minosoft.data.commands;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.stream.JsonReader;
import de.bixilon.minosoft.data.commands.parser.exceptions.BooleanCommandParseException;
import de.bixilon.minosoft.data.commands.parser.exceptions.InvalidJSONCommandParseException;
import de.bixilon.minosoft.data.commands.parser.exceptions.StringCommandParseException;
import de.bixilon.minosoft.data.commands.parser.exceptions.identifier.InvalidIdentifierCommandParseException;
import de.bixilon.minosoft.data.commands.parser.exceptions.nbt.CompoundTagBadFormatCommandParseException;
import de.bixilon.minosoft.data.commands.parser.exceptions.nbt.ListTagBadFormatCommandParseException;
import de.bixilon.minosoft.data.commands.parser.exceptions.number.DoubleCommandParseException;
import de.bixilon.minosoft.data.commands.parser.exceptions.number.FloatCommandParseException;
import de.bixilon.minosoft.data.commands.parser.exceptions.number.IntegerCommandParseException;
@ -24,13 +32,25 @@ import de.bixilon.minosoft.data.commands.parser.exceptions.properties.BadPropert
import de.bixilon.minosoft.data.commands.parser.exceptions.properties.DuplicatedPropertyKeyCommandParseException;
import de.bixilon.minosoft.data.mappings.ModIdentifier;
import de.bixilon.minosoft.util.Pair;
import de.bixilon.minosoft.util.nbt.tag.CompoundTag;
import de.bixilon.minosoft.util.Util;
import de.bixilon.minosoft.util.nbt.tag.*;
import org.jetbrains.annotations.NotNull;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
public class CommandStringReader {
private static final Pattern NBT_PATTERN_INT = Pattern.compile("[-+]?(0|[1-9][0-9]*)");
private static final Pattern NBT_PATTERN_BYTE = Pattern.compile(NBT_PATTERN_INT.pattern() + "b");
private static final Pattern NBT_PATTERN_SHORT = Pattern.compile(NBT_PATTERN_INT.pattern() + "s");
private static final Pattern NBT_PATTERN_LONG = Pattern.compile(NBT_PATTERN_INT.pattern() + "l");
private static final Pattern NBT_PATTERN_FLOAT = Pattern.compile(NBT_PATTERN_INT.pattern() + "([.][0-9]*)?f");
private static final Pattern NBT_PATTERN_DOUBLE = Pattern.compile(NBT_PATTERN_INT.pattern() + "([.][0-9]*)?d?");
private static final Gson GSON = new Gson();
private final String string;
private int cursor;
@ -88,11 +108,15 @@ public class CommandStringReader {
}
public void skip(int length) {
this.cursor += length;
int nextLength = this.cursor + length;
if (nextLength > this.string.length() || nextLength < 0) {
throw new IllegalStateException("Nothing to skip!");
}
this.cursor = nextLength;
}
public void skip() {
this.cursor++;
skip(1);
}
/**
@ -148,7 +172,6 @@ public class CommandStringReader {
@NotNull
public Pair<String, String> readProperty() throws StringCommandParseException, BadPropertyMapCommandParseException {
skipWhitespaces();
StringBuilder builder = new StringBuilder();
String key = readString();
skipWhitespaces();
if (read() != '=') {
@ -162,11 +185,12 @@ public class CommandStringReader {
@NotNull
public Map<String, String> readProperties() throws StringCommandParseException, DuplicatedPropertyKeyCommandParseException, BadPropertyMapCommandParseException {
Map<String, String> ret = new HashMap<>();
if (peek() != '[') {
throw new BadPropertyMapCommandParseException(this, String.valueOf(peek()), "Not a property map!");
}
Map<String, String> ret = new HashMap<>();
skip();
skipWhitespaces();
if (peek() == ']') {
return ret;
}
@ -338,8 +362,124 @@ public class CommandStringReader {
return readStringUntil(true, terminator);
}
public CompoundTag readNBTCompoundTag() {
throw new IllegalArgumentException("TODO");
public ListTag readNBTListTag() throws ListTagBadFormatCommandParseException, StringCommandParseException, CompoundTagBadFormatCommandParseException {
skipWhitespaces();
if (peek() != '[') {
throw new ListTagBadFormatCommandParseException(this, String.valueOf(peek()), "Not a list tag!");
}
skip();
ListTag listTag = new ListTag();
skipWhitespaces();
if (peek() == ']') {
skip();
return listTag;
}
while (canRead()) {
int start = getCursor();
try {
listTag.addTag(readNBTTag());
} catch (IllegalArgumentException exception) {
throw new ListTagBadFormatCommandParseException(this, this.string.substring(start, getCursor()), exception);
}
skipWhitespaces();
if (peek() == ',') {
skip();
skipWhitespaces();
}
if (peek() == ']') {
skip();
return listTag;
}
}
throw new ListTagBadFormatCommandParseException(this, String.valueOf(peek()), "No closing tag!");
}
public NBTTag readNBTTag() throws StringCommandParseException, ListTagBadFormatCommandParseException, CompoundTagBadFormatCommandParseException {
// ToDo: Array tags
skipWhitespaces();
if (peek() == '[') {
return readNBTListTag();
}
if (peek() == '{') {
return readNBTCompoundTag();
}
String data = readString().toLowerCase(Locale.ROOT);
if (data.equals("true")) {
return new ByteTag(true);
}
if (data.equals("false")) {
return new ByteTag(false);
}
try {
if (NBT_PATTERN_BYTE.matcher(data).matches()) {
return new ByteTag(Byte.parseByte(data.substring(0, data.length() - 1)));
}
if (NBT_PATTERN_SHORT.matcher(data).matches()) {
return new ShortTag(Short.parseShort(data.substring(0, data.length() - 1)));
}
if (NBT_PATTERN_LONG.matcher(data).matches()) {
return new LongTag(Long.parseLong(data.substring(0, data.length() - 1)));
}
if (NBT_PATTERN_INT.matcher(data).matches()) {
return new IntTag(Integer.parseInt(data));
}
if (NBT_PATTERN_FLOAT.matcher(data).matches()) {
return new FloatTag(Float.parseFloat(data.substring(0, data.length() - 1)));
}
if (NBT_PATTERN_DOUBLE.matcher(data).matches()) {
return new DoubleTag(Double.parseDouble(data.endsWith("d") ? data.substring(0, data.length() - 1) : data));
}
} catch (NumberFormatException ignored) {
}
return new StringTag(data);
}
public CompoundTag readNBTCompoundTag() throws CompoundTagBadFormatCommandParseException, StringCommandParseException, ListTagBadFormatCommandParseException {
if (peek() != '{') {
throw new CompoundTagBadFormatCommandParseException(this, String.valueOf(peek()), "Not a compound tag!");
}
CompoundTag compoundTag = new CompoundTag();
skip();
skipWhitespaces();
if (peek() == '}') {
return compoundTag;
}
while (canRead()) {
String key = readString();
skipWhitespaces();
if (peek() != ':') {
throw new CompoundTagBadFormatCommandParseException(this, String.valueOf(peek()), "Invalid map char!");
}
skip();
NBTTag value = readNBTTag();
compoundTag.writeTag(key, value);
skipWhitespaces();
char nextChar = read();
if (nextChar == '}') {
return compoundTag;
} else {
skipWhitespaces();
if (nextChar != ',') {
throw new CompoundTagBadFormatCommandParseException(this, String.valueOf(nextChar), "Invalid map char!");
}
}
}
throw new CompoundTagBadFormatCommandParseException(this, String.valueOf(peek()), "No closing tag!");
}
public JsonElement readJson() throws InvalidJSONCommandParseException {
try {
JsonReader jsonReader = new JsonReader(new StringReader(getRemaining()));
jsonReader.setLenient(false);
JsonObject json = GSON.fromJson(jsonReader, JsonObject.class);
skip(Util.getJsonReaderPosition(jsonReader) - 1);
return json;
} catch (JsonParseException exception) {
throw new InvalidJSONCommandParseException(this, String.valueOf(peek()), exception);
}
}
public boolean readExpected(char... expected) {
@ -367,6 +507,9 @@ public class CommandStringReader {
@Override
public String toString() {
return String.format("position=%d/%d: \"%s\"", this.cursor, this.string.length(), peekRemaining());
if (canRead()) {
return String.format("position=%d/%d: \"%s\"", this.cursor, this.string.length(), peekRemaining());
}
return String.format("position=%d/%d", this.cursor, this.string.length());
}
}

View File

@ -13,10 +13,7 @@
package de.bixilon.minosoft.data.commands.parser
import de.bixilon.minosoft.data.commands.CommandStringReader
import de.bixilon.minosoft.data.commands.parser.exceptions.BlockNotFoundCommandParseException
import de.bixilon.minosoft.data.commands.parser.exceptions.BlockPropertyDuplicatedCommandParseException
import de.bixilon.minosoft.data.commands.parser.exceptions.CommandParseException
import de.bixilon.minosoft.data.commands.parser.exceptions.UnknownBlockPropertyCommandParseException
import de.bixilon.minosoft.data.commands.parser.exceptions.*
import de.bixilon.minosoft.data.commands.parser.properties.ParserProperties
import de.bixilon.minosoft.data.mappings.blocks.BlockProperties
import de.bixilon.minosoft.data.mappings.blocks.BlockRotations
@ -26,7 +23,13 @@ class BlockStateParser : CommandParser() {
@Throws(CommandParseException::class)
override fun isParsable(connection: Connection, properties: ParserProperties?, stringReader: CommandStringReader) {
val identifier = stringReader.readModIdentifier()
if (this == BLOCK_PREDICATE_PARSER) {
if (stringReader.peek() != '#') {
throw InvalidBlockPredicateCommandParseException(stringReader, stringReader.read().toString())
}
stringReader.skip()
}
val identifier = stringReader.readModIdentifier() // ToDo: check tags
if (!connection.mapping.doesBlockExist(identifier.value)) {
throw BlockNotFoundCommandParseException(stringReader, identifier.key)
}
@ -51,11 +54,17 @@ class BlockStateParser : CommandParser() {
val blockProperty = blockPropertyKey[pair.value] ?: throw UnknownBlockPropertyCommandParseException(stringReader, pair.value)
allProperties.add(blockProperty)
}
}
if (this == BLOCK_PREDICATE_PARSER) {
if (stringReader.canRead() && stringReader.peek() == '{') {
stringReader.readNBTCompoundTag()
}
}
}
companion object {
val BLOCK_STACK_PARSER = BlockStateParser()
val BLOCK_PREDICATE_PARSER = BlockStateParser()
}
}

View File

@ -33,13 +33,13 @@ object CommandParsers {
ModIdentifier("vec3") to Vec3Parser.VEC3_PARSER,
ModIdentifier("vec2") to Vec2Parser.VEC2_PARSER,
ModIdentifier("block_state") to BlockStateParser.BLOCK_STACK_PARSER,
// block_predicate
ModIdentifier("block_predicate") to BlockStateParser.BLOCK_PREDICATE_PARSER,
ModIdentifier("item_stack") to ItemStackParser.ITEM_STACK_PARSER,
// item_predicate
ModIdentifier("item_predicate") to ItemStackParser.ITEM_PREDICATE_PARSER,
ModIdentifier("color") to ColorParser.COLOR_PARSER,
// chat component
ModIdentifier("component") to ComponentParser.COMPONENT_PARSER,
ModIdentifier("message") to MessageParser.MESSAGE_PARSER,
// nbt
ModIdentifier("nbt") to NBTParser.NBT_PARSER,
// nbt_path
ModIdentifier("objective") to ObjectiveParser.OBJECTIVE_PARSER,
// objective_criteria
@ -62,8 +62,8 @@ object CommandParsers {
ModIdentifier("entity_summon") to IdentifierListParser.SUMMONABLE_ENTITY_PARSER,
ModIdentifier("dimension") to IdentifierListParser.DIMENSION_EFFECT_PARSER,
ModIdentifier("uuid") to UUIDParser.UUID_PARSER,
// nbt_tag
// nbt_compound_tag
ModIdentifier("nbt_tag") to NBTParser.NBT_TAG_PARSER,
ModIdentifier("nbt_compound_tag") to NBTParser.NBT_COMPOUND_PARSER,
ModIdentifier("time") to TimeParser.TIME_PARSER
)
)

View File

@ -0,0 +1,37 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.commands.parser
import de.bixilon.minosoft.data.commands.CommandStringReader
import de.bixilon.minosoft.data.commands.parser.exceptions.CommandParseException
import de.bixilon.minosoft.data.commands.parser.exceptions.InvalidComponentCommandParseException
import de.bixilon.minosoft.data.commands.parser.properties.ParserProperties
import de.bixilon.minosoft.data.text.BaseComponent
import de.bixilon.minosoft.protocol.network.Connection
class ComponentParser : CommandParser() {
@Throws(CommandParseException::class)
override fun isParsable(connection: Connection, properties: ParserProperties?, stringReader: CommandStringReader) {
try {
BaseComponent(stringReader.readJson().asJsonObject)
} catch (exception: Exception) {
stringReader.skip(-1)
throw InvalidComponentCommandParseException(stringReader, stringReader.read().toString(), exception)
}
}
companion object {
val COMPONENT_PARSER = ComponentParser()
}
}

View File

@ -32,7 +32,7 @@ abstract class CoordinateParser : CommandParser() {
if (notation != CoordinateNotations.NONE) {
stringReader.skip()
}
if (!stringReader.canRead() || stringReader.peek() != ' ') {
if (!stringReader.canRead() || stringReader.peek() == ' ') {
return notation
}
if (allowDecimal) {

View File

@ -92,12 +92,10 @@ public class EntityParser extends CommandParser {
}
}
String value = stringReader.readUnquotedString();
try {
Util.doesStringEqualsRegex(value, ProtocolDefinition.MINECRAFT_NAME_VALIDATOR);
return;
} catch (IllegalArgumentException ignored) {
}
if (ProtocolDefinition.MINECRAFT_NAME_VALIDATOR.matcher(value).matches()) {
return;
}
if (entityParserProperties.isOnlyPlayers()) {
throw new PlayerOnlyEntityCommandParseException(stringReader, value);
}

View File

@ -14,6 +14,7 @@ package de.bixilon.minosoft.data.commands.parser
import de.bixilon.minosoft.data.commands.CommandStringReader
import de.bixilon.minosoft.data.commands.parser.exceptions.CommandParseException
import de.bixilon.minosoft.data.commands.parser.exceptions.InvalidItemPredicateCommandParseException
import de.bixilon.minosoft.data.commands.parser.exceptions.identifier.ItemNotFoundCommandParseException
import de.bixilon.minosoft.data.commands.parser.properties.ParserProperties
import de.bixilon.minosoft.protocol.network.Connection
@ -22,7 +23,13 @@ class ItemStackParser : CommandParser() {
@Throws(CommandParseException::class)
override fun isParsable(connection: Connection, properties: ParserProperties?, stringReader: CommandStringReader) {
val argument = stringReader.readModIdentifier()
if (this == ITEM_PREDICATE_PARSER) {
if (stringReader.peek() != '#') {
throw InvalidItemPredicateCommandParseException(stringReader, stringReader.read().toString())
}
stringReader.skip()
}
val argument = stringReader.readModIdentifier() // ToDo: Check predicates
if (!connection.mapping.doesItemExist(argument.value)) {
throw ItemNotFoundCommandParseException(stringReader, argument.key)
}
@ -33,5 +40,6 @@ class ItemStackParser : CommandParser() {
companion object {
val ITEM_STACK_PARSER = ItemStackParser()
val ITEM_PREDICATE_PARSER = ItemStackParser()
}
}

View File

@ -0,0 +1,47 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.commands.parser
import de.bixilon.minosoft.data.commands.CommandStringReader
import de.bixilon.minosoft.data.commands.parser.exceptions.CommandParseException
import de.bixilon.minosoft.data.commands.parser.exceptions.nbt.ExpectedPrimitiveTagCommandParseException
import de.bixilon.minosoft.data.commands.parser.properties.ParserProperties
import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.util.nbt.tag.CompoundTag
class NBTParser : CommandParser() {
@Throws(CommandParseException::class)
override fun isParsable(connection: Connection, properties: ParserProperties?, stringReader: CommandStringReader) {
when (this) {
NBT_PARSER -> {
stringReader.readNBTTag()
}
NBT_TAG_PARSER -> {
val startPos = stringReader.cursor
val tag = stringReader.readNBTTag()
if (tag!! is CompoundTag) {
throw ExpectedPrimitiveTagCommandParseException(stringReader, stringReader.string.substring(startPos, stringReader.cursor), "Compound Tag is invalid here!")
}
}
NBT_COMPOUND_PARSER -> stringReader.readNBTCompoundTag()
}
}
companion object {
val NBT_PARSER = NBTParser()
val NBT_TAG_PARSER = NBTParser()
val NBT_COMPOUND_PARSER = NBTParser()
}
}

View File

@ -18,17 +18,14 @@ import de.bixilon.minosoft.data.commands.parser.exceptions.identifier.InvalidIde
import de.bixilon.minosoft.data.commands.parser.properties.ParserProperties
import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.Util
class ObjectiveParser : CommandParser() {
@Throws(CommandParseException::class)
override fun isParsable(connection: Connection, properties: ParserProperties?, stringReader: CommandStringReader) {
val argument = stringReader.readUnquotedString()
try {
Util.doesStringEqualsRegex(argument, ProtocolDefinition.SCOREBOARD_OBJECTIVE_PATTERN)
} catch (exception: IllegalArgumentException) {
throw InvalidIdentifierCommandParseException(stringReader, argument, exception)
if (!ProtocolDefinition.SCOREBOARD_OBJECTIVE_PATTERN.matcher(argument).matches()) {
throw InvalidIdentifierCommandParseException(stringReader, argument)
}
}

View File

@ -0,0 +1,25 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.commands.parser.exceptions
import de.bixilon.minosoft.data.commands.CommandStringReader
class InvalidBlockPredicateCommandParseException : CommandParseException {
constructor(command: CommandStringReader, currentArgument: String) : super(ERROR_MESSAGE, command, currentArgument)
constructor(command: CommandStringReader, currentArgument: String, cause: Throwable) : super(ERROR_MESSAGE, command, currentArgument, cause)
companion object {
private const val ERROR_MESSAGE = "Invalid block predicate!"
}
}

View File

@ -0,0 +1,25 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.commands.parser.exceptions
import de.bixilon.minosoft.data.commands.CommandStringReader
class InvalidComponentCommandParseException : CommandParseException {
constructor(command: CommandStringReader, currentArgument: String) : super(ERROR_MESSAGE, command, currentArgument)
constructor(command: CommandStringReader, currentArgument: String, cause: Throwable) : super(ERROR_MESSAGE, command, currentArgument, cause)
companion object {
private const val ERROR_MESSAGE = "Malformed component!"
}
}

View File

@ -0,0 +1,25 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.commands.parser.exceptions
import de.bixilon.minosoft.data.commands.CommandStringReader
class InvalidItemPredicateCommandParseException : CommandParseException {
constructor(command: CommandStringReader, currentArgument: String) : super(ERROR_MESSAGE, command, currentArgument)
constructor(command: CommandStringReader, currentArgument: String, cause: Throwable) : super(ERROR_MESSAGE, command, currentArgument, cause)
companion object {
private const val ERROR_MESSAGE = "Invalid item predicate!"
}
}

View File

@ -0,0 +1,25 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.commands.parser.exceptions
import de.bixilon.minosoft.data.commands.CommandStringReader
class InvalidJSONCommandParseException : CommandParseException {
constructor(command: CommandStringReader, currentArgument: String) : super(ERROR_MESSAGE, command, currentArgument)
constructor(command: CommandStringReader, currentArgument: String, cause: Throwable) : super(ERROR_MESSAGE, command, currentArgument, cause)
companion object {
private const val ERROR_MESSAGE = "Malformed json!"
}
}

View File

@ -17,7 +17,7 @@ import de.bixilon.minosoft.data.commands.CommandStringReader;
public class UnknownCommandParseException extends CommandParseException {
private static final String ERROR_MESSAGE = "Unknown command!";
private static final String ERROR_MESSAGE = "Unknown command!" + 'a';
public UnknownCommandParseException(CommandStringReader command, String currentArgument) {
super(ERROR_MESSAGE, command, currentArgument);

View File

@ -0,0 +1,22 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.commands.parser.exceptions.nbt
import de.bixilon.minosoft.data.commands.CommandStringReader
import de.bixilon.minosoft.data.commands.parser.exceptions.CommandParseException
class CompoundTagBadFormatCommandParseException : CommandParseException {
constructor(command: CommandStringReader, currentArgument: String, message: String) : super(message, command, currentArgument)
constructor(command: CommandStringReader, currentArgument: String, cause: Throwable, message: String) : super(message, command, currentArgument, cause)
}

View File

@ -0,0 +1,22 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.commands.parser.exceptions.nbt
import de.bixilon.minosoft.data.commands.CommandStringReader
import de.bixilon.minosoft.data.commands.parser.exceptions.CommandParseException
class ExpectedPrimitiveTagCommandParseException : CommandParseException {
constructor(command: CommandStringReader, currentArgument: String, message: String) : super(message, command, currentArgument)
constructor(command: CommandStringReader, currentArgument: String, cause: Throwable, message: String) : super(message, command, currentArgument, cause)
}

View File

@ -0,0 +1,22 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.commands.parser.exceptions.nbt
import de.bixilon.minosoft.data.commands.CommandStringReader
import de.bixilon.minosoft.data.commands.parser.exceptions.CommandParseException
class ListTagBadFormatCommandParseException : CommandParseException {
constructor(command: CommandStringReader, currentArgument: String, message: String) : super(message, command, currentArgument)
constructor(command: CommandStringReader, currentArgument: String, cause: Throwable) : super(cause.message, command, currentArgument, cause)
}

View File

@ -14,7 +14,6 @@
package de.bixilon.minosoft.data.mappings;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
import de.bixilon.minosoft.util.Util;
import java.util.Objects;
@ -44,9 +43,7 @@ public class ModIdentifier {
}
public static ModIdentifier getIdentifier(String identifier) throws IllegalArgumentException {
try {
Util.doesStringEqualsRegex(identifier, ProtocolDefinition.IDENTIFIER_PATTERN);
} catch (IllegalArgumentException e) {
if (!ProtocolDefinition.IDENTIFIER_PATTERN.matcher(identifier).matches()) {
throw new IllegalArgumentException(String.format("%s in not a valid identifier!", identifier));
}

View File

@ -95,9 +95,7 @@ public class OfflineLoginController implements Initializable {
}
private void checkData(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
try {
Util.doesStringEqualsRegex(this.username.getText(), ProtocolDefinition.MINECRAFT_NAME_VALIDATOR);
} catch (IllegalArgumentException e) {
if (!ProtocolDefinition.MINECRAFT_NAME_VALIDATOR.matcher(this.username.getText()).matches()) {
this.addButton.setDisable(true);
return;
}

View File

@ -15,7 +15,6 @@ package de.bixilon.minosoft.protocol.packets.clientbound.play;
import de.bixilon.minosoft.data.commands.CommandNode;
import de.bixilon.minosoft.data.commands.CommandRootNode;
import de.bixilon.minosoft.data.commands.parser.exceptions.CommandParseException;
import de.bixilon.minosoft.logging.Log;
import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
@ -38,26 +37,6 @@ public class PacketDeclareCommands extends ClientboundPacket {
@Override
public void handle(Connection connection) {
connection.setCommandRootNode(getRootNode());
// ToDo: Remove these dummy commands
String[] commands = {
"setblock ~3 ^3 3 minecraft:anvil",
"setblock ~3 3 3 minecraft:anvila",
"setblock ~3 3 3 minecraft:anvil[facing=east]",
"setblock ~3 3 3 minecraft:anvil[facing=\"east\"]",
"setblock ~3 3 3 minecraft:anvil[facing=east\"]",
"setblock ~3 3 3 minecraft:anvil[facing=aeast]",
};
for (String command : commands) {
try {
getRootNode().isSyntaxCorrect(connection, command);
Log.game("Command \"%s\" is valid", command);
} catch (CommandParseException e) {
Log.game("Command \"%s\" is invalid, %s: %s", command, e.getClass().getSimpleName(), e.getErrorMessage());
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override

View File

@ -16,6 +16,7 @@ package de.bixilon.minosoft.util;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
@ -23,6 +24,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
@ -32,7 +34,6 @@ import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.*;
@ -42,6 +43,23 @@ public final class Util {
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
private static final Random THREAD_LOCAL_RANDOM = ThreadLocalRandom.current();
private static final Field JSON_READER_POS_FIELD;
private static final Field JSON_READER_LINE_START_FIELD;
static {
new JsonReader(new StringReader(""));
Class<?> jsonReadClass = JsonReader.class;
try {
JSON_READER_POS_FIELD = jsonReadClass.getDeclaredField("pos");
JSON_READER_POS_FIELD.setAccessible(true);
JSON_READER_LINE_START_FIELD = jsonReadClass.getDeclaredField("lineStart");
JSON_READER_LINE_START_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static UUID getUUIDFromString(String uuid) {
uuid = uuid.trim();
if (uuid.length() == 36) {
@ -292,10 +310,11 @@ public final class Util {
return !string.toLowerCase().equals(string);
}
public static void doesStringEqualsRegex(String input, Pattern pattern) throws IllegalArgumentException {
Matcher matcher = pattern.matcher(input);
if (!matcher.find() || !matcher.group().equals(input)) {
throw new IllegalArgumentException();
public static int getJsonReaderPosition(JsonReader jsonReader) {
try {
return JSON_READER_POS_FIELD.getInt(jsonReader) - JSON_READER_LINE_START_FIELD.getInt(jsonReader) + 1;
} catch (IllegalAccessException e) {
throw new IllegalStateException();
}
}
}

View File

@ -20,36 +20,43 @@ import java.util.ArrayList;
import java.util.Arrays;
public class ListTag extends NBTTag {
private final TagTypes type;
private final ArrayList<NBTTag> list;
private TagTypes type;
public ListTag(TagTypes type, ArrayList<NBTTag> list) {
this.type = type;
this.list = list;
}
public ListTag(TagTypes type, NBTTag[] list) {
public ListTag(TagTypes type, NBTTag... list) {
this.type = type;
this.list = new ArrayList<>(Arrays.asList(list));
}
public ListTag() {
this.type = TagTypes.BYTE; // idk, default value?
this.list = new ArrayList<>();
}
public ListTag(InByteBuffer buffer) {
this.type = TagTypes.byId(new ByteTag(buffer).getValue());
int length = new IntTag(buffer).getValue();
this.list = new ArrayList<>();
for (int i = 0; i < length; i++) {
switch (this.type) {
case BYTE -> this.list.add(new ByteTag(buffer));
case SHORT -> this.list.add(new ShortTag(buffer));
case INT -> this.list.add(new IntTag(buffer));
case LONG -> this.list.add(new LongTag(buffer));
case FLOAT -> this.list.add(new FloatTag(buffer));
case DOUBLE -> this.list.add(new DoubleTag(buffer));
case BYTE_ARRAY -> this.list.add(new ByteArrayTag(buffer));
case STRING -> this.list.add(new StringTag(buffer));
case LIST -> this.list.add(new ListTag(buffer));
case COMPOUND -> this.list.add(new CompoundTag(true, buffer));
}
this.list.add(switch (this.type) {
case BYTE -> new ByteTag(buffer);
case SHORT -> new ShortTag(buffer);
case INT -> new IntTag(buffer);
case LONG -> new LongTag(buffer);
case FLOAT -> new FloatTag(buffer);
case DOUBLE -> new DoubleTag(buffer);
case BYTE_ARRAY -> new ByteArrayTag(buffer);
case STRING -> new StringTag(buffer);
case LIST -> new ListTag(buffer);
case COMPOUND -> new CompoundTag(true, buffer);
default -> throw new IllegalStateException("Unexpected value: " + this.type);
});
}
}
@ -64,11 +71,23 @@ public class ListTag extends NBTTag {
new IntTag(this.list.size()).writeBytes(buffer);
for (NBTTag NBTTag : this.list) {
NBTTag.writeBytes(buffer);
for (NBTTag tag : this.list) {
tag.writeBytes(buffer);
}
}
public ListTag addTag(NBTTag tag) {
if (this.type == null) {
this.type = tag.getType();
} else {
if (this.type != tag.getType()) {
throw new IllegalArgumentException("Can not mix types!");
}
}
this.list.add(tag);
return this;
}
@SuppressWarnings("unchecked")
public <K extends NBTTag> ArrayList<K> getValue() {
return (ArrayList<K>) this.list;