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 damage = nbt.get("Damage") instanceof IntTag damageTag ? damageTag.getValue() : 0;
boolean unbreakable = !isDamaged(mapping, nbt, damage);
boolean checkDamage = mapping.getMaxDamage() > 0;
int damage = !checkDamage ? 0 : nbt.get("Damage") instanceof IntTag damageTag ? damageTag.getValue() : 0;
boolean unbreakable = checkDamage && !isDamaged(nbt, damage);
for (ObjectIntPair<CustomItemOptions> mappingTypes : customMappings) {
CustomItemOptions options = mappingTypes.key();
@ -67,8 +68,21 @@ final class CustomItemTranslator {
// 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.
if (unbreakable && options.unbreakable() != TriState.TRUE) {
continue;
if (checkDamage) {
if (unbreakable && options.unbreakable() == TriState.FALSE) {
continue;
}
OptionalInt damagePredicate = options.damagePredicate();
if (damagePredicate.isPresent() && damage < damagePredicate.getAsInt()) {
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();
@ -76,11 +90,6 @@ final class CustomItemTranslator {
continue;
}
OptionalInt damagePredicate = options.damagePredicate();
if (damagePredicate.isPresent() && damage < damagePredicate.getAsInt()) {
continue;
}
return mappingTypes.valueInt();
}
return -1;
@ -88,16 +97,15 @@ final class CustomItemTranslator {
/* These two functions are based off their Mojmap equivalents from 1.19.2 */
private static boolean isDamaged(ItemMapping mapping, CompoundTag nbt, int damage) {
return isDamagableItem(mapping, nbt) && damage > 0;
private static boolean isDamaged(CompoundTag nbt, int damage) {
return isDamagableItem(nbt) && damage > 0;
}
private static boolean isDamagableItem(ItemMapping mapping, CompoundTag nbt) {
if (mapping.getMaxDamage() > 0) {
Tag unbreakableTag = nbt.get("Unbreakable");
return unbreakableTag != null && unbreakableTag.getValue() instanceof Number number && number.byteValue() == 0;
}
return false;
private static boolean isDamagableItem(CompoundTag nbt) {
// 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 must either not be present or be set to false
return unbreakableTag == null || !(unbreakableTag.getValue() instanceof Number number) || number.byteValue() == 0;
}
private CustomItemTranslator() {

View File

@ -40,11 +40,14 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
import java.util.OptionalInt;
public class CustomItemsTest {
private ItemMapping testMappingWithDamage;
private Object2IntMap<CompoundTag> tagToCustomItemWithDamage;
private ItemMapping testMappingWithNoDamage;
private Object2IntMap<CompoundTag> tagToCustomItemWithNoDamage;
@Before
public void setup() {
@ -54,9 +57,11 @@ public class CustomItemsTest {
CustomItemOptions d = new GeyserCustomItemOptions(TriState.TRUE, OptionalInt.empty(), OptionalInt.of(8));
CustomItemOptions e = new GeyserCustomItemOptions(TriState.FALSE, OptionalInt.empty(), OptionalInt.of(12));
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<>();
// Order here is important, hence why we're using an array map
optionsToId.put(g, 7);
optionsToId.put(f, 6);
optionsToId.put(e, 5);
optionsToId.put(d, 4);
@ -72,38 +77,70 @@ public class CustomItemsTest {
tagToCustomItemWithDamage.put(tag, optionsToId.getInt(a));
tag = new CompoundTag("");
tag.put(new IntTag("CustomModelData", 3));
tag.put(new ByteTag("Unbreakable", (byte) 1));
addCustomModelData(20, tag);
// 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));
tag = new CompoundTag("");
tag.put(new IntTag("Damage", 16));
tag.put(new ByteTag("Unbreakable", (byte) 0));
addDamage(16, tag);
setUnbreakable(false, tag);
tagToCustomItemWithDamage.put(tag, optionsToId.getInt(e));
tag = new CompoundTag("");
tag.put(new IntTag("CustomModelData", 7));
tag.put(new IntTag("Damage", 6));
tag.put(new ByteTag("Unbreakable", (byte) 0));
addCustomModelData(7, tag);
addDamage(6, tag);
setUnbreakable(false, tag);
tagToCustomItemWithDamage.put(tag, optionsToId.getInt(c));
tag = new CompoundTag("");
tag.put(new IntTag("CustomModelData", 8));
tag.put(new IntTag("Damage", 6));
tag.put(new ByteTag("Unbreakable", (byte) 1));
addCustomModelData(9, tag);
addDamage(6, tag);
setUnbreakable(true, tag);
tagToCustomItemWithDamage.put(tag, optionsToId.getInt(a));
tag = new CompoundTag("");
tag.put(new IntTag("CustomModelData", 9));
tag.put(new IntTag("Damage", 6));
tag.put(new ByteTag("Unbreakable", (byte) 0));
addCustomModelData(9, tag);
addDamage(6, tag);
setUnbreakable(false, tag);
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()
.customItemOptions(optionsToId.object2IntEntrySet().stream().map(entry -> ObjectIntPair.of(entry.getKey(), entry.getIntValue())).toList())
.customItemOptions(customItemOptions)
.maxDamage(100)
.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
@ -112,5 +149,10 @@ public class CustomItemsTest {
int id = CustomItemTranslator.getCustomItem(entry.getKey(), this.testMappingWithDamage);
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);
}
}
}