Skip to content

Commit 8b0577b

Browse files
authored
Improve the api (#226)
* Add `UpgradeItem#isCompatibleWith` * Expose sync update tags * Add an event for when modules are executed * Add an event to register menu data * Add a capability cacher * Update .gitignore
1 parent ad33236 commit 8b0577b

File tree

9 files changed

+263
-21
lines changed

9 files changed

+263
-21
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,5 @@ mcmodsrepo
3232

3333
# datagen caches
3434
**/.cache
35+
36+
repo/

build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -216,4 +216,4 @@ publishMods {
216216
content = changelog.map { "# ${mod_name} v${mod_version} for MC ${minecraft_version} has been released! \n" + it}
217217
// setPlatforms(platforms.curseforge, platforms.modrinth)
218218
}
219-
}
219+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package me.desht.modularrouters.api.event;
2+
3+
import me.desht.modularrouters.block.tile.ModularRouterBlockEntity;
4+
import me.desht.modularrouters.logic.compiled.CompiledModule;
5+
import net.neoforged.bus.api.Event;
6+
import net.neoforged.bus.api.ICancellableEvent;
7+
8+
/**
9+
* Called when a router {@link me.desht.modularrouters.logic.compiled.CompiledModule#execute(ModularRouterBlockEntity) executes} a module.
10+
*/
11+
public class ExecuteModuleEvent extends Event implements ICancellableEvent {
12+
private boolean executed;
13+
private final ModularRouterBlockEntity router;
14+
private final CompiledModule module;
15+
16+
public ExecuteModuleEvent(ModularRouterBlockEntity router, CompiledModule module) {
17+
this.router = router;
18+
this.module = module;
19+
}
20+
21+
/**
22+
* {@return the router executing the module}
23+
*/
24+
public ModularRouterBlockEntity getRouter() {
25+
return router;
26+
}
27+
28+
/**
29+
* {@return the executed module}
30+
*/
31+
public CompiledModule getModule() {
32+
return module;
33+
}
34+
35+
/**
36+
* If set to {@code true}, the router will consider the module executed.
37+
*
38+
* @apiNote to prevent the module itself from being executed, you need to {@link #setCanceled(boolean) cancel} the event too
39+
*/
40+
public void setExecuted(boolean executed) {
41+
this.executed = executed;
42+
}
43+
44+
/**
45+
* {@return whether the router should consider the module executed}
46+
*/
47+
public boolean isExecuted() {
48+
return this.executed;
49+
}
50+
51+
/**
52+
* Cancel this event to prevent the module from executing.
53+
*/
54+
@Override
55+
public void setCanceled(boolean canceled) {
56+
ICancellableEvent.super.setCanceled(canceled);
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package me.desht.modularrouters.api.event;
2+
3+
import me.desht.modularrouters.block.tile.ModularRouterBlockEntity;
4+
import net.minecraft.resources.ResourceLocation;
5+
import net.minecraft.world.inventory.ContainerData;
6+
import net.minecraft.world.inventory.DataSlot;
7+
import net.neoforged.bus.api.Event;
8+
import org.jetbrains.annotations.ApiStatus;
9+
10+
import java.util.Collections;
11+
import java.util.HashMap;
12+
import java.util.Map;
13+
14+
/**
15+
* Fired to register {@link DataSlot data slots} for the router menu. <br>
16+
* Use this to sync data to display in the GUI.
17+
*/
18+
public class RegisterRouterContainerData extends Event {
19+
private final ModularRouterBlockEntity router;
20+
private final Map<ResourceLocation, DataSlot> data = new HashMap<>();
21+
22+
@ApiStatus.Internal
23+
public RegisterRouterContainerData(ModularRouterBlockEntity router) {
24+
this.router = router;
25+
}
26+
27+
/**
28+
* Register a {@link DataSlot data slot}.
29+
*
30+
* @param id the ID of the slot
31+
* @param dataSlot the slot to register
32+
*/
33+
public void register(ResourceLocation id, DataSlot dataSlot) {
34+
data.put(id, dataSlot);
35+
}
36+
37+
/**
38+
* {@return the router of the menu}
39+
*/
40+
public ModularRouterBlockEntity getRouter() {
41+
return router;
42+
}
43+
44+
@ApiStatus.Internal
45+
public Map<ResourceLocation, DataSlot> getData() {
46+
return Collections.unmodifiableMap(data);
47+
}
48+
}

src/main/java/me/desht/modularrouters/block/tile/ModularRouterBlockEntity.java

+46-7
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import net.minecraft.core.BlockPos;
3434
import net.minecraft.core.Direction;
3535
import net.minecraft.core.GlobalPos;
36+
import net.minecraft.core.registries.BuiltInRegistries;
3637
import net.minecraft.core.registries.Registries;
3738
import net.minecraft.nbt.CompoundTag;
3839
import net.minecraft.nbt.NbtUtils;
@@ -59,6 +60,7 @@
5960
import net.minecraft.world.level.block.state.BlockState;
6061
import net.minecraft.world.phys.AABB;
6162
import net.neoforged.neoforge.client.model.data.ModelData;
63+
import net.neoforged.neoforge.common.NeoForge;
6264
import net.neoforged.neoforge.energy.EnergyStorage;
6365
import net.neoforged.neoforge.energy.IEnergyStorage;
6466
import net.neoforged.neoforge.fluids.capability.IFluidHandlerItem;
@@ -114,7 +116,7 @@ public class ModularRouterBlockEntity extends BlockEntity implements ICamouflage
114116
private byte recompileNeeded = COMPILE_MODULES | COMPILE_UPGRADES;
115117
private int tickRate = ConfigHolder.common.router.baseTickRate.get();
116118
private int itemsPerTick = 1;
117-
private final Map<Item, Integer> upgradeCount = new HashMap<>();
119+
private final Map<UpgradeItem, Integer> upgradeCount = new HashMap<>();
118120

119121
private int fluidTransferRate; // mB/t
120122
private int fluidTransferRemainingIn = 0;
@@ -186,6 +188,13 @@ public CompoundTag getUpdateTag() {
186188
if (nEnergy > 0) {
187189
tag.putInt(NBT_ENERGY_UPGRADES, nEnergy);
188190
}
191+
192+
getAllUpgrades().keySet().forEach(item -> {
193+
final var updateTag = item.createUpdateTag(this);
194+
if (updateTag != null) {
195+
tag.put(BuiltInRegistries.ITEM.getKey(item).toString(), updateTag);
196+
}
197+
});
189198
});
190199
}
191200

