Backport Global Loot Functions by Draco18s

This commit is contained in:
David Quintana committed 2020-04-12 22:45:55 +02:00
1 parent 6aadd20f5f
commit 90403d18f9
17 files changed
+735 -44

No files matched your search

@@ -113,8 +113,11 @@ import net.minecraft.world.IWorld;
import net.minecraft.world.IWorldReader;
import net.minecraft.world.World;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.storage.loot.LootContext;
import net.minecraft.world.storage.loot.LootTable;
import net.minecraft.world.storage.loot.LootTableManager;
import net.minecraftforge.common.loot.IGlobalLootModifier;
import net.minecraftforge.common.loot.LootModifierManager;
import net.minecraftforge.common.util.BlockSnapshot;
import net.minecraftforge.event.AnvilUpdateEvent;
import net.minecraftforge.event.DifficultyChangeEvent;
@@ -1208,4 +1211,19 @@ public class ForgeHooks
FurnaceTileEntity.getBurnTimes().entrySet().forEach(e -> VANILLA_BURNS.put(e.getKey().delegate, e.getValue()));
}
/**
* All loot table drops should be passed to this function so that mod added effects
* (e.g. smelting enchantments) can be processed.
* @param list The loot generated
* @param context The loot context that generated that loot
* @return The modified list
*/
public static List<ItemStack> modifyLoot(List<ItemStack> list, LootContext context) {
LootModifierManager man = context.getWorld().getServer().getLootModifierManager();
for(IGlobalLootModifier mod : man.getAllLootMods()) {
list = mod.apply(list, context);
}
return list;
}
}
@@ -0,0 +1,80 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2019.
*
* 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.common.loot;
import com.google.gson.JsonObject;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.storage.loot.conditions.ILootCondition;
import net.minecraftforge.registries.GameData;
import net.minecraftforge.registries.IForgeRegistryEntry;
/**
* Abstract base deserializer for LootModifiers. Takes care of Forge registry things.<br/>
* Modders should extend this class to return their modifier and implement the abstract
* <code>read</code> method to deserialize from json.
* @param <T> the Type to deserialize
*/
public abstract class GlobalLootModifierSerializer<T extends IGlobalLootModifier> implements IForgeRegistryEntry<GlobalLootModifierSerializer<?>> {
private ResourceLocation registryName = null;
public final GlobalLootModifierSerializer<T> setRegistryName(String name) {
if (getRegistryName() != null)
throw new IllegalStateException("Attempted to set registry name with existing registry name! New: " + name + " Old: " + getRegistryName());
this.registryName = GameData.checkPrefix(name, true);
return this;
}
//Helpers
@Override
public final GlobalLootModifierSerializer<T> setRegistryName(ResourceLocation name){ return setRegistryName(name.toString()); }
public final GlobalLootModifierSerializer<T> setRegistryName(String modID, String name){ return setRegistryName(modID + ":" + name); }
@Override
public final ResourceLocation getRegistryName() {
return registryName;
}
/**
* Most mods will likely not need more than<br/>
* <code>return new MyModifier(conditionsIn)</code><br/>
* but any additional properties that are needed will need to be deserialized here.
* @param name The resource location (if needed)
* @param json The full json object (including ILootConditions)
* @param conditionsIn An already deserialized list of ILootConditions
*/
public abstract T read(ResourceLocation location, JsonObject object, ILootCondition[] ailootcondition);
/**
* Used by Forge's registry system.
*/
@Override
public final Class<GlobalLootModifierSerializer<?>> getRegistryType() {
return castClass(GlobalLootModifierSerializer.class);
}
@SuppressWarnings("unchecked") // Need this wrapper, because generics
private static <G> Class<G> castClass(Class<?> cls)
{
return (Class<G>)cls;
}
}
@@ -0,0 +1,44 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2019.
*
* 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.common.loot;
import java.util.List;
import javax.annotation.Nonnull;
import net.minecraft.item.ItemStack;
import net.minecraft.world.storage.loot.LootContext;
/**
* Implementation that defines what a global loot modifier must implement in order to be functional.
* {@link LootModifier} Supplies base functionality; most modders should only need to extend that.<br/>
* Requires an {@link GlobalLootModifierSerializer} to be registered via json (see forge:loot_modifiers/global_loot_modifiers).
*/
public interface IGlobalLootModifier {
/**
* Applies the modifier to the list of generated loot. This function needs to be responsible for
* checking ILootConditions as well.
* @param generatedLoot the list of ItemStacks that will be dropped, generated by loot tables
* @param context the LootContext, identical to what is passed to loot tables
* @return modified loot drops
*/
@Nonnull
List<ItemStack> apply(List<ItemStack> generatedLoot, LootContext context);
}
@@ -0,0 +1,65 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2019.
*
* 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.common.loot;
import java.util.List;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import net.minecraft.item.ItemStack;
import net.minecraft.world.storage.loot.LootContext;
import net.minecraft.world.storage.loot.conditions.ILootCondition;
import net.minecraft.world.storage.loot.conditions.LootConditionManager;
/**
* A base implementation of a Global Loot Modifier for modders to extend.
* Takes care of ILootCondition matching and comes with a base serializer
* implementation that takes care of Forge registry things.
*/
public abstract class LootModifier implements IGlobalLootModifier {
protected final ILootCondition[] conditions;
private final Predicate<LootContext> combinedConditions;
/**
* Constructs a LootModifier.
* @param conditionsIn the ILootConditions that need to be matched before the loot is modified.
*/
protected LootModifier(ILootCondition[] conditionsIn) {
this.conditions = conditionsIn;
this.combinedConditions = LootConditionManager.and(conditionsIn);
}
@Nonnull
@Override
public final List<ItemStack> apply(List<ItemStack> generatedLoot, LootContext context) {
return this.combinedConditions.test(context) ? this.doApply(generatedLoot, context) : generatedLoot;
}
/**
* Applies the modifier to the generated loot (all loot conditions have already been checked
* and have returned true).
* @param generatedLoot the list of ItemStacks that will be dropped, generated by loot tables
* @param context the LootContext, identical to what is passed to loot tables
* @return modified loot drops
*/
@Nonnull
protected abstract List<ItemStack> doApply(List<ItemStack> generatedLoot, LootContext context);
}
@@ -0,0 +1,148 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2019.
*
* 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.common.loot;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.minecraft.client.resources.JsonReloadListener;
import net.minecraft.profiler.IProfiler;
import net.minecraft.resources.IResource;
import net.minecraft.resources.IResourceManager;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.storage.loot.conditions.ILootCondition;
import net.minecraft.world.storage.loot.conditions.LootConditionManager;
import net.minecraft.world.storage.loot.functions.ILootFunction;
import net.minecraft.world.storage.loot.functions.LootFunctionManager;
import net.minecraftforge.registries.ForgeRegistries;
public class LootModifierManager extends JsonReloadListener {
public static final Logger LOGGER = LogManager.getLogger();
private static final Gson GSON_INSTANCE = (new GsonBuilder()).registerTypeHierarchyAdapter(ILootFunction.class, new LootFunctionManager.Serializer()).registerTypeHierarchyAdapter(ILootCondition.class, new LootConditionManager.Serializer()).create();
private Map<ResourceLocation, IGlobalLootModifier> registeredLootModifiers = ImmutableMap.of();
private static final String folder = "loot_modifiers";
public LootModifierManager() {
super(GSON_INSTANCE, folder);
}
@Override
protected void apply(Map<ResourceLocation, JsonObject> resourceList, IResourceManager resourceManagerIn, IProfiler profilerIn) {
Builder<ResourceLocation, IGlobalLootModifier> builder = ImmutableMap.builder();
//old way (for reference)
/*Map<IGlobalLootModifier, ResourceLocation> toLocation = new HashMap<IGlobalLootModifier, ResourceLocation>();
resourceList.forEach((location, object) -> {
try {
IGlobalLootModifier modifier = deserializeModifier(location, object);
builder.put(location, modifier);
toLocation.put(modifier, location);
} catch (Exception exception) {
LOGGER.error("Couldn't parse loot modifier {}", location, exception);
}
});
builder.orderEntriesByValue((x,y) -> {
return toLocation.get(x).compareTo(toLocation.get(y));
});*/
//new way
ArrayList<ResourceLocation> finalLocations = new ArrayList<ResourceLocation>();
ResourceLocation resourcelocation = new ResourceLocation("forge","loot_modifiers/global_loot_modifiers.json");
try {
//read in all data files from forge:loot_modifiers/global_loot_modifiers in order to do layering
for(IResource iresource : resourceManagerIn.getAllResources(resourcelocation)) {
try ( InputStream inputstream = iresource.getInputStream();
Reader reader = new BufferedReader(new InputStreamReader(inputstream, StandardCharsets.UTF_8));
) {
JsonObject jsonobject = JSONUtils.fromJson(GSON_INSTANCE, reader, JsonObject.class);
boolean replace = jsonobject.get("replace").getAsBoolean();
if(replace) finalLocations.clear();
JsonArray entryList = jsonobject.get("entries").getAsJsonArray();
for(JsonElement entry : entryList) {
String loc = entry.getAsString();
ResourceLocation res = new ResourceLocation(loc);
if(finalLocations.contains(res)) finalLocations.remove(res);
finalLocations.add(res);
}
}
catch (RuntimeException | IOException ioexception) {
LOGGER.error("Couldn't read global loot modifier list {} in data pack {}", resourcelocation, iresource.getPackName(), ioexception);
} finally {
IOUtils.closeQuietly((Closeable)iresource);
}
}
} catch (IOException ioexception1) {
LOGGER.error("Couldn't read global loot modifier list from {}", resourcelocation, ioexception1);
}
//use layered config to fetch modifier data files (modifiers missing from config are disabled)
finalLocations.forEach(location -> {
try {
IGlobalLootModifier modifier = deserializeModifier(location, resourceList.get(location));
builder.put(location, modifier);
} catch (Exception exception) {
LOGGER.error("Couldn't parse loot modifier {}", location, exception);
}
});
ImmutableMap<ResourceLocation, IGlobalLootModifier> immutablemap = builder.build();
this.registeredLootModifiers = immutablemap;
}
private IGlobalLootModifier deserializeModifier(ResourceLocation location, JsonObject object) {
ILootCondition[] lootConditions = GSON_INSTANCE.fromJson(object.get("conditions"), ILootCondition[].class);
ResourceLocation serializer = new ResourceLocation(JSONUtils.getString(object, "type"));
return ForgeRegistries.LOOT_MODIFIER_SERIALIZERS.getValue(serializer).read(location, object, lootConditions);
}
public static GlobalLootModifierSerializer<?> getSerializerForName(ResourceLocation resourcelocation) {
return ForgeRegistries.LOOT_MODIFIER_SERIALIZERS.getValue(resourcelocation);
}
/**
* An immutable collection of the registered loot modifiers in layered order.
* @return
*/
public Collection<IGlobalLootModifier> getAllLootMods() {
return registeredLootModifiers.values();
}
}
@@ -49,6 +49,7 @@ import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.placement.Placement;
import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder;
import net.minecraftforge.common.ModDimension;
import net.minecraftforge.common.loot.GlobalLootModifierSerializer;
import net.minecraftforge.fml.common.registry.GameRegistry;
/**
@@ -97,6 +98,7 @@ public class ForgeRegistries
// Custom forge registries
public static final IForgeRegistry<ModDimension> MOD_DIMENSIONS = RegistryManager.ACTIVE.getRegistry(ModDimension.class);
public static final IForgeRegistry<DataSerializerEntry> DATA_SERIALIZERS = RegistryManager.ACTIVE.getRegistry(DataSerializerEntry.class);
public static final IForgeRegistry<GlobalLootModifierSerializer<?>> LOOT_MODIFIER_SERIALIZERS = RegistryManager.ACTIVE.getRegistry(GlobalLootModifierSerializer.class);
/**
* This function is just to make sure static inializers in other classes have run and setup their registries before we query them.
@@ -63,6 +63,7 @@ import net.minecraft.world.gen.placement.Placement;
import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.ModDimension;
import net.minecraftforge.common.loot.GlobalLootModifierSerializer;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.event.RegistryEvent.MissingMappings;
import net.minecraftforge.fml.LifecycleEventProvider;
@@ -141,6 +142,7 @@ public class GameData
// Custom forge registries
public static final ResourceLocation MODDIMENSIONS = new ResourceLocation("forge:moddimensions");
public static final ResourceLocation SERIALIZERS = new ResourceLocation("minecraft:dataserializers");
public static final ResourceLocation LOOT_MODIFIER_SERIALIZERS = new ResourceLocation("forge:loot_modifier_serializers");
private static final int MAX_VARINT = Integer.MAX_VALUE - 1; //We were told it is their intention to have everything in a reg be unlimited, so assume that until we find cases where it isnt.
@@ -207,6 +209,7 @@ public class GameData
// Custom forge registries
makeRegistry(MODDIMENSIONS, ModDimension.class ).disableSaving().create();
makeRegistry(SERIALIZERS, DataSerializerEntry.class, 256 /*vanilla space*/, MAX_VARINT).disableSaving().disableOverrides().addCallback(SerializerCallbacks.INSTANCE).create();
makeRegistry(LOOT_MODIFIER_SERIALIZERS, GlobalLootModifierSerializer.class).disableSaving().disableSync().create();
}
private static <T extends IForgeRegistryEntry<T>> RegistryBuilder<T> makeRegistry(ResourceLocation name, Class<T> type)