Implements Lazy ItemStack Capabilities (#7879)

This commit is contained in:
Marc Hermans authored and GitHub committed 2021-11-20 16:33:00 +01:00
1 parent 2c36b306a4
commit 111d3ec5d5
4 files changed
+260 -21

No files matched your search

@@ -19,33 +19,29 @@
private static final Logger field_199558_c = LogManager.getLogger();
public static final ItemStack field_190927_a = new ItemStack((Item)null);
public static final DecimalFormat field_111284_a = Util.func_200696_a(new DecimalFormat("#.##"), (p_234699_0_) -> {
@@ -109,14 +_,19 @@
@@ -109,10 +_,15 @@
p_i231596_3_.ifPresent(this::func_77982_d);
}
- public ItemStack(IItemProvider p_i48204_1_, int p_i48204_2_) {
+ public ItemStack(IItemProvider p_i48204_1_, int p_i48204_2_) { this(p_i48204_1_, p_i48204_2_, (CompoundNBT) null); }
+ public ItemStack(IItemProvider p_i48204_1_, int p_i48204_2_, @Nullable CompoundNBT capNBT) {
+ super(ItemStack.class);
+ super(ItemStack.class, true);
+ this.capNBT = capNBT;
this.field_151002_e = p_i48204_1_ == null ? null : p_i48204_1_.func_199767_j();
+ this.delegate = p_i48204_1_ == null ? null : p_i48204_1_.func_199767_j().delegate;
this.field_77994_a = p_i48204_2_;
- if (this.field_151002_e != null && this.field_151002_e.func_77645_m()) {
+ this.forgeInit();
+ if (this.field_151002_e != null && this.field_151002_e.isDamageable(this)) {
this.func_196085_b(this.func_77952_i());
}
this.func_190923_F();
+ this.forgeInit();
}
private void func_190923_F() {
@@ -125,18 +_,23 @@
@@ -125,14 +_,18 @@
}
private ItemStack(CompoundNBT p_i47263_1_) {
+ super(ItemStack.class);
+ super(ItemStack.class, true);
+ this.capNBT = p_i47263_1_.func_74764_b("ForgeCaps") ? p_i47263_1_.func_74775_l("ForgeCaps") : null;
+ Item rawItem =
this.field_151002_e = Registry.field_212630_s.func_82594_a(new ResourceLocation(p_i47263_1_.func_74779_i("id")));
@@ -55,17 +51,13 @@
this.field_77990_d = p_i47263_1_.func_74775_l("tag");
this.func_77973_b().func_179215_a(p_i47263_1_);
}
-
- if (this.func_77973_b().func_77645_m()) {
+ this.forgeInit();
+ if (this.func_77973_b().isDamageable(this)) {
this.func_196085_b(this.func_77952_i());
}
this.func_190923_F();
+ this.forgeInit();
}
public static ItemStack func_199557_a(CompoundNBT p_199557_0_) {
@@ -167,10 +_,19 @@
}
@@ -220,7 +212,7 @@
return multimap;
}
@@ -952,6 +_,24 @@
@@ -952,6 +_,23 @@
public boolean func_222117_E() {
return this.func_77973_b().func_219971_r();
@@ -238,8 +230,7 @@
+ */
+ private void forgeInit() {
+ if (this.delegate != null) {
+ net.minecraftforge.common.capabilities.ICapabilityProvider provider = field_151002_e.initCapabilities(this, this.capNBT);
+ this.gatherCapabilities(provider);
+ this.gatherCapabilities(() -> field_151002_e.initCapabilities(this, this.capNBT));
+ if (this.capNBT != null) deserializeCaps(this.capNBT);
+ }
}
@@ -22,35 +22,80 @@ package net.minecraftforge.common.capabilities;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import com.google.common.annotations.VisibleForTesting;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.Direction;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.event.ForgeEventFactory;
import java.util.function.Supplier;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public abstract class CapabilityProvider<B extends CapabilityProvider<B>> implements ICapabilityProvider
{
@VisibleForTesting
static boolean SUPPORTS_LAZY_CAPABILITIES = true;
private final @Nonnull Class<B> baseClass;
private @Nullable CapabilityDispatcher capabilities;
private boolean valid = true;
private boolean isLazy = false;
private Supplier<ICapabilityProvider> lazyParentSupplier = null;
private CompoundNBT lazyData = null;
private boolean initialized = false;
protected CapabilityProvider(Class<B> baseClass)
{
this.baseClass = baseClass;
this(baseClass, false);
}
protected final void gatherCapabilities() { gatherCapabilities(null); }
protected CapabilityProvider(final Class<B> baseClass, final boolean isLazy)
{
this.baseClass = baseClass;
this.isLazy = SUPPORTS_LAZY_CAPABILITIES && isLazy;
}
protected final void gatherCapabilities()
{
gatherCapabilities(() -> null);
}
protected final void gatherCapabilities(@Nullable ICapabilityProvider parent)
{
gatherCapabilities(() -> parent);
}
protected final void gatherCapabilities(@Nullable Supplier<ICapabilityProvider> parent)
{
if (isLazy && !initialized)
{
lazyParentSupplier = parent == null ? () -> null : parent;
return;
}
doGatherCapabilities(parent == null ? null : parent.get());
}
private void doGatherCapabilities(@Nullable ICapabilityProvider parent)
{
this.capabilities = ForgeEventFactory.gatherCapabilities(baseClass, this, parent);
this.initialized = true;
}
protected final @Nullable CapabilityDispatcher getCapabilities()
{
return this.capabilities;
if(isLazy && !initialized)
{
doGatherCapabilities(lazyParentSupplier == null ? null : lazyParentSupplier.get());
if (lazyData != null)
{
deserializeCaps(lazyData);
}
}
return capabilities;
}
public final boolean areCapsCompatible(CapabilityProvider<B> other)
@@ -80,6 +125,11 @@ public abstract class CapabilityProvider<B extends CapabilityProvider<B>> implem
protected final @Nullable CompoundNBT serializeCaps()
{
if(isLazy && !initialized)
{
return lazyData;
}
final CapabilityDispatcher disp = getCapabilities();
if (disp != null)
{
@@ -90,6 +140,12 @@ public abstract class CapabilityProvider<B extends CapabilityProvider<B>> implem
protected final void deserializeCaps(CompoundNBT tag)
{
if (isLazy && !initialized)
{
lazyData = tag;
return;
}
final CapabilityDispatcher disp = getCapabilities();
if (disp != null)
{
@@ -0,0 +1,190 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2021.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.capabilities;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.NonNullLazy;
import net.minecraftforge.common.util.TablePrinter;
import net.minecraftforge.common.util.TextTable;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
import net.minecraftforge.fluids.capability.templates.FluidHandlerItemStackSimple;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import static net.minecraftforge.common.util.TextTable.column;
@Mod(LazyCapabilitiesOnItemsTest.MOD_ID)
@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD, modid = LazyCapabilitiesOnItemsTest.MOD_ID)
public class LazyCapabilitiesOnItemsTest
{
public static final String MOD_ID = "lazy_capabilities_on_items";
private static final Logger LOGGER = LogManager.getLogger();
private static final boolean ENABLED = true;
private static final int SAMPLE_SIZE = 100000;
private static final List<ItemStack> WARMUP_RESULTS = new ArrayList<>(SAMPLE_SIZE * 4);
private static final List<ItemStack> NO_CAPS_DISABLED_RESULTS = new ArrayList<>(SAMPLE_SIZE);
private static final List<ItemStack> WITH_CAPS_DISABLED_RESULTS = new ArrayList<>(SAMPLE_SIZE);
private static final List<ItemStack> NO_CAPS_ENABLED_RESULTS = new ArrayList<>(SAMPLE_SIZE);
private static final List<ItemStack> WITH_CAPS_ENABLED_RESULTS = new ArrayList<>(SAMPLE_SIZE);
public LazyCapabilitiesOnItemsTest()
{
if (!ENABLED)
return;
final IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus();
modBus.addListener(this::onCommonSetup);
}
private void onCommonSetup(FMLCommonSetupEvent event)
{
try
{
final Field supportsFlagField = CapabilityProvider.class.getDeclaredField("SUPPORTS_LAZY_CAPABILITIES");
supportsFlagField.setAccessible(true);
supportsFlagField.set(null, false);
final Stopwatch timer = Stopwatch.createUnstarted();
final IEventBus bus = MinecraftForge.EVENT_BUS;
final ResourceLocation testCapId = new ResourceLocation(MOD_ID, "test");
final Consumer<AttachCapabilitiesEvent<ItemStack>> capAttachmentHandler = e -> {
//Example capability we make everything a bucket :D
e.addCapability(testCapId, new FluidHandlerItemStackSimple(e.getObject(), SAMPLE_SIZE));
};
//Warmup:
for (int i = 0; i < (SAMPLE_SIZE); i++)
{
WARMUP_RESULTS.add(new ItemStack(Items.WATER_BUCKET));
}
///First test: SAMPLE_SIZE itemstacks which do not have a capability attached.
timer.start();
for (int i = 0; i < SAMPLE_SIZE; i++)
{
NO_CAPS_DISABLED_RESULTS.add(new ItemStack(Items.WATER_BUCKET));
}
timer.stop();
final long simpleNoCapsLazyDisabledElapsed = timer.elapsed(TimeUnit.MICROSECONDS);
timer.reset();
///Second test: SAMPLE_SIZE itemstacks with a capability attached.
bus.addGenericListener(ItemStack.class, capAttachmentHandler);
//Warmup:
for (int i = 0; i < (SAMPLE_SIZE); i++)
{
WARMUP_RESULTS.add(new ItemStack(Items.WATER_BUCKET));
}
timer.start();
for (int i = 0; i < SAMPLE_SIZE; i++)
{
WITH_CAPS_DISABLED_RESULTS.add(new ItemStack(Items.WATER_BUCKET));
}
timer.stop();
final long withCapsLazyDisabledElapsed = timer.elapsed(TimeUnit.MICROSECONDS);
timer.reset();
bus.unregister(capAttachmentHandler);
///Third test: SAMPLE_SIZE itemstacks which do not have a capability attached.
supportsFlagField.set(null, true);
//Warmup:
for (int i = 0; i < (SAMPLE_SIZE); i++)
{
WARMUP_RESULTS.add(new ItemStack(Items.WATER_BUCKET));
}
timer.start();
for (int i = 0; i < SAMPLE_SIZE; i++)
{
NO_CAPS_ENABLED_RESULTS.add(new ItemStack(Items.WATER_BUCKET));
}
timer.stop();
final long simpleNoCapsLazyEnabledElapsed = timer.elapsed(TimeUnit.MICROSECONDS);
timer.reset();
///Fourth test: SAMPLE_SIZE itemstacks with a capability attached.
bus.addGenericListener(ItemStack.class, capAttachmentHandler);
//Warmup:
for (int i = 0; i < (SAMPLE_SIZE); i++)
{
WARMUP_RESULTS.add(new ItemStack(Items.WATER_BUCKET));
}
timer.start();
for (int i = 0; i < SAMPLE_SIZE; i++)
{
WITH_CAPS_ENABLED_RESULTS.add(new ItemStack(Items.WATER_BUCKET));
}
timer.stop();
final long withCapsLazyEnabledElapsed = timer.elapsed(TimeUnit.MICROSECONDS);
timer.reset();
bus.unregister(capAttachmentHandler);
TextTable table = new TextTable(Lists.newArrayList(
column("Test type", TextTable.Alignment.LEFT),
column("Total time", TextTable.Alignment.CENTER)
));
table.add("Lazy: Disabled / Caps: None", simpleNoCapsLazyDisabledElapsed + " ms.");
table.add("Lazy: Disabled / Caps: One", withCapsLazyDisabledElapsed + " ms.");
table.add("Lazy: Enabled / Caps: None", simpleNoCapsLazyEnabledElapsed + " ms.");
table.add("Lazy: Enabled / Caps: One", withCapsLazyEnabledElapsed + " ms.");
final String[] resultData = table.build("\n").split("\n");
for (final String line : resultData)
{
LOGGER.warn(line);
}
}
catch (NoSuchFieldException | IllegalAccessException e)
{
LOGGER.error("Failed to run capabilities on items test!");
throw new IllegalStateException(e);
}
}
}
+2
View File
@@ -138,6 +138,8 @@ license="LGPL v2.1"
modId="dimension_settings_test"
[[mods]]
modId="player_attack_knockback_test"
[[mods]]
modId="lazy_capabilities_on_items"
[[mods]]
modId="send_datapacks_to_client"
[[mods]]