Skip to content

Instantly share code, notes, and snippets.

@LexManos
Created May 7, 2016 18:16
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LexManos/77c983d67b9ad27010428478b66d50fd to your computer and use it in GitHub Desktop.
Save LexManos/77c983d67b9ad27010428478b66d50fd to your computer and use it in GitHub Desktop.
First draft of modifications to Loot Tables for modder usage/modification.
This is a first draft of re-working the LootTables and applying a modder facing API for them.
Why now? Because i've finally gotten time to do it and someone reminded me.
Why not before? Because nobody seemed to care.
See this image for an example of how to modify tables.
http://puu.sh/oIOfk/a6eaedf1cf.png
Noiw for the nitty gritty.
I feel sick so i'll just do the bullet points.
Stuff:
LootTableLoadEvent:
Will be fired on the main forge event bus for every loot table loaded FROM RESOURCES by the LootTableManager.
It WILL NOT be fired for loot tables loaded from NBT/World Folder, or anything else as those are considered CONFIG files, to be taken as gospel and NOT touched by modders.
Can be canceled in which case EMPTY_TABLE is returned, or the table can be modified. THIS IS THE ONLY TIME A TABLE CAN BE MODIFIED ANYTHING OUTSIDE THE EVENT WILL THROW AN ERROR!!!!!
LootTable:
New functions for managing the encased pools. Done by name.
LootPool:
New functions for managing entries, and changing rolls info, Conditions are not yet editable. We'd have to uniquely name them as welll. Do people care to edit the conditions?
Now have names, anything outside the 'minecraft' domain MUST provide a 'name' each pool. If they do not an exception is thrown and EMPTY_TABLE is used instead.
Anything inside the 'minecraft' domain has names auto-generated for them. This is to keep compatiblity with loading vanilla jsons.
The names are simple, the first pool is called 'main' any others are called 'pool#', For example minecraft:chests/spawn_bunus_chest has the pools 'main', 'pool1', 'pool2', 'pool3'.
As these names are generated at loading time, they are static, and determinstic if you glance over the pool json.
And if you remove a pool THE OTHER NAMES DO NOT CHANGE. So for the bonus chest, if you remove pool2, pool3 is still named pool3
So modders can safely assume that table(minecraft:chests/spawn_bonus_chest).pool(pool3) will always be the building materials pool {sticks, planks whatever}
LootEntry:
New function to get the name, but beyond that the entries can't be edited, you have to remove+replace if you want to edit.
We could add functions to edit the conditions/functions.... but is that needed? We'd have to find a way to uniquely name them.
Again now have names, but this time in the 'entryName' field.
And again names are generated for vanilla. As there are only two types of entries at this point it's easy.
Items:
entryName = name
Going with the bonus chest example, the "main" pool has two entries, one named 'minecraft:wooden_axe' and the other named 'minecraft_stone_axe'
If there is a vanilla json that has two enties in the same pool for the same item name, a counter is appended.
'minecraft:stick', 'minecraft:stick#0', 'minecraft:stick#1'
Table:
entryName = name
Same style as items
Empty:
entryName == 'empty'
same style as items/table
However when it comes to MODDED tables. The name ARE generated, except that it will NOT uniqueafiy the names by appending a counter.
If you have two entries that are for 'minecraft:stick' then you MUST name the second entry something unique. It's suggested you name both, but for modder ease we do not force you to name something we can guess.
Now, again this is basically just a first draft but if I dont hear to much bitching this will be going into forge soon.
It allows you to do the basic things you guys want. Add/remove loot information.
YES I know that this technically deviates from the vanilla json format for modders, but honestly we need the extra information so suck it up and deal it's 1 extra property per pool.
And again the example usage can be seen in this image. http://puu.sh/oIOfk/a6eaedf1cf.png
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 0000000..31c3c53
--- /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<ItemStack> 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 0000000..058252b
--- /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<ItemStack> 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 0000000..ea63362
--- /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 0000000..fa2158b
--- /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 0000000..e6fd85c
--- /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<LootEntry> field_186453_a;
++ private final List<LootCondition> 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<ItemStack> p_186452_1_, Random p_186452_2_, LootContext p_186452_3_)
+@@ -72,7 +74,7 @@
+
+ public void func_186449_b(Collection<ItemStack> 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<LootPool>, JsonSerializer<LootPool>
+ {
+ 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 0000000..915e804
--- /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<LootPool> 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<ItemStack> 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<LootTable>, JsonSerializer<LootTable>
+ {
+ 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 0000000..3c75218
--- /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 0000000..511a8e4
--- /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<LootCondition> 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 09497d8..adcdd6c 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<Deque<LootTableContext>> lootContext = new ThreadLocal<Deque<LootTableContext>>();
+ 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<LootTableContext> 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<String> 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 9bbdfce..140a86a 100644
--- a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java
+++ b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java
@@ -23,6 +23,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;
@@ -38,6 +39,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;
@@ -530,4 +532,12 @@ public class ForgeEventFactory
MinecraftForge.EVENT_BUS.post(pre ? new PopulateChunkEvent.Pre(gen, world, world.rand, x, z, hasVillageGenerated) : new PopulateChunkEvent.Post(gen, world, 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 0000000..ab8870a
--- /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 6902fce..ddeb959 100644
--- a/src/main/resources/forge.exc
+++ b/src/main/resources/forge.exc
@@ -47,3 +47,9 @@ net/minecraft/item/ItemStack.<init>(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.<init>([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.<init>(Ljava/lang/String;Lnet/minecraft/client/renderer/texture/IIconCreator;Z)V=|p_i46100_1_,p_i46100_2_,skipFirst
+
+net/minecraft/world/storage/loot/LootPool.<init>([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.<init>(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.<init>(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.<init>(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.<init>(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 316062b..64c6552 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 0000000..f22247a
--- /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");
+ }
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment