Skip to content

Commit

Permalink
Improve the api (#226)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Matyrobbrt authored Apr 22, 2024
1 parent ad33236 commit 8b0577b
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 21 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ mcmodsrepo

# datagen caches
**/.cache

repo/
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,4 @@ publishMods {
content = changelog.map { "# ${mod_name} v${mod_version} for MC ${minecraft_version} has been released! \n" + it}
// setPlatforms(platforms.curseforge, platforms.modrinth)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package me.desht.modularrouters.api.event;

import me.desht.modularrouters.block.tile.ModularRouterBlockEntity;
import me.desht.modularrouters.logic.compiled.CompiledModule;
import net.neoforged.bus.api.Event;
import net.neoforged.bus.api.ICancellableEvent;

/**
* Called when a router {@link me.desht.modularrouters.logic.compiled.CompiledModule#execute(ModularRouterBlockEntity) executes} a module.
*/
public class ExecuteModuleEvent extends Event implements ICancellableEvent {
private boolean executed;
private final ModularRouterBlockEntity router;
private final CompiledModule module;

public ExecuteModuleEvent(ModularRouterBlockEntity router, CompiledModule module) {
this.router = router;
this.module = module;
}

/**
* {@return the router executing the module}
*/
public ModularRouterBlockEntity getRouter() {
return router;
}

/**
* {@return the executed module}
*/
public CompiledModule getModule() {
return module;
}

/**
* If set to {@code true}, the router will consider the module executed.
*
* @apiNote to prevent the module itself from being executed, you need to {@link #setCanceled(boolean) cancel} the event too
*/
public void setExecuted(boolean executed) {
this.executed = executed;
}

/**
* {@return whether the router should consider the module executed}
*/
public boolean isExecuted() {
return this.executed;
}

/**
* Cancel this event to prevent the module from executing.
*/
@Override
public void setCanceled(boolean canceled) {
ICancellableEvent.super.setCanceled(canceled);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package me.desht.modularrouters.api.event;

import me.desht.modularrouters.block.tile.ModularRouterBlockEntity;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.ContainerData;
import net.minecraft.world.inventory.DataSlot;
import net.neoforged.bus.api.Event;
import org.jetbrains.annotations.ApiStatus;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
* Fired to register {@link DataSlot data slots} for the router menu. <br>
* Use this to sync data to display in the GUI.
*/
public class RegisterRouterContainerData extends Event {
private final ModularRouterBlockEntity router;
private final Map<ResourceLocation, DataSlot> data = new HashMap<>();

@ApiStatus.Internal
public RegisterRouterContainerData(ModularRouterBlockEntity router) {
this.router = router;
}

/**
* Register a {@link DataSlot data slot}.
*
* @param id the ID of the slot
* @param dataSlot the slot to register
*/
public void register(ResourceLocation id, DataSlot dataSlot) {
data.put(id, dataSlot);
}

/**
* {@return the router of the menu}
*/
public ModularRouterBlockEntity getRouter() {
return router;
}

@ApiStatus.Internal
public Map<ResourceLocation, DataSlot> getData() {
return Collections.unmodifiableMap(data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.GlobalPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
Expand All @@ -59,6 +60,7 @@
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.neoforged.neoforge.client.model.data.ModelData;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.energy.EnergyStorage;
import net.neoforged.neoforge.energy.IEnergyStorage;
import net.neoforged.neoforge.fluids.capability.IFluidHandlerItem;
Expand Down Expand Up @@ -114,7 +116,7 @@ public class ModularRouterBlockEntity extends BlockEntity implements ICamouflage
private byte recompileNeeded = COMPILE_MODULES | COMPILE_UPGRADES;
private int tickRate = ConfigHolder.common.router.baseTickRate.get();
private int itemsPerTick = 1;
private final Map<Item, Integer> upgradeCount = new HashMap<>();
private final Map<UpgradeItem, Integer> upgradeCount = new HashMap<>();

private int fluidTransferRate; // mB/t
private int fluidTransferRemainingIn = 0;
Expand Down Expand Up @@ -186,6 +188,13 @@ public CompoundTag getUpdateTag() {
if (nEnergy > 0) {
tag.putInt(NBT_ENERGY_UPGRADES, nEnergy);
}

getAllUpgrades().keySet().forEach(item -> {
final var updateTag = item.createUpdateTag(this);
if (updateTag != null) {
tag.put(BuiltInRegistries.ITEM.getKey(item).toString(), updateTag);
}
});
});
}

Expand Down Expand Up @@ -214,6 +223,11 @@ private void processClientSync(CompoundTag compound) {
}

energyStorage.updateForEnergyUpgrades(compound.getInt(NBT_ENERGY_UPGRADES));

getAllUpgrades().keySet().forEach(item -> {
final var updateTag = compound.get(BuiltInRegistries.ITEM.getKey(item).toString());
item.processClientSync(this, (CompoundTag) updateTag);
});
}

@Override
Expand Down Expand Up @@ -397,7 +411,24 @@ private boolean runAllModules(boolean powered, boolean pulsed) {

for (CompiledIndexedModule cim : compiledModules) {
CompiledModule cm = cim.compiledModule;
if (cm != null && cm.hasTarget() && cm.getEnergyCost() <= getEnergyStorage().getEnergyStored() && cm.shouldRun(powered, pulsed))
if (cm != null && cm.hasTarget() && cm.getEnergyCost() <= getEnergyStorage().getEnergyStored() && cm.shouldRun(powered, pulsed)) {
var event = cm.getEvent();
if (event != null) {
event.setExecuted(false);
event.setCanceled(false);
NeoForge.EVENT_BUS.post(event);
if (event.isExecuted()) {
newActive = true;
}

if (event.isCanceled()) {
if ((newActive && cm.termination() == ModuleItem.Termination.RAN) || cm.termination() == ModuleItem.Termination.NOT_RAN) {
break;
}
continue;
}
}

if (cm.execute(this)) {
cm.getFilter().cycleRoundRobin().ifPresent(counter -> {
ItemStack moduleStack = modulesHandler.getStackInSlot(cim.index);
Expand All @@ -411,6 +442,7 @@ private boolean runAllModules(boolean powered, boolean pulsed) {
} else if (cm.termination() == ModuleItem.Termination.NOT_RAN) {
break;
}
}
}
return newActive;
}
Expand Down Expand Up @@ -550,7 +582,7 @@ private void compileUpgrades() {
for (int i = 0; i < N_UPGRADE_SLOTS; i++) {
ItemStack stack = upgradesHandler.getStackInSlot(i);
if (stack.getItem() instanceof UpgradeItem upgradeItem) {
upgradeCount.put(stack.getItem(), getUpgradeCount(stack.getItem()) + stack.getCount());
upgradeCount.put(upgradeItem, getUpgradeCount(stack.getItem()) + stack.getCount());
upgradeItem.onCompiled(stack, this);
}
}
Expand Down Expand Up @@ -605,7 +637,7 @@ public int getUpgradeCount(Item type) {
return upgradeCount.getOrDefault(type, 0);
}

public Map<Item,Integer> getAllUpgrades() {
public Map<UpgradeItem, Integer> getAllUpgrades() {
return Collections.unmodifiableMap(upgradeCount);
}

Expand Down Expand Up @@ -984,11 +1016,18 @@ class UpgradeHandler extends RouterItemHandler {

@Override
public boolean isItemValid(int slot, @Nonnull ItemStack stack) {
// can't have the same upgrade in more than one slot
if (!super.isItemValid(slot, stack)) return false;
UpgradeItem item = (UpgradeItem) stack.getItem();
for (int i = 0; i < getSlots(); i++) {
if (slot != i && stack.getItem() == getStackInSlot(i).getItem()) return false;
ItemStack inSlot = getStackInSlot(i);
if (inSlot.isEmpty() || slot == i) continue;
// can't have the same upgrade in more than one slot
// incompatible upgrades can't coexist
if (stack.getItem() == inSlot.getItem() || !((UpgradeItem) inSlot.getItem()).isCompatibleWith(item)) {
return false;
}
}
return super.isItemValid(slot, stack);
return true;
}

@Override
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/me/desht/modularrouters/container/RouterMenu.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package me.desht.modularrouters.container;

import me.desht.modularrouters.api.event.RegisterRouterContainerData;
import me.desht.modularrouters.block.tile.ModularRouterBlockEntity;
import me.desht.modularrouters.core.ModBlockEntities;
import me.desht.modularrouters.core.ModMenuTypes;
Expand All @@ -12,9 +13,13 @@
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.items.IItemHandler;
import net.neoforged.neoforge.items.SlotItemHandler;

import java.util.Comparator;
import java.util.Map;

import static me.desht.modularrouters.container.Layout.SLOT_X_SPACING;
import static me.desht.modularrouters.container.Layout.SLOT_Y_SPACING;

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

addDataSlots(data);

final var event = new RegisterRouterContainerData(router);
NeoForge.EVENT_BUS.post(event);
event.getData().entrySet()
.stream().sorted(Map.Entry.comparingByKey())
.forEach(entry -> addDataSlot(entry.getValue()));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
import me.desht.modularrouters.client.util.TintColor;
import me.desht.modularrouters.core.ModItems;
import me.desht.modularrouters.item.MRBaseItem;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;

import java.util.List;

Expand All @@ -19,14 +22,47 @@ public TintColor getItemTint() {
return TintColor.WHITE;
}

/**
* Called when the router's upgrades are "compiled". <br>
* Can be used to update buffer capacities, for instance.
*
* @param stack the upgrade stack
* @param router the router
*/
public void onCompiled(ItemStack stack, ModularRouterBlockEntity router) {
// no-op by default
}

/**
* {@return the tag that will be sent for clients, to sync data}
* @param router the router
*/
@Nullable
public CompoundTag createUpdateTag(ModularRouterBlockEntity router) {
return null;
}

/**
* Process an update packet.
*
* @param router the router
* @param tag the update tag
*/
public void processClientSync(ModularRouterBlockEntity router, @Nullable CompoundTag tag) {

}

protected void addExtraInformation(ItemStack stack, List<Component> list) {

}

/**
* {@return {@code true} if this module can coexist with the {@code other} upgrade}
*/
public boolean isCompatibleWith(UpgradeItem other) {
return true;
}

/**
* Get the maximum number of this upgrade that can be put in an upgrade slot
* @param slot the slot number
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/me/desht/modularrouters/logic/ModuleTarget.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.neoforged.neoforge.capabilities.BlockCapability;
import net.neoforged.neoforge.capabilities.BlockCapabilityCache;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.energy.IEnergyStorage;
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
import net.neoforged.neoforge.items.IItemHandler;

import javax.annotation.Nullable;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

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

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

/**
* Get a cached capability of the module target.
*
* @param capability the capability
* @param context the capability context
* @return the capability
*/
@Nullable
public <T, C> T getCapability(BlockCapability<T, C> capability, @Nullable C context) {
var cached = (BlockCapabilityCache<T, C>)capabilityCache.get(capability);
if (cached != null && Objects.equals(cached.context(), context)) {
return cached.getCapability();
}
ServerLevel level = MiscUtil.getWorldForGlobalPos(gPos);
if (level == null) {
return null;
}
cached = BlockCapabilityCache.create(capability, level, gPos.pos(), context);
capabilityCache.put(capability, cached);
return cached.getCapability();
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Loading

0 comments on commit 8b0577b

Please sign in to comment.