Loot table API v2, iteration 2 (#1241)

* Improve loot table API

Alternative to #629.

- Deprecates all classes and methods that use outdated Yarn names.
- Adds FabricLootTable and FabricLootTableBuilder to replace
  the LootSupplier naming variants.
- Deprecates LootEntryTypeRegistry and LootJsonParser
  as their functionality is exposed in vanilla now.
- Adds methods to FabricLootPoolBuilder for working
  with collections as builder parameters.
- FabricLootPool and FabricLootTable/Supplier now return immutable lists
  instead of modifiable fixed-size ones.

Still WIP: LootTableLoadingCallback uses the deprecated FabricLootSupplier.

Update fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootTableBuilder.java

Co-authored-by: i509VCB <i509vcb@gmail.com>

Fix compilation

Create loot table API v2

Move incorrect classes and revert unnecessary change

Add test for replacing loot tables

Document FabricLootPools

builder() -> create()

Add accessor for LootPool.bonusRolls

Add loot pool builder method for bonus rolls

Use Blocks.DIRT.getLootTableId() instead of raw string in v1 loot test mod

Make links in deprecation docs cleaner

Make FabricLootPoolBuilder.copyFrom(pool, true) also copy bonus rolls

...and mention it in the javadoc

Remove copyFrom from v2 builders

It seems like a maintainability mess if Mojang ever decides
to extend loot tables, and the chosen boolean flags are arbitrary.
It also doesn't really have use cases apart from the internal use
in the copyOf methods, and even then users can replicate its functionality
with the other API methods.

Rename 'supplier' to 'table' in LootManagerMixin

Add 'stable' lifecycle to loot-table-api-v2, deprecate v1

Add internal comment for implementors about updates

Cancel all remaining callbacks when a loot table is replaced

Remove unused shadowed logger from LootManagerMixin

Migrate subproject versioning to new system

Start the AW migration

Update test mod

* Use interface injection

* Fix some issues

* Remove outdated bonusRolls test from LootTest

It's a vanilla feature now.

* Create transitive access widener module

* Replace LootTableLoadingCallback with two events

* Use friendlier exception message

* Add resource source tracking

* Add loot table sources

* Add resource pack source for DefaultResourcePack$1 (anon resource)

* Add license header

* Make FabricResource an internal API in resource loader

* Remove my TAW module

* Add loot table-related TAWs

* Run CI

* Fix LootUtil.determineSource giving null values

* Clarify LootUtil comment

* Rename loot-table-api => loot-api + minor comment changes

* Add README

* Fix mixin file name

* Use ImmutableMap.Builder instead of HashMap.computeIfAbsent in loot event impl

* Prefix accessor methods with fabric_ to prevent conflicts with loot v1

* Document mixins

* Document mixins more extensively

* Improve NRMMixin comments

* Change weird wording in FabricResourceImpl

* Minor updates

- Support new built-in mod respack source
- Fix ResourceMixin comment
- Add more docs to LootTableEvents.MODIFY
- Add package-info

* Add license header
This commit is contained in:
Juuxel 2022-05-31 14:12:10 +03:00 committed by GitHub
parent 2c7b4abb73
commit e747827905
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 1590 additions and 175 deletions

View file

@ -0,0 +1,29 @@
# Fabric Loot API (v2)
This module includes APIs for modifying and creating loot tables.
## [Loot table events](src/main/java/net/fabricmc/fabric/api/loot/v2/LootTableEvents.java)
This class provides two events for modifying loot tables.
`LootTableEvents.REPLACE` runs first and lets you replace loot tables completely.
`LootTableEvents.MODIFY` runs after and lets you modify loot tables, including the ones created in `REPLACE`,
by adding new loot pools or loot functions to them.
### Loot table sources
Both events have access to a [loot table source](src/main/java/net/fabricmc/fabric/api/loot/v2/LootTableSource.java)
that you can use to check where a loot table is loaded from.
For example, you can use this to check if a loot table is from a user data pack and
not modify the user-provided data in your event.
## Enhanced loot table and loot pool builders
`LootTable.Builder` and `LootPool.Builder` implement
injected interfaces ([`FabricLootTableBuilder`](src/main/java/net/fabricmc/fabric/api/loot/v2/FabricLootTableBuilder.java)
and [`FabricLootPoolBuilder`](src/main/java/net/fabricmc/fabric/api/loot/v2/FabricLootPoolBuilder.java))
which have additional methods for dealing with already-built objects and collections of objects.
Those interfaces also have `copyOf` methods for creating copies of existing loot tables/pools as builders.

View file

@ -0,0 +1,7 @@
archivesBaseName = "fabric-loot-api-v2"
version = getSubprojectVersion(project)
moduleDependencies(project, [
'fabric-api-base',
'fabric-resource-loader-v0'
])

View file

@ -0,0 +1,114 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.v2;
import java.util.Collection;
import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import net.minecraft.loot.LootPool;
import net.minecraft.loot.condition.LootCondition;
import net.minecraft.loot.entry.LootPoolEntry;
import net.minecraft.loot.function.LootFunction;
import net.fabricmc.fabric.mixin.loot.LootPoolAccessor;
/**
* Convenience extensions to {@link LootPool.Builder}
* for adding pre-built objects or collections.
*
* <p>This interface is automatically injected to {@link LootPool.Builder}.
*/
@ApiStatus.NonExtendable
public interface FabricLootPoolBuilder {
/**
* Adds an entry to this builder.
*
* @param entry the added loot entry
* @return this builder
*/
default LootPool.Builder with(LootPoolEntry entry) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Adds entries to this builder.
*
* @param entries the added loot entries
* @return this builder
*/
default LootPool.Builder with(Collection<? extends LootPoolEntry> entries) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Adds a condition to this builder.
*
* @param condition the added condition
* @return this builder
*/
default LootPool.Builder conditionally(LootCondition condition) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Adds conditions to this builder.
*
* @param conditions the added conditions
* @return this builder
*/
default LootPool.Builder conditionally(Collection<? extends LootCondition> conditions) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Applies a function to this builder.
*
* @param function the applied loot function
* @return this builder
*/
default LootPool.Builder apply(LootFunction function) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Applies loot functions to this builder.
*
* @param functions the applied loot functions
* @return this builder
*/
default LootPool.Builder apply(Collection<? extends LootFunction> functions) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Creates a builder copy of a loot pool.
*
* @param pool the loot pool
* @return the copied builder
*/
static LootPool.Builder copyOf(LootPool pool) {
LootPoolAccessor accessor = (LootPoolAccessor) pool;
return LootPool.builder()
.rolls(accessor.fabric_getRolls())
.bonusRolls(accessor.fabric_getBonusRolls())
.with(List.of(accessor.fabric_getEntries()))
.conditionally(List.of(accessor.fabric_getConditions()))
.apply(List.of(accessor.fabric_getFunctions()));
}
}

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.v2;
import java.util.Collection;
import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import net.minecraft.loot.LootPool;
import net.minecraft.loot.LootTable;
import net.minecraft.loot.function.LootFunction;
import net.fabricmc.fabric.mixin.loot.LootTableAccessor;
/**
* Convenience extensions to {@link LootTable.Builder}
* for adding pre-built objects or collections.
*
* <p>This interface is automatically injected to {@link LootTable.Builder}.
*/
@ApiStatus.NonExtendable
public interface FabricLootTableBuilder {
/**
* Adds a loot pool to this builder.
*
* @param pool the added pool
* @return this builder
*/
default LootTable.Builder pool(LootPool pool) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Applies a loot function to this builder.
*
* @param function the applied function
* @return this builder
*/
default LootTable.Builder apply(LootFunction function) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Adds loot pools to this builder.
*
* @param pools the added pools
* @return this builder
*/
default LootTable.Builder pools(Collection<? extends LootPool> pools) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Applies loot functions to this builder.
*
* @param functions the applied functions
* @return this builder
*/
default LootTable.Builder apply(Collection<? extends LootFunction> functions) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Creates a builder copy of a loot table.
*
* @param table the loot table
* @return the copied builder
*/
static LootTable.Builder copyOf(LootTable table) {
LootTable.Builder builder = LootTable.builder();
LootTableAccessor accessor = (LootTableAccessor) table;
builder.type(table.getType());
builder.pools(List.of(accessor.fabric_getPools()));
builder.apply(List.of(accessor.fabric_getFunctions()));
return builder;
}
}

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.v2;
import org.jetbrains.annotations.Nullable;
import net.minecraft.loot.LootManager;
import net.minecraft.loot.LootTable;
import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
/**
* Events for manipulating loot tables.
*/
public final class LootTableEvents {
/**
* This event can be used to replace loot tables.
* If a loot table is replaced, the iteration will stop for that loot table.
*/
public static final Event<Replace> REPLACE = EventFactory.createArrayBacked(Replace.class, listeners -> (resourceManager, lootManager, id, original, source) -> {
for (Replace listener : listeners) {
@Nullable LootTable replaced = listener.replaceLootTable(resourceManager, lootManager, id, original, source);
if (replaced != null) {
return replaced;
}
}
return null;
});
/**
* This event can be used to modify loot tables.
* The main use case is to add items to vanilla or mod loot tables (e.g. modded seeds to grass).
*
* <p>You can also modify loot tables that are created by {@link #REPLACE}.
* They have the loot table source {@link LootTableSource#REPLACED}.
*
* <h2>Example: adding diamonds to the cobblestone loot table</h2>
* <pre>
* {@code
* LootTableEvents.MODIFY.register((resourceManager, lootManager, id, tableBuilder, source) -> {
* // If the loot table is for the cobblestone block and it is not overridden by a user:
* if (Blocks.COBBLESTONE.getLootTableId().equals(id) && source.isBuiltin()) {
* // Create a new loot pool that will hold the diamonds.
* LootPool.Builder pool = LootPool.builder()
* // Add diamonds...
* .with(ItemEntry.builder(Items.DIAMOND))
* // ...only if the block would survive a potential explosion.
* .conditionally(SurvivesExplosionLootCondition.builder());
*
* // Add the loot pool to the loot table
* tableBuilder.pool(pool);
* }
* });
* }
* </pre>
*/
public static final Event<Modify> MODIFY = EventFactory.createArrayBacked(Modify.class, listeners -> (resourceManager, lootManager, id, tableBuilder, source) -> {
for (Modify listener : listeners) {
listener.modifyLootTable(resourceManager, lootManager, id, tableBuilder, source);
}
});
public interface Replace {
/**
* Replaces loot tables.
*
* @param resourceManager the server resource manager
* @param lootManager the loot manager
* @param id the loot table ID
* @param original the original loot table
* @param source the source of the original loot table
* @return the new loot table, or null if it wasn't replaced
*/
@Nullable
LootTable replaceLootTable(ResourceManager resourceManager, LootManager lootManager, Identifier id, LootTable original, LootTableSource source);
}
public interface Modify {
/**
* Called when a loot table is loading to modify loot tables.
*
* @param resourceManager the server resource manager
* @param lootManager the loot manager
* @param id the loot table ID
* @param tableBuilder a builder of the loot table being loaded
* @param source the source of the loot table
*/
void modifyLootTable(ResourceManager resourceManager, LootManager lootManager, Identifier id, LootTable.Builder tableBuilder, LootTableSource source);
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.v2;
/**
* Describes where a loot table has been loaded from.
*/
public enum LootTableSource {
/**
* A loot table loaded from the default data pack.
*/
VANILLA(true),
/**
* A loot table loaded from mods' bundled resources.
*
* <p>This includes the additional builtin data packs registered by mods
* with Fabric Resource Loader.
*/
MOD(true),
/**
* A loot table loaded from an external data pack.
*/
DATA_PACK(false),
/**
* A loot table created in {@link LootTableEvents#REPLACE}.
*/
REPLACED(false);
private final boolean builtin;
LootTableSource(boolean builtin) {
this.builtin = builtin;
}
/**
* Returns whether this loot table source is builtin
* and bundled in the vanilla or mod resources.
*
* <p>{@link #VANILLA} and {@link #MOD} are builtin.
*
* @return {@code true} if builtin, {@code false} otherwise
*/
public boolean isBuiltin() {
return builtin;
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.
*/
/**
* The Fabric Loot API for manipulating and creating loot tables.
*
* <h2>Events</h2>
* {@link net.fabricmc.fabric.api.loot.v2.LootTableEvents} has events to modify existing loot tables,
* or outright replace them with a new loot table.
*
* <p>You can also check where loot tables are coming from in those events with
* {@link net.fabricmc.fabric.api.loot.v2.LootTableSource}. This is useful when you only want to modify
* loot tables from mods or vanilla, but not user-created data packs.
*
* <h2>Extended loot table and pool builders</h2>
* This API has injected interfaces to add useful methods to
* {@linkplain net.fabricmc.fabric.api.loot.v2.FabricLootTableBuilder loot table} and
* {@linkplain net.fabricmc.fabric.api.loot.v2.FabricLootPoolBuilder loot pool} builders.
* They let you add pre-built objects instead of builders, and collections of objects to the builder
* with one method call.
*/
package net.fabricmc.fabric.api.loot.v2;

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.impl.loot;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.minecraft.resource.Resource;
import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.ResourcePackSource;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.loot.v2.LootTableSource;
import net.fabricmc.fabric.impl.resource.loader.BuiltinModResourcePackSource;
import net.fabricmc.fabric.impl.resource.loader.FabricResource;
import net.fabricmc.fabric.impl.resource.loader.ModResourcePackCreator;
public final class LootUtil {
public static final Logger LOGGER = LoggerFactory.getLogger("fabric-loot-api-v2");
public static LootTableSource determineSource(Identifier lootTableId, ResourceManager resourceManager) {
Identifier resourceId = new Identifier(lootTableId.getNamespace(), "loot_tables/%s.json".formatted(lootTableId.getPath()));
try (Resource resource = resourceManager.getResource(resourceId)) {
ResourcePackSource packSource = ((FabricResource) resource).getFabricPackSource();
if (packSource == ResourcePackSource.PACK_SOURCE_BUILTIN) {
return LootTableSource.VANILLA;
} else if (packSource == ModResourcePackCreator.RESOURCE_PACK_SOURCE || packSource instanceof BuiltinModResourcePackSource) {
return LootTableSource.MOD;
}
} catch (IOException e) {
LOGGER.error("Could not open resource for loot table {} to check its source", lootTableId, e);
}
// If not builtin or mod, assume external data pack.
// It might also be a virtual loot table injected via mixin instead of being loaded
// from a resource, but we can't determine that here.
return LootTableSource.DATA_PACK;
}
}

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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 java.util.Map;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonObject;
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 net.minecraft.loot.LootManager;
import net.minecraft.loot.LootTable;
import net.minecraft.loot.LootTables;
import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
import net.minecraft.util.profiler.Profiler;
import net.fabricmc.fabric.api.loot.v2.FabricLootTableBuilder;
import net.fabricmc.fabric.api.loot.v2.LootTableEvents;
import net.fabricmc.fabric.api.loot.v2.LootTableSource;
import net.fabricmc.fabric.impl.loot.LootUtil;
/**
* Implements the events from {@link LootTableEvents}.
*/
@Mixin(LootManager.class)
abstract class LootManagerMixin {
@Shadow
private Map<Identifier, LootTable> tables;
@Inject(method = "apply", at = @At("RETURN"))
private void apply(Map<Identifier, JsonObject> jsonMap, ResourceManager resourceManager, Profiler profiler, CallbackInfo info) {
// The builder for the new LootManager.tables map with modified loot tables.
// We're using an immutable map to match vanilla.
ImmutableMap.Builder<Identifier, LootTable> newTables = ImmutableMap.builder();
tables.forEach((id, table) -> {
if (id.equals(LootTables.EMPTY)) {
// This is a special table and cannot be modified.
// Vanilla also warns about that.
return;
}
// noinspection ConstantConditions
LootManager lootManager = (LootManager) (Object) this;
LootTableSource source = LootUtil.determineSource(id, resourceManager);
// Invoke the REPLACE event for the current loot table.
LootTable replacement = LootTableEvents.REPLACE.invoker().replaceLootTable(resourceManager, lootManager, id, table, source);
if (replacement != null) {
// Set the loot table to MODIFY to be the replacement loot table.
// The MODIFY event will also see it as a replaced loot table via the source.
table = replacement;
source = LootTableSource.REPLACED;
}
// Turn the current table into a modifiable builder and invoke the MODIFY event.
LootTable.Builder builder = FabricLootTableBuilder.copyOf(table);
LootTableEvents.MODIFY.invoker().modifyLootTable(resourceManager, lootManager, id, builder, source);
// Turn the builder back into a loot table and store it in the new table.
newTables.put(id, builder.build());
});
// Finally, store the new loot table map in the field.
tables = newTables.build();
}
}

View file

@ -14,9 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package net.fabricmc.fabric.mixin.loot.table; package net.fabricmc.fabric.mixin.loot;
import java.util.List;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Accessor;
@ -25,13 +23,26 @@ import net.minecraft.loot.LootPool;
import net.minecraft.loot.condition.LootCondition; import net.minecraft.loot.condition.LootCondition;
import net.minecraft.loot.entry.LootPoolEntry; import net.minecraft.loot.entry.LootPoolEntry;
import net.minecraft.loot.function.LootFunction; import net.minecraft.loot.function.LootFunction;
import net.minecraft.loot.provider.number.LootNumberProvider;
@Mixin(LootPool.Builder.class) /**
public interface LootPoolBuilderHooks { * Accesses loot pool fields for {@link net.fabricmc.fabric.api.loot.v2.FabricLootPoolBuilder#copyOf(LootPool)}.
@Accessor * These are normally available in the transitive access widener module.
List<LootPoolEntry> getEntries(); */
@Accessor @Mixin(LootPool.class)
List<LootCondition> getConditions(); public interface LootPoolAccessor {
@Accessor @Accessor("rolls")
List<LootFunction> getFunctions(); LootNumberProvider fabric_getRolls();
@Accessor("bonusRolls")
LootNumberProvider fabric_getBonusRolls();
@Accessor("entries")
LootPoolEntry[] fabric_getEntries();
@Accessor("conditions")
LootCondition[] fabric_getConditions();
@Accessor("functions")
LootFunction[] fabric_getFunctions();
} }

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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 java.util.Collection;
import java.util.List;
import org.spongepowered.asm.mixin.Final;
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.condition.LootCondition;
import net.minecraft.loot.entry.LootPoolEntry;
import net.minecraft.loot.function.LootFunction;
import net.fabricmc.fabric.api.loot.v2.FabricLootPoolBuilder;
/**
* The implementation of the injected interface {@link FabricLootPoolBuilder}.
* Simply implements the new methods by adding the relevant objects inside the lists.
*/
@Mixin(LootPool.Builder.class)
abstract class LootPoolBuilderMixin implements FabricLootPoolBuilder {
@Shadow
@Final
private List<LootPoolEntry> entries;
@Shadow
@Final
private List<LootCondition> conditions;
@Shadow
@Final
private List<LootFunction> functions;
@Unique
private LootPool.Builder self() {
// noinspection ConstantConditions
return (LootPool.Builder) (Object) this;
}
@Override
public LootPool.Builder with(LootPoolEntry entry) {
this.entries.add(entry);
return self();
}
@Override
public LootPool.Builder with(Collection<? extends LootPoolEntry> entries) {
this.entries.addAll(entries);
return self();
}
@Override
public LootPool.Builder conditionally(LootCondition condition) {
this.conditions.add(condition);
return self();
}
@Override
public LootPool.Builder conditionally(Collection<? extends LootCondition> conditions) {
this.conditions.addAll(conditions);
return self();
}
@Override
public LootPool.Builder apply(LootFunction function) {
this.functions.add(function);
return self();
}
@Override
public LootPool.Builder apply(Collection<? extends LootFunction> functions) {
this.functions.addAll(functions);
return self();
}
}

View file

@ -14,9 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package net.fabricmc.fabric.mixin.loot.table; package net.fabricmc.fabric.mixin.loot;
import java.util.List;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Accessor;
@ -25,10 +23,15 @@ import net.minecraft.loot.LootPool;
import net.minecraft.loot.LootTable; import net.minecraft.loot.LootTable;
import net.minecraft.loot.function.LootFunction; import net.minecraft.loot.function.LootFunction;
@Mixin(LootTable.Builder.class) /**
public interface LootSupplierBuilderHooks { * Accesses loot table fields for {@link net.fabricmc.fabric.api.loot.v2.FabricLootTableBuilder#copyOf(LootTable)}.
@Accessor * These are normally available in the transitive access widener module.
List<LootPool> getPools(); */
@Accessor @Mixin(LootTable.class)
List<LootFunction> getFunctions(); public interface LootTableAccessor {
@Accessor("pools")
LootPool[] fabric_getPools();
@Accessor("functions")
LootFunction[] fabric_getFunctions();
} }

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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 java.util.Collection;
import java.util.List;
import org.spongepowered.asm.mixin.Final;
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.LootTable;
import net.minecraft.loot.function.LootFunction;
import net.fabricmc.fabric.api.loot.v2.FabricLootTableBuilder;
/**
* The implementation of the injected interface {@link FabricLootTableBuilder}.
* Simply implements the new methods by adding the relevant objects inside the lists.
*/
@Mixin(LootTable.Builder.class)
abstract class LootTableBuilderMixin implements FabricLootTableBuilder {
@Shadow
@Final
private List<LootPool> pools;
@Shadow
@Final
private List<LootFunction> functions;
@Unique
private LootTable.Builder self() {
// noinspection ConstantConditions
return (LootTable.Builder) (Object) this;
}
@Override
public LootTable.Builder pool(LootPool pool) {
this.pools.add(pool);
return self();
}
@Override
public LootTable.Builder apply(LootFunction function) {
this.functions.add(function);
return self();
}
@Override
public LootTable.Builder pools(Collection<? extends LootPool> pools) {
this.pools.addAll(pools);
return self();
}
@Override
public LootTable.Builder apply(Collection<? extends LootFunction> functions) {
this.functions.addAll(functions);
return self();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,15 @@
{
"required": true,
"package": "net.fabricmc.fabric.mixin.loot",
"compatibilityLevel": "JAVA_8",
"mixins": [
"LootManagerMixin",
"LootPoolAccessor",
"LootPoolBuilderMixin",
"LootTableAccessor",
"LootTableBuilderMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View file

@ -0,0 +1,34 @@
{
"schemaVersion": 1,
"id": "fabric-loot-api-v2",
"name": "Fabric Loot API (v2)",
"version": "${version}",
"environment": "*",
"license": "Apache-2.0",
"icon": "assets/fabric-loot-api-v2/icon.png",
"contact": {
"homepage": "https://fabricmc.net",
"irc": "irc://irc.esper.net:6667/fabric",
"issues": "https://github.com/FabricMC/fabric/issues",
"sources": "https://github.com/FabricMC/fabric"
},
"authors": [
"FabricMC"
],
"depends": {
"fabricloader": ">=0.4.0",
"fabric-api-base": "*",
"fabric-resource-loader-v0": "*"
},
"description": "Hooks for manipulating loot tables.",
"mixins": [
"fabric-loot-api-v2.mixins.json"
],
"custom": {
"fabric-api:module-lifecycle": "stable",
"loom:injected_interfaces": {
"net/minecraft/class_52\u0024class_53": ["net/fabricmc/fabric/api/loot/v2/FabricLootTableBuilder"],
"net/minecraft/class_55\u0024class_56": ["net/fabricmc/fabric/api/loot/v2/FabricLootPoolBuilder"]
}
}
}

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.test.loot;
import net.minecraft.block.Blocks;
import net.minecraft.item.Items;
import net.minecraft.loot.LootPool;
import net.minecraft.loot.LootTable;
import net.minecraft.loot.condition.SurvivesExplosionLootCondition;
import net.minecraft.loot.entry.ItemEntry;
import net.minecraft.loot.function.SetNameLootFunction;
import net.minecraft.text.LiteralText;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.loot.v2.LootTableEvents;
import net.fabricmc.fabric.api.loot.v2.LootTableSource;
public class LootTest implements ModInitializer {
@Override
public void onInitialize() {
// Test loot table load event
// The LootTable.Builder LootPool.Builder methods here should use
// prebuilt entries and pools to test the injected methods.
LootTableEvents.REPLACE.register((resourceManager, lootManager, id, original, source) -> {
if (Blocks.BLACK_WOOL.getLootTableId().equals(id)) {
if (source != LootTableSource.VANILLA) {
throw new AssertionError("black wool loot table should have LootTableSource.VANILLA");
}
// Replace black wool drops with an iron ingot
LootPool pool = LootPool.builder()
.with(ItemEntry.builder(Items.IRON_INGOT).build())
.build();
return LootTable.builder().pool(pool).build();
}
return null;
});
// Test that the event is stopped when the loot table is replaced
LootTableEvents.REPLACE.register((resourceManager, lootManager, id, original, source) -> {
if (Blocks.BLACK_WOOL.getLootTableId().equals(id)) {
throw new AssertionError("Event should have been stopped from replaced loot table");
}
return null;
});
LootTableEvents.MODIFY.register((resourceManager, lootManager, id, tableBuilder, source) -> {
if (Blocks.BLACK_WOOL.getLootTableId().equals(id) && source != LootTableSource.REPLACED) {
throw new AssertionError("black wool loot table should have LootTableSource.REPLACED");
}
if (Blocks.WHITE_WOOL.getLootTableId().equals(id)) {
if (source != LootTableSource.VANILLA) {
throw new AssertionError("white wool loot table should have LootTableSource.VANILLA");
}
// Add gold ingot with custom name to white wool drops
LootPool pool = LootPool.builder()
.with(ItemEntry.builder(Items.GOLD_INGOT).build())
.conditionally(SurvivesExplosionLootCondition.builder().build())
.apply(SetNameLootFunction.builder(new LiteralText("Gold from White Wool")).build())
.build();
tableBuilder.pool(pool);
}
// We modify red wool to drop diamonds in the test mod resources.
if (Blocks.RED_WOOL.getLootTableId().equals(id) && source != LootTableSource.MOD) {
throw new AssertionError("red wool loot table should have LootTableSource.MOD");
}
});
}
}

View file

@ -0,0 +1,19 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "minecraft:diamond"
}
],
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
}
]
}

View file

@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"id": "fabric-loot-api-v2-testmod",
"name": "Fabric Loot Table API (v2) Test Mod",
"version": "1.0.0",
"environment": "*",
"license": "Apache-2.0",
"depends": {
"fabric-loot-api-v2": "*"
},
"entrypoints": {
"main": [
"net.fabricmc.fabric.test.loot.LootTest"
]
}
}

View file

@ -2,5 +2,10 @@ archivesBaseName = "fabric-loot-tables-v1"
version = getSubprojectVersion(project) version = getSubprojectVersion(project)
moduleDependencies(project, [ moduleDependencies(project, [
'fabric-api-base' 'fabric-api-base',
'fabric-loot-api-v2'
]) ])
dependencies {
testmodRuntimeOnly(project(path: ':fabric-resource-loader-v0', configuration: 'namedElements'))
}

View file

@ -19,15 +19,18 @@ package net.fabricmc.fabric.api.loot.v1;
import java.util.List; import java.util.List;
import net.minecraft.loot.LootPool; import net.minecraft.loot.LootPool;
import net.minecraft.loot.provider.number.LootNumberProvider;
import net.minecraft.loot.condition.LootCondition; import net.minecraft.loot.condition.LootCondition;
import net.minecraft.loot.entry.LootPoolEntry; import net.minecraft.loot.entry.LootPoolEntry;
import net.minecraft.loot.function.LootFunction; import net.minecraft.loot.function.LootFunction;
import net.minecraft.loot.provider.number.LootNumberProvider;
/** /**
* An interface implemented by all {@code net.minecraft.loot.LootPool} instances when * An interface implemented by all {@code net.minecraft.loot.LootPool} instances when
* Fabric API is present. Contains accessors for various fields. * Fabric API is present. Contains accessors for various fields.
*
* @deprecated Replaced with transitive access wideners in Fabric Transitive Access Wideners (v1).
*/ */
@Deprecated
public interface FabricLootPool { public interface FabricLootPool {
default LootPool asVanilla() { default LootPool asVanilla() {
return (LootPool) this; return (LootPool) this;

View file

@ -22,17 +22,21 @@ import net.minecraft.loot.provider.number.LootNumberProvider;
import net.minecraft.loot.entry.LootPoolEntry; import net.minecraft.loot.entry.LootPoolEntry;
import net.minecraft.loot.function.LootFunction; import net.minecraft.loot.function.LootFunction;
import net.fabricmc.fabric.mixin.loot.table.LootPoolBuilderHooks; /**
* @deprecated Replaced with {@link net.fabricmc.fabric.api.loot.v2.FabricLootPoolBuilder}.
*/
@Deprecated
public class FabricLootPoolBuilder extends LootPool.Builder { public class FabricLootPoolBuilder extends LootPool.Builder {
private final LootPoolBuilderHooks extended = (LootPoolBuilderHooks) this;
private FabricLootPoolBuilder() { } private FabricLootPoolBuilder() { }
private FabricLootPoolBuilder(LootPool pool) { private FabricLootPoolBuilder(LootPool pool) {
copyFrom(pool, true); copyFrom(pool, true);
} }
private net.fabricmc.fabric.api.loot.v2.FabricLootPoolBuilder asV2() {
return (net.fabricmc.fabric.api.loot.v2.FabricLootPoolBuilder) this;
}
@Override @Override
public FabricLootPoolBuilder rolls(LootNumberProvider range) { public FabricLootPoolBuilder rolls(LootNumberProvider range) {
super.rolls(range); super.rolls(range);
@ -58,17 +62,17 @@ public class FabricLootPoolBuilder extends LootPool.Builder {
} }
public FabricLootPoolBuilder withEntry(LootPoolEntry entry) { public FabricLootPoolBuilder withEntry(LootPoolEntry entry) {
extended.getEntries().add(entry); asV2().with(entry);
return this; return this;
} }
public FabricLootPoolBuilder withCondition(LootCondition condition) { public FabricLootPoolBuilder withCondition(LootCondition condition) {
extended.getConditions().add(condition); asV2().conditionally(condition);
return this; return this;
} }
public FabricLootPoolBuilder withFunction(LootFunction function) { public FabricLootPoolBuilder withFunction(LootFunction function) {
extended.getFunctions().add(function); asV2().apply(function);
return this; return this;
} }
@ -89,13 +93,13 @@ public class FabricLootPoolBuilder extends LootPool.Builder {
* <p>If {@code copyRolls} is true, the {@link FabricLootPool#getRolls rolls} of the pool are also copied. * <p>If {@code copyRolls} is true, the {@link FabricLootPool#getRolls rolls} of the pool are also copied.
*/ */
public FabricLootPoolBuilder copyFrom(LootPool pool, boolean copyRolls) { public FabricLootPoolBuilder copyFrom(LootPool pool, boolean copyRolls) {
FabricLootPool extendedPool = (FabricLootPool) pool; FabricLootPool extended = (FabricLootPool) pool;
extended.getConditions().addAll(extendedPool.getConditions()); asV2().with(extended.getEntries());
extended.getFunctions().addAll(extendedPool.getFunctions()); asV2().conditionally(extended.getConditions());
extended.getEntries().addAll(extendedPool.getEntries()); asV2().apply(extended.getFunctions());
if (copyRolls) { if (copyRolls) {
rolls(extendedPool.getRolls()); rolls(extended.getRolls());
} }
return this; return this;

View file

@ -24,9 +24,12 @@ import net.minecraft.loot.context.LootContextType;
import net.minecraft.loot.function.LootFunction; import net.minecraft.loot.function.LootFunction;
/** /**
* An interface implemented by all {@code net.minecraft.loot.LootSupplier} instances when * An interface implemented by all {@link LootTable} instances when
* Fabric API is present. Contains accessors for various fields. * Fabric API is present. Contains accessors for various fields.
*
* @deprecated Replaced with transitive access wideners in Fabric Transitive Access Wideners (v1).
*/ */
@Deprecated
public interface FabricLootSupplier { public interface FabricLootSupplier {
default LootTable asVanilla() { default LootTable asVanilla() {
return (LootTable) this; return (LootTable) this;
@ -34,5 +37,7 @@ public interface FabricLootSupplier {
List<LootPool> getPools(); List<LootPool> getPools();
List<LootFunction> getFunctions(); List<LootFunction> getFunctions();
LootContextType getType(); default LootContextType getType() {
return asVanilla().getType(); // Vanilla has this now
}
} }

View file

@ -23,17 +23,23 @@ import net.minecraft.loot.LootTable;
import net.minecraft.loot.context.LootContextType; import net.minecraft.loot.context.LootContextType;
import net.minecraft.loot.function.LootFunction; import net.minecraft.loot.function.LootFunction;
import net.fabricmc.fabric.mixin.loot.table.LootSupplierBuilderHooks; import net.fabricmc.fabric.api.loot.v2.FabricLootTableBuilder;
/**
* @deprecated Replaced with {@link FabricLootTableBuilder}.
*/
@Deprecated
public class FabricLootSupplierBuilder extends LootTable.Builder { public class FabricLootSupplierBuilder extends LootTable.Builder {
private final LootSupplierBuilderHooks extended = (LootSupplierBuilderHooks) this;
protected FabricLootSupplierBuilder() { } protected FabricLootSupplierBuilder() { }
private FabricLootSupplierBuilder(LootTable supplier) { private FabricLootSupplierBuilder(LootTable supplier) {
copyFrom(supplier, true); copyFrom(supplier, true);
} }
private FabricLootTableBuilder asV2() {
return (FabricLootTableBuilder) this;
}
@Override @Override
public FabricLootSupplierBuilder pool(LootPool.Builder pool) { public FabricLootSupplierBuilder pool(LootPool.Builder pool) {
super.pool(pool); super.pool(pool);
@ -53,22 +59,22 @@ public class FabricLootSupplierBuilder extends LootTable.Builder {
} }
public FabricLootSupplierBuilder withPool(LootPool pool) { public FabricLootSupplierBuilder withPool(LootPool pool) {
extended.getPools().add(pool); asV2().pool(pool);
return this; return this;
} }
public FabricLootSupplierBuilder withFunction(LootFunction function) { public FabricLootSupplierBuilder withFunction(LootFunction function) {
extended.getFunctions().add(function); asV2().apply(function);
return this; return this;
} }
public FabricLootSupplierBuilder withPools(Collection<LootPool> pools) { public FabricLootSupplierBuilder withPools(Collection<LootPool> pools) {
pools.forEach(this::withPool); asV2().pools(pools);
return this; return this;
} }
public FabricLootSupplierBuilder withFunctions(Collection<LootFunction> functions) { public FabricLootSupplierBuilder withFunctions(Collection<LootFunction> functions) {
functions.forEach(this::withFunction); asV2().apply(functions);
return this; return this;
} }
@ -86,8 +92,8 @@ public class FabricLootSupplierBuilder extends LootTable.Builder {
*/ */
public FabricLootSupplierBuilder copyFrom(LootTable supplier, boolean copyType) { public FabricLootSupplierBuilder copyFrom(LootTable supplier, boolean copyType) {
FabricLootSupplier extendedSupplier = (FabricLootSupplier) supplier; FabricLootSupplier extendedSupplier = (FabricLootSupplier) supplier;
extended.getPools().addAll(extendedSupplier.getPools()); asV2().pools(extendedSupplier.getPools());
extended.getFunctions().addAll(extendedSupplier.getFunctions()); asV2().apply(extendedSupplier.getFunctions());
if (copyType) { if (copyType) {
type(extendedSupplier.getType()); type(extendedSupplier.getType());

View file

@ -23,11 +23,13 @@ import net.minecraft.util.JsonSerializer;
import net.fabricmc.fabric.impl.loot.table.LootEntryTypeRegistryImpl; import net.fabricmc.fabric.impl.loot.table.LootEntryTypeRegistryImpl;
/** /**
* Fabric's extensions to {@code net.minecraft.loot.entry.LootEntries} for registering * Fabric's extensions to {@link net.minecraft.loot.entry.LootPoolEntryTypes} for registering
* custom loot entry types. * custom loot entry types.
* *
* @see #register * @see #register
* @deprecated Use {@link net.minecraft.util.registry.Registry#LOOT_POOL_ENTRY_TYPE} from vanilla instead.
*/ */
@Deprecated
public interface LootEntryTypeRegistry { public interface LootEntryTypeRegistry {
LootEntryTypeRegistry INSTANCE = new LootEntryTypeRegistryImpl(); LootEntryTypeRegistry INSTANCE = new LootEntryTypeRegistryImpl();

View file

@ -17,37 +17,26 @@
package net.fabricmc.fabric.api.loot.v1; package net.fabricmc.fabric.api.loot.v1;
import java.io.Reader; import java.io.Reader;
import java.lang.reflect.Field;
import java.util.stream.Stream;
import com.google.gson.Gson; import com.google.gson.Gson;
import net.minecraft.loot.LootGsons;
import net.minecraft.util.JsonHelper; import net.minecraft.util.JsonHelper;
import net.minecraft.util.Lazy;
import net.minecraft.loot.LootManager;
/**
* @deprecated Use {@link LootGsons#getTableGsonBuilder()} from vanilla instead.
*/
@Deprecated
public final class LootJsonParser { public final class LootJsonParser {
/* Reading this from LootManager to access all serializers from vanilla. */ private static final Gson GSON = LootGsons.getTableGsonBuilder().create();
private static final Lazy<Gson> GSON = new Lazy<>(() -> {
try {
Field gsonField = Stream.of(LootManager.class.getDeclaredFields())
.filter(field -> field.getType() == Gson.class)
.findFirst()
.orElseThrow(() -> new RuntimeException("Gson not found in LootManager!"));
gsonField.setAccessible(true);
return (Gson) gsonField.get(null);
} catch (Exception e) {
throw new RuntimeException("Exception while getting Gson instance from LootManager", e);
}
});
private LootJsonParser() { } private LootJsonParser() { }
public static <T> T read(Reader json, Class<T> c) { public static <T> T read(Reader json, Class<T> c) {
return JsonHelper.deserialize(GSON.get(), json, c); return JsonHelper.deserialize(GSON, json, c);
} }
public static <T> T read(String json, Class<T> c) { public static <T> T read(String json, Class<T> c) {
return JsonHelper.deserialize(GSON.get(), json, c); return JsonHelper.deserialize(GSON, json, c);
} }
} }

View file

@ -28,9 +28,13 @@ import net.fabricmc.fabric.api.loot.v1.FabricLootSupplierBuilder;
/** /**
* An event handler that is called when loot tables are loaded. * An event handler that is called when loot tables are loaded.
* Use {@link #EVENT} to register instances. * Use {@link #EVENT} to register instances.
*
* @deprecated Replaced with {@link net.fabricmc.fabric.api.loot.v2.LootTableEvents}.
*/ */
@Deprecated
@FunctionalInterface @FunctionalInterface
public interface LootTableLoadingCallback { public interface LootTableLoadingCallback {
@Deprecated
@FunctionalInterface @FunctionalInterface
interface LootTableSetter { interface LootTableSetter {
void set(LootTable supplier); void set(LootTable supplier);

View file

@ -0,0 +1,116 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.impl.loot.table;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import net.minecraft.loot.LootPool;
import net.minecraft.loot.LootTable;
import net.minecraft.loot.context.LootContextType;
import net.minecraft.loot.function.LootFunction;
import net.fabricmc.fabric.api.loot.v1.FabricLootSupplier;
import net.fabricmc.fabric.api.loot.v1.FabricLootSupplierBuilder;
import net.fabricmc.fabric.api.loot.v2.FabricLootTableBuilder;
/**
* A {@link FabricLootSupplierBuilder} that caches all methods so they can be applied to a v2 {@link FabricLootTableBuilder}.
* Used for hooking {@code LootTableLoadingCallback} to the two different {@link net.fabricmc.fabric.api.loot.v2.LootTableEvents}.
*/
public class BufferingLootTableBuilder extends FabricLootSupplierBuilder {
private final List<Consumer<LootTable.Builder>> modifications = new ArrayList<>();
private FabricLootSupplierBuilder addAction(Consumer<LootTable.Builder> action) {
modifications.add(action);
return this;
}
private FabricLootSupplierBuilder addV2Action(Consumer<FabricLootTableBuilder> action) {
return addAction(builder -> action.accept((FabricLootTableBuilder) builder));
}
@Override
public FabricLootSupplierBuilder pool(LootPool.Builder pool) {
super.pool(pool);
return addAction(builder -> builder.pool(pool));
}
@Override
public FabricLootSupplierBuilder type(LootContextType type) {
super.type(type);
return addAction(builder -> builder.type(type));
}
@Override
public FabricLootSupplierBuilder apply(LootFunction.Builder function) {
super.apply(function);
return addAction(builder -> builder.apply(function));
}
@Override
public FabricLootSupplierBuilder withPool(LootPool pool) {
super.withPool(pool);
return addV2Action(builder -> builder.pool(pool));
}
@Override
public FabricLootSupplierBuilder withFunction(LootFunction function) {
super.withFunction(function);
return addV2Action(builder -> builder.apply(function));
}
@Override
public FabricLootSupplierBuilder withPools(Collection<LootPool> pools) {
super.withPools(pools);
return addV2Action(builder -> builder.pools(pools));
}
@Override
public FabricLootSupplierBuilder withFunctions(Collection<LootFunction> functions) {
super.withFunctions(functions);
return addV2Action(builder -> builder.apply(functions));
}
@Override
public FabricLootSupplierBuilder copyFrom(LootTable supplier, boolean copyType) {
super.copyFrom(supplier, copyType);
return addV2Action(builder -> {
FabricLootSupplier extended = (FabricLootSupplier) supplier;
builder.pools(extended.getPools());
builder.apply(extended.getFunctions());
if (copyType) {
((LootTable.Builder) builder).type(supplier.getType());
}
});
}
public void init(LootTable original) {
super.type(original.getType());
super.withPools(((FabricLootSupplier) original).getPools());
super.withFunctions(((FabricLootSupplier) original).getFunctions());
}
public void applyTo(LootTable.Builder builder) {
for (Consumer<LootTable.Builder> modification : modifications) {
modification.accept(builder);
}
}
}

View file

@ -17,16 +17,16 @@
package net.fabricmc.fabric.impl.loot.table; package net.fabricmc.fabric.impl.loot.table;
import net.minecraft.loot.entry.LootPoolEntry; import net.minecraft.loot.entry.LootPoolEntry;
import net.minecraft.loot.entry.LootPoolEntryType;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.JsonSerializer; import net.minecraft.util.JsonSerializer;
import net.minecraft.util.registry.Registry;
import net.fabricmc.fabric.mixin.loot.table.LootPoolEntryTypesAccessor;
public final class LootEntryTypeRegistryImpl implements net.fabricmc.fabric.api.loot.v1.LootEntryTypeRegistry { public final class LootEntryTypeRegistryImpl implements net.fabricmc.fabric.api.loot.v1.LootEntryTypeRegistry {
public LootEntryTypeRegistryImpl() { } public LootEntryTypeRegistryImpl() { }
@Override @Override
public void register(Identifier id, JsonSerializer<? extends LootPoolEntry> serializer) { public void register(Identifier id, JsonSerializer<? extends LootPoolEntry> serializer) {
LootPoolEntryTypesAccessor.register(id.toString(), serializer); Registry.register(Registry.LOOT_POOL_ENTRY_TYPE, id, new LootPoolEntryType(serializer));
} }
} }

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.impl.loot.table;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.loot.LootTable;
import net.minecraft.util.Identifier;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback;
import net.fabricmc.fabric.api.loot.v2.LootTableEvents;
public final class LootTablesV1Init implements ModInitializer {
private static final ThreadLocal<Map<Identifier, BufferingLootTableBuilder>> BUFFERS = ThreadLocal.withInitial(HashMap::new);
@Override
public void onInitialize() {
LootTableEvents.REPLACE.register((resourceManager, lootManager, id, original, source) -> {
BufferingLootTableBuilder builder = new BufferingLootTableBuilder();
builder.init(original);
BUFFERS.get().put(id, builder);
LootTable[] result = new LootTable[1];
LootTableLoadingCallback.EVENT.invoker().onLootTableLoading(
resourceManager,
lootManager,
id,
builder,
table -> result[0] = table
);
return result[0];
});
LootTableEvents.MODIFY.register((resourceManager, lootManager, id, tableBuilder, source) -> {
Map<Identifier, BufferingLootTableBuilder> buffers = BUFFERS.get();
if (buffers.containsKey(id)) {
try {
buffers.get(id).applyTo(tableBuilder);
} finally {
buffers.remove(id);
if (buffers.isEmpty()) {
BUFFERS.remove();
}
}
}
});
}
}

View file

@ -1,60 +0,0 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.table;
import java.util.HashMap;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonObject;
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 net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
import net.minecraft.util.profiler.Profiler;
import net.minecraft.loot.LootManager;
import net.minecraft.loot.LootTable;
import net.fabricmc.fabric.api.loot.v1.FabricLootSupplierBuilder;
import net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback;
@Mixin(LootManager.class)
public class MixinLootManager {
@Shadow private Map<Identifier, LootTable> tables;
@Inject(method = "apply", at = @At("RETURN"))
private void apply(Map<Identifier, JsonObject> objectMap, ResourceManager manager, Profiler profiler, CallbackInfo info) {
Map<Identifier, LootTable> newSuppliers = new HashMap<>();
tables.forEach((id, supplier) -> {
FabricLootSupplierBuilder builder = FabricLootSupplierBuilder.of(supplier);
//noinspection ConstantConditions
LootTableLoadingCallback.EVENT.invoker().onLootTableLoading(
manager, (LootManager) (Object) this, id, builder, (s) -> newSuppliers.put(id, s)
);
newSuppliers.computeIfAbsent(id, (i) -> builder.build());
});
tables = ImmutableMap.copyOf(newSuppliers);
}
}

View file

@ -16,19 +16,18 @@
package net.fabricmc.fabric.mixin.loot.table; package net.fabricmc.fabric.mixin.loot.table;
import java.util.Arrays;
import java.util.List; import java.util.List;
import com.google.common.collect.ImmutableList;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.loot.LootPool; import net.minecraft.loot.LootPool;
import net.minecraft.loot.provider.number.LootNumberProvider;
import net.minecraft.loot.condition.LootCondition; import net.minecraft.loot.condition.LootCondition;
import net.minecraft.loot.entry.LootPoolEntry; import net.minecraft.loot.entry.LootPoolEntry;
import net.minecraft.loot.function.LootFunction; import net.minecraft.loot.function.LootFunction;
import net.minecraft.loot.provider.number.LootNumberProvider;
import net.fabricmc.fabric.api.loot.v1.FabricLootPool; import net.fabricmc.fabric.api.loot.v1.FabricLootPool;
@ -36,32 +35,37 @@ import net.fabricmc.fabric.api.loot.v1.FabricLootPool;
public abstract class MixinLootPool implements FabricLootPool { public abstract class MixinLootPool implements FabricLootPool {
@Shadow @Shadow
@Final @Final
private LootPoolEntry[] entries; LootPoolEntry[] entries;
@Shadow @Shadow
@Final @Final
private LootCondition[] conditions; LootCondition[] conditions;
@Shadow @Shadow
@Final @Final
private LootFunction[] functions; LootFunction[] functions;
@Shadow
@Final
LootNumberProvider rolls;
@Override @Override
public List<LootPoolEntry> getEntries() { public List<LootPoolEntry> getEntries() {
return Arrays.asList(entries); return ImmutableList.copyOf(entries);
} }
@Override @Override
public List<LootCondition> getConditions() { public List<LootCondition> getConditions() {
return Arrays.asList(conditions); return ImmutableList.copyOf(conditions);
} }
@Override @Override
public List<LootFunction> getFunctions() { public List<LootFunction> getFunctions() {
return Arrays.asList(functions); return ImmutableList.copyOf(functions);
} }
@Accessor
@Override @Override
public abstract LootNumberProvider getRolls(); public LootNumberProvider getRolls() {
return rolls;
}
} }

View file

@ -16,17 +16,15 @@
package net.fabricmc.fabric.mixin.loot.table; package net.fabricmc.fabric.mixin.loot.table;
import java.util.Arrays;
import java.util.List; import java.util.List;
import com.google.common.collect.ImmutableList;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.loot.LootPool; import net.minecraft.loot.LootPool;
import net.minecraft.loot.LootTable; import net.minecraft.loot.LootTable;
import net.minecraft.loot.context.LootContextType;
import net.minecraft.loot.function.LootFunction; import net.minecraft.loot.function.LootFunction;
import net.fabricmc.fabric.api.loot.v1.FabricLootSupplier; import net.fabricmc.fabric.api.loot.v1.FabricLootSupplier;
@ -35,22 +33,19 @@ import net.fabricmc.fabric.api.loot.v1.FabricLootSupplier;
public abstract class MixinLootSupplier implements FabricLootSupplier { public abstract class MixinLootSupplier implements FabricLootSupplier {
@Shadow @Shadow
@Final @Final
private LootPool[] pools; LootPool[] pools;
@Shadow @Shadow
@Final @Final
private LootFunction[] functions; LootFunction[] functions;
@Override @Override
public List<LootPool> getPools() { public List<LootPool> getPools() {
return Arrays.asList(pools); return ImmutableList.copyOf(pools);
} }
@Override @Override
public List<LootFunction> getFunctions() { public List<LootFunction> getFunctions() {
return Arrays.asList(functions); return ImmutableList.copyOf(functions);
} }
@Accessor
@Override
public abstract LootContextType getType();
} }

View file

@ -3,10 +3,6 @@
"package": "net.fabricmc.fabric.mixin.loot.table", "package": "net.fabricmc.fabric.mixin.loot.table",
"compatibilityLevel": "JAVA_16", "compatibilityLevel": "JAVA_16",
"mixins": [ "mixins": [
"LootPoolBuilderHooks",
"LootPoolEntryTypesAccessor",
"LootSupplierBuilderHooks",
"MixinLootManager",
"MixinLootPool", "MixinLootPool",
"MixinLootSupplier" "MixinLootSupplier"
], ],

View file

@ -17,13 +17,17 @@
], ],
"depends": { "depends": {
"fabricloader": ">=0.4.0", "fabricloader": ">=0.4.0",
"fabric-api-base": "*" "fabric-api-base": "*",
"fabric-loot-api-v2": "*"
},
"entrypoints": {
"main": ["net.fabricmc.fabric.impl.loot.table.LootTablesV1Init"]
}, },
"description": "Hooks for manipulating loot tables.", "description": "Hooks for manipulating loot tables.",
"mixins": [ "mixins": [
"fabric-loot-tables-v1.mixins.json" "fabric-loot-tables-v1.mixins.json"
], ],
"custom": { "custom": {
"fabric-api:module-lifecycle": "stable" "fabric-api:module-lifecycle": "deprecated"
} }
} }

View file

@ -20,28 +20,31 @@ import com.google.gson.Gson;
import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializationContext;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.minecraft.block.Blocks;
import net.minecraft.item.Items; import net.minecraft.item.Items;
import net.minecraft.loot.provider.number.ConstantLootNumberProvider;
import net.minecraft.loot.LootGsons; import net.minecraft.loot.LootGsons;
import net.minecraft.loot.LootPool; import net.minecraft.loot.LootPool;
import net.minecraft.loot.LootTable;
import net.minecraft.loot.condition.LootCondition; import net.minecraft.loot.condition.LootCondition;
import net.minecraft.loot.condition.SurvivesExplosionLootCondition; import net.minecraft.loot.condition.SurvivesExplosionLootCondition;
import net.minecraft.loot.entry.ItemEntry; import net.minecraft.loot.entry.ItemEntry;
import net.minecraft.loot.entry.LootPoolEntry; import net.minecraft.loot.entry.LootPoolEntry;
import net.minecraft.loot.entry.TagEntry; import net.minecraft.loot.entry.TagEntry;
import net.minecraft.loot.provider.number.ConstantLootNumberProvider;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper; import net.minecraft.util.JsonHelper;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.loot.v1.FabricLootPoolBuilder; import net.fabricmc.fabric.api.loot.v1.FabricLootPoolBuilder;
import net.fabricmc.fabric.api.loot.v1.FabricLootSupplierBuilder;
import net.fabricmc.fabric.api.loot.v1.LootEntryTypeRegistry; import net.fabricmc.fabric.api.loot.v1.LootEntryTypeRegistry;
import net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback; import net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback;
public class LootTest implements ModInitializer { public class LootV1Test implements ModInitializer {
private static final Logger LOGGER = LoggerFactory.getLogger(LootTest.class); private static final Logger LOGGER = LoggerFactory.getLogger(LootV1Test.class);
private static final Gson LOOT_GSON = LootGsons.getTableGsonBuilder().create(); private static final Gson LOOT_GSON = LootGsons.getTableGsonBuilder().create();
private static final String LOOT_ENTRY_JSON = "{\"type\":\"minecraft:item\",\"name\":\"minecraft:apple\"}"; private static final String LOOT_ENTRY_JSON = "{\"type\":\"minecraft:item\",\"name\":\"minecraft:apple\"}";
@ -53,7 +56,9 @@ public class LootTest implements ModInitializer {
// Test loot table load event // Test loot table load event
LootTableLoadingCallback.EVENT.register((resourceManager, manager, id, supplier, setter) -> { LootTableLoadingCallback.EVENT.register((resourceManager, manager, id, supplier, setter) -> {
if ("minecraft:blocks/dirt".equals(id.toString())) { // Add feathers and apples to dirt (only one drops at the time since they're in the same pool),
// and replace grass block drops with wheat
if (Blocks.DIRT.getLootTableId().equals(id)) {
LootPoolEntry entryFromString = LOOT_GSON.fromJson(LOOT_ENTRY_JSON, LootPoolEntry.class); LootPoolEntry entryFromString = LOOT_GSON.fromJson(LOOT_ENTRY_JSON, LootPoolEntry.class);
LootPool pool = FabricLootPoolBuilder.builder() LootPool pool = FabricLootPoolBuilder.builder()
@ -64,6 +69,11 @@ public class LootTest implements ModInitializer {
.build(); .build();
supplier.withPool(pool); supplier.withPool(pool);
} else if (Blocks.GRASS_BLOCK.getLootTableId().equals(id)) {
LootTable table = FabricLootSupplierBuilder.builder()
.pool(FabricLootPoolBuilder.builder().with(ItemEntry.builder(Items.WHEAT)))
.build();
setter.set(table);
} }
}); });
} }

View file

@ -10,7 +10,7 @@
}, },
"entrypoints": { "entrypoints": {
"main": [ "main": [
"net.fabricmc.fabric.test.loot.LootTest" "net.fabricmc.fabric.test.loot.LootV1Test"
] ]
} }
} }

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.impl.resource.loader;
import org.slf4j.LoggerFactory;
import net.minecraft.resource.ResourcePackSource;
/**
* Extensions to {@link net.minecraft.resource.Resource}.
* Automatically implemented there via a mixin.
* Currently, this is only for use in other Fabric API modules.
*/
public interface FabricResource {
/**
* Gets the resource pack source of this resource.
* The source is used to separate vanilla/mod resources from user resources in Fabric API.
*
* <p>Custom {@link net.minecraft.resource.Resource} implementations should override this method.
*
* @return the resource pack source
*/
default ResourcePackSource getFabricPackSource() {
LoggerFactory.getLogger(FabricResource.class).error("Unknown Resource implementation {}, returning PACK_SOURCE_NONE as the source", getClass().getName());
return ResourcePackSource.PACK_SOURCE_NONE;
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.impl.resource.loader;
import net.minecraft.resource.Resource;
import net.minecraft.resource.ResourcePackSource;
/**
* An extended version of {@link FabricResource} that supports
* setting the pack resource. Only for use from within this module.
* Note that <strong>not all</strong> resources are instances of this interface.
*/
public interface FabricResourceImpl extends Resource, FabricResource {
void setFabricPackSource(ResourcePackSource packSource);
}

View file

@ -130,7 +130,9 @@ public abstract class GroupResourcePack implements ResourcePack {
for (ModResourcePack pack : packs) { for (ModResourcePack pack : packs) {
if (pack.contains(manager.getType(), id)) { if (pack.contains(manager.getType(), id)) {
InputStream metadataInputStream = pack.contains(manager.getType(), metadataId) ? manager.fabric$accessor_open(metadataId, pack) : null; InputStream metadataInputStream = pack.contains(manager.getType(), metadataId) ? manager.fabric$accessor_open(metadataId, pack) : null;
resources.add(new ResourceImpl(pack.getName(), id, manager.fabric$accessor_open(id, pack), metadataInputStream)); ResourceImpl resource = new ResourceImpl(pack.getName(), id, manager.fabric$accessor_open(id, pack), metadataInputStream);
((FabricResourceImpl) resource).setFabricPackSource(ModResourcePackCreator.RESOURCE_PACK_SOURCE);
resources.add(resource);
} }
} }
} }

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.impl.resource.loader;
import java.util.WeakHashMap;
import net.minecraft.resource.ResourcePack;
import net.minecraft.resource.ResourcePackSource;
/**
* Tracks the sources of resource packs in a global weak hash map.
* {@link ResourcePack} doesn't hold a reference to its {@link ResourcePackSource}
* so we store the source in the map when the resource packs are created.
* See {@link net.fabricmc.fabric.mixin.resource.loader.ResourcePackProfileMixin ResourcePackProfileMixin}.
*
* <p>The sources are later read for use in {@link FabricResource} and {@link FabricResourceImpl}.
* See {@link net.fabricmc.fabric.mixin.resource.loader.NamespaceResourceManagerMixin NamespaceResourceManagerMixin}.
*/
public final class ResourcePackSourceTracker {
// Use a weak hash map so that if resource packs would be deleted, this won't keep them alive.
private static final WeakHashMap<ResourcePack, ResourcePackSource> SOURCES = new WeakHashMap<>();
/**
* Gets the source of a pack.
*
* @param pack the resource pack
* @return the source, or {@link ResourcePackSource#PACK_SOURCE_NONE} if not tracked
*/
public static ResourcePackSource getSource(ResourcePack pack) {
return SOURCES.getOrDefault(pack, ResourcePackSource.PACK_SOURCE_NONE);
}
/**
* Sets the source of a pack.
*
* @param pack the resource pack
* @param source the source
*/
public static void setSource(ResourcePack pack, ResourcePackSource source) {
SOURCES.put(pack, source);
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.resource.loader;
import org.spongepowered.asm.mixin.Mixin;
import net.minecraft.resource.ResourcePackSource;
import net.fabricmc.fabric.impl.resource.loader.FabricResource;
/**
* Implements {@link FabricResource} for the anonymous resource implementation
* in {@link net.minecraft.resource.DefaultResourcePack#getResource}.
*/
@Mixin(targets = "net/minecraft/resource/DefaultResourcePack$1")
abstract class DefaultResourcePackResourceMixin implements FabricResource {
@Override
public ResourcePackSource getFabricPackSource() {
// The default resource pack only contains built-in vanilla resources.
return ResourcePackSource.PACK_SOURCE_BUILTIN;
}
}

View file

@ -17,6 +17,7 @@
package net.fabricmc.fabric.mixin.resource.loader; package net.fabricmc.fabric.mixin.resource.loader;
import java.io.IOException; import java.io.IOException;
import java.util.Iterator;
import java.util.List; import java.util.List;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@ -32,7 +33,9 @@ import net.minecraft.resource.ResourcePack;
import net.minecraft.resource.ResourceType; import net.minecraft.resource.ResourceType;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.fabricmc.fabric.impl.resource.loader.FabricResourceImpl;
import net.fabricmc.fabric.impl.resource.loader.GroupResourcePack; import net.fabricmc.fabric.impl.resource.loader.GroupResourcePack;
import net.fabricmc.fabric.impl.resource.loader.ResourcePackSourceTracker;
@Mixin(NamespaceResourceManager.class) @Mixin(NamespaceResourceManager.class)
public class NamespaceResourceManagerMixin { public class NamespaceResourceManagerMixin {
@ -66,4 +69,32 @@ public class NamespaceResourceManagerMixin {
return pack.contains(type, id); return pack.contains(type, id);
} }
/* The two injectors below set the resource pack sources (see FabricResourceImpl)
* for resources created in NamespaceResourceManager.getAllResources and NamespaceResourceManager.getResource.
*
* Since (in 1.18.2) ResourceImpl doesn't hold a reference to its resource pack,
* we have to get the source from the resource pack when the resource is created.
* These are the main creation sites for resources in 1.18.2
* along with DefaultResourcePack.getResource and Fabric API's GroupResourcePack,
* which also either track the source similarly or provide other types of Resource instances
* that have a different FabricResource implementation.
*/
@Inject(method = "getAllResources", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z", remap = false, shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD)
private void trackSourceOnGetAllResources(Identifier id, CallbackInfoReturnable<List<Resource>> cir, List<Resource> resources, Identifier metadataPath, Iterator<ResourcePack> packs, ResourcePack resourcePack) {
// After the created resource has been added, read it from the list and set its source
// to match the tracked source of its resource pack.
if (resources.get(resources.size() - 1) instanceof FabricResourceImpl resource) {
resource.setFabricPackSource(ResourcePackSourceTracker.getSource(resourcePack));
}
}
@Inject(method = "getResource", at = @At("RETURN"), locals = LocalCapture.CAPTURE_FAILHARD)
private void trackSourceOnGetResource(Identifier id, CallbackInfoReturnable<Resource> cir, ResourcePack metaResourcePack, Identifier metadataPath, int i, ResourcePack resourcePack) {
// Set the resource's source to match the tracked source of its resource pack.
if (cir.getReturnValue() instanceof FabricResourceImpl resource) {
resource.setFabricPackSource(ResourcePackSourceTracker.getSource(resourcePack));
}
}
} }

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.resource.loader;
import java.util.Objects;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import net.minecraft.resource.ResourceImpl;
import net.minecraft.resource.ResourcePackSource;
import net.fabricmc.fabric.impl.resource.loader.FabricResourceImpl;
/**
* Implements {@link FabricResourceImpl} (resource source getter/setter)
* for vanilla's basic {@link ResourceImpl} used for most game resources.
*
* @see NamespaceResourceManagerMixin the usage site for this mixin
*/
@Mixin(ResourceImpl.class)
abstract class ResourceImplMixin implements FabricResourceImpl {
@Unique
private @Nullable ResourcePackSource fabric_packSource;
@Override
public ResourcePackSource getFabricPackSource() {
return Objects.requireNonNullElse(fabric_packSource, ResourcePackSource.PACK_SOURCE_NONE);
}
@Override
public void setFabricPackSource(ResourcePackSource packSource) {
this.fabric_packSource = packSource;
}
}

View file

@ -14,20 +14,15 @@
* limitations under the License. * limitations under the License.
*/ */
package net.fabricmc.fabric.mixin.loot.table; package net.fabricmc.fabric.mixin.resource.loader;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
import net.minecraft.loot.entry.LootPoolEntry; import net.minecraft.resource.Resource;
import net.minecraft.loot.entry.LootPoolEntryType;
import net.minecraft.loot.entry.LootPoolEntryTypes;
import net.minecraft.util.JsonSerializer;
@Mixin(LootPoolEntryTypes.class) import net.fabricmc.fabric.impl.resource.loader.FabricResource;
public interface LootPoolEntryTypesAccessor {
@Invoker("register") // Add FabricResource to Resource's superinterfaces.
static LootPoolEntryType register(String id, JsonSerializer<? extends LootPoolEntry> serializer) { @Mixin(Resource.class)
throw new UnsupportedOperationException("Mixin dummy"); interface ResourceMixin extends FabricResource {
}
} }

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 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.resource.loader;
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.CallbackInfoReturnable;
import net.minecraft.resource.ResourcePack;
import net.minecraft.resource.ResourcePackProfile;
import net.minecraft.resource.ResourcePackSource;
import net.fabricmc.fabric.impl.resource.loader.ResourcePackSourceTracker;
/**
* Implements resource pack source tracking (for {@link net.fabricmc.fabric.impl.resource.loader.FabricResource}).
* {@link ResourcePack} doesn't hold a reference to its {@link ResourcePackSource}
* so we store the source in a global tracker when the resource packs are created.
*
* @see ResourcePackSourceTracker
*/
@Mixin(ResourcePackProfile.class)
abstract class ResourcePackProfileMixin {
@Shadow
@Final
private ResourcePackSource source;
@Inject(method = "createResourcePack", at = @At("RETURN"))
private void onCreateResourcePack(CallbackInfoReturnable<ResourcePack> info) {
ResourcePackSourceTracker.setSource(info.getReturnValue(), source);
}
}

View file

@ -5,14 +5,18 @@
"mixins": [ "mixins": [
"FileResourcePackProviderAccessor", "FileResourcePackProviderAccessor",
"DefaultResourcePackMixin", "DefaultResourcePackMixin",
"DefaultResourcePackResourceMixin",
"KeyedResourceReloadListenerMixin", "KeyedResourceReloadListenerMixin",
"LifecycledResourceManagerImplMixin", "LifecycledResourceManagerImplMixin",
"MinecraftServerMixin", "MinecraftServerMixin",
"NamespaceResourceManagerAccessor", "NamespaceResourceManagerAccessor",
"NamespaceResourceManagerMixin", "NamespaceResourceManagerMixin",
"ReloadableResourceManagerImplMixin", "ReloadableResourceManagerImplMixin",
"ResourceImplMixin",
"ResourceMixin",
"ResourcePackManagerMixin", "ResourcePackManagerMixin",
"ResourcePackManagerAccessor", "ResourcePackManagerAccessor",
"ResourcePackProfileMixin",
"SimpleResourceReloadMixin" "SimpleResourceReloadMixin"
], ],
"client": [ "client": [

View file

@ -15,6 +15,15 @@ transitive-accessible method net/minecraft/screen/ScreenHandlerType <init> (Lnet
transitive-accessible class net/minecraft/client/gui/screen/ingame/HandledScreens$Provider transitive-accessible class net/minecraft/client/gui/screen/ingame/HandledScreens$Provider
transitive-accessible method net/minecraft/client/gui/screen/ingame/HandledScreens register (Lnet/minecraft/screen/ScreenHandlerType;Lnet/minecraft/client/gui/screen/ingame/HandledScreens$Provider;)V transitive-accessible method net/minecraft/client/gui/screen/ingame/HandledScreens register (Lnet/minecraft/screen/ScreenHandlerType;Lnet/minecraft/client/gui/screen/ingame/HandledScreens$Provider;)V
# Data contained in loot tables and pools
transitive-accessible field net/minecraft/loot/LootPool entries [Lnet/minecraft/loot/entry/LootPoolEntry;
transitive-accessible field net/minecraft/loot/LootPool conditions [Lnet/minecraft/loot/condition/LootCondition;
transitive-accessible field net/minecraft/loot/LootPool functions [Lnet/minecraft/loot/function/LootFunction;
transitive-accessible field net/minecraft/loot/LootPool rolls Lnet/minecraft/loot/provider/number/LootNumberProvider;
transitive-accessible field net/minecraft/loot/LootPool bonusRolls Lnet/minecraft/loot/provider/number/LootNumberProvider;
transitive-accessible field net/minecraft/loot/LootTable pools [Lnet/minecraft/loot/LootPool;
transitive-accessible field net/minecraft/loot/LootTable functions [Lnet/minecraft/loot/function/LootFunction;
### Generated access wideners below ### Generated access wideners below
# Constructors of non-abstract block classes # Constructors of non-abstract block classes
transitive-accessible method net/minecraft/block/AirBlock <init> (Lnet/minecraft/block/AbstractBlock$Settings;)V transitive-accessible method net/minecraft/block/AirBlock <init> (Lnet/minecraft/block/AbstractBlock$Settings;)V

View file

@ -15,4 +15,13 @@ transitive-accessible method net/minecraft/screen/ScreenHandlerType <init> (Lnet
transitive-accessible class net/minecraft/client/gui/screen/ingame/HandledScreens$Provider transitive-accessible class net/minecraft/client/gui/screen/ingame/HandledScreens$Provider
transitive-accessible method net/minecraft/client/gui/screen/ingame/HandledScreens register (Lnet/minecraft/screen/ScreenHandlerType;Lnet/minecraft/client/gui/screen/ingame/HandledScreens$Provider;)V transitive-accessible method net/minecraft/client/gui/screen/ingame/HandledScreens register (Lnet/minecraft/screen/ScreenHandlerType;Lnet/minecraft/client/gui/screen/ingame/HandledScreens$Provider;)V
# Data contained in loot tables and pools
transitive-accessible field net/minecraft/loot/LootPool entries [Lnet/minecraft/loot/entry/LootPoolEntry;
transitive-accessible field net/minecraft/loot/LootPool conditions [Lnet/minecraft/loot/condition/LootCondition;
transitive-accessible field net/minecraft/loot/LootPool functions [Lnet/minecraft/loot/function/LootFunction;
transitive-accessible field net/minecraft/loot/LootPool rolls Lnet/minecraft/loot/provider/number/LootNumberProvider;
transitive-accessible field net/minecraft/loot/LootPool bonusRolls Lnet/minecraft/loot/provider/number/LootNumberProvider;
transitive-accessible field net/minecraft/loot/LootTable pools [Lnet/minecraft/loot/LootPool;
transitive-accessible field net/minecraft/loot/LootTable functions [Lnet/minecraft/loot/function/LootFunction;
### Generated access wideners below ### Generated access wideners below

View file

@ -29,6 +29,7 @@ fabric-item-groups-v0-version=0.3.12
fabric-key-binding-api-v1-version=1.0.12 fabric-key-binding-api-v1-version=1.0.12
fabric-keybindings-v0-version=0.2.10 fabric-keybindings-v0-version=0.2.10
fabric-lifecycle-events-v1-version=2.0.4 fabric-lifecycle-events-v1-version=2.0.4
fabric-loot-api-v2-version=1.0.0
fabric-loot-tables-v1-version=1.0.11 fabric-loot-tables-v1-version=1.0.11
fabric-mining-level-api-v1-version=2.1.1 fabric-mining-level-api-v1-version=2.1.1
fabric-models-v0-version=0.3.6 fabric-models-v0-version=0.3.6

View file

@ -33,6 +33,7 @@ include 'fabric-item-groups-v0'
include 'fabric-keybindings-v0' include 'fabric-keybindings-v0'
include 'fabric-key-binding-api-v1' include 'fabric-key-binding-api-v1'
include 'fabric-lifecycle-events-v1' include 'fabric-lifecycle-events-v1'
include 'fabric-loot-api-v2'
include 'fabric-loot-tables-v1' include 'fabric-loot-tables-v1'
include 'fabric-mining-level-api-v1' include 'fabric-mining-level-api-v1'
include 'fabric-models-v0' include 'fabric-models-v0'