From 8104b96b8c6d4c3171e5bfa64d293b97e37a76d5 Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Thu, 4 Nov 2021 19:43:48 +0100 Subject: [PATCH] Fix premature snbt numeric parsing --- .../api/minecraft/nbt/TagStringReader.java | 87 +++++++++---------- .../viaversion/common/nbt/NBTTagTest.java | 2 + 2 files changed, 42 insertions(+), 47 deletions(-) diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/TagStringReader.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/TagStringReader.java index f70eca8ec..c24eddf46 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/TagStringReader.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/TagStringReader.java @@ -256,64 +256,57 @@ final class TagStringReader { */ private Tag scalar() { final StringBuilder builder = new StringBuilder(); - boolean possiblyNumeric = true; + int noLongerNumericAt = -1; while (this.buffer.hasMore()) { - final char current = this.buffer.peek(); - if (possiblyNumeric && !Tokens.numeric(current)) { - if (builder.length() != 0) { - Tag result = null; - try { - switch (Character.toLowerCase(current)) { // try to read and return as a number - case Tokens.TYPE_BYTE: - result = new ByteTag(Byte.parseByte(builder.toString())); - break; - case Tokens.TYPE_SHORT: - result = new ShortTag(Short.parseShort(builder.toString())); - break; - case Tokens.TYPE_INT: - result = new IntTag(Integer.parseInt(builder.toString())); - break; - case Tokens.TYPE_LONG: - result = new LongTag(Long.parseLong(builder.toString())); - break; - case Tokens.TYPE_FLOAT: - final float floatValue = Float.parseFloat(builder.toString()); - if (!Float.isNaN(floatValue)) { - result = new FloatTag(floatValue); - } - break; - case Tokens.TYPE_DOUBLE: - final double doubleValue = Double.parseDouble(builder.toString()); - if (!Double.isNaN(doubleValue)) { - result = new DoubleTag(doubleValue); - } - break; - } - } catch (final NumberFormatException ex) { - possiblyNumeric = false; // fallback to treating as a String - } - if (result != null) { - this.buffer.take(); - return result; - } - } - } + char current = this.buffer.peek(); if (current == '\\') { // escape -- we are significantly more lenient than original format at the moment this.buffer.advance(); - builder.append(this.buffer.take()); + current = this.buffer.take(); } else if (Tokens.id(current)) { - builder.append(this.buffer.take()); + this.buffer.advance(); } else { // end of value break; } + builder.append(current); + if (noLongerNumericAt == -1 && !Tokens.numeric(current)) { + noLongerNumericAt = builder.length(); + } } - // if we run out of content without an explicit value separator, then we're either an integer or string tag -- all others have a character at the end + + final int length = builder.length(); final String built = builder.toString(); - if (possiblyNumeric) { + if (noLongerNumericAt == length) { + final char last = built.charAt(length - 1); + try { + switch (Character.toLowerCase(last)) { // try to read and return as a number + case Tokens.TYPE_BYTE: + return new ByteTag(Byte.parseByte(built.substring(0, length - 1))); + case Tokens.TYPE_SHORT: + return new ShortTag(Short.parseShort(built.substring(0, length - 1))); + case Tokens.TYPE_INT: + return new IntTag(Integer.parseInt(built.substring(0, length - 1))); + case Tokens.TYPE_LONG: + return new LongTag(Long.parseLong(built.substring(0, length - 1))); + case Tokens.TYPE_FLOAT: + final float floatValue = Float.parseFloat(built.substring(0, length - 1)); + if (Float.isFinite(floatValue)) { // don't accept NaN and Infinity + return new FloatTag(floatValue); + } + break; + case Tokens.TYPE_DOUBLE: + final double doubleValue = Double.parseDouble(built.substring(0, length - 1)); + if (Double.isFinite(doubleValue)) { // don't accept NaN and Infinity + return new DoubleTag(doubleValue); + } + break; + } + } catch (final NumberFormatException ignored) { + } + } else if (noLongerNumericAt == -1) { // if we run out of content without an explicit value separator, then we're either an integer or string tag -- all others have a character at the end try { return new IntTag(Integer.parseInt(built)); - } catch (final NumberFormatException ignored) { - // Via - don't try to parse as DoubleTag here + } catch (final NumberFormatException ex) { + // Via - don't try to parse as DoubleTag her } } diff --git a/common/src/test/java/com/viaversion/viaversion/common/nbt/NBTTagTest.java b/common/src/test/java/com/viaversion/viaversion/common/nbt/NBTTagTest.java index 5aff3fd4c..1d14ec8cc 100644 --- a/common/src/test/java/com/viaversion/viaversion/common/nbt/NBTTagTest.java +++ b/common/src/test/java/com/viaversion/viaversion/common/nbt/NBTTagTest.java @@ -43,7 +43,9 @@ public class NBTTagTest { readString("{id:[I;1,2,3,]}"); readString("{id:[1,2,3,]}"); + Assertions.assertEquals("0da", readString("{id:0da}").get("id").getValue()); Assertions.assertEquals("NaNd", readString("{id:NaNd}").get("id").getValue()); + Assertions.assertEquals("Infinityd", readString("{id:Infinityd}").get("id").getValue()); Assertions.assertEquals("2147483649", readString("{id:9000b,thisisastring:2147483649}").get("thisisastring").getValue()); Assertions.assertEquals((byte) 1, readString("{thisisabyte:true}").get("thisisabyte").getValue()); Assertions.assertEquals((byte) 0, readString("{thisisabyte:false}").get("thisisabyte").getValue());