@@ -214,6 +223,11 @@ private void processClientSync(CompoundTag compound) {
214223
}
215224

216225
energyStorage.updateForEnergyUpgrades(compound.getInt(NBT_ENERGY_UPGRADES));
226+
227+
getAllUpgrades().keySet().forEach(item -> {
228+
final var updateTag = compound.get(BuiltInRegistries.ITEM.getKey(item).toString());
229+
item.processClientSync(this, (CompoundTag) updateTag);
230+
});
217231
}
218232

219233
@Override
@@ -397,7 +411,24 @@ private boolean runAllModules(boolean powered, boolean pulsed) {
397411

398412
for (CompiledIndexedModule cim : compiledModules) {
399413
CompiledModule cm = cim.compiledModule;
400-
if (cm != null && cm.hasTarget() && cm.getEnergyCost() <= getEnergyStorage().getEnergyStored() && cm.shouldRun(powered, pulsed))
414+
if (cm != null && cm.hasTarget() && cm.getEnergyCost() <= getEnergyStorage().getEnergyStored() && cm.shouldRun(powered, pulsed)) {
415+
var event = cm.getEvent();
416+
if (event != null) {
417+
event.setExecuted(false);
418+
event.setCanceled(false);
419+
NeoForge.EVENT_BUS.post(event);
420+
if (event.isExecuted()) {
421+
newActive = true;
422+
}
423+
424+
if (event.isCanceled()) {
425+
if ((newActive && cm.termination() == ModuleItem.Termination.RAN) || cm.termination() == ModuleItem.Termination.NOT_RAN) {
426+
break;
427+
}
428+
continue;
429+
}
430+
}
431+
401432
if (cm.execute(this)) {
402433
cm.getFilter().cycleRoundRobin().ifPresent(counter -> {
403434
ItemStack moduleStack = modulesHandler.getStackInSlot(cim.index);
@@ -411,6 +442,7 @@ private boolean runAllModules(boolean powered, boolean pulsed) {
411442
} else if (cm.termination() == ModuleItem.Termination.NOT_RAN) {
412443
break;
413444
}
445+
}
414446
}
415447
return newActive;
416448
}
@@ -550,7 +582,7 @@ private void compileUpgrades() {
550582
for (int i = 0; i < N_UPGRADE_SLOTS; i++) {
551583
ItemStack stack = upgradesHandler.getStackInSlot(i);
552584
if (stack.getItem() instanceof UpgradeItem upgradeItem) {
553-
upgradeCount.put(stack.getItem(), getUpgradeCount(stack.getItem()) + stack.getCount());
585+
upgradeCount.put(upgradeItem, getUpgradeCount(stack.getItem()) + stack.getCount());
554586
upgradeItem.onCompiled(stack, this);
555587
}
556588
}
@@ -605,7 +637,7 @@ public int getUpgradeCount(Item type) {
605637
return upgradeCount.getOrDefault(type, 0);
606638
}
607639

608-
public Map<Item,Integer> getAllUpgrades() {
640+
public Map<UpgradeItem, Integer> getAllUpgrades() {
609641
return Collections.unmodifiableMap(upgradeCount);
610642
}
611643

@@ -984,11 +1016,18 @@ class UpgradeHandler extends RouterItemHandler {
9841016

9851017
@Override
9861018
public boolean isItemValid(int slot, @Nonnull ItemStack stack) {
987-
// can't have the same upgrade in more than one slot
1019+
if (!super.isItemValid(slot, stack)) return false;
1020+
UpgradeItem item = (UpgradeItem) stack.getItem();
9881021
for (int i = 0; i < getSlots(); i++) {
989-
if (slot != i && stack.getItem() == getStackInSlot(i).getItem()) return false;
1022+
ItemStack inSlot = getStackInSlot(i);
1023+
if (inSlot.isEmpty() || slot == i) continue;
1024+
// can't have the same upgrade in more than one slot
1025+
// incompatible upgrades can't coexist
1026+
if (stack.getItem() == inSlot.getItem() || !((UpgradeItem) inSlot.getItem()).isCompatibleWith(item)) {
1027+
return false;
1028+
}
9901029
}
991-
return super.isItemValid(slot, stack);
1030+
return true;
9921031
}
9931032

9941033
@Override

src/main/java/me/desht/modularrouters/container/RouterMenu.java

+11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package me.desht.modularrouters.container;
22

3+
import me.desht.modularrouters.api.event.RegisterRouterContainerData;
34
import me.desht.modularrouters.block.tile.ModularRouterBlockEntity;
45
import me.desht.modularrouters.core.ModBlockEntities;
56
import me.desht.modularrouters.core.ModMenuTypes;
@@ -12,9 +13,13 @@
1213
import net.minecraft.world.inventory.Slot;
1314
import net.minecraft.world.item.ItemStack;
1415
import net.minecraft.world.phys.Vec3;
16+
import net.neoforged.neoforge.common.NeoForge;
1517
import net.neoforged.neoforge.items.IItemHandler;
1618
import net.neoforged.neoforge.items.SlotItemHandler;
1719

20+
import java.util.Comparator;
21+
import java.util.Map;
22+
1823
import static me.desht.modularrouters.container.Layout.SLOT_X_SPACING;
1924
import static me.desht.modularrouters.container.Layout.SLOT_Y_SPACING;
2025

@@ -79,6 +84,12 @@ public RouterMenu(int windowId, Inventory invPlayer, BlockPos routerPos) {
7984
}
8085

8186
addDataSlots(data);
87+
88+
final var event = new RegisterRouterContainerData(router);
89+
NeoForge.EVENT_BUS.post(event);
90+
event.getData().entrySet()
91+
.stream().sorted(Map.Entry.comparingByKey())
92+
.forEach(entry -> addDataSlot(entry.getValue()));
8293
}
8394

8495
@Override

src/main/java/me/desht/modularrouters/item/upgrade/UpgradeItem.java

+36
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
import me.desht.modularrouters.client.util.TintColor;
55
import me.desht.modularrouters.core.ModItems;
66
import me.desht.modularrouters.item.MRBaseItem;
7+
import net.minecraft.nbt.CompoundTag;
8+
import net.minecraft.nbt.Tag;
79
import net.minecraft.network.chat.Component;
810
import net.minecraft.world.item.ItemStack;
11+
import org.jetbrains.annotations.Nullable;
912

1013
import java.util.List;
1114

@@ -19,14 +22,47 @@ public TintColor getItemTint() {
1922
return TintColor.WHITE;
2023
}
2124

25+
/**
26+
* Called when the router's upgrades are "compiled". <br>
27+
* Can be used to update buffer capacities, for instance.
28+
*
29+
* @param stack the upgrade stack
30+
* @param router the router
31+
*/
2232
public void onCompiled(ItemStack stack, ModularRouterBlockEntity router) {
2333
// no-op by default
2434
}
2535

36+
/**
37+
* {@return the tag that will be sent for clients, to sync data}
38+
* @param router the router
39+
*/
40+
@Nullable
41+
public CompoundTag createUpdateTag(ModularRouterBlockEntity router) {
42+
return null;
43+
}
44+
45+
/**
46+
* Process an update packet.
47+
*
48+
* @param router the router
49+
* @param tag the update tag
50+
*/
51+
public void processClientSync(ModularRouterBlockEntity router, @Nullable CompoundTag tag) {
52+
53+
}
54+
2655
protected void addExtraInformation(ItemStack stack, List<Component> list) {
2756

2857
}
2958

59+
/**
60+
* {@return {@code true} if this module can coexist with the {@code other} upgrade}
61+
*/
62+
public boolean isCompatibleWith(UpgradeItem other) {
63+
return true;
64+
}
65+
3066
/**
3167
* Get the maximum number of this upgrade that can be put in an upgrade slot
3268
* @param slot the slot number

src/main/java/me/desht/modularrouters/logic/ModuleTarget.java

+26
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
1010
import net.minecraft.network.chat.Component;
1111
import net.minecraft.server.level.ServerLevel;
1212
import net.minecraft.world.level.Level;
13+
import net.neoforged.neoforge.capabilities.BlockCapability;
1314
import net.neoforged.neoforge.capabilities.BlockCapabilityCache;
1415
import net.neoforged.neoforge.capabilities.Capabilities;
1516
import net.neoforged.neoforge.energy.IEnergyStorage;
1617
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
1718
import net.neoforged.neoforge.items.IItemHandler;
1819

1920
import javax.annotation.Nullable;
21+
import java.util.IdentityHashMap;
22+
import java.util.Map;
2023
import java.util.Objects;
2124
import java.util.Optional;
2225

@@ -32,6 +35,7 @@ public class ModuleTarget {
3235
private BlockCapabilityCache<IItemHandler,Direction> itemCapCache;
3336
private BlockCapabilityCache<IFluidHandler,Direction> fluidCapCache;
3437
private BlockCapabilityCache<IEnergyStorage,Direction> energyCapCache;
38+
private final Map<BlockCapability<?, ?>, BlockCapabilityCache<?, ?>> capabilityCache = new IdentityHashMap<>();
3539

3640
public ModuleTarget(GlobalPos gPos, Direction face, String blockTranslationKey) {
3741
this.gPos = gPos;
@@ -128,6 +132,28 @@ public Optional<IEnergyStorage> getEnergyHandler() {
128132
return Optional.ofNullable(energyCapCache.getCapability());
129133
}
130134

135+
/**
136+
* Get a cached capability of the module target.
137+
*
138+
* @param capability the capability
139+
* @param context the capability context
140+
* @return the capability
141+
*/
142+
@Nullable
143+
public <T, C> T getCapability(BlockCapability<T, C> capability, @Nullable C context) {
144+
var cached = (BlockCapabilityCache<T, C>)capabilityCache.get(capability);
145+
if (cached != null && Objects.equals(cached.context(), context)) {
146+
return cached.getCapability();
147+
}
148+
ServerLevel level = MiscUtil.getWorldForGlobalPos(gPos);
149+
if (level == null) {
150+
return null;
151+
}
152+
cached = BlockCapabilityCache.create(capability, level, gPos.pos(), context);
153+
capabilityCache.put(capability, cached);
154+
return cached.getCapability();
155+
}
156+
131157
@Override
132158
public boolean equals(Object o) {
133159
if (this == o) return true;

0 commit comments

Comments
 (0)