From aaa146c10da9869f59c97105411aa767d4712785 Mon Sep 17 00:00:00 2001 From: LexManos Date: Mon, 9 May 2016 14:15:59 -0700 Subject: [PATCH] New LootTable interaction system and event. Modders can now modify and reference internal elements of a loot table by name. Editing can ONLY be done in the event and any external editing will cause a exception to be thrown. See this gist for more information: https://gist.github.com/LexManos/77c983d67b9ad27010428478b66d50fd --- .../world/storage/loot/LootEntry.java.patch | 59 ++++++++ .../storage/loot/LootEntryEmpty.java.patch | 22 +++ .../storage/loot/LootEntryItem.java.patch | 39 +++++ .../storage/loot/LootEntryTable.java.patch | 24 +++ .../world/storage/loot/LootPool.java.patch | 121 +++++++++++++++ .../world/storage/loot/LootTable.java.patch | 75 +++++++++ .../storage/loot/LootTableManager.java.patch | 20 +++ .../LootConditionManager.java.patch | 19 +++ .../net/minecraftforge/common/ForgeHooks.java | 143 ++++++++++++++++++ .../event/ForgeEventFactory.java | 10 ++ .../event/LootTableLoadEvent.java | 43 ++++++ src/main/resources/forge.exc | 6 + src/main/resources/forge_at.cfg | 4 + .../minecraftforge/debug/LootTablesDebug.java | 42 +++++ 14 files changed, 627 insertions(+) create mode 100644 patches/minecraft/net/minecraft/world/storage/loot/LootEntry.java.patch create mode 100644 patches/minecraft/net/minecraft/world/storage/loot/LootEntryEmpty.java.patch create mode 100644 patches/minecraft/net/minecraft/world/storage/loot/LootEntryItem.java.patch create mode 100644 patches/minecraft/net/minecraft/world/storage/loot/LootEntryTable.java.patch create mode 100644 patches/minecraft/net/minecraft/world/storage/loot/LootPool.java.patch create mode 100644 patches/minecraft/net/minecraft/world/storage/loot/LootTable.java.patch create mode 100644 patches/minecraft/net/minecraft/world/storage/loot/LootTableManager.java.patch create mode 100644 patches/minecraft/net/minecraft/world/storage/loot/conditions/LootConditionManager.java.patch create mode 100644 src/main/java/net/minecraftforge/event/LootTableLoadEvent.java create mode 100644 src/test/java/net/minecraftforge/debug/LootTablesDebug.java diff --git a/patches/minecraft/net/minecraft/world/storage/loot/LootEntry.java.patch b/patches/minecraft/net/minecraft/world/storage/loot/LootEntry.java.patch new file mode 100644 index 000000000..31c3c5384 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/storage/loot/LootEntry.java.patch @@ -0,0 +1,59 @@ +--- ../src-base/minecraft/net/minecraft/world/storage/loot/LootEntry.java ++++ ../src-work/minecraft/net/minecraft/world/storage/loot/LootEntry.java +@@ -18,15 +18,17 @@ + + public abstract class LootEntry + { ++ protected final String entryName; + protected final int field_186364_c; + protected final int field_186365_d; + protected final LootCondition[] field_186366_e; + +- protected LootEntry(int p_i46642_1_, int p_i46642_2_, LootCondition[] p_i46642_3_) ++ protected LootEntry(int p_i46642_1_, int p_i46642_2_, LootCondition[] p_i46642_3_, String entryName) + { + this.field_186364_c = p_i46642_1_; + this.field_186365_d = p_i46642_2_; + this.field_186366_e = p_i46642_3_; ++ this.entryName = entryName; + } + + public int func_186361_a(float p_186361_1_) +@@ -34,6 +36,8 @@ + return Math.max(MathHelper.func_76141_d((float)this.field_186364_c + (float)this.field_186365_d * p_186361_1_), 0); + } + ++ public String getEntryName(){ return this.entryName; } ++ + public abstract void func_186363_a(Collection p_186363_1_, Random p_186363_2_, LootContext p_186363_3_); + + protected abstract void func_186362_a(JsonObject p_186362_1_, JsonSerializationContext p_186362_2_); +@@ -57,6 +61,9 @@ + alootcondition = new LootCondition[0]; + } + ++ LootEntry ret = net.minecraftforge.common.ForgeHooks.deserializeJsonLootEntry(s, jsonobject, i, j, alootcondition); ++ if (ret != null) return ret; ++ + if (s.equals("item")) + { + return LootEntryItem.func_186367_a(jsonobject, p_deserialize_3_, i, j, alootcondition); +@@ -78,6 +85,8 @@ + public JsonElement serialize(LootEntry p_serialize_1_, Type p_serialize_2_, JsonSerializationContext p_serialize_3_) + { + JsonObject jsonobject = new JsonObject(); ++ if (p_serialize_1_.entryName != null && !p_serialize_1_.entryName.startsWith("custom#")) ++ jsonobject.addProperty("entryName", p_serialize_1_.entryName); + jsonobject.addProperty("weight", (Number)Integer.valueOf(p_serialize_1_.field_186364_c)); + jsonobject.addProperty("quality", (Number)Integer.valueOf(p_serialize_1_.field_186365_d)); + +@@ -86,6 +95,9 @@ + jsonobject.add("conditions", p_serialize_3_.serialize(p_serialize_1_.field_186366_e)); + } + ++ String type = net.minecraftforge.common.ForgeHooks.getLootEntryType(p_serialize_1_); ++ if (type != null) jsonobject.addProperty("type", type); ++ else + if (p_serialize_1_ instanceof LootEntryItem) + { + jsonobject.addProperty("type", "item"); diff --git a/patches/minecraft/net/minecraft/world/storage/loot/LootEntryEmpty.java.patch b/patches/minecraft/net/minecraft/world/storage/loot/LootEntryEmpty.java.patch new file mode 100644 index 000000000..058252bfc --- /dev/null +++ b/patches/minecraft/net/minecraft/world/storage/loot/LootEntryEmpty.java.patch @@ -0,0 +1,22 @@ +--- ../src-base/minecraft/net/minecraft/world/storage/loot/LootEntryEmpty.java ++++ ../src-work/minecraft/net/minecraft/world/storage/loot/LootEntryEmpty.java +@@ -10,9 +10,9 @@ + + public class LootEntryEmpty extends LootEntry + { +- public LootEntryEmpty(int p_i46645_1_, int p_i46645_2_, LootCondition[] p_i46645_3_) ++ public LootEntryEmpty(int p_i46645_1_, int p_i46645_2_, LootCondition[] p_i46645_3_, String entryName) + { +- super(p_i46645_1_, p_i46645_2_, p_i46645_3_); ++ super(p_i46645_1_, p_i46645_2_, p_i46645_3_, entryName); + } + + public void func_186363_a(Collection p_186363_1_, Random p_186363_2_, LootContext p_186363_3_) +@@ -25,6 +25,6 @@ + + public static LootEntryEmpty func_186372_a(JsonObject p_186372_0_, JsonDeserializationContext p_186372_1_, int p_186372_2_, int p_186372_3_, LootCondition[] p_186372_4_) + { +- return new LootEntryEmpty(p_186372_2_, p_186372_3_, p_186372_4_); ++ return new LootEntryEmpty(p_186372_2_, p_186372_3_, p_186372_4_, net.minecraftforge.common.ForgeHooks.readLootEntryName(p_186372_0_, "empty")); + } + } diff --git a/patches/minecraft/net/minecraft/world/storage/loot/LootEntryItem.java.patch b/patches/minecraft/net/minecraft/world/storage/loot/LootEntryItem.java.patch new file mode 100644 index 000000000..ea6336293 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/storage/loot/LootEntryItem.java.patch @@ -0,0 +1,39 @@ +--- ../src-base/minecraft/net/minecraft/world/storage/loot/LootEntryItem.java ++++ ../src-work/minecraft/net/minecraft/world/storage/loot/LootEntryItem.java +@@ -18,9 +18,9 @@ + protected final Item field_186368_a; + protected final LootFunction[] field_186369_b; + +- public LootEntryItem(Item p_i46644_1_, int p_i46644_2_, int p_i46644_3_, LootFunction[] p_i46644_4_, LootCondition[] p_i46644_5_) ++ public LootEntryItem(Item p_i46644_1_, int p_i46644_2_, int p_i46644_3_, LootFunction[] p_i46644_4_, LootCondition[] p_i46644_5_, String entryName) + { +- super(p_i46644_2_, p_i46644_3_, p_i46644_5_); ++ super(p_i46644_2_, p_i46644_3_, p_i46644_5_, entryName); + this.field_186368_a = p_i46644_1_; + this.field_186369_b = p_i46644_4_; + } +@@ -42,7 +42,7 @@ + + if (itemstack.field_77994_a > 0) + { +- if (itemstack.field_77994_a < this.field_186368_a.func_77639_j()) ++ if (itemstack.field_77994_a < this.field_186368_a.getItemStackLimit(itemstack)) + { + p_186363_1_.add(itemstack); + } +@@ -82,6 +82,7 @@ + + public static LootEntryItem func_186367_a(JsonObject p_186367_0_, JsonDeserializationContext p_186367_1_, int p_186367_2_, int p_186367_3_, LootCondition[] p_186367_4_) + { ++ String name = net.minecraftforge.common.ForgeHooks.readLootEntryName(p_186367_0_, "item"); + Item item = JsonUtils.func_188180_i(p_186367_0_, "name"); + LootFunction[] alootfunction; + +@@ -94,6 +95,6 @@ + alootfunction = new LootFunction[0]; + } + +- return new LootEntryItem(item, p_186367_2_, p_186367_3_, alootfunction, p_186367_4_); ++ return new LootEntryItem(item, p_186367_2_, p_186367_3_, alootfunction, p_186367_4_, name); + } + } diff --git a/patches/minecraft/net/minecraft/world/storage/loot/LootEntryTable.java.patch b/patches/minecraft/net/minecraft/world/storage/loot/LootEntryTable.java.patch new file mode 100644 index 000000000..fa2158b8c --- /dev/null +++ b/patches/minecraft/net/minecraft/world/storage/loot/LootEntryTable.java.patch @@ -0,0 +1,24 @@ +--- ../src-base/minecraft/net/minecraft/world/storage/loot/LootEntryTable.java ++++ ../src-work/minecraft/net/minecraft/world/storage/loot/LootEntryTable.java +@@ -14,9 +14,9 @@ + { + protected final ResourceLocation field_186371_a; + +- public LootEntryTable(ResourceLocation p_i46639_1_, int p_i46639_2_, int p_i46639_3_, LootCondition[] p_i46639_4_) ++ public LootEntryTable(ResourceLocation p_i46639_1_, int p_i46639_2_, int p_i46639_3_, LootCondition[] p_i46639_4_, String entryName) + { +- super(p_i46639_2_, p_i46639_3_, p_i46639_4_); ++ super(p_i46639_2_, p_i46639_3_, p_i46639_4_, entryName); + this.field_186371_a = p_i46639_1_; + } + +@@ -34,7 +34,8 @@ + + public static LootEntryTable func_186370_a(JsonObject p_186370_0_, JsonDeserializationContext p_186370_1_, int p_186370_2_, int p_186370_3_, LootCondition[] p_186370_4_) + { ++ String name = net.minecraftforge.common.ForgeHooks.readLootEntryName(p_186370_0_, "loot_table"); + ResourceLocation resourcelocation = new ResourceLocation(JsonUtils.func_151200_h(p_186370_0_, "name")); +- return new LootEntryTable(resourcelocation, p_186370_2_, p_186370_3_, p_186370_4_); ++ return new LootEntryTable(resourcelocation, p_186370_2_, p_186370_3_, p_186370_4_, name); + } + } diff --git a/patches/minecraft/net/minecraft/world/storage/loot/LootPool.java.patch b/patches/minecraft/net/minecraft/world/storage/loot/LootPool.java.patch new file mode 100644 index 000000000..e6fd85c7f --- /dev/null +++ b/patches/minecraft/net/minecraft/world/storage/loot/LootPool.java.patch @@ -0,0 +1,121 @@ +--- ../src-base/minecraft/net/minecraft/world/storage/loot/LootPool.java ++++ ../src-work/minecraft/net/minecraft/world/storage/loot/LootPool.java +@@ -21,17 +21,19 @@ + + public class LootPool + { +- private final LootEntry[] field_186453_a; +- private final LootCondition[] field_186454_b; ++ private final List field_186453_a; ++ private final List field_186454_b; + private RandomValueRange field_186455_c; + private RandomValueRange field_186456_d; ++ private final String name; + +- public LootPool(LootEntry[] p_i46643_1_, LootCondition[] p_i46643_2_, RandomValueRange p_i46643_3_, RandomValueRange p_i46643_4_) ++ private LootPool(LootEntry[] p_i46643_1_, LootCondition[] p_i46643_2_, RandomValueRange p_i46643_3_, RandomValueRange p_i46643_4_, String name) + { +- this.field_186453_a = p_i46643_1_; +- this.field_186454_b = p_i46643_2_; ++ this.field_186453_a = Lists.newArrayList(p_i46643_1_); ++ this.field_186454_b = Lists.newArrayList(p_i46643_2_); + this.field_186455_c = p_i46643_3_; + this.field_186456_d = p_i46643_4_; ++ this.name = name; + } + + protected void func_186452_a(Collection p_186452_1_, Random p_186452_2_, LootContext p_186452_3_) +@@ -72,7 +74,7 @@ + + public void func_186449_b(Collection p_186449_1_, Random p_186449_2_, LootContext p_186449_3_) + { +- if (LootConditionManager.func_186638_a(this.field_186454_b, p_186449_2_, p_186449_3_)) ++ if (LootConditionManager.testAllConditions(this.field_186454_b, p_186449_2_, p_186449_3_)) + { + int i = this.field_186455_c.func_186511_a(p_186449_2_) + MathHelper.func_76141_d(this.field_186456_d.func_186507_b(p_186449_2_) * p_186449_3_.func_186491_f()); + +@@ -83,21 +85,74 @@ + } + } + ++ //======================== FORGE START ============================================= ++ private boolean isFinalized = false; ++ public void finalize() ++ { ++ this.isFinalized = true; ++ } ++ public boolean isFinalized(){ return this.isFinalized; } ++ private void checkFinalized() ++ { ++ if (this.isFinalized()) ++ throw new RuntimeException("Attempted to modify LootPool after being finalized!"); ++ } ++ ++ public String getName(){ return this.name; } ++ public RandomValueRange getRolls() { return this.field_186455_c; } ++ public RandomValueRange getBonusRolls() { return this.field_186456_d; } ++ public void setRolls (RandomValueRange v){ checkFinalized(); this.field_186455_c = v; } ++ public void setBonusRolls(RandomValueRange v){ checkFinalized(); this.field_186456_d = v; } ++ ++ public LootEntry getEntry(String name) ++ { ++ for (LootEntry entry : this.field_186453_a) ++ if (name.equals(entry.getEntryName())) ++ return entry; ++ return null; ++ } ++ public LootEntry removeEntry(String name) ++ { ++ checkFinalized(); ++ for (LootEntry entry : this.field_186453_a) ++ { ++ if (name.equals(entry.getEntryName())) ++ { ++ this.field_186453_a.remove(entry); ++ return entry; ++ } ++ } ++ return null; ++ } ++ public void addEntry(LootEntry entry) ++ { ++ checkFinalized(); ++ for (LootEntry e : this.field_186453_a) ++ if (e == entry || e.getEntryName().equals(entry.getEntryName())) ++ throw new RuntimeException("Attempted to add a duplicate entry to pool: " + e.getEntryName()); ++ this.field_186453_a.add(entry); ++ } ++ //TODO: Allow modifications of conditions? If so need a way to uniquely identify them. ++ //======================== FORGE END =============================================== ++ + public static class Serializer implements JsonDeserializer, JsonSerializer + { + public LootPool deserialize(JsonElement p_deserialize_1_, Type p_deserialize_2_, JsonDeserializationContext p_deserialize_3_) throws JsonParseException + { + JsonObject jsonobject = JsonUtils.func_151210_l(p_deserialize_1_, "loot pool"); ++ String name = net.minecraftforge.common.ForgeHooks.readPoolName(jsonobject); + LootEntry[] alootentry = (LootEntry[])JsonUtils.func_188174_a(jsonobject, "entries", p_deserialize_3_, LootEntry[].class); + LootCondition[] alootcondition = (LootCondition[])JsonUtils.func_188177_a(jsonobject, "conditions", new LootCondition[0], p_deserialize_3_, LootCondition[].class); + RandomValueRange randomvaluerange = (RandomValueRange)JsonUtils.func_188174_a(jsonobject, "rolls", p_deserialize_3_, RandomValueRange.class); + RandomValueRange randomvaluerange1 = (RandomValueRange)JsonUtils.func_188177_a(jsonobject, "bonus_rolls", new RandomValueRange(0.0F, 0.0F), p_deserialize_3_, RandomValueRange.class); +- return new LootPool(alootentry, alootcondition, randomvaluerange, randomvaluerange1); ++ return new LootPool(alootentry, alootcondition, randomvaluerange, randomvaluerange1, name); + } + + public JsonElement serialize(LootPool p_serialize_1_, Type p_serialize_2_, JsonSerializationContext p_serialize_3_) + { + JsonObject jsonobject = new JsonObject(); ++ if (p_serialize_1_.name != null && !p_serialize_1_.name.startsWith("custom#")) ++ jsonobject.add("name", p_serialize_3_.serialize(p_serialize_1_.name)); + jsonobject.add("entries", p_serialize_3_.serialize(p_serialize_1_.field_186453_a)); + jsonobject.add("rolls", p_serialize_3_.serialize(p_serialize_1_.field_186455_c)); + +@@ -106,7 +161,7 @@ + jsonobject.add("bonus_rolls", p_serialize_3_.serialize(p_serialize_1_.field_186456_d)); + } + +- if (!ArrayUtils.isEmpty((Object[])p_serialize_1_.field_186454_b)) ++ if (!p_serialize_1_.field_186454_b.isEmpty()) + { + jsonobject.add("conditions", p_serialize_3_.serialize(p_serialize_1_.field_186454_b)); + } diff --git a/patches/minecraft/net/minecraft/world/storage/loot/LootTable.java.patch b/patches/minecraft/net/minecraft/world/storage/loot/LootTable.java.patch new file mode 100644 index 000000000..915e80435 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/storage/loot/LootTable.java.patch @@ -0,0 +1,75 @@ +--- ../src-base/minecraft/net/minecraft/world/storage/loot/LootTable.java ++++ ../src-work/minecraft/net/minecraft/world/storage/loot/LootTable.java +@@ -24,11 +24,11 @@ + { + private static final Logger field_186465_b = LogManager.getLogger(); + public static final LootTable field_186464_a = new LootTable(new LootPool[0]); +- private final LootPool[] field_186466_c; ++ private final List field_186466_c; + + public LootTable(LootPool[] p_i46641_1_) + { +- this.field_186466_c = p_i46641_1_; ++ this.field_186466_c = Lists.newArrayList(p_i46641_1_); + } + + public List func_186462_a(Random p_186462_1_, LootContext p_186462_2_) +@@ -146,6 +146,58 @@ + return list; + } + ++ //======================== FORGE START ============================================= ++ private boolean isFinalized = false; ++ public void finalize() ++ { ++ this.isFinalized = true; ++ for (LootPool pool : this.field_186466_c) ++ pool.finalize(); ++ } ++ public boolean isFinalized(){ return this.isFinalized; } ++ private void checkFinalized() ++ { ++ if (this.isFinalized()) ++ throw new RuntimeException("Attempted to modify LootTable after being finalized!"); ++ } ++ ++ public LootPool getPool(String name) ++ { ++ for (LootPool pool : this.field_186466_c) ++ { ++ if (name.equals(pool.getName())) ++ return pool; ++ } ++ return null; ++ } ++ ++ public LootPool removePool(String name) ++ { ++ checkFinalized(); ++ for (LootPool pool : this.field_186466_c) ++ { ++ if (name.equals(pool.getName())) ++ { ++ this.field_186466_c.remove(pool); ++ return pool; ++ } ++ } ++ ++ return null; ++ } ++ ++ public void addPool(LootPool pool) ++ { ++ checkFinalized(); ++ for (LootPool p : this.field_186466_c) ++ { ++ if (p == pool || p.getName().equals(pool.getName())) ++ throw new RuntimeException("Attempted to add a duplicate pool to loot table: " + pool.getName()); ++ } ++ this.field_186466_c.add(pool); ++ } ++ //======================== FORGE END =============================================== ++ + public static class Serializer implements JsonDeserializer, JsonSerializer + { + public LootTable deserialize(JsonElement p_deserialize_1_, Type p_deserialize_2_, JsonDeserializationContext p_deserialize_3_) throws JsonParseException diff --git a/patches/minecraft/net/minecraft/world/storage/loot/LootTableManager.java.patch b/patches/minecraft/net/minecraft/world/storage/loot/LootTableManager.java.patch new file mode 100644 index 000000000..3c752186b --- /dev/null +++ b/patches/minecraft/net/minecraft/world/storage/loot/LootTableManager.java.patch @@ -0,0 +1,20 @@ +--- ../src-base/minecraft/net/minecraft/world/storage/loot/LootTableManager.java ++++ ../src-work/minecraft/net/minecraft/world/storage/loot/LootTableManager.java +@@ -102,7 +102,7 @@ + + try + { +- return (LootTable)LootTableManager.field_186526_b.fromJson(s, LootTable.class); ++ return net.minecraftforge.common.ForgeHooks.loadLootTable(LootTableManager.field_186526_b, p_186517_1_, s, true); + } + catch (JsonParseException jsonparseexception) + { +@@ -142,7 +142,7 @@ + + try + { +- return (LootTable)LootTableManager.field_186526_b.fromJson(s, LootTable.class); ++ return net.minecraftforge.common.ForgeHooks.loadLootTable(LootTableManager.field_186526_b, p_186518_1_, s, false); + } + catch (JsonParseException jsonparseexception) + { diff --git a/patches/minecraft/net/minecraft/world/storage/loot/conditions/LootConditionManager.java.patch b/patches/minecraft/net/minecraft/world/storage/loot/conditions/LootConditionManager.java.patch new file mode 100644 index 000000000..511a8e4f5 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/storage/loot/conditions/LootConditionManager.java.patch @@ -0,0 +1,19 @@ +--- ../src-base/minecraft/net/minecraft/world/storage/loot/conditions/LootConditionManager.java ++++ ../src-work/minecraft/net/minecraft/world/storage/loot/conditions/LootConditionManager.java +@@ -41,6 +41,16 @@ + } + } + ++ ++ public static boolean testAllConditions(Iterable conditions, Random rand, LootContext context) ++ { ++ if (conditions == null) return true; ++ for (LootCondition cond : conditions) ++ if (!cond.func_186618_a(rand, context)) ++ return false; ++ return true; ++ } ++ + public static boolean func_186638_a(LootCondition[] p_186638_0_, Random p_186638_1_, LootContext p_186638_2_) + { + if (p_186638_0_ == null) diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index 09497d8d9..adcdd6c00 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -3,12 +3,21 @@ package net.minecraftforge.common; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.Deque; +import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.google.common.base.Throwables; +import com.google.common.collect.Queues; +import com.google.common.collect.Sets; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + import net.minecraft.block.Block; import net.minecraft.block.BlockLiquid; import net.minecraft.block.material.Material; @@ -47,6 +56,8 @@ import net.minecraft.util.DamageSource; import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; +import net.minecraft.util.JsonUtils; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.WeightedRandom; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; @@ -61,6 +72,10 @@ import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import net.minecraft.world.WorldSettings; import net.minecraft.world.WorldSettings.GameType; +import net.minecraft.world.storage.loot.LootEntry; +import net.minecraft.world.storage.loot.LootTable; +import net.minecraft.world.storage.loot.LootTableManager; +import net.minecraft.world.storage.loot.conditions.LootCondition; import net.minecraftforge.common.util.BlockSnapshot; import net.minecraftforge.event.AnvilUpdateEvent; import net.minecraftforge.event.ForgeEventFactory; @@ -970,4 +985,132 @@ public class ForgeHooks { MinecraftForge.EVENT_BUS.post(new PlayerInteractEvent.RightClickEmpty(player, hand)); } + + private static ThreadLocal> lootContext = new ThreadLocal>(); + private static LootTableContext getLootTableContext() + { + LootTableContext ctx = lootContext.get().peek(); + + if (ctx == null) + throw new JsonParseException("Invalid call stack, could to grab json context!"); // Show I throw this? Do we care about custom deserializers outside the manager? + + return ctx; + } + public static LootTable loadLootTable(Gson gson, ResourceLocation name, String data, boolean custom) + { + Deque que = lootContext.get(); + if (que == null) + { + que = Queues.newArrayDeque(); + lootContext.set(que); + } + + LootTable ret = null; + try + { + que.push(new LootTableContext(name, custom)); + ret = gson.fromJson(data, LootTable.class); + que.pop(); + } + catch (JsonParseException e) + { + que.pop(); + throw e; + } + + if (!custom) + ret = ForgeEventFactory.loadLootTable(name, ret); + + return ret; + } + + private static class LootTableContext + { + public final ResourceLocation name; + private final boolean vanilla; + public final boolean custom; + public int poolCount = 0; + public int entryCount = 0; + private HashSet entryNames = Sets.newHashSet(); + + private LootTableContext(ResourceLocation name, boolean custom) + { + this.name = name; + this.custom = custom; + this.vanilla = "minecraft".equals(this.name.getResourceDomain()); + } + + private void resetPoolCtx() + { + this.entryCount = 0; + this.entryNames.clear(); + } + + public String validateEntryName(String name) + { + if (!this.entryNames.contains(name)) + { + this.entryNames.add(name); + return name; + } + + if (!this.vanilla) + throw new JsonParseException("Loot Table \"" + this.name.toString() + "\" Duplicate entry name \"" + name + "\" for pool #" + (this.poolCount - 1) + " entry #" + (this.entryCount-1)); + + int x = 0; + while (this.entryNames.contains(name + "#" + x)) + x++; + + name = name + "#" + x; + this.entryNames.add(name); + + return name; + } + } + + public static String readPoolName(JsonObject json) + { + LootTableContext ctx = ForgeHooks.getLootTableContext(); + ctx.resetPoolCtx(); + + if (json.has("name")) + return JsonUtils.getString(json, "name"); + + if (ctx.custom) + return "custom#" + json.hashCode(); //We don't care about custom ones modders shouldn't be editing them! + + ctx.poolCount++; + + if (!ctx.vanilla) + throw new JsonParseException("Loot Table \"" + ctx.name.toString() + "\" Missing `name` entry for pool #" + (ctx.poolCount - 1)); + + return ctx.poolCount == 1 ? "main" : "pool" + (ctx.poolCount - 1); + } + + public static String readLootEntryName(JsonObject json, String type) + { + LootTableContext ctx = ForgeHooks.getLootTableContext(); + ctx.entryCount++; + + if (json.has("entryName")) + return ctx.validateEntryName(JsonUtils.getString(json, "EntryName")); + + if (ctx.custom) + return "custom#" + json.hashCode(); //We don't care about custom ones modders shouldn't be editing them! + + String name = null; + if ("item".equals(type)) + name = JsonUtils.getString(json, "name"); + else if ("loot_table".equals(type)) + name = JsonUtils.getString(json, "name"); + else if ("empty".equals(type)) + name = "empty"; + + return ctx.validateEntryName(name); + } + + + //TODO: Some registry to support custom LootEntry types? + public static LootEntry deserializeJsonLootEntry(String type, JsonObject json, int weight, int quality, LootCondition[] conditions){ return null; } + public static String getLootEntryType(LootEntry entry){ return null; } //Companion to above function } diff --git a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java index d590a891a..8afe6d354 100644 --- a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java +++ b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java @@ -24,6 +24,7 @@ import net.minecraft.util.DamageSource; import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundEvent; import net.minecraft.util.math.BlockPos; @@ -39,6 +40,7 @@ import net.minecraft.world.chunk.ChunkPrimer; import net.minecraft.world.chunk.IChunkGenerator; import net.minecraft.world.storage.IPlayerFileData; import net.minecraft.world.storage.SaveHandler; +import net.minecraft.world.storage.loot.LootTable; import net.minecraftforge.client.event.ClientChatReceivedEvent; import net.minecraftforge.client.event.RenderBlockOverlayEvent; import net.minecraftforge.client.event.RenderBlockOverlayEvent.OverlayType; @@ -541,4 +543,12 @@ public class ForgeEventFactory MinecraftForge.EVENT_BUS.post(pre ? new PopulateChunkEvent.Pre(gen, world, rand, x, z, hasVillageGenerated) : new PopulateChunkEvent.Post(gen, world, rand, x, z, hasVillageGenerated)); } + public static LootTable loadLootTable(ResourceLocation name, LootTable table) + { + LootTableLoadEvent event = new LootTableLoadEvent(name, table); + if (MinecraftForge.EVENT_BUS.post(event)) + return LootTable.EMPTY_LOOT_TABLE; + return event.getTable(); + } + } diff --git a/src/main/java/net/minecraftforge/event/LootTableLoadEvent.java b/src/main/java/net/minecraftforge/event/LootTableLoadEvent.java new file mode 100644 index 000000000..ab8870ae9 --- /dev/null +++ b/src/main/java/net/minecraftforge/event/LootTableLoadEvent.java @@ -0,0 +1,43 @@ +package net.minecraftforge.event; + +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.storage.loot.LootTable; +import net.minecraftforge.fml.common.eventhandler.Cancelable; +import net.minecraftforge.fml.common.eventhandler.Event; + +/** + * Event fired when a LootTable json is loaded from json. + * This event is fired whenever resources are loaded, or when the server starts. + * This event will NOT be fired for LootTables loaded from the world folder, these are + * considered configurations files and should not be modified by mods. + * + * Canceling the event will make it load a empty loot table. + * + */ +@Cancelable +public class LootTableLoadEvent extends Event +{ + private final ResourceLocation name; + private LootTable table; + + public LootTableLoadEvent(ResourceLocation name, LootTable table) + { + this.name = name; + this.table = table; + } + + public ResourceLocation getName() + { + return this.name; + } + + public LootTable getTable() + { + return this.table; + } + + public void setTable(LootTable table) + { + this.table = table; + } +} diff --git a/src/main/resources/forge.exc b/src/main/resources/forge.exc index 6902fcec9..ddeb959c8 100644 --- a/src/main/resources/forge.exc +++ b/src/main/resources/forge.exc @@ -47,3 +47,9 @@ net/minecraft/item/ItemStack.(Lnet/minecraft/item/Item;IILnet/minecraft/nb net/minecraft/block/BlockRedstoneWire.canConnectTo(Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/util/EnumFacing;Lnet/minecraft/world/IBlockAccess;Lnet/minecraft/util/math/BlockPos;)Z=p_176343_0_,p_176343_1_,world,pos net/minecraft/client/renderer/block/model/BakedQuad.([IILnet/minecraft/util/EnumFacing;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;ZLnet/minecraft/client/renderer/vertex/VertexFormat;)V=|p_i46574_1_,p_i46574_2_,p_i46574_3_,p_i46574_4_,applyDiffuseLighting,format net/minecraft/client/renderer/texture/TextureMap.(Ljava/lang/String;Lnet/minecraft/client/renderer/texture/IIconCreator;Z)V=|p_i46100_1_,p_i46100_2_,skipFirst + +net/minecraft/world/storage/loot/LootPool.([Lnet/minecraft/world/storage/loot/LootEntry;[Lnet/minecraft/world/storage/loot/conditions/LootCondition;Lnet/minecraft/world/storage/loot/RandomValueRange;Lnet/minecraft/world/storage/loot/RandomValueRange;Ljava/lang/String;)V=|p_i46643_1_,p_i46643_2_,p_i46643_3_,p_i46643_4_ +net/minecraft/world/storage/loot/LootEntry.(II[Lnet/minecraft/world/storage/loot/conditions/LootCondition;Ljava/lang/String;)V=|p_i46642_1_,p_i46642_2_,p_i46642_3_,entryName +net/minecraft/world/storage/loot/LootEntryItem.(Lnet/minecraft/item/Item;II[Lnet/minecraft/world/storage/loot/functions/LootFunction;[Lnet/minecraft/world/storage/loot/conditions/LootCondition;Ljava/lang/String;)V=|p_i46644_1_,p_i46644_2_,p_i46644_3_,p_i46644_4_,p_i46644_5_,entryName +net/minecraft/world/storage/loot/LootEntryTable.(Lnet/minecraft/util/ResourceLocation;II[Lnet/minecraft/world/storage/loot/conditions/LootCondition;Ljava/lang/String;)V=|p_i46639_1_,p_i46639_2_,p_i46639_3_,p_i46639_4_,entryName +net/minecraft/world/storage/loot/LootEntryEmpty.(II[Lnet/minecraft/world/storage/loot/conditions/LootCondition;Ljava/lang/String;)V=|p_i46645_1_,p_i46645_2_,p_i46645_3_,entryName diff --git a/src/main/resources/forge_at.cfg b/src/main/resources/forge_at.cfg index 316062b14..64c6552b9 100644 --- a/src/main/resources/forge_at.cfg +++ b/src/main/resources/forge_at.cfg @@ -289,3 +289,7 @@ private-f net.minecraft.server.management.PlayerManager$PlayerInstance field_187 # RenderLivingBase public net.minecraft.client.renderer.entity.RenderLivingBase func_177094_a(Lnet/minecraft/client/renderer/entity/layers/LayerRenderer;)Z # addLayer public net.minecraft.client.renderer.entity.RenderLivingBase func_177089_b(Lnet/minecraft/client/renderer/entity/layers/LayerRenderer;)Z # removeLayer + +# LootTable Stuff +private-f net.minecraft.world.storage.loot.LootPool field_186455_c # rolls +private-f net.minecraft.world.storage.loot.LootPool field_186456_d # bonusRolls \ No newline at end of file diff --git a/src/test/java/net/minecraftforge/debug/LootTablesDebug.java b/src/test/java/net/minecraftforge/debug/LootTablesDebug.java new file mode 100644 index 000000000..f22247a13 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/LootTablesDebug.java @@ -0,0 +1,42 @@ +package net.minecraftforge.debug; + +import net.minecraft.init.Items; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.storage.loot.LootEntryItem; +import net.minecraft.world.storage.loot.LootPool; +import net.minecraft.world.storage.loot.LootTableList; +import net.minecraft.world.storage.loot.conditions.LootCondition; +import net.minecraft.world.storage.loot.functions.LootFunction; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.LootTableLoadEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +@Mod(modid=LootTablesDebug.MODID) +public class LootTablesDebug { + public static final String MODID = "loot_table_debug"; + private static final ResourceLocation CUSTOM_LOOT = LootTableList.register(new ResourceLocation(MODID, "custom_loot")); + + @Mod.EventHandler + public void init(FMLInitializationEvent event) + { + MinecraftForge.EVENT_BUS.register(this); + } + + @SubscribeEvent + public void lootLoad(LootTableLoadEvent event) + { + if (!event.getName().equals(LootTableList.CHESTS_SPAWN_BONUS_CHEST)) + return; + + // Remove axes and replace with chestpeice, First vanilla entry is always called "main" + LootPool main = event.getTable().getPool("main"); //Note: This CAN NPE if another mod removes things + main.removeEntry("minecraft:wooden_axe"); + main.removeEntry("minecraft:stone_axe"); + main.addEntry(new LootEntryItem(Items.diamond_chestplate, 1, 0, new LootFunction[0], new LootCondition[0], MODID + ":diamond_chestplate")); + + // Get rid of all building mats. Which is pool #3, index starts at 0, but 0 is named "main" + event.getTable().removePool("pool3"); + } +}