Wrap NBTTagCompound#setTag in static helper with safeguards against circular references, fixes #299

This commit is contained in:
Electroblob77
2020-01-27 13:45:47 +00:00
parent 6cd6411c25
commit f5da195605
31 changed files with 96 additions and 48 deletions
@@ -8,7 +8,10 @@ import net.minecraft.entity.monster.*;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EntityDamageSource;
import java.util.*;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
// A note on the use of the vanilla damagesources:
// When using indirect damage sources, the SECOND argument is the original entity (i.e. the caster), and the
@@ -51,8 +51,8 @@ public final class NBTExtras {
for(Map.Entry<K, V> entry : map.entrySet()){
NBTTagCompound mapping = new NBTTagCompound();
mapping.setTag(keyTagName, keyFunction.apply(entry.getKey()));
mapping.setTag(valueTagName, valueFunction.apply(entry.getValue()));
NBTExtras.storeTagSafely(mapping, keyTagName, keyFunction.apply(entry.getKey()));
NBTExtras.storeTagSafely(mapping, valueTagName, valueFunction.apply(entry.getValue()));
tagList.appendTag(mapping);
}
@@ -205,6 +205,51 @@ public final class NBTExtras {
tag.removeTag(key + "Least");
}
/**
* Stores the given NBT tag inside the given NBT tag compound using the given key. Under normal circumstances, this
* is equivalent to {@link NBTTagCompound#setTag(String, NBTBase)}, but this method performs safety checks to
* prevent circular references. If storing the given tag would cause a circular reference, the tag is not stored
* and an error is printed to the console.
* @param compound The {@link NBTTagCompound} in which to store the tag.
* @param key The key to store the tag under.
* @param tag The tag to store.
*/
// This is a catch-all fix for issue #299.
public static void storeTagSafely(NBTTagCompound compound, String key, NBTBase tag){
if(compound == tag || deepContains(tag, compound)){
Wizardry.logger.error("Cannot store tag of type {} under key '{}' as it would result in a circular reference!",
NBTBase.getTypeName(tag.getId()), key);
}else{
compound.setTag(key, tag);
}
}
/**
* Recursively searches within the first NBT tag for the second NBT tag. This handles both compound and list tags.
* @param toSearch The NBT tag to search inside. If this is not a compound or list tag, this method will always
* return false.
* @param searchFor The NBT tag to search for.
* @return True if the second tag appears anywhere within the NBT tree contained within the first tag, false if not.
*/
public static boolean deepContains(NBTBase toSearch, NBTBase searchFor){
if(toSearch instanceof NBTTagCompound){
for(String subKey : ((NBTTagCompound)toSearch).getKeySet()){
NBTBase subTag = ((NBTTagCompound)toSearch).getTag(subKey);
if(subTag == searchFor || deepContains(subTag, searchFor)) return true;
}
}else if(toSearch instanceof NBTTagList){
for(NBTBase subTag : (NBTTagList)toSearch){
if(subTag == searchFor || deepContains(subTag, searchFor)) return true;
}
}
return false;
}
/**
* Returns an NBTTagCompound which contains only the given UUID, stored using
* {@link NBTTagCompound#setUniqueId(String, UUID)}. Allows for neater storage to NBTTagLists.
@@ -398,7 +398,7 @@ public final class WandHelper {
if(wand.getTagCompound() == null) wand.setTagCompound((new NBTTagCompound()));
if(!wand.getTagCompound().hasKey(UPGRADES_KEY))
wand.getTagCompound().setTag(UPGRADES_KEY, new NBTTagCompound());
NBTExtras.storeTagSafely(wand.getTagCompound(), UPGRADES_KEY, new NBTTagCompound());
NBTTagCompound upgrades = wand.getTagCompound().getCompoundTag(UPGRADES_KEY);
@@ -406,7 +406,7 @@ public final class WandHelper {
if(key != null) upgrades.setInteger(key, upgrades.getInteger(key) + 1);
wand.getTagCompound().setTag(UPGRADES_KEY, upgrades);
NBTExtras.storeTagSafely(wand.getTagCompound(), UPGRADES_KEY, upgrades);
}
/** Returns true if the given item is a valid special wand upgrade. */