Description
I'm the maintainer of Chimera, a library that allows the Brigadier command framework to be used in Spigot plugins. Recently, a developer opened an interesting issue in which they reported that argument parsing failed when non-ASCII characters were specified.
Looking into the issue, the cause of the argument parsing failing for non-ASCII characters can be traced to:
// StringArgumentType's parse() method
@Override
public String parse(final StringReader reader) throws CommandSyntaxException {
if (type == StringType.GREEDY_PHRASE) {
final String text = reader.getRemaining();
reader.setCursor(reader.getTotalLength());
return text;
} else if (type == StringType.SINGLE_WORD) {
return reader.readUnquotedString(); // <-- calls this
} else {
return reader.readString();
}
}
// StringReader's readUnquotedString() method
public String readUnquotedString() {
final int start = cursor;
while (canRead() && isAllowedInUnquotedString(peek())) { // <-- calls this
skip();
}
return string.substring(start, cursor);
}
// Cause of failure
public static boolean isAllowedInUnquotedString(final char c) {
return c >= '0' && c <= '9'
|| c >= 'A' && c <= 'Z'
|| c >= 'a' && c <= 'z'
|| c == '_' || c == '-'
|| c == '.' || c == '+';
}
This affects StringArgumentType.word()
, StringArgumentType.string()
, StringReader.readString()
and StringReader.readUnquotedString()
and any other dependencies on these classes/methods.
Maybe I'm missing some context but it seems weird to only allow a small subset of ASCII characters. In my opinion, this implementation of StringReader.readUnquotedString()
is extremely wonky and should be refactored to support non-ASCII characters.
In the interim, I decided to create a simple utility method that reads strings until a whitespace is encountered. It behaves similarly to StringReader.readUnquotedString()
while supporting special characters.
public static String unquoted(StringReader reader) {
var start = reader.getCursor();
while (reader.canRead() && reader.peek() != ' ') {
reader.skip();
}
return reader.getString().substring(start, reader.getCursor());
}