-
-
Notifications
You must be signed in to change notification settings - Fork 195
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[1.20.6] Add ComponentEnergyStorage for using IEnergyStorage with Dat…
…a Components (#985)
- Loading branch information
1 parent
8a7c09c
commit 75ca31a
Showing
4 changed files
with
222 additions
and
22 deletions.
There are no files selected for viewing
123 changes: 123 additions & 0 deletions
123
src/main/java/net/neoforged/neoforge/energy/ComponentEnergyStorage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
/* | ||
* Copyright (c) NeoForged and contributors | ||
* SPDX-License-Identifier: LGPL-2.1-only | ||
*/ | ||
|
||
package net.neoforged.neoforge.energy; | ||
|
||
import net.minecraft.core.component.DataComponentType; | ||
import net.minecraft.util.Mth; | ||
import net.minecraft.world.item.ItemStack; | ||
import net.neoforged.neoforge.capabilities.ICapabilityProvider; | ||
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; | ||
import net.neoforged.neoforge.common.MutableDataComponentHolder; | ||
|
||
/** | ||
* Variant of {@link EnergyStorage} for use with data components. | ||
* <p> | ||
* The actual data storage is managed by a data component, and all changes will write back to that component. | ||
* <p> | ||
* To use this class, register a new {@link DataComponentType} which holds an {@link Integer} for your item. | ||
* Then reference that component from your {@link ICapabilityProvider} passed to {@link RegisterCapabilitiesEvent#registerItem} to create an instance of this class. | ||
*/ | ||
public class ComponentEnergyStorage implements IEnergyStorage { | ||
protected final MutableDataComponentHolder parent; | ||
protected final DataComponentType<Integer> energyComponent; | ||
protected final int capacity; | ||
protected final int maxReceive; | ||
protected final int maxExtract; | ||
|
||
/** | ||
* Creates a new ComponentEnergyStorage with a data component as the backing store for the energy value. | ||
* | ||
* @param parent The parent component holder, such as an {@link ItemStack} | ||
* @param energyComponent The data component referencing the stored energy of the item stack | ||
* @param capacity The max capacity of the energy being stored | ||
* @param maxReceive The max per-transfer power input rate | ||
* @param maxExtract The max per-transfer power output rate | ||
*/ | ||
public ComponentEnergyStorage(MutableDataComponentHolder parent, DataComponentType<Integer> energyComponent, int capacity, int maxReceive, int maxExtract) { | ||
this.parent = parent; | ||
this.energyComponent = energyComponent; | ||
this.capacity = capacity; | ||
this.maxReceive = maxReceive; | ||
this.maxExtract = maxExtract; | ||
} | ||
|
||
/** | ||
* Creates a new ItemEnergyStorage with a unified receive / extract rate. | ||
* | ||
* @see ComponentEnergyStorage#ItemEnergyStorage(ItemStack, DataComponentType, int, int, int) | ||
*/ | ||
public ComponentEnergyStorage(MutableDataComponentHolder parent, DataComponentType<Integer> energyComponent, int capacity, int maxTransfer) { | ||
this(parent, energyComponent, capacity, maxTransfer, maxTransfer); | ||
} | ||
|
||
/** | ||
* Creates a new ItemEnergyStorage with a transfer rate equivalent to the capacity. | ||
* | ||
* @see ComponentEnergyStorage#ItemEnergyStorage(ItemStack, DataComponentType, int, int, int) | ||
*/ | ||
public ComponentEnergyStorage(MutableDataComponentHolder parent, DataComponentType<Integer> energyComponent, int capacity) { | ||
this(parent, energyComponent, capacity, capacity); | ||
} | ||
|
||
@Override | ||
public int receiveEnergy(int toReceive, boolean simulate) { | ||
if (!canReceive() || toReceive <= 0) { | ||
return 0; | ||
} | ||
|
||
int energy = this.getEnergyStored(); | ||
int energyReceived = Mth.clamp(this.capacity - energy, 0, Math.min(this.maxReceive, toReceive)); | ||
if (!simulate && energyReceived > 0) { | ||
this.setEnergy(energy + energyReceived); | ||
} | ||
return energyReceived; | ||
} | ||
|
||
@Override | ||
public int extractEnergy(int toExtract, boolean simulate) { | ||
if (!canExtract() || toExtract <= 0) { | ||
return 0; | ||
} | ||
|
||
int energy = this.getEnergyStored(); | ||
int energyExtracted = Math.min(energy, Math.min(this.maxExtract, toExtract)); | ||
if (!simulate && energyExtracted > 0) { | ||
this.setEnergy(energy - energyExtracted); | ||
} | ||
return energyExtracted; | ||
} | ||
|
||
@Override | ||
public int getEnergyStored() { | ||
int rawEnergy = this.parent.getOrDefault(this.energyComponent, 0); | ||
return Mth.clamp(rawEnergy, 0, this.capacity); | ||
} | ||
|
||
@Override | ||
public int getMaxEnergyStored() { | ||
return this.capacity; | ||
} | ||
|
||
@Override | ||
public boolean canExtract() { | ||
return this.maxExtract > 0; | ||
} | ||
|
||
@Override | ||
public boolean canReceive() { | ||
return this.maxReceive > 0; | ||
} | ||
|
||
/** | ||
* Writes a new energy value to the data component. Clamps to [0, capacity] | ||
* | ||
* @param energy The new energy value | ||
*/ | ||
protected void setEnergy(int energy) { | ||
int realEnergy = Mth.clamp(energy, 0, this.capacity); | ||
this.parent.set(this.energyComponent, realEnergy); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
tests/src/main/java/net/neoforged/neoforge/debug/capabilities/ItemEnergyTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/* | ||
* Copyright (c) NeoForged and contributors | ||
* SPDX-License-Identifier: LGPL-2.1-only | ||
*/ | ||
|
||
package net.neoforged.neoforge.debug.capabilities; | ||
|
||
import com.mojang.serialization.Codec; | ||
import java.util.function.Supplier; | ||
import net.minecraft.core.component.DataComponentType; | ||
import net.minecraft.core.registries.Registries; | ||
import net.minecraft.gametest.framework.GameTest; | ||
import net.minecraft.network.codec.ByteBufCodecs; | ||
import net.minecraft.world.item.Item; | ||
import net.minecraft.world.item.ItemStack; | ||
import net.neoforged.neoforge.capabilities.Capabilities.EnergyStorage; | ||
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; | ||
import net.neoforged.neoforge.energy.ComponentEnergyStorage; | ||
import net.neoforged.neoforge.energy.IEnergyStorage; | ||
import net.neoforged.neoforge.registries.DeferredItem; | ||
import net.neoforged.neoforge.registries.DeferredRegister; | ||
import net.neoforged.testframework.DynamicTest; | ||
import net.neoforged.testframework.TestFramework; | ||
import net.neoforged.testframework.annotation.ForEachTest; | ||
import net.neoforged.testframework.annotation.OnInit; | ||
import net.neoforged.testframework.annotation.TestHolder; | ||
import net.neoforged.testframework.gametest.EmptyTemplate; | ||
import net.neoforged.testframework.registration.DeferredItems; | ||
import net.neoforged.testframework.registration.RegistrationHelper; | ||
|
||
@ForEachTest(groups = "capabilities.itemenergy") | ||
public class ItemEnergyTests { | ||
public static final int MAX_CAPACITY = 16384; | ||
|
||
private static final RegistrationHelper HELPER = RegistrationHelper.create("item_energy_tests"); | ||
|
||
private static final DeferredRegister<DataComponentType<?>> COMPONENTS = HELPER.registrar(Registries.DATA_COMPONENT_TYPE); | ||
private static final Supplier<DataComponentType<Integer>> ENERGY_COMPONENT = COMPONENTS.register("test_energy", () -> DataComponentType.<Integer>builder() | ||
.persistent(Codec.intRange(0, MAX_CAPACITY)) | ||
.networkSynchronized(ByteBufCodecs.INT) | ||
.build()); | ||
|
||
private static final DeferredItems ITEMS = HELPER.items(); | ||
private static final DeferredItem<Item> BATTERY = ITEMS.register("test_battery", () -> new Item(new Item.Properties().component(ENERGY_COMPONENT, MAX_CAPACITY))); | ||
|
||
@OnInit | ||
static void init(final TestFramework framework) { | ||
COMPONENTS.register(framework.modEventBus()); | ||
ITEMS.register(framework.modEventBus()); | ||
framework.modEventBus().<RegisterCapabilitiesEvent>addListener(e -> { | ||
e.registerItem(EnergyStorage.ITEM, (stack, ctx) -> { | ||
return new ComponentEnergyStorage(stack, ENERGY_COMPONENT.get(), MAX_CAPACITY); | ||
}, BATTERY); | ||
}); | ||
} | ||
|
||
@GameTest | ||
@EmptyTemplate | ||
@TestHolder(description = "Tests that ComponentEnergyStorage can read and write from a data component") | ||
public static void testItemEnergy(DynamicTest test, RegistrationHelper reg) { | ||
test.onGameTest(helper -> { | ||
ItemStack stack = BATTERY.toStack(); | ||
IEnergyStorage energy = stack.getCapability(EnergyStorage.ITEM); | ||
helper.assertValueEqual(energy.getEnergyStored(), MAX_CAPACITY, "Default stored energy should be equal to the max capacity."); | ||
|
||
helper.assertValueEqual(energy.extractEnergy(MAX_CAPACITY, false), MAX_CAPACITY, "Extracted energy should be equal to the target value."); | ||
helper.assertValueEqual(energy.getEnergyStored(), 0, "Post-extraction energy stored should be zero."); | ||
|
||
// Sanity check the real component here | ||
helper.assertValueEqual(stack.get(ENERGY_COMPONENT), 0, "Post-extraction data component value should be zero."); | ||
|
||
helper.assertValueEqual(energy.receiveEnergy(MAX_CAPACITY, false), MAX_CAPACITY, "Received energy should be equal to the target value."); | ||
helper.assertValueEqual(energy.getEnergyStored(), MAX_CAPACITY, "Post-insertion energy stored should be max capacity."); | ||
|
||
helper.succeed(); | ||
}); | ||
} | ||
} |