Fix some regressions in custom item handling

This commit is contained in:
Camotoy 2022-10-12 17:21:58 -04:00
parent f59e33d749
commit 8bf8b22d6b
No known key found for this signature in database
GPG key ID: 7EEFB66FE798081F
2 changed files with 82 additions and 32 deletions

View file

@ -51,8 +51,9 @@ final class CustomItemTranslator {
} }
int customModelData = nbt.get("CustomModelData") instanceof IntTag customModelDataTag ? customModelDataTag.getValue() : 0; int customModelData = nbt.get("CustomModelData") instanceof IntTag customModelDataTag ? customModelDataTag.getValue() : 0;
int damage = nbt.get("Damage") instanceof IntTag damageTag ? damageTag.getValue() : 0; boolean checkDamage = mapping.getMaxDamage() > 0;
boolean unbreakable = !isDamaged(mapping, nbt, damage); int damage = !checkDamage ? 0 : nbt.get("Damage") instanceof IntTag damageTag ? damageTag.getValue() : 0;
boolean unbreakable = checkDamage && !isDamaged(nbt, damage);
for (ObjectIntPair<CustomItemOptions> mappingTypes : customMappings) { for (ObjectIntPair<CustomItemOptions> mappingTypes : customMappings) {
CustomItemOptions options = mappingTypes.key(); CustomItemOptions options = mappingTypes.key();
@ -67,12 +68,8 @@ final class CustomItemTranslator {
// The same behavior exists for Damage (in fraction form instead of whole numbers), // The same behavior exists for Damage (in fraction form instead of whole numbers),
// and Damaged/Unbreakable handles no damage as 0f and damaged as 1f. // and Damaged/Unbreakable handles no damage as 0f and damaged as 1f.
if (unbreakable && options.unbreakable() != TriState.TRUE) { if (checkDamage) {
continue; if (unbreakable && options.unbreakable() == TriState.FALSE) {
}
OptionalInt customModelDataOption = options.customModelData();
if (customModelDataOption.isPresent() && customModelData < customModelDataOption.getAsInt()) {
continue; continue;
} }
@ -80,6 +77,18 @@ final class CustomItemTranslator {
if (damagePredicate.isPresent() && damage < damagePredicate.getAsInt()) { if (damagePredicate.isPresent() && damage < damagePredicate.getAsInt()) {
continue; continue;
} }
} else {
if (options.unbreakable() != TriState.NOT_SET || options.damagePredicate().isPresent()) {
// These will never match on this item. 1.19.2 behavior
// Maybe move this to CustomItemRegistryPopulator since it'll be the same for every item? If so, add a test.
continue;
}
}
OptionalInt customModelDataOption = options.customModelData();
if (customModelDataOption.isPresent() && customModelData < customModelDataOption.getAsInt()) {
continue;
}
return mappingTypes.valueInt(); return mappingTypes.valueInt();
} }
@ -88,16 +97,15 @@ final class CustomItemTranslator {
/* These two functions are based off their Mojmap equivalents from 1.19.2 */ /* These two functions are based off their Mojmap equivalents from 1.19.2 */
private static boolean isDamaged(ItemMapping mapping, CompoundTag nbt, int damage) { private static boolean isDamaged(CompoundTag nbt, int damage) {
return isDamagableItem(mapping, nbt) && damage > 0; return isDamagableItem(nbt) && damage > 0;
} }
private static boolean isDamagableItem(ItemMapping mapping, CompoundTag nbt) { private static boolean isDamagableItem(CompoundTag nbt) {
if (mapping.getMaxDamage() > 0) { // mapping.getMaxDamage > 0 should also be checked (return false if not true) but we already check prior to this function
Tag unbreakableTag = nbt.get("Unbreakable"); Tag unbreakableTag = nbt.get("Unbreakable");
return unbreakableTag != null && unbreakableTag.getValue() instanceof Number number && number.byteValue() == 0; // Tag must either not be present or be set to false
} return unbreakableTag == null || !(unbreakableTag.getValue() instanceof Number number) || number.byteValue() == 0;
return false;
} }
private CustomItemTranslator() { private CustomItemTranslator() {

View file

@ -40,11 +40,14 @@ import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.List;
import java.util.OptionalInt; import java.util.OptionalInt;
public class CustomItemsTest { public class CustomItemsTest {
private ItemMapping testMappingWithDamage; private ItemMapping testMappingWithDamage;
private Object2IntMap<CompoundTag> tagToCustomItemWithDamage; private Object2IntMap<CompoundTag> tagToCustomItemWithDamage;
private ItemMapping testMappingWithNoDamage;
private Object2IntMap<CompoundTag> tagToCustomItemWithNoDamage;
@Before @Before
public void setup() { public void setup() {
@ -54,9 +57,11 @@ public class CustomItemsTest {
CustomItemOptions d = new GeyserCustomItemOptions(TriState.TRUE, OptionalInt.empty(), OptionalInt.of(8)); CustomItemOptions d = new GeyserCustomItemOptions(TriState.TRUE, OptionalInt.empty(), OptionalInt.of(8));
CustomItemOptions e = new GeyserCustomItemOptions(TriState.FALSE, OptionalInt.empty(), OptionalInt.of(12)); CustomItemOptions e = new GeyserCustomItemOptions(TriState.FALSE, OptionalInt.empty(), OptionalInt.of(12));
CustomItemOptions f = new GeyserCustomItemOptions(TriState.FALSE, OptionalInt.of(8), OptionalInt.of(6)); CustomItemOptions f = new GeyserCustomItemOptions(TriState.FALSE, OptionalInt.of(8), OptionalInt.of(6));
CustomItemOptions g = new GeyserCustomItemOptions(TriState.NOT_SET, OptionalInt.of(20), OptionalInt.empty());
Object2IntMap<CustomItemOptions> optionsToId = new Object2IntArrayMap<>(); Object2IntMap<CustomItemOptions> optionsToId = new Object2IntArrayMap<>();
// Order here is important, hence why we're using an array map // Order here is important, hence why we're using an array map
optionsToId.put(g, 7);
optionsToId.put(f, 6); optionsToId.put(f, 6);
optionsToId.put(e, 5); optionsToId.put(e, 5);
optionsToId.put(d, 4); optionsToId.put(d, 4);
@ -72,38 +77,70 @@ public class CustomItemsTest {
tagToCustomItemWithDamage.put(tag, optionsToId.getInt(a)); tagToCustomItemWithDamage.put(tag, optionsToId.getInt(a));
tag = new CompoundTag(""); tag = new CompoundTag("");
tag.put(new IntTag("CustomModelData", 3)); addCustomModelData(20, tag);
tag.put(new ByteTag("Unbreakable", (byte) 1)); // Test that an unbreakable item isn't tested for Damaged if there is no damaged predicate
tagToCustomItemWithDamage.put(tag, optionsToId.getInt(g));
tag = new CompoundTag("");
addCustomModelData(3, tag);
setUnbreakable(true, tag);
tagToCustomItemWithDamage.put(tag, optionsToId.getInt(a)); tagToCustomItemWithDamage.put(tag, optionsToId.getInt(a));
tag = new CompoundTag(""); tag = new CompoundTag("");
tag.put(new IntTag("Damage", 16)); addDamage(16, tag);
tag.put(new ByteTag("Unbreakable", (byte) 0)); setUnbreakable(false, tag);
tagToCustomItemWithDamage.put(tag, optionsToId.getInt(e)); tagToCustomItemWithDamage.put(tag, optionsToId.getInt(e));
tag = new CompoundTag(""); tag = new CompoundTag("");
tag.put(new IntTag("CustomModelData", 7)); addCustomModelData(7, tag);
tag.put(new IntTag("Damage", 6)); addDamage(6, tag);
tag.put(new ByteTag("Unbreakable", (byte) 0)); setUnbreakable(false, tag);
tagToCustomItemWithDamage.put(tag, optionsToId.getInt(c)); tagToCustomItemWithDamage.put(tag, optionsToId.getInt(c));
tag = new CompoundTag(""); tag = new CompoundTag("");
tag.put(new IntTag("CustomModelData", 8)); addCustomModelData(9, tag);
tag.put(new IntTag("Damage", 6)); addDamage(6, tag);
tag.put(new ByteTag("Unbreakable", (byte) 1)); setUnbreakable(true, tag);
tagToCustomItemWithDamage.put(tag, optionsToId.getInt(a)); tagToCustomItemWithDamage.put(tag, optionsToId.getInt(a));
tag = new CompoundTag(""); tag = new CompoundTag("");
tag.put(new IntTag("CustomModelData", 9)); addCustomModelData(9, tag);
tag.put(new IntTag("Damage", 6)); addDamage(6, tag);
tag.put(new ByteTag("Unbreakable", (byte) 0)); setUnbreakable(false, tag);
tagToCustomItemWithDamage.put(tag, optionsToId.getInt(f)); tagToCustomItemWithDamage.put(tag, optionsToId.getInt(f));
List<ObjectIntPair<CustomItemOptions>> customItemOptions = optionsToId.object2IntEntrySet().stream().map(entry -> ObjectIntPair.of(entry.getKey(), entry.getIntValue())).toList();
testMappingWithDamage = ItemMapping.builder() testMappingWithDamage = ItemMapping.builder()
.customItemOptions(optionsToId.object2IntEntrySet().stream().map(entry -> ObjectIntPair.of(entry.getKey(), entry.getIntValue())).toList()) .customItemOptions(customItemOptions)
.maxDamage(100) .maxDamage(100)
.build(); .build();
// Later, possibly add a condition with a mapping with no damage
// Test differences with items with no max damage
tagToCustomItemWithNoDamage = new Object2IntOpenHashMap<>();
tag = new CompoundTag("");
tag.put(new IntTag("CustomModelData", 2));
// Damage predicates existing mean an item will never match if the item mapping has no max damage
tagToCustomItemWithNoDamage.put(tag, -1);
testMappingWithNoDamage = ItemMapping.builder()
.customItemOptions(customItemOptions)
.maxDamage(0)
.build();
}
private void addCustomModelData(int value, CompoundTag tag) {
tag.put(new IntTag("CustomModelData", value));
}
private void addDamage(int value, CompoundTag tag) {
tag.put(new IntTag("Damage", value));
}
private void setUnbreakable(boolean value, CompoundTag tag) {
tag.put(new ByteTag("Unbreakable", (byte) (value ? 1 : 0)));
} }
@Test @Test
@ -112,5 +149,10 @@ public class CustomItemsTest {
int id = CustomItemTranslator.getCustomItem(entry.getKey(), this.testMappingWithDamage); int id = CustomItemTranslator.getCustomItem(entry.getKey(), this.testMappingWithDamage);
Assert.assertEquals(entry.getKey() + " did not produce the correct custom item", entry.getIntValue(), id); Assert.assertEquals(entry.getKey() + " did not produce the correct custom item", entry.getIntValue(), id);
} }
for (Object2IntMap.Entry<CompoundTag> entry : this.tagToCustomItemWithNoDamage.object2IntEntrySet()) {
int id = CustomItemTranslator.getCustomItem(entry.getKey(), this.testMappingWithNoDamage);
Assert.assertEquals(entry.getKey() + " did not produce the correct custom item", entry.getIntValue(), id);
}
} }
} }