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..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 @@ -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; @@ -48,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-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..6527d290 --- /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")) + private void serverStoppedHook(CallbackInfo ci) { + LifecycleEvents.handleServerStopped((MinecraftServer) (Object) this); + } +} 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, 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..93492205 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/LootHooks.java @@ -0,0 +1,149 @@ +/* + * 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; + +// 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>(); + + 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..4ae6cb97 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java @@ -0,0 +1,81 @@ +/* + * 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 java.util.function.BiConsumer; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +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; +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; +import net.minecraft.util.profiler.Profiler; + +import net.patchworkmc.impl.loot.LootHooks; + +@Mixin(LootManager.class) +public abstract class MixinLootManager extends MixinJsonDataLoader { + @Shadow + @Final + private static Gson GSON; + + @Shadow + @Final + private static Logger LOGGER; + + @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 + } + + @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); + } + }); + } + + @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. + } +} 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..b538bfbf --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPool.java @@ -0,0 +1,82 @@ +/* + * 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? + @Override + public void patchwork$setName(String name) { + this.name = name; + } + + // Forge methods that should be added directly to the type + + // 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 new file mode 100644 index 00000000..d2d2d8fc --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolBuilder.java @@ -0,0 +1,60 @@ +/* + * 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); + } + + @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/MixinLootPoolSerializer.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolSerializer.java new file mode 100644 index 00000000..5f57ca15 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolSerializer.java @@ -0,0 +1,64 @@ +/* + * 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 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; +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.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)); + + // 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..346b04c3 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootTable.java @@ -0,0 +1,75 @@ +/* + * 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 + + @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) { + 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; + } + + @Override + 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