From fa90e0835062d50fb4c7354eb757e605c097b358 Mon Sep 17 00:00:00 2001 From: Kitlith Date: Wed, 8 Jul 2020 18:35:11 -0700 Subject: [PATCH 1/9] Add LootTableLoadEvent, methods on LootPool/LootTable The mixin to LootManager is messy, and I'm open to alternative ways to doing it, even if it involves scrapping the 'forge way' of doing it and doing it a different way. For now, I'm going to get this up so that bikeshedding can happen. I'm using the "net.patchworkmc.api.*.Forge" pattern for interfaces that describe public methods added by Forge for use by mods in this commit. This is up for bikeshedding. This is untested other than starting up Minecraft and opening a world. --- patchwork-loot/build.gradle | 5 + .../event/LootTableLoadEvent.java | 67 ++++++++ .../patchworkmc/api/loot/ForgeLootPool.java | 42 +++++ .../patchworkmc/api/loot/ForgeLootTable.java | 33 ++++ .../impl/event/loot/LootEvents.java | 39 +++++ .../net/patchworkmc/impl/loot/LootHooks.java | 148 ++++++++++++++++++ .../impl/loot/PatchworkLootPool.java | 28 ++++ .../mixin/loot/MixinJsonDataLoader.java | 44 ++++++ .../mixin/loot/MixinLootManager.java | 89 +++++++++++ .../patchworkmc/mixin/loot/MixinLootPool.java | 76 +++++++++ .../mixin/loot/MixinLootPoolBuilder.java | 61 ++++++++ .../mixin/loot/MixinLootPoolSerializer.java | 62 ++++++++ .../mixin/loot/MixinLootTable.java | 72 +++++++++ .../main/resources/patchwork-loot.mixins.json | 8 +- 14 files changed, 773 insertions(+), 1 deletion(-) create mode 100644 patchwork-loot/src/main/java/net/minecraftforge/event/LootTableLoadEvent.java create mode 100644 patchwork-loot/src/main/java/net/patchworkmc/api/loot/ForgeLootPool.java create mode 100644 patchwork-loot/src/main/java/net/patchworkmc/api/loot/ForgeLootTable.java create mode 100644 patchwork-loot/src/main/java/net/patchworkmc/impl/event/loot/LootEvents.java create mode 100644 patchwork-loot/src/main/java/net/patchworkmc/impl/loot/LootHooks.java create mode 100644 patchwork-loot/src/main/java/net/patchworkmc/impl/loot/PatchworkLootPool.java create mode 100644 patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinJsonDataLoader.java create mode 100644 patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java create mode 100644 patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPool.java create mode 100644 patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolBuilder.java create mode 100644 patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolSerializer.java create mode 100644 patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootTable.java diff --git a/patchwork-loot/build.gradle b/patchwork-loot/build.gradle index 9029bd24..0caa77dc 100644 --- a/patchwork-loot/build.gradle +++ b/patchwork-loot/build.gradle @@ -1,2 +1,7 @@ archivesBaseName = "patchwork-loot" version = getSubprojectVersion(project, "0.2.0") + +dependencies { + compile project(path: ':patchwork-fml', configuration: 'dev') +} + diff --git a/patchwork-loot/src/main/java/net/minecraftforge/event/LootTableLoadEvent.java b/patchwork-loot/src/main/java/net/minecraftforge/event/LootTableLoadEvent.java new file mode 100644 index 00000000..93ecb6b8 --- /dev/null +++ b/patchwork-loot/src/main/java/net/minecraftforge/event/LootTableLoadEvent.java @@ -0,0 +1,67 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.event; + +import net.minecraftforge.eventbus.api.Event; + +import net.minecraft.loot.LootManager; +import net.minecraft.loot.LootTable; +import net.minecraft.util.Identifier; + +/** + * 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.

