Skip to content

Commit

Permalink
Add a loot table loading event
Browse files Browse the repository at this point in the history
  • Loading branch information
Juuxel committed Feb 2, 2019
1 parent 17659b8 commit 502b678
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 93 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.fabric.api.events;

import net.fabricmc.fabric.api.loot.FabricLootSupplier;
import net.fabricmc.fabric.util.HandlerArray;
import net.fabricmc.fabric.util.HandlerRegistry;
import net.minecraft.util.Identifier;

import java.util.function.BiConsumer;

/**
* An event handler that is called when loot tables are loaded.
* Use {@link #REGISTRY} to register instances.
*/
@FunctionalInterface
public interface LootTableLoadingCallback extends BiConsumer<Identifier, FabricLootSupplier> {
final HandlerRegistry<LootTableLoadingCallback> REGISTRY = new HandlerArray<>(LootTableLoadingCallback.class);
}
44 changes: 44 additions & 0 deletions src/main/java/net/fabricmc/fabric/api/loot/FabricLootPool.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.fabric.api.loot;

import net.minecraft.world.loot.LootPool;
import net.minecraft.world.loot.LootTableRange;
import net.minecraft.world.loot.UniformLootTableRange;
import net.minecraft.world.loot.condition.LootCondition;
import net.minecraft.world.loot.entry.LootEntry;
import net.minecraft.world.loot.function.LootFunction;

