diff --git a/src/main/java/de/bixilon/minosoft/commands/errors/ReaderError.kt b/src/main/java/de/bixilon/minosoft/commands/errors/ReaderError.kt index 32e153093..f85bb6ac1 100644 --- a/src/main/java/de/bixilon/minosoft/commands/errors/ReaderError.kt +++ b/src/main/java/de/bixilon/minosoft/commands/errors/ReaderError.kt @@ -19,4 +19,4 @@ abstract class ReaderError( val reader: CommandReader, val start: Int, val end: Int, -) : Exception("Error at $start-$end: ${reader.string}") +) : Exception("Error at $start-$end: ${reader.string} (at ${reader.string.substring(start, end)}") diff --git a/src/main/java/de/bixilon/minosoft/commands/errors/reader/map/DuplicatedKeyMapError.kt b/src/main/java/de/bixilon/minosoft/commands/errors/reader/map/DuplicatedKeyMapError.kt new file mode 100644 index 000000000..26f3449ce --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/commands/errors/reader/map/DuplicatedKeyMapError.kt @@ -0,0 +1,24 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.commands.errors.reader.map + +import de.bixilon.minosoft.commands.errors.ReaderError +import de.bixilon.minosoft.commands.util.CommandReader +import de.bixilon.minosoft.commands.util.ReadResult + +class DuplicatedKeyMapError( + reader: CommandReader, + val key: ReadResult<*>, + val existingValue: Any?, +) : ReaderError(reader, key.start, key.end) diff --git a/src/main/java/de/bixilon/minosoft/commands/errors/reader/map/ExpectedKeyMapError.kt b/src/main/java/de/bixilon/minosoft/commands/errors/reader/map/ExpectedKeyMapError.kt new file mode 100644 index 000000000..bba080dc1 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/commands/errors/reader/map/ExpectedKeyMapError.kt @@ -0,0 +1,23 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.commands.errors.reader.map + +import de.bixilon.minosoft.commands.errors.ReaderError +import de.bixilon.minosoft.commands.util.CommandReader +import de.bixilon.minosoft.commands.util.ReadResult + +class ExpectedKeyMapError( + reader: CommandReader, + result: ReadResult<*>, +) : ReaderError(reader, result.start, result.end) diff --git a/src/main/java/de/bixilon/minosoft/commands/errors/reader/map/InvalidAssignCharMapError.kt b/src/main/java/de/bixilon/minosoft/commands/errors/reader/map/InvalidAssignCharMapError.kt new file mode 100644 index 000000000..caa02a09b --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/commands/errors/reader/map/InvalidAssignCharMapError.kt @@ -0,0 +1,23 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.commands.errors.reader.map + +import de.bixilon.minosoft.commands.errors.ReaderError +import de.bixilon.minosoft.commands.util.CommandReader + +class InvalidAssignCharMapError( + reader: CommandReader, + pointer: Int, + val found: Int?, +) : ReaderError(reader, pointer - 1, pointer) diff --git a/src/main/java/de/bixilon/minosoft/commands/errors/reader/map/InvalidMapSeparatorError.kt b/src/main/java/de/bixilon/minosoft/commands/errors/reader/map/InvalidMapSeparatorError.kt new file mode 100644 index 000000000..c2d3fb299 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/commands/errors/reader/map/InvalidMapSeparatorError.kt @@ -0,0 +1,23 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.commands.errors.reader.map + +import de.bixilon.minosoft.commands.errors.ReaderError +import de.bixilon.minosoft.commands.util.CommandReader + +class InvalidMapSeparatorError( + reader: CommandReader, + pointer: Int, + val found: Int?, +) : ReaderError(reader, pointer - 1, pointer) diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetParser.kt b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetParser.kt index c0f9c0e12..b7c0ed988 100644 --- a/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetParser.kt +++ b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetParser.kt @@ -22,7 +22,13 @@ import de.bixilon.minosoft.commands.parser.minecraft.target.targets.identifier.n import de.bixilon.minosoft.commands.parser.minecraft.target.targets.identifier.name.NameEntityTarget import de.bixilon.minosoft.commands.parser.minecraft.target.targets.identifier.uuid.InvalidUUIDError import de.bixilon.minosoft.commands.parser.minecraft.target.targets.identifier.uuid.UUIDEntityTarget +import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.SelectorEntityTarget +import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.error.InvalidSelectorKeyError +import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.error.InvalidTargetSelector +import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.TargetProperties +import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.TargetProperty import de.bixilon.minosoft.commands.util.CommandReader +import de.bixilon.minosoft.commands.util.ReadResult import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.text.ChatComponent import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection @@ -40,7 +46,7 @@ class TargetParser( override val placeholder = ChatComponent.of("") override fun parse(reader: CommandReader): EntityTarget { - if (!reader.canPeekNext()) { + if (!reader.canPeek()) { throw ExpectedArgumentError(reader) } return if (reader.peek() == '@'.code) { @@ -50,8 +56,26 @@ class TargetParser( } } - fun CommandReader.parseSelector(): EntityTarget { - TODO() + fun CommandReader.parseSelector(): SelectorEntityTarget { + unsafeRead('@'.code) + val selectorChar = readNext() ?: throw ExpectedArgumentError(this) + val selector = TargetSelectors.BY_CHAR[selectorChar.toChar()] ?: throw InvalidTargetSelector(this) + + val properties: Map = readMap({ readKey() }, { readValue(it) }) ?: emptyMap() + + return SelectorEntityTarget(selector, properties) + } + + private fun CommandReader.readKey(): String? { + if (peek() == '"'.code) { + return readUnquotedString() + } + return readUntil('='.code) + } + + private fun CommandReader.readValue(key: ReadResult): TargetProperty { + val target = TargetProperties[key.result] ?: throw InvalidSelectorKeyError(this, key) + return target.read(this) } fun parseEntityIdentifier(reader: CommandReader): EntityTarget { diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetProperties.kt b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetProperties.kt index 385341880..4a85f78ed 100644 --- a/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetProperties.kt +++ b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetProperties.kt @@ -13,20 +13,13 @@ package de.bixilon.minosoft.commands.parser.minecraft.target -import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.DistanceProperty -import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.GamemodeProperty -import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.NameProperty -import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.TypeProperty -import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.rotation.PitchRotation -import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.rotation.YawRotation import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.sort.Sorting -data class TargetProperties( - val selector: TargetSelectors, +@Deprecated("") +data class TargetPropertiesLEGACY( var x: Double?, var y: Double?, var z: Double?, - var distance: DistanceProperty?, var volumeX: Double?, var volumeY: Double?, var volumeZ: Double?, @@ -36,11 +29,6 @@ data class TargetProperties( var sort: Sorting?, var limit: Int? = null, var level: IntRange? = null, - var gamemode: GamemodeProperty? = null, - var name: NameProperty? = null, - var xRotation: PitchRotation? = null, - var yRotation: YawRotation? = null, - var type: TypeProperty? = null, var nbt: Any? = null, // ToDo var advancements: Any? = null, // ToDo var predicate: Any? = null, // ToDo diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetSelectors.kt b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetSelectors.kt index c3125bce4..abcb8ad1f 100644 --- a/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetSelectors.kt +++ b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetSelectors.kt @@ -13,6 +13,8 @@ package de.bixilon.minosoft.commands.parser.minecraft.target +import de.bixilon.kutil.enums.EnumUtil +import de.bixilon.kutil.enums.ValuesEnum import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.sort.Sorting import de.bixilon.minosoft.data.entities.entities.Entity @@ -30,4 +32,16 @@ enum class TargetSelectors( fun sort(selected: MutableList) { sorting.sort(selected) } + + companion object : ValuesEnum { + override val VALUES: Array = values() + override val NAME_MAP: Map = EnumUtil.getEnumValues(VALUES) + val BY_CHAR: MutableMap = mutableMapOf() + + init { + for (value in VALUES) { + BY_CHAR[value.char] = value + } + } + } } diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/SelectorParserError.kt b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/SelectorParserError.kt new file mode 100644 index 000000000..ec42baf12 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/SelectorParserError.kt @@ -0,0 +1,24 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector + +import de.bixilon.minosoft.commands.errors.parser.ParserError +import de.bixilon.minosoft.commands.util.CommandReader +import de.bixilon.minosoft.commands.util.ReadResult + + +class SelectorParserError( + reader: CommandReader, + result: ReadResult<*>, +) : ParserError(reader, result) diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/error/InvalidSelectorKeyError.kt b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/error/InvalidSelectorKeyError.kt new file mode 100644 index 000000000..97aecebcc --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/error/InvalidSelectorKeyError.kt @@ -0,0 +1,23 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.error + +import de.bixilon.minosoft.commands.errors.parser.ParserError +import de.bixilon.minosoft.commands.util.CommandReader +import de.bixilon.minosoft.commands.util.ReadResult + +class InvalidSelectorKeyError( + reader: CommandReader, + key: ReadResult, +) : ParserError(reader, key) diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/error/InvalidTargetSelector.kt b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/error/InvalidTargetSelector.kt new file mode 100644 index 000000000..e1baa4d5b --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/error/InvalidTargetSelector.kt @@ -0,0 +1,21 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.error + +import de.bixilon.minosoft.commands.errors.ReaderError +import de.bixilon.minosoft.commands.util.CommandReader + +class InvalidTargetSelector( + reader: CommandReader, +) : ReaderError(reader, reader.pointer - 1, reader.pointer) diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/properties/TargetProperties.kt b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/properties/TargetProperties.kt index baf6e4bc7..5f53f3c43 100644 --- a/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/properties/TargetProperties.kt +++ b/src/main/java/de/bixilon/minosoft/commands/parser/minecraft/target/targets/selector/properties/TargetProperties.kt @@ -34,4 +34,8 @@ object TargetProperties { fun register(factory: TargetPropertyFactory<*>) { properties[factory.name] = factory } + + operator fun get(key: String): TargetPropertyFactory<*>? { + return properties[key] + } } diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParser.kt b/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParser.kt index ca94cf776..9542e2705 100644 --- a/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParser.kt +++ b/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParser.kt @@ -37,7 +37,7 @@ class EnumParser>( } fun CommandReader.readEnum(): E? { - return values.getOrNull(readString()?.lowercase()) // ToDo: Allow ordinals + return values.getOrNull(readWord()?.lowercase()) // ToDo: Allow ordinals } override fun getSuggestions(reader: CommandReader): List { diff --git a/src/main/java/de/bixilon/minosoft/commands/util/CommandReader.kt b/src/main/java/de/bixilon/minosoft/commands/util/CommandReader.kt index 0645668a3..598fc60b6 100644 --- a/src/main/java/de/bixilon/minosoft/commands/util/CommandReader.kt +++ b/src/main/java/de/bixilon/minosoft/commands/util/CommandReader.kt @@ -13,7 +13,12 @@ package de.bixilon.minosoft.commands.util +import de.bixilon.kutil.cast.CastUtil.unsafeCast import de.bixilon.minosoft.commands.errors.reader.* +import de.bixilon.minosoft.commands.errors.reader.map.DuplicatedKeyMapError +import de.bixilon.minosoft.commands.errors.reader.map.ExpectedKeyMapError +import de.bixilon.minosoft.commands.errors.reader.map.InvalidAssignCharMapError +import de.bixilon.minosoft.commands.errors.reader.map.InvalidMapSeparatorError import de.bixilon.minosoft.commands.errors.reader.number.NegativeNumberError import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.util.KUtil.toResourceLocation @@ -214,7 +219,7 @@ open class CommandReader(val string: String) { } val builder = StringBuilder() while (true) { - val peek = peek() + val peek = peekNext() if (peek == null) { if (required) { throw OutOfBoundsError(this, length - 1) @@ -308,6 +313,51 @@ open class CommandReader(val string: String) { return ReadResult(start, end, read, result) } + fun readMap(keyReader: CommandReader.() -> K?, valueReader: CommandReader.(key: ReadResult) -> V): Map? { + if (!canPeekNext()) { + return null + } + if (peekNext() != '['.code) { + return null + } + readNext() // [ + val map: MutableMap = mutableMapOf() + while (true) { + if (peek() == ']'.code) { + break + } + skipWhitespaces() + val key = readResult { keyReader(this) } + if (key.result == null) { + throw ExpectedKeyMapError(this, key) + } + val existing: V? = map[key.result] + if (existing != null) { + throw DuplicatedKeyMapError(this, key, existing) + } + val assign = read() + if (assign != '='.code) { + throw InvalidAssignCharMapError(this, pointer - 1, assign) + } + skipWhitespaces() + val value = valueReader(this, key.unsafeCast()) + map[key.result] = value + val end = read() + if (end == ']'.code) { + break + } + if (end != ','.code) { + throw InvalidMapSeparatorError(this, pointer - 1, end) + } + } + + return map + } + + override fun toString(): String { + return string.substring(pointer, string.length) + } + companion object { const val STRING_QUOTE = '"'.code const val STRING_SINGLE_QUOTE = '\''.code diff --git a/src/test/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetParserTest.kt b/src/test/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetParserTest.kt index 99c84eabb..969f88857 100644 --- a/src/test/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetParserTest.kt +++ b/src/test/java/de/bixilon/minosoft/commands/parser/minecraft/target/TargetParserTest.kt @@ -13,15 +13,21 @@ package de.bixilon.minosoft.commands.parser.minecraft.target +import de.bixilon.minosoft.commands.errors.reader.map.DuplicatedKeyMapError import de.bixilon.minosoft.commands.parser.minecraft.target.targets.identifier.name.InvalidNameError import de.bixilon.minosoft.commands.parser.minecraft.target.targets.identifier.name.NameEntityTarget import de.bixilon.minosoft.commands.parser.minecraft.target.targets.identifier.uuid.InvalidUUIDError import de.bixilon.minosoft.commands.parser.minecraft.target.targets.identifier.uuid.UUIDEntityTarget +import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.SelectorEntityTarget +import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.GamemodeProperty +import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.NameProperty import de.bixilon.minosoft.commands.util.CommandReader +import de.bixilon.minosoft.data.abilities.Gamemodes import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import java.util.* import kotlin.test.assertEquals +import kotlin.test.assertTrue internal class TargetParserTest { @@ -75,4 +81,61 @@ internal class TargetParserTest { val parser = TargetParser() assertThrows { parser.parse(reader) } } + + @Test + fun testSelectorDetection() { + val reader = CommandReader("@a") + val parser = TargetParser() + val parsed = parser.parse(reader) + assert(parsed is SelectorEntityTarget) + parsed as SelectorEntityTarget + } + + private fun getSelector(command: String): SelectorEntityTarget { + val reader = CommandReader(command) + val parser = TargetParser() + val parsed = parser.parse(reader) + return parsed as SelectorEntityTarget + } + + @Test + fun testSimpleSelector() { + val parsed = getSelector("@a") + assertEquals(parsed.selector, TargetSelectors.ALL_PLAYERS) + } + + @Test + fun testPropertiesEmpty() { + val parsed = getSelector("@a") + assertTrue(parsed.properties.isEmpty()) + } + + @Test + fun testEmptyPropertiesEmpty() { + val parsed = getSelector("@a[]") + assertTrue(parsed.properties.isEmpty()) + } + + @Test + fun testSingleProperties() { + val parsed = getSelector("@a[name=Test]") + assertEquals(parsed.properties.size, 1) + assertTrue(parsed.properties["name"] is NameProperty) + assertTrue((parsed.properties["name"] as NameProperty).name == "Test") + } + + @Test + fun testMultipleProperties() { + val parsed = getSelector("@a[name=Test,gamemode=creative]") + assertEquals(parsed.properties.size, 2) + assertTrue(parsed.properties["name"] is NameProperty) + assertEquals((parsed.properties["name"] as NameProperty).name, "Test") + assertTrue(parsed.properties["gamemode"] is GamemodeProperty) + assertEquals((parsed.properties["gamemode"] as GamemodeProperty).gamemode, Gamemodes.CREATIVE) + } + + @Test + fun testDuplicatedProperties() { + assertThrows { getSelector("@a[name=Test,name=Test2]") } + } }