+ */ +public class LootTableLoadEvent extends Event { + private final Identifier name; + private LootTable table; + private LootManager lootTableManager; + + public LootTableLoadEvent(Identifier name, LootTable table, LootManager lootTableManager) { + this.name = name; + this.table = table; + this.lootTableManager = lootTableManager; + } + + public Identifier getName() { + return this.name; + } + + public LootTable getTable() { + return this.table; + } + + public void setTable(LootTable table) { + this.table = table; + } + + public LootManager getLootTableManager() { + return this.lootTableManager; + } + + @Override + public boolean isCancelable() { + return true; + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/api/loot/ForgeLootPool.java b/patchwork-loot/src/main/java/net/patchworkmc/api/loot/ForgeLootPool.java new file mode 100644 index 00000000..7aef5158 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/api/loot/ForgeLootPool.java @@ -0,0 +1,42 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.api.loot; + +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTableRange; +import net.minecraft.loot.UniformLootTableRange; + +/** + * Interface for adding Forge methods added to LootPool and its inner classes. + */ +public interface ForgeLootPool { + // TODO: doesn't include methods having to do with freezing yet + + String getName(); + LootTableRange getRolls(); + LootTableRange getBonusRolls(); + void setRolls(UniformLootTableRange v); + void setBonusRolls(UniformLootTableRange v); + + public interface Builder { + LootPool.Builder name(String name); + LootPool.Builder bonusRolls(float min, float max); + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/api/loot/ForgeLootTable.java b/patchwork-loot/src/main/java/net/patchworkmc/api/loot/ForgeLootTable.java new file mode 100644 index 00000000..86650bac --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/api/loot/ForgeLootTable.java @@ -0,0 +1,33 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.api.loot; + +import net.minecraft.loot.LootPool; + +/** + * Interface for adding Forge methods added to LootTable. + */ +public interface ForgeLootTable { + // TODO: doesn't include methods having to do with freezing yet + + LootPool getPool(String name); + LootPool removePool(String name); + void addPool(LootPool pool); +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/impl/event/loot/LootEvents.java b/patchwork-loot/src/main/java/net/patchworkmc/impl/event/loot/LootEvents.java new file mode 100644 index 00000000..c732f201 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/impl/event/loot/LootEvents.java @@ -0,0 +1,39 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.event.loot; + +import net.minecraftforge.event.LootTableLoadEvent; +import net.minecraftforge.common.MinecraftForge; + +import net.minecraft.loot.LootManager; +import net.minecraft.loot.LootTable; +import net.minecraft.util.Identifier; + +public class LootEvents { + public static LootTable loadLootTable(Identifier name, LootTable table, LootManager manager) { + LootTableLoadEvent event = new LootTableLoadEvent(name, table, manager); + + if (MinecraftForge.EVENT_BUS.post(event)) { + return LootTable.EMPTY; + } + + return event.getTable(); + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/LootHooks.java b/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/LootHooks.java new file mode 100644 index 00000000..3d270755 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/LootHooks.java @@ -0,0 +1,148 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.loot; + +import java.util.Deque; +import java.util.HashSet; + +import javax.annotation.Nullable; + +import com.google.common.collect.Queues; +import com.google.common.collect.Sets; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import org.spongepowered.asm.mixin.Unique; + +import net.minecraft.loot.LootManager; +import net.minecraft.loot.LootTable; +import net.minecraft.util.Identifier; +import net.minecraft.util.JsonHelper; + +import net.patchworkmc.impl.event.loot.LootEvents; + +public class LootHooks { + @Unique + private static ThreadLocal> lootContext = new ThreadLocal>(); + + public static LootTable loadLootTable(Gson gson, Identifier name, JsonElement data, boolean custom, LootManager lootTableManager) { + 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 = LootEvents.loadLootTable(name, ret, lootTableManager); + } + + // if (ret != null) { + // ret.freeze(); + // } + + return ret; + } + + private static LootTableContext getLootTableContext() { + LootTableContext ctx = lootContext.get().peek(); + + if (ctx == null) { + throw new JsonParseException("Invalid call stack, could not grab json context!"); // Should I throw this? Do we care about custom deserializers outside the manager? + } + + return ctx; + } + + public static String readPoolName(JsonObject json) { + LootTableContext ctx = LootHooks.getLootTableContext(); + ctx.resetPoolCtx(); + + if (json.has("name")) { + return JsonHelper.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); + } + + private static class LootTableContext { + public final Identifier name; + public final boolean custom; + private final boolean vanilla; + public int poolCount = 0; + public int entryCount = 0; + private HashSet entryNames = Sets.newHashSet(); + + private LootTableContext(Identifier name, boolean custom) { + this.name = name; + this.custom = custom; + this.vanilla = "minecraft".equals(this.name.getNamespace()); + } + + private void resetPoolCtx() { + this.entryCount = 0; + this.entryNames.clear(); + } + + public String validateEntryName(@Nullable String name) { + if (name != null && !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; + } + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/PatchworkLootPool.java b/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/PatchworkLootPool.java new file mode 100644 index 00000000..68770139 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/PatchworkLootPool.java @@ -0,0 +1,28 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.loot; + +/** + * Forge does this through patching the constructor, we just add methods with + * mixins instead. + */ +public interface PatchworkLootPool { + void patchwork$setName(String name); +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinJsonDataLoader.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinJsonDataLoader.java new file mode 100644 index 00000000..85b3b420 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinJsonDataLoader.java @@ -0,0 +1,44 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.loot; + +import java.util.Map; + +import com.google.gson.JsonObject; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import net.minecraft.resource.JsonDataLoader; +import net.minecraft.resource.SinglePreparationResourceReloadListener; +import net.minecraft.util.Identifier; + +// TODO: Is there a better place to put this? +@Mixin(JsonDataLoader.class) +public abstract class MixinJsonDataLoader extends SinglePreparationResourceReloadListener> { + @Shadow + @Final + String dataType; + + // This does not get its own interface -- any mixins on subclasses wishing to use it should probably extend this mixin. + protected Identifier getPreparedPath(Identifier id) { + return new Identifier(id.getNamespace(), dataType + "/" + id.getPath() + ".json"); + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java new file mode 100644 index 00000000..3091aade --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java @@ -0,0 +1,89 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.loot; + +import java.io.IOException; +import java.util.Deque; +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Queues; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.loot.LootManager; +import net.minecraft.resource.Resource; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +import net.minecraft.util.profiler.Profiler; + +import net.patchworkmc.impl.loot.LootHooks; + +@Mixin(LootManager.class) +public abstract class MixinLootManager extends MixinJsonDataLoader { + // should this also be part of the static threadlocal? + @Unique + private ResourceManager resourceManager; + + @Unique + private static ThreadLocal> lootContext = new ThreadLocal>(); + + // TODO: is reentrancy necessary? + @Inject(method = "apply", at = @At("HEAD")) + private void getResourceManager(Map map, ResourceManager resourceManager, Profiler profiler, CallbackInfo info) { + this.resourceManager = resourceManager; + Deque que = lootContext.get(); + + if (que == null) { + que = Queues.newArrayDeque(); + lootContext.set(que); + } + + que.push((LootManager) (Object) this); + } + + @Inject(method = "apply", at = @At("RETURN")) + private void delResourceManager(CallbackInfo info) { + // TODO: what if an exception is thrown? + resourceManager = null; + lootContext.get().pop(); + } + + @Redirect(method = "method_20711", at = @At(value = "INVOKE", target = "com/google/gson/Gson.fromJson (Lcom/google/gson/JsonElement;Ljava/lang/Class;)Ljava/lang/Object;")) + private static Object loadLootTable(Gson GSON, JsonElement elem, Class cls, ImmutableMap.Builder _bld, Identifier id, JsonObject obj) throws IOException { + LootManager lootManager = lootContext.get().peek(); + + if (lootManager == null) { + throw new JsonParseException("Invalid call stack, could not grab loot manager!"); // Should I throw this? Do we care about custom deserializers outside the manager? + } + + ResourceManager resourceManager = ((MixinLootManager) (Object) lootManager).resourceManager; + Resource res = resourceManager.getResource(((MixinLootManager) (Object) lootManager).getPreparedPath(id)); + return LootHooks.loadLootTable(GSON, id, elem, res == null || !res.getResourcePackName().equals("Default"), lootManager); + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPool.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPool.java new file mode 100644 index 00000000..5eddb8a2 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPool.java @@ -0,0 +1,76 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.loot; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTableRange; +import net.minecraft.loot.UniformLootTableRange; + +import net.patchworkmc.api.loot.ForgeLootPool; +import net.patchworkmc.impl.loot.PatchworkLootPool; + +@Mixin(LootPool.class) +public class MixinLootPool implements PatchworkLootPool, ForgeLootPool { + // Forge has this as final, but I don't have a good way to initialize it if it is final. + @Unique + private String name; + + @Shadow + private UniformLootTableRange bonusRollsRange; + + @Shadow + private LootTableRange rollsRange; + + // implementation detail + // TODO: if we could have an inner class that was also a mixin, we could set this as protected? + public void patchwork$setName(String name) { + this.name = name; + } + + // Forge methods that should be added directly to the type + + // TODO: freezing stuff + + public String getName() { + return this.name; + } + + public LootTableRange getRolls() { + return rollsRange; + } + + public LootTableRange getBonusRolls() { + return this.bonusRollsRange; + } + + public void setRolls(UniformLootTableRange v) { + // checkFrozen(); + this.rollsRange = v; + } + + public void setBonusRolls(UniformLootTableRange v) { + // checkFrozen(); + this.bonusRollsRange = v; + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolBuilder.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolBuilder.java new file mode 100644 index 00000000..0c26a1fd --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolBuilder.java @@ -0,0 +1,61 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.loot; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.loot.LootPool; +import net.minecraft.loot.UniformLootTableRange; + +import net.patchworkmc.api.loot.ForgeLootPool; +import net.patchworkmc.impl.loot.PatchworkLootPool; + +@Mixin(LootPool.Builder.class) +public abstract class MixinLootPoolBuilder implements ForgeLootPool.Builder { + @Unique + private String name; + + @Shadow + private UniformLootTableRange bonusRollsRange; + + @Inject(method = "build", at = @At("RETURN"), cancellable = true) + private void addNameToConstructor(CallbackInfoReturnable cir) { + LootPool ret = cir.getReturnValue(); + ((PatchworkLootPool) ret).patchwork$setName(name); + + // is this necessary? + cir.setReturnValue(ret); + } + + public LootPool.Builder name(String name) { + this.name = name; + return (LootPool.Builder) (Object) this; + } + + public LootPool.Builder bonusRolls(float min, float max) { + this.bonusRollsRange = new UniformLootTableRange(min, max); + return (LootPool.Builder) (Object) this; + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolSerializer.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolSerializer.java new file mode 100644 index 00000000..dcb929a9 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolSerializer.java @@ -0,0 +1,62 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.loot; + +import java.lang.reflect.Type; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; + +import net.minecraft.loot.LootPool; + +import net.patchworkmc.api.loot.ForgeLootPool; +import net.patchworkmc.impl.loot.LootHooks; +import net.patchworkmc.impl.loot.PatchworkLootPool; + +@Mixin(LootPool.Serializer.class) +public class MixinLootPoolSerializer { + @Inject(method = "deserialize", at = @At("RETURN"), cancellable = true, locals = LocalCapture.PRINT) + private void addNameToConstructor(CallbackInfoReturnable cir, JsonObject obj) { + LootPool ret = cir.getReturnValue(); + ((PatchworkLootPool) ret).patchwork$setName(LootHooks.readPoolName(obj)); + + // is this necessary? + cir.setReturnValue(ret); + } + + @Redirect(method = "serialize", at = @At(value = "NEW", args = "class=com/google/gson/JsonObject")) + private static JsonObject serializeName(LootPool pool, Type type, JsonSerializationContext ctx) { + JsonObject ret = new JsonObject(); + + String name = ((ForgeLootPool) pool).getName(); + + if (name != null && !name.startsWith("custom#")) { + ret.add("name", ctx.serialize(name)); + } + + return ret; + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootTable.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootTable.java new file mode 100644 index 00000000..bff1b26f --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootTable.java @@ -0,0 +1,72 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.loot; + +import java.util.Arrays; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; + +import net.fabricmc.fabric.api.loot.v1.FabricLootSupplier; + +import net.patchworkmc.api.loot.ForgeLootTable; +import net.patchworkmc.api.loot.ForgeLootPool; + +@Mixin(LootTable.class) +public class MixinLootTable implements ForgeLootTable { + @Shadow + LootPool[] pools; + + // Forge added methods + + // TODO: freezing stuff + + public LootPool getPool(String name) { + return ((FabricLootSupplier) this).getPools().stream().filter(e -> name.equals(((ForgeLootPool) e).getName())).findFirst().orElse(null); + } + + public LootPool removePool(String name) { + // checkFrozen(); + for (int idx = 0; idx < pools.length; ++idx) { + LootPool pool = pools[idx]; + + if (name.equals(((ForgeLootPool) pool).getName())) { + // https://stackoverflow.com/a/644764 + System.arraycopy(pools, idx + 1, pools, idx, pools.length - 1 - idx); + return pool; + } + } + + return null; + } + + public void addPool(LootPool pool) { + // checkFrozen(); + if (((FabricLootSupplier) this).getPools().stream().anyMatch(e -> e == pool || ((ForgeLootPool) e).getName().equals(((ForgeLootPool) pool).getName()))) { + throw new RuntimeException("Attempted to add a duplicate pool to loot table: " + ((ForgeLootPool) pool).getName()); + } + + pools = Arrays.copyOf(pools, pools.length + 1); + pools[pools.length - 1] = pool; + } +} diff --git a/patchwork-loot/src/main/resources/patchwork-loot.mixins.json b/patchwork-loot/src/main/resources/patchwork-loot.mixins.json index 137951b0..4cedf63b 100644 --- a/patchwork-loot/src/main/resources/patchwork-loot.mixins.json +++ b/patchwork-loot/src/main/resources/patchwork-loot.mixins.json @@ -5,7 +5,13 @@ "mixins": [ "MixinAdvancementRewards", "MixinFishingBobberEntity", - "MixinLootContextTypes" + "MixinJsonDataLoader", + "MixinLootContextTypes", + "MixinLootManager", + "MixinLootPool", + "MixinLootPoolBuilder", + "MixinLootPoolSerializer", + "MixinLootTable" ], "injectors": { "defaultRequire": 1 From 8210832823bb4fb9f1b56971be5a32e31b69904d Mon Sep 17 00:00:00 2001 From: Kitlith Date: Fri, 10 Jul 2020 18:57:03 -0700 Subject: [PATCH 2/9] Fix LocalCapture.PRINT that I forgot about --- .../net/patchworkmc/mixin/loot/MixinLootPoolSerializer.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolSerializer.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolSerializer.java index dcb929a9..5f57ca15 100644 --- a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolSerializer.java +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolSerializer.java @@ -21,6 +21,8 @@ import java.lang.reflect.Type; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonElement; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -38,8 +40,8 @@ @Mixin(LootPool.Serializer.class) public class MixinLootPoolSerializer { - @Inject(method = "deserialize", at = @At("RETURN"), cancellable = true, locals = LocalCapture.PRINT) - private void addNameToConstructor(CallbackInfoReturnable cir, JsonObject obj) { + @Inject(method = "deserialize", at = @At("RETURN"), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD) + private void addNameToConstructor(JsonElement elem, Type ty, JsonDeserializationContext ctx, CallbackInfoReturnable cir, JsonObject obj) { LootPool ret = cir.getReturnValue(); ((PatchworkLootPool) ret).patchwork$setName(LootHooks.readPoolName(obj)); From a7071e4ac8ffa76d98edb341649efc1e7293dda6 Mon Sep 17 00:00:00 2001 From: Kitlith Date: Fri, 10 Jul 2020 19:06:41 -0700 Subject: [PATCH 3/9] Implement FMLServerStoppedEvent --- .../event/server/FMLServerStoppedEvent.java | 35 ++++++++++++++++++ .../fml/server/ServerLifecycleHooks.java | 3 ++ .../impl/event/lifecycle/LifecycleEvents.java | 14 +++++++ .../event/lifecycle/MixinMinecraftServer.java | 37 +++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/event/server/FMLServerStoppedEvent.java create mode 100644 patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftServer.java diff --git a/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/event/server/FMLServerStoppedEvent.java b/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/event/server/FMLServerStoppedEvent.java new file mode 100644 index 00000000..fdd56c06 --- /dev/null +++ b/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/event/server/FMLServerStoppedEvent.java @@ -0,0 +1,35 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.fml.event.server; + +import net.minecraft.server.MinecraftServer; + +/** + * Called after {@link FMLServerStoppingEvent} when the server has completely shut down. + * Called immediately before shutting down, on the dedicated server, and before returning + * to the main menu on the client. + * + * @author cpw + */ +public class FMLServerStoppedEvent extends ServerLifecycleEvent { + public FMLServerStoppedEvent(MinecraftServer server) { + super(server); + } +} diff --git a/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java b/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java index c3c1b8fc..60ebf46a 100644 --- a/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java +++ b/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java @@ -19,6 +19,8 @@ package net.minecraftforge.fml.server; +import java.util.concurrent.CountDownLatch; + import net.minecraft.server.MinecraftServer; import net.patchworkmc.impl.event.lifecycle.LifecycleEvents; @@ -28,6 +30,7 @@ */ public class ServerLifecycleHooks { public static MinecraftServer currentServer; + public static volatile CountDownLatch exitLatch = null; public static MinecraftServer getCurrentServer() { return currentServer; diff --git a/patchwork-events-lifecycle/src/main/java/net/patchworkmc/impl/event/lifecycle/LifecycleEvents.java b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/impl/event/lifecycle/LifecycleEvents.java index 8c950a0c..5dd0e083 100644 --- a/patchwork-events-lifecycle/src/main/java/net/patchworkmc/impl/event/lifecycle/LifecycleEvents.java +++ b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/impl/event/lifecycle/LifecycleEvents.java @@ -20,6 +20,7 @@ package net.patchworkmc.impl.event.lifecycle; import java.nio.file.Path; +import java.util.concurrent.CountDownLatch; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.TickEvent; @@ -30,6 +31,7 @@ import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent; import net.minecraftforge.fml.event.server.FMLServerStartedEvent; import net.minecraftforge.fml.event.server.FMLServerStartingEvent; +import net.minecraftforge.fml.event.server.FMLServerStoppedEvent; import net.minecraftforge.fml.loading.FileUtils; import net.minecraftforge.fml.server.ServerLifecycleHooks; @@ -93,6 +95,18 @@ public static void handleLoadComplete() { loadCompleteCallback.run(); } + public static void handleServerStopped(final MinecraftServer server) { + MinecraftForge.EVENT_BUS.post(new FMLServerStoppedEvent(server)); + ServerLifecycleHooks.currentServer = null; + LogicalSidedProvider.setServer(null); + CountDownLatch latch = ServerLifecycleHooks.exitLatch; + + if (latch != null) { + latch.countDown(); + ServerLifecycleHooks.exitLatch = null; + } + } + @Override public void onInitialize() { WorldTickCallback.EVENT.register(world -> fireWorldTickEvent(TickEvent.Phase.END, world)); diff --git a/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftServer.java b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftServer.java new file mode 100644 index 00000000..642d22dd --- /dev/null +++ b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftServer.java @@ -0,0 +1,37 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.lifecycle; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.server.MinecraftServer; + +import net.patchworkmc.impl.event.lifecycle.LifecycleEvents; + +@Mixin(MinecraftServer.class) +public class MixinMinecraftServer { + @Inject(method = "run", at = @At(value = "INVOKE", target = "net/minecraft/server/MinecraftServer.exit ()V")) + void serverStoppedHook(CallbackInfo ci) { + LifecycleEvents.handleServerStopped((MinecraftServer) (Object) this); + } +} From bd89e3383f45f35c817a4fb056354d5a1c0055bd Mon Sep 17 00:00:00 2001 From: Kitlith Date: Fri, 17 Jul 2020 16:45:32 -0700 Subject: [PATCH 4/9] Dispatch from the god classes for the events added in this PR --- .../fml/server/ServerLifecycleHooks.java | 4 ++++ patchwork-god-classes/build.gradle | 1 + .../net/minecraftforge/common/ForgeHooks.java | 15 +++++++++++++++ .../minecraftforge/event/ForgeEventFactory.java | 8 ++++++++ .../src/main/resources/fabric.mod.json | 3 ++- 5 files changed, 30 insertions(+), 1 deletion(-) diff --git a/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java b/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java index 60ebf46a..034bf4bb 100644 --- a/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java +++ b/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java @@ -51,4 +51,8 @@ public static boolean handleServerStarting(final MinecraftServer server) { public static void handleServerStarted(final MinecraftServer server) { LifecycleEvents.handleServerStarted(server); } + + public static void handleServerStopped(final MinecraftServer server) { + LifecycleEvents.handleServerStopped(server); + } } diff --git a/patchwork-god-classes/build.gradle b/patchwork-god-classes/build.gradle index 24901999..ad0f0d6a 100644 --- a/patchwork-god-classes/build.gradle +++ b/patchwork-god-classes/build.gradle @@ -7,4 +7,5 @@ dependencies { compile project(path: ':patchwork-events-entity', configuration: 'dev') compile project(path: ':patchwork-events-lifecycle', configuration: 'dev') compile project(path: ':patchwork-events-rendering', configuration: 'dev') + compile project(path: ':patchwork-loot', configuration: 'dev') } diff --git a/patchwork-god-classes/src/main/java/net/minecraftforge/common/ForgeHooks.java b/patchwork-god-classes/src/main/java/net/minecraftforge/common/ForgeHooks.java index dea1ec51..65e86ca0 100644 --- a/patchwork-god-classes/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/patchwork-god-classes/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -23,6 +23,8 @@ import javax.annotation.Nullable; +import com.google.gson.Gson; +import com.google.gson.JsonObject; import net.minecraftforge.event.ForgeEventFactory; import net.minecraftforge.eventbus.api.Event; @@ -33,12 +35,16 @@ import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.mob.MobEntity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.loot.LootManager; +import net.minecraft.loot.LootTable; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; import net.minecraft.world.IWorld; import net.minecraft.world.MobSpawnerLogic; import net.patchworkmc.impl.event.entity.EntityEvents; +import net.patchworkmc.impl.loot.LootHooks; /* * Note: this class is intended for mod use only, to dispatch to the implementations kept in their own modules. @@ -98,4 +104,13 @@ public static boolean onLivingDrops(LivingEntity entity, DamageSource source, Co public static boolean onPlayerAttackTarget(PlayerEntity player, Entity target) { return EntityEvents.attackEntity(player, target); } + + @Nullable + public static LootTable loadLootTable(Gson gson, Identifier name, JsonObject data, boolean custom, LootManager lootTableManager) { + return LootHooks.loadLootTable(gson, name, data, custom, lootTableManager); + } + + public static String readPoolName(JsonObject json) { + return LootHooks.readPoolName(json); + } } diff --git a/patchwork-god-classes/src/main/java/net/minecraftforge/event/ForgeEventFactory.java b/patchwork-god-classes/src/main/java/net/minecraftforge/event/ForgeEventFactory.java index f6bc5485..665926e3 100644 --- a/patchwork-god-classes/src/main/java/net/minecraftforge/event/ForgeEventFactory.java +++ b/patchwork-god-classes/src/main/java/net/minecraftforge/event/ForgeEventFactory.java @@ -28,12 +28,16 @@ import net.minecraft.entity.SpawnType; import net.minecraft.entity.mob.MobEntity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.loot.LootManager; +import net.minecraft.loot.LootTable; +import net.minecraft.util.Identifier; import net.minecraft.world.IWorld; import net.minecraft.world.MobSpawnerLogic; import net.minecraft.world.World; import net.patchworkmc.impl.capability.CapabilityEvents; import net.patchworkmc.impl.event.entity.EntityEvents; +import net.patchworkmc.impl.event.loot.LootEvents; /* * Note: this class is intended for mod use only, to dispatch to the implementations kept in their own modules. @@ -65,4 +69,8 @@ public static void onPlayerFall(PlayerEntity player, float distance, float multi public static boolean doSpecialSpawn(MobEntity entity, World world, float x, float y, float z, MobSpawnerLogic spawner, SpawnType spawnReason) { return EntityEvents.doSpecialSpawn(entity, world, x, y, z, spawner, spawnReason); } + + public static LootTable loadLootTable(Identifier name, LootTable table, LootManager lootTableManager) { + return LootEvents.loadLootTable(name, table, lootTableManager); + } } diff --git a/patchwork-god-classes/src/main/resources/fabric.mod.json b/patchwork-god-classes/src/main/resources/fabric.mod.json index 07c35810..388b9196 100644 --- a/patchwork-god-classes/src/main/resources/fabric.mod.json +++ b/patchwork-god-classes/src/main/resources/fabric.mod.json @@ -19,7 +19,8 @@ "patchwork-fml": "*", "patchwork-capabilities": "*", "patchwork-events-lifecycle": "*", - "patchwork-events-rendering": "*" + "patchwork-events-rendering": "*", + "patchwork-loot": "*" }, "custom": { "modmenu:api": true, From 5f795cb84f19e44b8b23eb9387e8a77a080295a7 Mon Sep 17 00:00:00 2001 From: Kitlith Date: Sun, 19 Jul 2020 15:27:01 -0700 Subject: [PATCH 5/9] prevent memory leak --- .../mixin/loot/MixinLootManager.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java index 3091aade..4933717a 100644 --- a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.util.Deque; import java.util.Map; +import java.util.function.BiConsumer; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Queues; @@ -29,6 +30,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import jdk.nashorn.internal.codegen.CompilerConstants; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -50,12 +52,12 @@ public abstract class MixinLootManager extends MixinJsonDataLoader { @Unique private ResourceManager resourceManager; + // TODO: is reentrancy necessary? @Unique private static ThreadLocal> lootContext = new ThreadLocal>(); - // TODO: is reentrancy necessary? - @Inject(method = "apply", at = @At("HEAD")) - private void getResourceManager(Map map, ResourceManager resourceManager, Profiler profiler, CallbackInfo info) { + @Redirect(method = "apply", at = @At(value = "INVOKE", target = "java/util/Map.forEach (Ljava/util/function/BiConsumer;)V")) + private void handleContext(Map map, BiConsumer consumer, Map sameMap, ResourceManager resourceManager, Profiler profiler) { this.resourceManager = resourceManager; Deque que = lootContext.get(); @@ -65,13 +67,13 @@ private void getResourceManager(Map map, ResourceManager } que.push((LootManager) (Object) this); - } - @Inject(method = "apply", at = @At("RETURN")) - private void delResourceManager(CallbackInfo info) { - // TODO: what if an exception is thrown? - resourceManager = null; - lootContext.get().pop(); + try { + map.forEach(consumer); + } finally { + this.resourceManager = null; + lootContext.get().pop(); + } } @Redirect(method = "method_20711", at = @At(value = "INVOKE", target = "com/google/gson/Gson.fromJson (Lcom/google/gson/JsonElement;Ljava/lang/Class;)Ljava/lang/Object;")) From 1e2885ad92d61c0c44f8b8bd454ace40d5c4045e Mon Sep 17 00:00:00 2001 From: Kitlith Date: Sun, 19 Jul 2020 16:37:32 -0700 Subject: [PATCH 6/9] fixup! checkstype --- .../main/java/net/patchworkmc/mixin/loot/MixinLootManager.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java index 4933717a..917ddf74 100644 --- a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java @@ -30,13 +30,10 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import jdk.nashorn.internal.codegen.CompilerConstants; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import net.minecraft.loot.LootManager; import net.minecraft.resource.Resource; From 4b79ec8513714b37772ce76af930dc65bb2f524a Mon Sep 17 00:00:00 2001 From: Kitlith Date: Sun, 19 Jul 2020 22:41:05 -0700 Subject: [PATCH 7/9] Make slightly less hacky! The idea is to add an inject in the same place as the method we would be redirecting (but are instead cancelling) so that we can capture locals, and grab the builder that we would otherwise be missing. The downside is that we are effectively overwriting the lambda that vanilla uses in favor of our own. --- .../mixin/loot/MixinLootManager.java | 69 ++++++++----------- 1 file changed, 28 insertions(+), 41 deletions(-) diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java index 917ddf74..7a44592c 100644 --- a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java @@ -19,23 +19,24 @@ package net.patchworkmc.mixin.loot; -import java.io.IOException; -import java.util.Deque; import java.util.Map; import java.util.function.BiConsumer; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Queues; import com.google.gson.Gson; -import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; +import org.apache.logging.log4j.Logger; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import net.minecraft.loot.LootManager; +import net.minecraft.loot.LootTable; import net.minecraft.resource.Resource; import net.minecraft.resource.ResourceManager; import net.minecraft.util.Identifier; @@ -45,44 +46,30 @@ @Mixin(LootManager.class) public abstract class MixinLootManager extends MixinJsonDataLoader { - // should this also be part of the static threadlocal? - @Unique - private ResourceManager resourceManager; + @Shadow + @Final + private static Gson GSON; - // TODO: is reentrancy necessary? - @Unique - private static ThreadLocal> lootContext = new ThreadLocal>(); + @Shadow + @Final + private static Logger LOGGER; - @Redirect(method = "apply", at = @At(value = "INVOKE", target = "java/util/Map.forEach (Ljava/util/function/BiConsumer;)V")) - private void handleContext(Map map, BiConsumer consumer, Map sameMap, ResourceManager resourceManager, Profiler profiler) { - this.resourceManager = resourceManager; - Deque que = lootContext.get(); - - if (que == null) { - que = Queues.newArrayDeque(); - lootContext.set(que); - } - - que.push((LootManager) (Object) this); - - try { - map.forEach(consumer); - } finally { - this.resourceManager = null; - lootContext.get().pop(); - } + @Redirect(method = "apply", at = @At(value = "INVOKE", target = "java/util/Map.forEach (Ljava/util/function/BiConsumer;)V", ordinal = 0)) + private void cancel_forEach(Map map, BiConsumer consumer) { + // ignore this call, we're gonna reintroduce it but with capturing locals } - @Redirect(method = "method_20711", at = @At(value = "INVOKE", target = "com/google/gson/Gson.fromJson (Lcom/google/gson/JsonElement;Ljava/lang/Class;)Ljava/lang/Object;")) - private static Object loadLootTable(Gson GSON, JsonElement elem, Class cls, ImmutableMap.Builder _bld, Identifier id, JsonObject obj) throws IOException { - LootManager lootManager = lootContext.get().peek(); - - if (lootManager == null) { - throw new JsonParseException("Invalid call stack, could not grab loot manager!"); // Should I throw this? Do we care about custom deserializers outside the manager? - } - - ResourceManager resourceManager = ((MixinLootManager) (Object) lootManager).resourceManager; - Resource res = resourceManager.getResource(((MixinLootManager) (Object) lootManager).getPreparedPath(id)); - return LootHooks.loadLootTable(GSON, id, elem, res == null || !res.getResourcePackName().equals("Default"), lootManager); + @Inject(method = "apply", at = @At(value = "INVOKE", target = "java/util/Map.forEach (Ljava/util/function/BiConsumer;)V", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD) + private void reintroduce_forEach(Map map, ResourceManager resourceManager, Profiler profiler, CallbackInfo info, ImmutableMap.Builder builder) { + map.forEach((id, jsonObject) -> { + try { + LootManager lootManager = (LootManager) (Object) this; + Resource res = resourceManager.getResource(this.getPreparedPath(id)); + LootTable lootTable = LootHooks.loadLootTable(GSON, id, jsonObject, res == null || !res.getResourcePackName().equals("Default"), lootManager); + builder.put(id, lootTable); + } catch (Exception ex) { + LOGGER.error("Couldn't parse loot table {}", id, ex); + } + }); } } From 85cddc7f4472afa0d52a66dfa2de4aecda14c3cf Mon Sep 17 00:00:00 2001 From: Kitlith Date: Sun, 19 Jul 2020 23:36:10 -0700 Subject: [PATCH 8/9] Make the implicit "overwrite" explicit. --- .../java/net/patchworkmc/mixin/loot/MixinLootManager.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java index 7a44592c..4ae6cb97 100644 --- a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java @@ -28,6 +28,7 @@ import org.apache.logging.log4j.Logger; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -72,4 +73,9 @@ private void reintroduce_forEach(Map map, ResourceManage } }); } + + @Overwrite + private static void method_20711(ImmutableMap.Builder builder, Identifier id, JsonObject obj) { + // We are effectively overwriting this lambda with our own, so let's make that explicit by actually overwriting it. + } } From 9e9326ea50583445a72bf04d081b2acbdc31f340 Mon Sep 17 00:00:00 2001 From: Kitlith Date: Tue, 21 Jul 2020 15:55:17 -0700 Subject: [PATCH 9/9] Make requested changes. Mainly adding Override annotations to methods for interfaces, but a few other things as well. --- .../mixin/event/lifecycle/MixinMinecraftServer.java | 2 +- .../src/main/java/net/patchworkmc/impl/loot/LootHooks.java | 1 + .../main/java/net/patchworkmc/mixin/loot/MixinLootPool.java | 6 ++++++ .../net/patchworkmc/mixin/loot/MixinLootPoolBuilder.java | 5 ++--- .../java/net/patchworkmc/mixin/loot/MixinLootTable.java | 3 +++ 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftServer.java b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftServer.java index 642d22dd..6527d290 100644 --- a/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftServer.java +++ b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftServer.java @@ -31,7 +31,7 @@ @Mixin(MinecraftServer.class) public class MixinMinecraftServer { @Inject(method = "run", at = @At(value = "INVOKE", target = "net/minecraft/server/MinecraftServer.exit ()V")) - void serverStoppedHook(CallbackInfo ci) { + private void serverStoppedHook(CallbackInfo ci) { LifecycleEvents.handleServerStopped((MinecraftServer) (Object) this); } } diff --git a/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/LootHooks.java b/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/LootHooks.java index 3d270755..93492205 100644 --- a/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/LootHooks.java +++ b/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/LootHooks.java @@ -39,6 +39,7 @@ import net.patchworkmc.impl.event.loot.LootEvents; +// NOTE: this class is more or less a direct copy of parts of Forge's ForgeHooks. public class LootHooks { @Unique private static ThreadLocal> lootContext = new ThreadLocal>(); diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPool.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPool.java index 5eddb8a2..b538bfbf 100644 --- a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPool.java +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPool.java @@ -44,6 +44,7 @@ public class MixinLootPool implements PatchworkLootPool, ForgeLootPool { // implementation detail // TODO: if we could have an inner class that was also a mixin, we could set this as protected? + @Override public void patchwork$setName(String name) { this.name = name; } @@ -52,23 +53,28 @@ public class MixinLootPool implements PatchworkLootPool, ForgeLootPool { // TODO: freezing stuff + @Override public String getName() { return this.name; } + @Override public LootTableRange getRolls() { return rollsRange; } + @Override public LootTableRange getBonusRolls() { return this.bonusRollsRange; } + @Override public void setRolls(UniformLootTableRange v) { // checkFrozen(); this.rollsRange = v; } + @Override public void setBonusRolls(UniformLootTableRange v) { // checkFrozen(); this.bonusRollsRange = v; diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolBuilder.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolBuilder.java index 0c26a1fd..d2d2d8fc 100644 --- a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolBuilder.java +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolBuilder.java @@ -44,16 +44,15 @@ public abstract class MixinLootPoolBuilder implements ForgeLootPool.Builder { private void addNameToConstructor(CallbackInfoReturnable cir) { LootPool ret = cir.getReturnValue(); ((PatchworkLootPool) ret).patchwork$setName(name); - - // is this necessary? - cir.setReturnValue(ret); } + @Override public LootPool.Builder name(String name) { this.name = name; return (LootPool.Builder) (Object) this; } + @Override public LootPool.Builder bonusRolls(float min, float max) { this.bonusRollsRange = new UniformLootTableRange(min, max); return (LootPool.Builder) (Object) this; diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootTable.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootTable.java index bff1b26f..346b04c3 100644 --- a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootTable.java +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootTable.java @@ -41,10 +41,12 @@ public class MixinLootTable implements ForgeLootTable { // TODO: freezing stuff + @Override public LootPool getPool(String name) { return ((FabricLootSupplier) this).getPools().stream().filter(e -> name.equals(((ForgeLootPool) e).getName())).findFirst().orElse(null); } + @Override public LootPool removePool(String name) { // checkFrozen(); for (int idx = 0; idx < pools.length; ++idx) { @@ -60,6 +62,7 @@ public LootPool removePool(String name) { return null; } + @Override public void addPool(LootPool pool) { // checkFrozen(); if (((FabricLootSupplier) this).getPools().stream().anyMatch(e -> e == pool || ((ForgeLootPool) e).getName().equals(((ForgeLootPool) pool).getName()))) {