/**
* An interface implemented by all {@code net.minecraft.world.loot.LootPool} instances when
* Fabric API is present. Contains accessors for various fields.
*/
public interface FabricLootPool {
default LootPool toVanilla() {
return (LootPool) this;
}
LootEntry[] getEntries();
void setEntries(LootEntry[] entries);
LootCondition[] getConditions();
void setConditions(LootCondition[] conditions);
LootFunction[] getFunctions();
void setFunctions(LootFunction[] entries);
LootTableRange getRolls();
void setRolls(LootTableRange rolls);
UniformLootTableRange getBonusRolls();
void setBonusRolls(UniformLootTableRange bonusRolls);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,22 @@
* limitations under the License.
*/

package net.fabricmc.fabric.impl.loot;
package net.fabricmc.fabric.api.loot;

import net.minecraft.world.loot.LootPool;
import net.minecraft.world.loot.LootSupplier;
import net.minecraft.world.loot.function.LootFunction;

import java.util.List;

public class LootPoolAdder {
public List<String> targets;
public List<LootPool> pools;
/**
* An interface implemented by all {@code net.minecraft.world.loot.LootSupplier} instances when
* Fabric API is present. Contains accessors for various fields.
*/
public interface FabricLootSupplier {
default LootSupplier toVanilla() {
return (LootSupplier) this;
}
LootPool[] getPools();
void setPools(LootPool[] pools);
LootFunction[] getFunctions();
void setFunctions(LootFunction[] functions);
}
23 changes: 0 additions & 23 deletions src/main/java/net/fabricmc/fabric/impl/loot/LootSupplierHooks.java

This file was deleted.

61 changes: 9 additions & 52 deletions src/main/java/net/fabricmc/fabric/mixin/loot/MixinLootManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,78 +16,35 @@

package net.fabricmc.fabric.mixin.loot;

import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.google.gson.Gson;
import net.fabricmc.fabric.impl.loot.LootPoolAdder;
import net.fabricmc.fabric.impl.loot.LootSupplierHooks;
import net.minecraft.resource.Resource;
import net.fabricmc.fabric.api.events.LootTableLoadingCallback;
import net.fabricmc.fabric.api.loot.FabricLootSupplier;
import net.fabricmc.fabric.util.HandlerArray;
import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper;
import net.minecraft.world.loot.LootManager;
import net.minecraft.world.loot.LootPool;
import net.minecraft.world.loot.LootSupplier;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.Logger;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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.callback.CallbackInfo;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Map;

@Mixin(LootManager.class)
public class MixinLootManager {
@Shadow @Final public static int jsonLength;
@Shadow @Final private static Logger LOGGER;
@Shadow @Final private static Gson gson;
@Shadow @Final private Map<Identifier, LootSupplier> suppliers;
private static final String PATH = "loot_pools";
private static final int PATH_LENGTH = PATH.length() + 1; // Includes the slash as well

/**
* A map of loot pools adders with the keys being the targets.
*/
private final Multimap<Identifier, LootPoolAdder> lootPoolAdders = MultimapBuilder.hashKeys().hashSetValues().build();

@Inject(method = "onResourceReload", at = @At("HEAD"))
private void loadAdders(ResourceManager manager, CallbackInfo info) {
manager.findResources(PATH, (name) -> name.endsWith(".json")).forEach(fullId -> {
String originalPath = fullId.getPath();
Identifier id = new Identifier(fullId.getNamespace(), originalPath.substring(PATH_LENGTH, originalPath.length() - jsonLength));
try {
try (Resource resource = manager.getResource(fullId)) {
LootPoolAdder adder = JsonHelper.deserialize(gson, IOUtils.toString(resource.getInputStream(), StandardCharsets.UTF_8), LootPoolAdder.class);
if (adder != null) {
if (adder.targets.size() == 0) {
LOGGER.warn("[Fabric] Loot pool adder {} has no targets", id);
}

for (String target : adder.targets) {
lootPoolAdders.put(new Identifier(target), adder);
}
}
}
} catch (Throwable t) {
LOGGER.error("[Fabric] Couldn't read loot pool adder {} from {}", id, fullId, t);
}
});
}

@Inject(method = "onResourceReload", at = @At("RETURN"))
private void injectAdders(ResourceManager manager, CallbackInfo info) {
private void onResourceReload(ResourceManager manager, CallbackInfo info) {
suppliers.forEach((id, supplier) -> {
ArrayList<LootPool> pools = new ArrayList<>();
for (LootPoolAdder adder : lootPoolAdders.get(id)) {
pools.addAll(adder.pools);
}
LootTableLoadingCallback[] handlers = ((HandlerArray<LootTableLoadingCallback>) LootTableLoadingCallback.REGISTRY)
.getBackingArray();

((LootSupplierHooks) supplier).fabric_addPools(pools.toArray(new LootPool[0]));
for (LootTableLoadingCallback handler : handlers) {
handler.accept(id, (FabricLootSupplier) supplier);
}
});
}
}
70 changes: 70 additions & 0 deletions src/main/java/net/fabricmc/fabric/mixin/loot/MixinLootPool.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.fabric.mixin.loot;

import net.fabricmc.fabric.api.loot.FabricLootPool;
import net.minecraft.world.loot.LootPool;
import net.minecraft.world.loot.LootTableRange;
import net.minecraft.world.loot.UniformLootTableRange;
import net.minecraft.world.loot.condition.LootCondition;
import net.minecraft.world.loot.entry.LootEntry;
import net.minecraft.world.loot.function.LootFunction;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

@Mixin(LootPool.class)
public interface MixinLootPool extends FabricLootPool {
@Accessor
@Override
LootEntry[] getEntries();

@Accessor
@Override
LootCondition[] getConditions();

@Accessor
@Override
LootFunction[] getFunctions();

@Accessor
@Override
LootTableRange getRolls();

@Accessor
@Override
UniformLootTableRange getBonusRolls();

@Accessor
@Override
void setEntries(LootEntry[] entries);

@Accessor
@Override
void setConditions(LootCondition[] conditions);

@Accessor
@Override
void setFunctions(LootFunction[] entries);

@Accessor
@Override
void setRolls(LootTableRange rolls);

@Accessor
@Override
void setBonusRolls(UniformLootTableRange bonusRolls);
}
29 changes: 17 additions & 12 deletions src/main/java/net/fabricmc/fabric/mixin/loot/MixinLootSupplier.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,28 @@

package net.fabricmc.fabric.mixin.loot;

import net.fabricmc.fabric.impl.loot.LootSupplierHooks;
import net.fabricmc.fabric.api.loot.FabricLootSupplier;
import net.minecraft.world.loot.LootPool;
import net.minecraft.world.loot.LootSupplier;
import org.spongepowered.asm.mixin.Final;
import net.minecraft.world.loot.function.LootFunction;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.gen.Accessor;

@Mixin(LootSupplier.class)
public class MixinLootSupplier implements LootSupplierHooks {
@Shadow @Final @Mutable private LootPool[] pools;
public interface MixinLootSupplier extends FabricLootSupplier {
@Accessor
@Override
LootPool[] getPools();

@Accessor
@Override
void setPools(LootPool[] pools);

@Accessor
@Override
LootFunction[] getFunctions();

@Accessor
@Override
public void fabric_addPools(LootPool[] pools) {
LootPool[] oldPools = this.pools;
this.pools = new LootPool[oldPools.length + pools.length];
System.arraycopy(oldPools, 0, this.pools, 0, oldPools.length);
System.arraycopy(pools, 0, this.pools, oldPools.length, pools.length);
}
void setFunctions(LootFunction[] functions);
}
1 change: 1 addition & 0 deletions src/main/resources/net.fabricmc.fabric.mixins.common.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"item.MixinAbstractFurnaceBlockEntity",
"itemgroup.MixinItemGroup",
"loot.MixinLootManager",
"loot.MixinLootPool",
"loot.MixinLootSupplier",
"misc.MixinCrashReport",
"networking.MixinServerPlayNetworkHandler",
Expand Down
45 changes: 45 additions & 0 deletions src/test/java/net/fabricmc/fabric/loot/LootTableMod.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.fabric.loot;

import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.events.LootTableLoadingCallback;
import net.minecraft.item.Items;
import net.minecraft.world.loot.ConstantLootTableRange;
import net.minecraft.world.loot.LootPool;
import net.minecraft.world.loot.condition.SurvivesExplosionLootCondition;
import net.minecraft.world.loot.entry.ItemEntry;

import java.util.Arrays;

public class LootTableMod implements ModInitializer {
@Override
public void onInitialize() {
LootTableLoadingCallback.REGISTRY.register((id, supplier) -> {
if ("minecraft:blocks/dirt".equals(id.toString())) {
LootPool[] pools = Arrays.copyOf(supplier.getPools(), supplier.getPools().length + 1);
pools[pools.length - 1] = LootPool.create()
.withEntry(ItemEntry.builder(Items.FEATHER))
.withRolls(ConstantLootTableRange.create(1))
.withCondition(SurvivesExplosionLootCondition.method_871())
.build();

supplier.setPools(pools);
}
});
}
}

0 comments on commit 502b678

Please sign in to comment.