diff --git a/src/main/java/com/gregtechceu/gtceu/api/block/AppearanceBlock.java b/src/main/java/com/gregtechceu/gtceu/api/block/AppearanceBlock.java index 95ee548a54d..a112471202c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/block/AppearanceBlock.java +++ b/src/main/java/com/gregtechceu/gtceu/api/block/AppearanceBlock.java @@ -8,8 +8,10 @@ import org.jetbrains.annotations.Nullable; -/// @deprecated Use normal Block class instead - replace `AppearanceBlock::getBlockAppearance` with -/// `Block::getAppearance` +/** + * @deprecated Use normal Block class instead - replace {@code AppearanceBlock::getBlockAppearance} with + * {@code Block::getAppearance} + */ @Deprecated(forRemoval = true) public class AppearanceBlock extends Block implements IAppearance { diff --git a/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java b/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java index f06f8e0b0f9..f2f4ee4248c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java +++ b/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java @@ -10,6 +10,7 @@ import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.MultiblockMachineDefinition; import com.gregtechceu.gtceu.api.machine.feature.*; +import com.gregtechceu.gtceu.api.machine.trait.feature.IInteractionTrait; import com.gregtechceu.gtceu.common.data.GTItems; import com.gregtechceu.gtceu.common.machine.owner.MachineOwner; import com.gregtechceu.gtceu.syncsystem.ManagedSyncBlockEntity; @@ -246,13 +247,8 @@ public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState if (pState.hasBlockEntity()) { if (!pState.is(pNewState.getBlock())) { // new block MetaMachine machine = MetaMachine.getMachine(pLevel, pPos); - if (machine instanceof IMachineLife machineLife) { - machineLife.onMachineRemoved(); - } if (machine != null) { - for (Direction direction : GTUtil.DIRECTIONS) { - machine.getCoverContainer().removeCover(direction, null); - } + machine.onRemoved(); } pLevel.updateNeighbourForOutputSignal(pPos, this); @@ -274,16 +270,16 @@ public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { var machine = MetaMachine.getMachine(world, pos); + if (machine == null) return InteractionResult.FAIL; ItemStack itemStack = player.getItemInHand(hand); boolean shouldOpenUi = true; - if (machine != null && machine.getOwnerUUID() == null && player instanceof ServerPlayer sPlayer) { + if (machine.getOwnerUUID() == null && player instanceof ServerPlayer sPlayer) { machine.setOwnerUUID(sPlayer.getUUID()); } Set types = ToolHelper.getToolTypes(itemStack); - if (machine != null && - (!types.isEmpty() && ToolHelper.canUse(itemStack) || types.isEmpty() && player.isShiftKeyDown())) { + if (!types.isEmpty() && ToolHelper.canUse(itemStack) || types.isEmpty() && player.isShiftKeyDown()) { var result = machine.onToolClick(types, itemStack, new UseOnContext(player, hand, hit)); if (result.getSecond() == InteractionResult.CONSUME && player instanceof ServerPlayer serverPlayer) { ToolHelper.playToolSound(result.getFirst(), serverPlayer); @@ -303,6 +299,13 @@ public InteractionResult use(BlockState state, Level world, BlockPos pos, Player shouldOpenUi = gtToolItem.definition$shouldOpenUIAfterUse(new UseOnContext(player, hand, hit)); } + for (var trait : machine.getTraitHolder().getAllTraits()) { + if (trait instanceof IInteractionTrait interactionTrait) { + InteractionResult result = interactionTrait.onUse(state, world, pos, player, hand, hit); + if (result != InteractionResult.PASS) return result; + } + } + if (machine instanceof IInteractedMachine interactedMachine) { var result = interactedMachine.onUse(state, world, pos, player, hand, hit); if (result != InteractionResult.PASS) return result; diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/MachineCoverContainer.java b/src/main/java/com/gregtechceu/gtceu/api/machine/MachineCoverContainer.java index 1e7981d0672..9605acf4fc0 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/MachineCoverContainer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/MachineCoverContainer.java @@ -27,7 +27,7 @@ public class MachineCoverContainer implements ICoverable, ISyncManaged { @SyncToClient @SaveField @RerenderOnChanged - private CoverBehavior up, down, north, south, west, east; + private @Nullable CoverBehavior up, down, north, south, west, east; public MachineCoverContainer(MetaMachine machine) { this.machine = machine; @@ -67,7 +67,7 @@ public boolean shouldRenderBackSide() { } @Override - public CoverBehavior getCoverAtSide(Direction side) { + public @Nullable CoverBehavior getCoverAtSide(Direction side) { return switch (side) { case UP -> up; case SOUTH -> south; @@ -92,12 +92,12 @@ public void setCoverAtSide(@Nullable CoverBehavior coverBehavior, Direction side } @Override - public IItemHandlerModifiable getItemHandlerCap(@Nullable Direction side, boolean useCoverCapability) { + public @Nullable IItemHandlerModifiable getItemHandlerCap(@Nullable Direction side, boolean useCoverCapability) { return machine.getItemHandlerCap(side, useCoverCapability); } @Override - public IFluidHandlerModifiable getFluidHandlerCap(@Nullable Direction side, boolean useCoverCapability) { + public @Nullable IFluidHandlerModifiable getFluidHandlerCap(@Nullable Direction side, boolean useCoverCapability) { return machine.getFluidHandlerCap(side, useCoverCapability); } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/MetaMachine.java b/src/main/java/com/gregtechceu/gtceu/api/machine/MetaMachine.java index 0831a8d97e1..0f778843e26 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/MetaMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/MetaMachine.java @@ -22,7 +22,11 @@ import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiPart; import com.gregtechceu.gtceu.api.machine.property.GTMachineModelProperties; import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; +import com.gregtechceu.gtceu.api.machine.trait.MachineTraitHolder; import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; +import com.gregtechceu.gtceu.api.machine.trait.feature.IFrontFacingTrait; +import com.gregtechceu.gtceu.api.machine.trait.feature.IInteractionTrait; +import com.gregtechceu.gtceu.api.machine.trait.feature.IRenderingTrait; import com.gregtechceu.gtceu.api.misc.*; import com.gregtechceu.gtceu.api.pattern.util.RelativeDirection; import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; @@ -90,16 +94,12 @@ import com.mojang.datafixers.util.Pair; import lombok.AccessLevel; import lombok.Getter; -import org.jetbrains.annotations.MustBeInvokedByOverriders; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; +import org.jetbrains.annotations.*; import java.util.*; import java.util.function.Consumer; import java.util.function.Predicate; -import javax.annotation.OverridingMethodsMustInvokeSuper; import javax.annotation.ParametersAreNonnullByDefault; import static com.gregtechceu.gtceu.api.item.tool.ToolHelper.getBehaviorsTag; @@ -132,9 +132,6 @@ public class MetaMachine extends ManagedSyncBlockEntity implements IGregtechBloc @RerenderOnChanged private int paintingColor = -1; - @Getter - protected final List traits; - @Getter @SaveField @SyncToClient @@ -143,6 +140,9 @@ public class MetaMachine extends ManagedSyncBlockEntity implements IGregtechBloc @Getter(value = AccessLevel.PROTECTED) private final long offset = GTValues.RNG.nextInt(20); + @Getter + protected final MachineTraitHolder traitHolder; + private final List serverTicks; private final List waitingToAdd; @@ -150,7 +150,7 @@ public MetaMachine(BlockEntityCreationInfo info) { super(info); this.renderState = getDefinition().defaultRenderState(); this.coverContainer = new MachineCoverContainer(this); - this.traits = new ArrayList<>(); + this.traitHolder = new MachineTraitHolder(this); this.serverTicks = new ArrayList<>(); this.waitingToAdd = new ArrayList<>(); } @@ -179,9 +179,9 @@ public void onRemoved() { if (this instanceof IMachineLife l) l.onMachineRemoved(); } - @OverridingMethodsMustInvokeSuper + @MustBeInvokedByOverriders public void onLoad() { - traits.forEach(MachineTrait::onMachineLoad); + getTraitHolder().getAllTraits().forEach(MachineTrait::onMachineLoad); coverContainer.onLoad(); // update the painted model property if the machine is painted @@ -206,9 +206,9 @@ public final void setRemoved() { onUnload(); } - @OverridingMethodsMustInvokeSuper + @MustBeInvokedByOverriders public void onUnload() { - traits.forEach(MachineTrait::onMachineUnLoad); + getTraitHolder().getAllTraits().forEach(MachineTrait::onMachineUnload); coverContainer.onUnload(); for (TickableSubscription serverTick : serverTicks) { serverTick.unsubscribe(); @@ -303,34 +303,45 @@ public final Pair onToolClick(Set too CoverBehavior coverBehavior = gridSide == null ? null : coverContainer.getCoverAtSide(gridSide); if (gridSide == null) gridSide = hitResult.getDirection(); + Pair result = null; + // Prioritize covers where they apply (Screwdriver, Soft Mallet) if (toolType.isEmpty() && playerIn.isShiftKeyDown()) { if (coverBehavior != null) { - return Pair.of(null, coverBehavior.onScrewdriverClick(playerIn, hand, hitResult)); + result = Pair.of(null, coverBehavior.onScrewdriverClick(playerIn, hand, hitResult)); } - } - if (toolType.contains(GTToolType.SCREWDRIVER)) { + } else if (toolType.contains(GTToolType.SCREWDRIVER)) { if (coverBehavior != null) { - return Pair.of(GTToolType.SCREWDRIVER, coverBehavior.onScrewdriverClick(playerIn, hand, hitResult)); - } else return Pair.of(GTToolType.SCREWDRIVER, onScrewdriverClick(playerIn, hand, gridSide, hitResult)); + result = Pair.of(GTToolType.SCREWDRIVER, coverBehavior.onScrewdriverClick(playerIn, hand, hitResult)); + } else result = Pair.of(GTToolType.SCREWDRIVER, onScrewdriverClick(playerIn, hand, gridSide, hitResult)); } else if (toolType.contains(GTToolType.SOFT_MALLET)) { if (coverBehavior != null) { - return Pair.of(GTToolType.SOFT_MALLET, coverBehavior.onSoftMalletClick(playerIn, hand, hitResult)); - } else return Pair.of(GTToolType.SOFT_MALLET, onSoftMalletClick(playerIn, hand, gridSide, hitResult)); + result = Pair.of(GTToolType.SOFT_MALLET, coverBehavior.onSoftMalletClick(playerIn, hand, hitResult)); + } else result = Pair.of(GTToolType.SOFT_MALLET, onSoftMalletClick(playerIn, hand, gridSide, hitResult)); } else if (toolType.contains(GTToolType.WRENCH)) { - return Pair.of(GTToolType.WRENCH, onWrenchClick(playerIn, hand, gridSide, hitResult)); + result = Pair.of(GTToolType.WRENCH, onWrenchClick(playerIn, hand, gridSide, hitResult)); } else if (toolType.contains(GTToolType.CROWBAR)) { if (coverBehavior != null) { if (!isRemote()) { getCoverContainer().removeCover(gridSide, playerIn); } - return Pair.of(GTToolType.CROWBAR, InteractionResult.CONSUME); + } - return Pair.of(GTToolType.CROWBAR, onCrowbarClick(playerIn, hand, gridSide, hitResult)); + result = Pair.of(GTToolType.CROWBAR, onCrowbarClick(playerIn, hand, gridSide, hitResult)); } else if (toolType.contains(GTToolType.HARD_HAMMER)) { - return Pair.of(GTToolType.HARD_HAMMER, onHardHammerClick(playerIn, hand, gridSide, hitResult)); + result = Pair.of(GTToolType.HARD_HAMMER, onHardHammerClick(playerIn, hand, gridSide, hitResult)); } - return Pair.of(null, InteractionResult.PASS); + + if (result != null && result.getSecond() != InteractionResult.PASS) return result; + + for (var trait : getTraitHolder().getAllTraits()) { + if (trait instanceof IInteractionTrait interactionTrait) { + var r = interactionTrait.onToolClick(toolType, playerIn, hand, gridSide, hitResult); + if (r.getSecond() != InteractionResult.PASS) return r; + } + } + + return result != null ? result : Pair.of(null, InteractionResult.PASS); } protected InteractionResult onHardHammerClick(Player playerIn, InteractionHand hand, Direction gridSide, @@ -501,13 +512,6 @@ public void setPaintingColor(int color) { public void onPaintingColorChanged(int color) {} - /** - * All traits should be initialized while MetaMachine is creating. you cannot add them on the fly. - */ - public void attachTraits(MachineTrait trait) { - traits.add(trait); - } - public void clearInventory(IItemHandlerModifiable inventory) { for (int i = 0; i < inventory.getSlots(); i++) { ItemStack stackInSlot = inventory.getStackInSlot(i); @@ -528,6 +532,14 @@ public boolean shouldRenderGrid(Player player, BlockPos pos, BlockState state, I for (CoverBehavior cover : coverContainer.getCovers()) { if (cover.shouldRenderGrid(player, pos, state, held, toolTypes)) return true; } + + for (var trait : getTraitHolder().getAllTraits()) { + if (trait instanceof IRenderingTrait renderingTrait) { + var result = renderingTrait.shouldRenderGridOverlay(player, pos, state, held, toolTypes); + if (result) return result; + } + } + return false; } @@ -555,6 +567,14 @@ public boolean shouldRenderGrid(Player player, BlockPos pos, BlockState state, I return mufflableMachine.isMuffled() ? GuiTextures.TOOL_SOUND : GuiTextures.TOOL_MUTE; } } + + for (var trait : getTraitHolder().getAllTraits()) { + if (trait instanceof IRenderingTrait renderingTrait) { + var result = renderingTrait.getGridOverlayIcon(player, pos, state, toolTypes, side); + if (result != null) return result; + } + } + return null; } @@ -590,10 +610,6 @@ public void addCollisionBoundingBox(List collisionList) { collisionList.add(Shapes.block()); } - public boolean canSetIoOnSide(@Nullable Direction direction) { - return !hasFrontFacing() || getFrontFacing() != direction; - } - public static Direction getFrontFacing(@Nullable MetaMachine machine) { return machine == null ? Direction.NORTH : machine.getFrontFacing(); } @@ -618,6 +634,13 @@ public boolean isFacingValid(Direction facing) { return false; } } + + for (var trait : getTraitHolder().getAllTraits()) { + if (trait instanceof IFrontFacingTrait modifyFacingTrait) { + if (!modifyFacingTrait.isValidFrontFace(facing)) return false; + } + } + return getRotationState().test(facing); } @@ -686,6 +709,7 @@ public int tintColor(int index) { public void onNeighborChanged(Block block, BlockPos fromPos, boolean isMoving) { coverContainer.onNeighborChanged(block, fromPos, isMoving); + getTraitHolder().getAllTraits().forEach(t -> t.onMachineNeighborChanged(block, fromPos, isMoving)); } public void animateTick(RandomSource random) {} @@ -704,8 +728,8 @@ public BlockState getBlockAppearance(BlockState state, BlockAndTintGetter level, @MustBeInvokedByOverriders public void updateModelData(ModelData.Builder builder) { - for (MachineTrait trait : this.getTraits()) { - trait.updateModelData(builder); + for (MachineTrait trait : traitHolder.getAllTraits()) { + if (trait instanceof IRenderingTrait renderingTrait) renderingTrait.updateModelData(builder); } } @@ -737,6 +761,11 @@ public int getOutputSignal(@Nullable Direction side) { return cover.getRedstoneSignalOutput(); } + public int getOutputDirectSignal(@Nullable Direction side) { + // IDK what this does but MC wants it + return 0; + } + public int getAnalogOutputSignal() { return 0; } @@ -853,7 +882,7 @@ public Predicate getFluidCapFilter(@Nullable Direction side, IO io) @Nullable public IItemHandlerModifiable getItemHandlerCap(@Nullable Direction side, boolean useCoverCapability) { - var list = getTraits().stream() + var list = traitHolder.getAllTraits().stream() .filter(IItemHandlerModifiable.class::isInstance) .filter(t -> t.hasCapability(side)) .map(IItemHandlerModifiable.class::cast) @@ -877,7 +906,7 @@ public IItemHandlerModifiable getItemHandlerCap(@Nullable Direction side, boolea @Nullable public IFluidHandlerModifiable getFluidHandlerCap(@Nullable Direction side, boolean useCoverCapability) { - var list = getTraits().stream() + var list = traitHolder.getAllTraits().stream() .filter(IFluidHandler.class::isInstance) .filter(t -> t.hasCapability(side)) .map(IFluidHandler.class::cast) @@ -927,7 +956,7 @@ private static List getCapabilitiesFromTraits(List traits, if (machine instanceof IWorkable workable) { return GTCapability.CAPABILITY_WORKABLE.orEmpty(cap, LazyOptional.of(() -> workable)); } - for (MachineTrait trait : machine.getTraits()) { + for (MachineTrait trait : machine.traitHolder.getAllTraits()) { if (trait instanceof IWorkable workable) { return GTCapability.CAPABILITY_WORKABLE.orEmpty(cap, LazyOptional.of(() -> workable)); } @@ -936,13 +965,13 @@ private static List getCapabilitiesFromTraits(List traits, if (machine instanceof IControllable controllable) { return GTCapability.CAPABILITY_CONTROLLABLE.orEmpty(cap, LazyOptional.of(() -> controllable)); } - for (MachineTrait trait : machine.getTraits()) { + for (MachineTrait trait : machine.traitHolder.getAllTraits()) { if (trait instanceof IControllable controllable) { return GTCapability.CAPABILITY_CONTROLLABLE.orEmpty(cap, LazyOptional.of(() -> controllable)); } } } else if (cap == GTCapability.CAPABILITY_RECIPE_LOGIC) { - for (MachineTrait trait : machine.getTraits()) { + for (MachineTrait trait : machine.traitHolder.getAllTraits()) { if (trait instanceof RecipeLogic recipeLogic) { return GTCapability.CAPABILITY_RECIPE_LOGIC.orEmpty(cap, LazyOptional.of(() -> recipeLogic)); } @@ -951,7 +980,7 @@ private static List getCapabilitiesFromTraits(List traits, if (machine instanceof IEnergyContainer energyContainer) { return GTCapability.CAPABILITY_ENERGY_CONTAINER.orEmpty(cap, LazyOptional.of(() -> energyContainer)); } - var list = getCapabilitiesFromTraits(machine.getTraits(), side, IEnergyContainer.class); + var list = getCapabilitiesFromTraits(machine.traitHolder.getAllTraits(), side, IEnergyContainer.class); if (!list.isEmpty()) { return GTCapability.CAPABILITY_ENERGY_CONTAINER.orEmpty(cap, LazyOptional.of(() -> list.size() == 1 ? list.get(0) : new EnergyContainerList(list))); @@ -961,7 +990,7 @@ private static List getCapabilitiesFromTraits(List traits, return GTCapability.CAPABILITY_ENERGY_INFO_PROVIDER.orEmpty(cap, LazyOptional.of(() -> energyInfoProvider)); } - var list = getCapabilitiesFromTraits(machine.getTraits(), side, IEnergyInfoProvider.class); + var list = getCapabilitiesFromTraits(machine.traitHolder.getAllTraits(), side, IEnergyInfoProvider.class); if (!list.isEmpty()) { return GTCapability.CAPABILITY_ENERGY_INFO_PROVIDER.orEmpty(cap, LazyOptional.of(() -> list.size() == 1 ? list.get(0) : new EnergyInfoProviderList(list))); @@ -995,7 +1024,7 @@ private static List getCapabilitiesFromTraits(List traits, if (machine instanceof IEnergyStorage energyStorage) { return ForgeCapabilities.ENERGY.orEmpty(cap, LazyOptional.of(() -> energyStorage)); } - var list = getCapabilitiesFromTraits(machine.getTraits(), side, IEnergyStorage.class); + var list = getCapabilitiesFromTraits(machine.traitHolder.getAllTraits(), side, IEnergyStorage.class); if (!list.isEmpty()) { // TODO wrap list in the future return ForgeCapabilities.ENERGY.orEmpty(cap, LazyOptional.of(() -> list.get(0))); @@ -1004,7 +1033,7 @@ private static List getCapabilitiesFromTraits(List traits, if (machine instanceof ILaserContainer energyContainer) { return GTCapability.CAPABILITY_LASER.orEmpty(cap, LazyOptional.of(() -> energyContainer)); } - var list = getCapabilitiesFromTraits(machine.getTraits(), side, ILaserContainer.class); + var list = getCapabilitiesFromTraits(machine.traitHolder.getAllTraits(), side, ILaserContainer.class); if (!list.isEmpty()) { return GTCapability.CAPABILITY_LASER.orEmpty(cap, LazyOptional.of(() -> list.size() == 1 ? list.get(0) : new LaserContainerList(list))); @@ -1014,7 +1043,8 @@ private static List getCapabilitiesFromTraits(List traits, return GTCapability.CAPABILITY_COMPUTATION_PROVIDER.orEmpty(cap, LazyOptional.of(() -> computationProvider)); } - var list = getCapabilitiesFromTraits(machine.getTraits(), side, IOpticalComputationProvider.class); + var list = getCapabilitiesFromTraits(machine.traitHolder.getAllTraits(), side, + IOpticalComputationProvider.class); if (!list.isEmpty()) { return GTCapability.CAPABILITY_COMPUTATION_PROVIDER.orEmpty(cap, LazyOptional.of(() -> list.get(0))); } @@ -1022,7 +1052,7 @@ private static List getCapabilitiesFromTraits(List traits, if (machine instanceof IDataAccessHatch computationProvider) { return GTCapability.CAPABILITY_DATA_ACCESS.orEmpty(cap, LazyOptional.of(() -> computationProvider)); } - var list = getCapabilitiesFromTraits(machine.getTraits(), side, IDataAccessHatch.class); + var list = getCapabilitiesFromTraits(machine.traitHolder.getAllTraits(), side, IDataAccessHatch.class); if (!list.isEmpty()) { return GTCapability.CAPABILITY_DATA_ACCESS.orEmpty(cap, LazyOptional.of(() -> list.get(0))); } @@ -1030,7 +1060,7 @@ private static List getCapabilitiesFromTraits(List traits, if (machine instanceof IMonitorComponent monitorComponent) { return GTCapability.CAPABILITY_MONITOR_COMPONENT.orEmpty(cap, LazyOptional.of(() -> monitorComponent)); } - var list = getCapabilitiesFromTraits(machine.getTraits(), side, IMonitorComponent.class); + var list = getCapabilitiesFromTraits(machine.traitHolder.getAllTraits(), side, IMonitorComponent.class); if (!list.isEmpty()) { return GTCapability.CAPABILITY_MONITOR_COMPONENT.orEmpty(cap, LazyOptional.of(() -> list.get(0))); } @@ -1038,13 +1068,13 @@ private static List getCapabilitiesFromTraits(List traits, if (machine instanceof ICentralMonitor centralMonitor) { return GTCapability.CAPABILITY_CENTRAL_MONITOR.orEmpty(cap, LazyOptional.of(() -> centralMonitor)); } - var list = getCapabilitiesFromTraits(machine.getTraits(), side, ICentralMonitor.class); + var list = getCapabilitiesFromTraits(machine.traitHolder.getAllTraits(), side, ICentralMonitor.class); if (!list.isEmpty()) { return GTCapability.CAPABILITY_CENTRAL_MONITOR.orEmpty(cap, LazyOptional.of(() -> list.get(0))); } } if (GTCEu.Mods.isAE2Loaded()) { - LazyOptional opt = MetaMachine.AE2CallWrapper.getGridNodeHostCapability(cap, machine, side); + LazyOptional opt = AE2CallWrapper.getGridNodeHostCapability(cap, machine, side); if (opt.isPresent()) { // noinspection unchecked return (LazyOptional) opt; @@ -1061,7 +1091,8 @@ public static LazyOptional getGridNodeHostCapability(Capability cap, MetaM if (machine instanceof IInWorldGridNodeHost nodeHost) { return Capabilities.IN_WORLD_GRID_NODE_HOST.orEmpty(cap, LazyOptional.of(() -> nodeHost)); } - var list = getCapabilitiesFromTraits(machine.getTraits(), side, IInWorldGridNodeHost.class); + var list = getCapabilitiesFromTraits(machine.traitHolder.getAllTraits(), side, + IInWorldGridNodeHost.class); if (!list.isEmpty()) { // TODO wrap list in the future (or not.) return Capabilities.IN_WORLD_GRID_NODE_HOST.orEmpty(cap, LazyOptional.of(() -> list.get(0))); diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/WorkableTieredMachine.java b/src/main/java/com/gregtechceu/gtceu/api/machine/WorkableTieredMachine.java index 5e7d723a943..524ed816642 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/WorkableTieredMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/WorkableTieredMachine.java @@ -117,7 +117,7 @@ public void onLoad() { // attach self traits Map>> ioTraits = new EnumMap<>(IO.class); - for (MachineTrait trait : getTraits()) { + for (MachineTrait trait : traitHolder.getAllTraits()) { if (trait instanceof IRecipeHandlerTrait handlerTrait) { ioTraits.computeIfAbsent(handlerTrait.getHandlerIO(), i -> new ArrayList<>()).add(handlerTrait); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/feature/IDropSaveMachine.java b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/IDropSaveMachine.java index 69169a80ab5..1f55ee1dfcc 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/feature/IDropSaveMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/IDropSaveMachine.java @@ -2,7 +2,7 @@ import net.minecraft.nbt.CompoundTag; -import javax.annotation.OverridingMethodsMustInvokeSuper; +import org.jetbrains.annotations.MustBeInvokedByOverriders; /** * A machine that can save its contents when dropped. @@ -28,12 +28,12 @@ default boolean savePickClone() { * * @param tag The tag to save to. */ - @OverridingMethodsMustInvokeSuper + @MustBeInvokedByOverriders void saveToItem(CompoundTag tag); /** * Loads the contents of the block entity from a compound tag. */ - @OverridingMethodsMustInvokeSuper + @MustBeInvokedByOverriders void loadFromItem(CompoundTag tag); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/feature/IFancyUIMachine.java b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/IFancyUIMachine.java index 99d38a97cc2..d7dc3b46867 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/feature/IFancyUIMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/IFancyUIMachine.java @@ -133,7 +133,8 @@ default void attachConfigurators(ConfiguratorPanel configuratorPanel) { @Override default void attachTooltips(TooltipsPanel tooltipsPanel) { tooltipsPanel.attachTooltips(self()); - self().getTraits().stream().filter(IFancyTooltip.class::isInstance).map(IFancyTooltip.class::cast) + self().getTraitHolder().getAllTraits().stream().filter(IFancyTooltip.class::isInstance) + .map(IFancyTooltip.class::cast) .forEach(tooltipsPanel::attachTooltips); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/WorkableMultiblockMachine.java b/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/WorkableMultiblockMachine.java index 43a14b50c53..ec503aa84d6 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/WorkableMultiblockMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/WorkableMultiblockMachine.java @@ -139,7 +139,7 @@ public void onStructureFormed() { // attach self traits Map>> ioTraits = new EnumMap<>(IO.class); - for (MachineTrait trait : getTraits()) { + for (MachineTrait trait : traitHolder.getAllTraits()) { if (trait instanceof IRecipeHandlerTrait handlerTrait) { ioTraits.computeIfAbsent(handlerTrait.getHandlerIO(), i -> new ArrayList<>()).add(handlerTrait); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/part/MultiblockPartMachine.java b/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/part/MultiblockPartMachine.java index f4e4630d4da..c1c3640d981 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/part/MultiblockPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/part/MultiblockPartMachine.java @@ -87,7 +87,7 @@ protected RecipeHandlerList getHandlerList() { if (handlerList == null) { List> handlers = new ArrayList<>(); IO handlerIO = null; - for (var trait : traits) { + for (var trait : traitHolder.getAllTraits()) { if (trait instanceof IRecipeHandlerTrait rht) { if (handlerIO == null) handlerIO = rht.getHandlerIO(); handlers.add(rht); diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/package-info.java b/src/main/java/com/gregtechceu/gtceu/api/machine/package-info.java new file mode 100644 index 00000000000..5b9e6e8e866 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/package-info.java @@ -0,0 +1,4 @@ +@NotNullByDefault +package com.gregtechceu.gtceu.api.machine; + +import org.jetbrains.annotations.NotNullByDefault; diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/steam/SteamWorkableMachine.java b/src/main/java/com/gregtechceu/gtceu/api/machine/steam/SteamWorkableMachine.java index 144e8218a84..5f7f827cbc6 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/steam/SteamWorkableMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/steam/SteamWorkableMachine.java @@ -107,7 +107,7 @@ public void onLoad() { // attach self traits Map>> ioTraits = new Object2ObjectOpenHashMap<>(); - for (MachineTrait trait : getTraits()) { + for (MachineTrait trait : traitHolder.getAllTraits()) { if (trait instanceof IRecipeHandlerTrait handlerTrait) { ioTraits.computeIfAbsent(handlerTrait.getHandlerIO(), i -> new ArrayList<>()).add(handlerTrait); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/FluidTankProxyTrait.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/FluidTankProxyTrait.java index b9fc462b299..22d54ff1ef4 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/FluidTankProxyTrait.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/FluidTankProxyTrait.java @@ -16,6 +16,13 @@ @Accessors(chain = true) public class FluidTankProxyTrait extends MachineTrait implements IFluidHandlerModifiable, ICapabilityTrait { + public static final MachineTraitType TYPE = new MachineTraitType<>(FluidTankProxyTrait.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + @Getter public final IO capabilityIO; @Setter diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/ItemHandlerProxyTrait.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/ItemHandlerProxyTrait.java index 6aab403d666..ae16dbd9058 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/ItemHandlerProxyTrait.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/ItemHandlerProxyTrait.java @@ -4,6 +4,7 @@ import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.utils.GTTransferUtils; +import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.Direction; import net.minecraft.world.item.ItemStack; import net.minecraftforge.items.IItemHandlerModifiable; @@ -15,8 +16,17 @@ import org.jetbrains.annotations.Nullable; @Accessors(chain = true) +@MethodsReturnNonnullByDefault public class ItemHandlerProxyTrait extends MachineTrait implements IItemHandlerModifiable, ICapabilityTrait { + public static final MachineTraitType TYPE = new MachineTraitType<>( + ItemHandlerProxyTrait.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + @Getter public final IO capabilityIO; @Setter diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MachineTrait.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MachineTrait.java index a72c7a58c62..9edf100f733 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MachineTrait.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MachineTrait.java @@ -5,8 +5,11 @@ import com.gregtechceu.gtceu.syncsystem.ISyncManaged; import com.gregtechceu.gtceu.syncsystem.SyncDataHolder; +import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraftforge.client.model.data.ModelData; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; import lombok.Getter; import lombok.Setter; @@ -15,9 +18,13 @@ import java.util.function.Predicate; /** - * represents an abstract capability held by machine. Such as item, fluid, energy, etc. - * All trait should be added while MetaMachine is creating. you cannot modify it on the fly。 + * A machine trait represents a generic capability or behaviour that is attached to a machine. + * For example, machine traits may provide a recipe handler that can handle specific inputs/outputs of a recipe (e.g. + * {@link NotifiableItemStackHandler for items}). + * Machine traits can also attach additional behaviours to a machine (e.g. {@link AutoOutputTrait}, + * {@link CleanroomProviderTrait}) */ +@MethodsReturnNonnullByDefault public abstract class MachineTrait implements ISyncManaged { @Getter @@ -31,9 +38,15 @@ public abstract class MachineTrait implements ISyncManaged { public MachineTrait(MetaMachine machine) { this.machine = machine; this.capabilityValidator = side -> true; - /// Machine should never be null, unless this trait is a recipe handler instantiated outside a machine for - /// recipe search. - if (machine != null) machine.attachTraits(this); + // Machine should never be null, unless this trait is a recipe handler instantiated outside a machine for + // recipe search. + if (machine != null) machine.getTraitHolder().attachTrait(this); + } + + public abstract MachineTraitType getTraitType(); + + public Level getLevel() { + return machine.getLevel(); } public final boolean hasCapability(@Nullable Direction side) { @@ -45,12 +58,6 @@ public void markAsChanged() { machine.markAsChanged(); } - public void onMachineLoad() {} - - public void onMachineUnLoad() {} - - public void updateModelData(ModelData.Builder builder) {} - public MachineRenderState getRenderState() { return getMachine().getRenderState(); } @@ -62,4 +69,10 @@ public void setRenderState(MachineRenderState state) { public void scheduleRenderUpdate() { machine.scheduleRenderUpdate(); } + + public void onMachineLoad() {} + + public void onMachineUnload() {} + + public void onMachineNeighborChanged(Block block, BlockPos fromPos, boolean isMoving) {} } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MachineTraitHolder.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MachineTraitHolder.java new file mode 100644 index 00000000000..7cfc5cd711f --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MachineTraitHolder.java @@ -0,0 +1,60 @@ +package com.gregtechceu.gtceu.api.machine.trait; + +import com.gregtechceu.gtceu.api.machine.MetaMachine; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public final class MachineTraitHolder { + + private final MetaMachine machine; + private final List traits; + private final Map, List> traitsByType; + + public MachineTraitHolder(MetaMachine machine) { + this.machine = machine; + this.traits = new ObjectArrayList<>(); + this.traitsByType = new Object2ObjectOpenHashMap<>(); + } + + public @UnmodifiableView List getAllTraits() { + return traits; + } + + public void attachTrait(MachineTrait trait) { + var traitType = trait.getTraitType(); + + var list = traitsByType.computeIfAbsent(traitType, $ -> new ObjectArrayList<>(1)); + if (!traitType.allowsMultipleInstances() && !list.isEmpty()) { + throw new IllegalArgumentException("Attempted to add multiple traits of type: " + trait.getClass()); + } + + list.add(trait); + traits.add(trait); + } + + /** + * Gets the first trait with the specified type. + */ + public @Nullable T getTrait(MachineTraitType type) { + List traitList = traitsByType.get(type); + if (traitList == null || traitList.isEmpty()) return null; + return type.castTrait(traitList.get(0)); + } + + /** + * Get all traits with the specified type. + */ + @SuppressWarnings("unchecked") + public @UnmodifiableView List getTraits(MachineTraitType type) { + List traitList = (List) traitsByType.get(type); + if (traitList == null) return List.of(); + return Collections.unmodifiableList(traitList); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MachineTraitType.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MachineTraitType.java new file mode 100644 index 00000000000..aeb337615c7 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/MachineTraitType.java @@ -0,0 +1,26 @@ +package com.gregtechceu.gtceu.api.machine.trait; + +import org.jetbrains.annotations.NotNull; + +public final class MachineTraitType { + + private final Class clazz; + private final boolean allowMultipleInstances; + + public MachineTraitType(@NotNull Class clazz) { + this(clazz, true); + } + + public MachineTraitType(@NotNull Class clazz, boolean allowMultipleInstances) { + this.clazz = clazz; + this.allowMultipleInstances = allowMultipleInstances; + } + + public boolean allowsMultipleInstances() { + return allowMultipleInstances; + } + + public @NotNull T castTrait(@NotNull MachineTrait trait) { + return clazz.cast(trait); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableComputationContainer.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableComputationContainer.java index 2ce4bbd3241..db277ba1a54 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableComputationContainer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableComputationContainer.java @@ -28,6 +28,14 @@ public class NotifiableComputationContainer extends NotifiableRecipeHandlerTrait implements IOpticalComputationHatch, IOpticalComputationReceiver { + public static final MachineTraitType TYPE = new MachineTraitType<>( + NotifiableComputationContainer.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + @Getter protected IO handlerIO; @Getter @@ -67,7 +75,7 @@ public int requestCWUt(int cwut, boolean simulate, @NotNull Collection seen) { if (controller instanceof IOpticalComputationProvider provider) { return provider.getMaxCWUt(seen); } - for (MachineTrait trait : controller.self().getTraits()) { + for (MachineTrait trait : controller.self().getTraitHolder().getAllTraits()) { if (trait instanceof IOpticalComputationProvider provider) { return provider.getMaxCWUt(seen); } @@ -154,7 +162,7 @@ public boolean canBridge(@NotNull Collection seen) if (controller instanceof IOpticalComputationProvider provider) { return provider.canBridge(seen); } - for (MachineTrait trait : controller.self().getTraits()) { + for (MachineTrait trait : controller.self().getTraitHolder().getAllTraits()) { if (trait instanceof IOpticalComputationProvider provider) { return provider.canBridge(seen); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableEnergyContainer.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableEnergyContainer.java index d8ab29624ed..5868a1fffe9 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableEnergyContainer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableEnergyContainer.java @@ -19,6 +19,7 @@ import com.gregtechceu.gtceu.syncsystem.annotations.SyncToClient; import com.gregtechceu.gtceu.utils.GTUtil; +import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.Direction; import net.minecraftforge.energy.IEnergyStorage; import net.minecraftforge.items.IItemHandlerModifiable; @@ -32,8 +33,20 @@ import java.util.List; import java.util.function.Predicate; +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault public class NotifiableEnergyContainer extends NotifiableRecipeHandlerTrait implements IEnergyContainer { + public static final MachineTraitType TYPE = new MachineTraitType<>( + NotifiableEnergyContainer.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + @Getter protected IO handlerIO; @Getter @@ -101,8 +114,8 @@ public void onMachineLoad() { } @Override - public void onMachineUnLoad() { - super.onMachineUnLoad(); + public void onMachineUnload() { + super.onMachineUnload(); if (updateSubs != null) { updateSubs.unsubscribe(); updateSubs = null; diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableFluidTank.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableFluidTank.java index 15b16f284d2..b4eedaf3e97 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableFluidTank.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableFluidTank.java @@ -26,6 +26,13 @@ public class NotifiableFluidTank extends NotifiableRecipeHandlerTrait implements ICapabilityTrait, IFluidHandlerModifiable { + public static final MachineTraitType TYPE = new MachineTraitType<>(NotifiableFluidTank.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + @Getter public final IO handlerIO; @Getter diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java index 84e4074d1eb..68ac24f5f24 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableItemStackHandler.java @@ -15,6 +15,7 @@ import com.gregtechceu.gtceu.utils.GTTransferUtils; import com.gregtechceu.gtceu.utils.GTUtil; +import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.Direction; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Ingredient; @@ -32,9 +33,21 @@ import java.util.function.IntFunction; import java.util.function.Predicate; +import javax.annotation.ParametersAreNonnullByDefault; + +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault public class NotifiableItemStackHandler extends NotifiableRecipeHandlerTrait implements ICapabilityTrait, IItemHandlerModifiable { + public static final MachineTraitType TYPE = new MachineTraitType<>( + NotifiableItemStackHandler.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + @Getter public final IO handlerIO; @Getter diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableLaserContainer.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableLaserContainer.java index acba206d27e..6f2642e1142 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableLaserContainer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableLaserContainer.java @@ -5,11 +5,19 @@ import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.utils.GTUtil; +import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.Direction; import net.minecraft.world.level.block.entity.BlockEntity; +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault public class NotifiableLaserContainer extends NotifiableEnergyContainer implements ILaserContainer { + public static final MachineTraitType TYPE = new MachineTraitType<>( + NotifiableLaserContainer.class); + public NotifiableLaserContainer(MetaMachine machine, long maxCapacity, long maxInputVoltage, long maxInputAmperage, long maxOutputVoltage, long maxOutputAmperage) { super(machine, maxCapacity, maxInputVoltage, maxInputAmperage, maxOutputVoltage, maxOutputAmperage); @@ -25,6 +33,11 @@ public static NotifiableLaserContainer receiverContainer(MetaMachine machine, lo return new NotifiableLaserContainer(machine, maxCapacity, maxInputVoltage, maxInputAmperage, 0L, 0L); } + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + @Override public void serverTick() { amps = 0; diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/RecipeLogic.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/RecipeLogic.java index 7e7590f1b04..bd338f3193e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/RecipeLogic.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/RecipeLogic.java @@ -45,6 +45,13 @@ public class RecipeLogic extends MachineTrait implements IWorkable, IFancyTooltip { + public static final MachineTraitType TYPE = new MachineTraitType<>(RecipeLogic.class, false); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + public enum Status implements StringRepresentable { IDLE("idle"), diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/feature/IFrontFacingTrait.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/feature/IFrontFacingTrait.java new file mode 100644 index 00000000000..cff3a98b3fb --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/feature/IFrontFacingTrait.java @@ -0,0 +1,16 @@ +package com.gregtechceu.gtceu.api.machine.trait.feature; + +import net.minecraft.core.Direction; + +/** + * A machine trait that modifies the rotation behaviour of a machine. + */ +public interface IFrontFacingTrait { + + /** + * Returns if a machine can be rotated to be facing the given direction. + */ + default boolean isValidFrontFace(Direction direction) { + return true; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/feature/IInteractionTrait.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/feature/IInteractionTrait.java new file mode 100644 index 00000000000..1f8d0e23ea7 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/feature/IInteractionTrait.java @@ -0,0 +1,38 @@ +package com.gregtechceu.gtceu.api.machine.trait.feature; + +import com.gregtechceu.gtceu.api.item.tool.GTToolType; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; + +import com.mojang.datafixers.util.Pair; + +import java.util.Set; + +import javax.annotation.ParametersAreNonnullByDefault; + +/** + * A machine trait that provides special interaction behaviour. + */ +@ParametersAreNonnullByDefault +public interface IInteractionTrait extends ITraitFeature { + + /// Called when a player interacts with a machine without a tool. + default InteractionResult onUse(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, + BlockHitResult hit) { + return InteractionResult.PASS; + } + + /// Called when a player interacts with a machine with a tool. + default Pair onToolClick(Set toolType, + Player player, InteractionHand hand, Direction gridSide, + BlockHitResult hitResult) { + return Pair.of(null, InteractionResult.PASS); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/feature/IRenderingTrait.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/feature/IRenderingTrait.java new file mode 100644 index 00000000000..bdf175dd2b2 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/feature/IRenderingTrait.java @@ -0,0 +1,45 @@ +package com.gregtechceu.gtceu.api.machine.trait.feature; + +import com.gregtechceu.gtceu.api.item.tool.GTToolType; + +import com.lowdragmc.lowdraglib.gui.texture.ResourceTexture; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.client.model.data.ModelData; + +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +import javax.annotation.ParametersAreNonnullByDefault; + +/** + * A machine trait that overrides some of the default machine rendering behaviour. + */ +@ParametersAreNonnullByDefault +public interface IRenderingTrait extends ITraitFeature { + + /** + * Called when a player is looking at this machine, returns whether the grid overlay should be rendered. + */ + default boolean shouldRenderGridOverlay(Player player, BlockPos pos, BlockState state, ItemStack held, + Set toolTypes) { + return false; + } + + /** + * Called when the machine grid overlay is being rendered to determine the icon to be rendered within the grid + * segment on a specifc side. + */ + default @Nullable ResourceTexture getGridOverlayIcon(Player player, BlockPos pos, BlockState state, + Set toolTypes, + Direction side) { + return null; + } + + default void updateModelData(ModelData.Builder builder) {} +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/feature/ITraitFeature.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/feature/ITraitFeature.java new file mode 100644 index 00000000000..93694f7d2ea --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/feature/ITraitFeature.java @@ -0,0 +1,4 @@ +package com.gregtechceu.gtceu.api.machine.trait.feature; + +//// Represents an aspect of MetaMachine behaviour which this trait modifies. +public interface ITraitFeature {} diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/package-info.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/package-info.java new file mode 100644 index 00000000000..e50ed2813e2 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/package-info.java @@ -0,0 +1,4 @@ +@NotNullByDefault +package com.gregtechceu.gtceu.api.machine.trait; + +import org.jetbrains.annotations.NotNullByDefault; diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/PowerSubstationMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/PowerSubstationMachine.java index 7f52589b610..413eb181e78 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/PowerSubstationMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/PowerSubstationMachine.java @@ -18,6 +18,7 @@ import com.gregtechceu.gtceu.api.machine.multiblock.IBatteryData; import com.gregtechceu.gtceu.api.machine.multiblock.WorkableMultiblockMachine; import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; +import com.gregtechceu.gtceu.api.machine.trait.MachineTraitType; import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; import com.gregtechceu.gtceu.api.misc.EnergyContainerList; import com.gregtechceu.gtceu.config.ConfigHolder; @@ -374,6 +375,14 @@ public void attachTooltips(TooltipsPanel tooltipsPanel) { public static class PowerStationEnergyBank extends MachineTrait implements INBTSerializable { + public static final MachineTraitType TYPE = new MachineTraitType<>( + PowerStationEnergyBank.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + private static final String NBT_SIZE = "Size"; private static final String NBT_STORED = "Stored"; private static final String NBT_MAX = "Max"; diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/storage/QuantumChestMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/storage/QuantumChestMachine.java index 6c8169ce664..29096b7ff37 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/storage/QuantumChestMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/storage/QuantumChestMachine.java @@ -16,6 +16,7 @@ import com.gregtechceu.gtceu.api.machine.feature.IFancyUIMachine; import com.gregtechceu.gtceu.api.machine.feature.IInteractedMachine; import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; +import com.gregtechceu.gtceu.api.machine.trait.MachineTraitType; import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; import com.gregtechceu.gtceu.syncsystem.annotations.RerenderOnChanged; @@ -441,6 +442,13 @@ public Widget createUIWidget() { protected class ItemCache extends MachineTrait implements IItemHandlerModifiable { + public static final MachineTraitType TYPE = new MachineTraitType<>(ItemCache.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + private final Predicate filter = i -> !isLocked() || GTUtil.isSameItemSameTags(i, getLockedItem()); diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/storage/QuantumTankMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/storage/QuantumTankMachine.java index db4825061ec..c675f5f3107 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/storage/QuantumTankMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/storage/QuantumTankMachine.java @@ -14,6 +14,7 @@ import com.gregtechceu.gtceu.api.machine.feature.IFancyUIMachine; import com.gregtechceu.gtceu.api.machine.feature.IInteractedMachine; import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; +import com.gregtechceu.gtceu.api.machine.trait.MachineTraitType; import com.gregtechceu.gtceu.api.transfer.fluid.CustomFluidTank; import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; import com.gregtechceu.gtceu.syncsystem.annotations.RerenderOnChanged; @@ -375,6 +376,13 @@ public Widget createUIWidget() { protected class FluidCache extends MachineTrait implements IFluidHandler { + public static final MachineTraitType TYPE = new MachineTraitType<>(FluidCache.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + private final Predicate filter = f -> !isLocked() || getLockedFluid().isFluidEqual(f); public FluidCache(MetaMachine holder) { diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/trait/ConverterTrait.java b/src/main/java/com/gregtechceu/gtceu/common/machine/trait/ConverterTrait.java index 31bda70a7d8..f70d6b98229 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/trait/ConverterTrait.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/trait/ConverterTrait.java @@ -6,6 +6,7 @@ import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.property.GTMachineModelProperties; import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; +import com.gregtechceu.gtceu.api.machine.trait.MachineTraitType; import com.gregtechceu.gtceu.api.machine.trait.NotifiableEnergyContainer; import com.gregtechceu.gtceu.common.machine.electric.ConverterMachine; import com.gregtechceu.gtceu.syncsystem.annotations.RerenderOnChanged; @@ -87,6 +88,13 @@ public void serverTick() { private class FEContainer extends MachineTrait implements IEnergyStorage { + public static final MachineTraitType TYPE = new MachineTraitType<>(FEContainer.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + public FEContainer(MetaMachine machine) { super(machine); } diff --git a/src/main/java/com/gregtechceu/gtceu/common/recipe/condition/EUToStartCondition.java b/src/main/java/com/gregtechceu/gtceu/common/recipe/condition/EUToStartCondition.java index 354bc16eade..7bac79a76ae 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/recipe/condition/EUToStartCondition.java +++ b/src/main/java/com/gregtechceu/gtceu/common/recipe/condition/EUToStartCondition.java @@ -46,7 +46,8 @@ public Component getTooltips() { @Override public boolean testCondition(@NotNull GTRecipe recipe, @NotNull RecipeLogic recipeLogic) { - return recipeLogic.getMachine().getTraits().stream().filter(IEnergyContainer.class::isInstance) + return recipeLogic.getMachine().getTraitHolder().getAllTraits().stream() + .filter(IEnergyContainer.class::isInstance) .anyMatch(energyContainer -> ((IEnergyContainer) energyContainer).getEnergyCapacity() > euToStart); } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHolder.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHolder.java index dba156b9d10..eb5b2dce6db 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHolder.java @@ -1,6 +1,7 @@ package com.gregtechceu.gtceu.integration.ae2.machine.trait; import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; +import com.gregtechceu.gtceu.api.machine.trait.MachineTraitType; import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.integration.ae2.machine.feature.IGridConnectedMachine; import com.gregtechceu.gtceu.integration.ae2.utils.SerializableManagedGridNode; @@ -23,6 +24,13 @@ */ public class GridNodeHolder extends MachineTrait { + public static final MachineTraitType TYPE = new MachineTraitType<>(GridNodeHolder.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + @Getter @SaveField protected final SerializableManagedGridNode mainNode; @@ -59,8 +67,8 @@ public void onMachineLoad() { } @Override - public void onMachineUnLoad() { - super.onMachineUnLoad(); + public void onMachineUnload() { + super.onMachineUnload(); mainNode.destroy(); } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHostTrait.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHostTrait.java index 7d643bbf81d..17df1ca7a00 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHostTrait.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/GridNodeHostTrait.java @@ -2,6 +2,7 @@ import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; +import com.gregtechceu.gtceu.api.machine.trait.MachineTraitType; import net.minecraft.core.Direction; @@ -13,6 +14,13 @@ public class GridNodeHostTrait extends MachineTrait implements IGridConnectedBlockEntity { + public static final MachineTraitType TYPE = new MachineTraitType<>(GridNodeHostTrait.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + private final IManagedGridNode proxy; public GridNodeHostTrait(MetaMachine machine) { diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/InternalSlotRecipeHandler.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/InternalSlotRecipeHandler.java index 4a2e4e49431..b2c675ba41f 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/InternalSlotRecipeHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/InternalSlotRecipeHandler.java @@ -1,6 +1,7 @@ package com.gregtechceu.gtceu.integration.ae2.machine.trait; import com.gregtechceu.gtceu.api.capability.recipe.*; +import com.gregtechceu.gtceu.api.machine.trait.MachineTraitType; import com.gregtechceu.gtceu.api.machine.trait.NotifiableRecipeHandlerTrait; import com.gregtechceu.gtceu.api.machine.trait.RecipeHandlerGroupDistinctness; import com.gregtechceu.gtceu.api.machine.trait.RecipeHandlerList; @@ -59,6 +60,14 @@ public void setDistinct(boolean ignored, boolean notify) {} @Getter private static class SlotItemRecipeHandler extends NotifiableRecipeHandlerTrait { + public static final MachineTraitType TYPE = new MachineTraitType<>( + SlotItemRecipeHandler.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + private final InternalSlot slot; private final int priority; @@ -94,6 +103,14 @@ public double getTotalContentAmount() { @Getter private static class SlotFluidRecipeHandler extends NotifiableRecipeHandlerTrait { + public static final MachineTraitType TYPE = new MachineTraitType<>( + SlotFluidRecipeHandler.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + private final InternalSlot slot; private final int priority; diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/ProxySlotRecipeHandler.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/ProxySlotRecipeHandler.java index 5696a5c16b4..80ebceee189 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/ProxySlotRecipeHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/machine/trait/ProxySlotRecipeHandler.java @@ -2,10 +2,7 @@ import com.gregtechceu.gtceu.api.capability.recipe.*; import com.gregtechceu.gtceu.api.machine.MetaMachine; -import com.gregtechceu.gtceu.api.machine.trait.IRecipeHandlerTrait; -import com.gregtechceu.gtceu.api.machine.trait.NotifiableRecipeHandlerTrait; -import com.gregtechceu.gtceu.api.machine.trait.RecipeHandlerGroupDistinctness; -import com.gregtechceu.gtceu.api.machine.trait.RecipeHandlerList; +import com.gregtechceu.gtceu.api.machine.trait.*; import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.api.recipe.ingredient.FluidIngredient; import com.gregtechceu.gtceu.integration.ae2.machine.MEPatternBufferPartMachine; @@ -96,6 +93,14 @@ public void setDistinct(boolean ignored, boolean notify) {} @Getter private static class ProxyItemRecipeHandler extends NotifiableRecipeHandlerTrait { + public static final MachineTraitType TYPE = new MachineTraitType<>( + ProxyItemRecipeHandler.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + private IRecipeHandlerTrait proxy = null; private ISubscription proxySub = null; @@ -151,6 +156,14 @@ public int getPriority() { @Getter private static class ProxyFluidRecipeHandler extends NotifiableRecipeHandlerTrait { + public static final MachineTraitType TYPE = new MachineTraitType<>( + ProxyFluidRecipeHandler.class); + + @Override + public MachineTraitType getTraitType() { + return TYPE; + } + private IRecipeHandlerTrait proxy = null; private ISubscription proxySub = null; diff --git a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/MachineTraitProvider.java b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/MachineTraitProvider.java new file mode 100644 index 00000000000..209f0803135 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/MachineTraitProvider.java @@ -0,0 +1,56 @@ +package com.gregtechceu.gtceu.integration.jade.provider; + +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; +import com.gregtechceu.gtceu.api.machine.trait.MachineTraitType; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.block.entity.BlockEntity; + +import lombok.Getter; +import snownee.jade.api.BlockAccessor; +import snownee.jade.api.IBlockComponentProvider; +import snownee.jade.api.IServerDataProvider; +import snownee.jade.api.ITooltip; +import snownee.jade.api.config.IPluginConfig; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +public abstract class MachineTraitProvider + implements IBlockComponentProvider, IServerDataProvider { + + @Getter + private final ResourceLocation uid; + public final MachineTraitType traitType; + + protected MachineTraitProvider(ResourceLocation uid, MachineTraitType type) { + this.uid = uid; + this.traitType = type; + } + + @Override + public void appendTooltip(ITooltip iTooltip, BlockAccessor block, IPluginConfig iPluginConfig) { + var be = block.getBlockEntity(); + if (be == null || !block.getServerData().contains(uid.toString(), CompoundTag.TAG_COMPOUND)) return; + + var serverData = block.getServerData().getCompound(uid.toString()); + addTooltip(serverData, iTooltip, block.getPlayer(), block, be, iPluginConfig); + } + + @Override + public void appendServerData(CompoundTag compoundTag, BlockAccessor blockAccessor) { + var be = blockAccessor.getBlockEntity(); + if (be instanceof MetaMachine machine) { + var t = machine.getTraitHolder().getTrait(traitType); + if (t != null) write(compoundTag.getCompound(uid.toString()), t); + } + } + + protected abstract void write(CompoundTag data, T trait); + + protected abstract void addTooltip(CompoundTag data, ITooltip tooltip, Player player, BlockAccessor block, + BlockEntity blockEntity, IPluginConfig config); +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/top/provider/MachineTraitInfoProvider.java b/src/main/java/com/gregtechceu/gtceu/integration/top/provider/MachineTraitInfoProvider.java new file mode 100644 index 00000000000..33977534b5f --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/top/provider/MachineTraitInfoProvider.java @@ -0,0 +1,42 @@ +package com.gregtechceu.gtceu.integration.top.provider; + +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; +import com.gregtechceu.gtceu.api.machine.trait.MachineTraitType; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +import mcjty.theoneprobe.api.IProbeHitData; +import mcjty.theoneprobe.api.IProbeInfo; +import mcjty.theoneprobe.api.IProbeInfoProvider; +import mcjty.theoneprobe.api.ProbeMode; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +public abstract class MachineTraitInfoProvider implements IProbeInfoProvider { + + private final MachineTraitType traitType; + + public MachineTraitInfoProvider(MachineTraitType traitType) { + this.traitType = traitType; + } + + protected abstract void addProbeInfo(T trait, IProbeInfo probeInfo, Player player, BlockEntity blockEntity, + IProbeHitData data); + + @Override + public void addProbeInfo(ProbeMode mode, IProbeInfo probeInfo, Player player, Level world, BlockState blockState, + IProbeHitData data) { + if (blockState.hasBlockEntity()) { + BlockEntity blockEntity = world.getBlockEntity(data.getPos()); + if (blockEntity instanceof MetaMachine machine) { + var t = machine.getTraitHolder().getTrait(traitType); + if (t != null) addProbeInfo(t, probeInfo, player, blockEntity, data); + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/syncsystem/ManagedSyncBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/syncsystem/ManagedSyncBlockEntity.java index fb696360635..04de631b808 100644 --- a/src/main/java/com/gregtechceu/gtceu/syncsystem/ManagedSyncBlockEntity.java +++ b/src/main/java/com/gregtechceu/gtceu/syncsystem/ManagedSyncBlockEntity.java @@ -14,6 +14,7 @@ import lombok.Getter; import lombok.Setter; +import org.jetbrains.annotations.MustBeInvokedByOverriders; import javax.annotation.ParametersAreNonnullByDefault; @@ -49,7 +50,8 @@ protected final void saveAdditional(CompoundTag tag) { } @Override - public final void load(CompoundTag tag) { + @MustBeInvokedByOverriders + public void load(CompoundTag tag) { super.load(tag); getSyncDataHolder().deserializeNBT(tag, (getLevel() == null ? GTCEu.isClientThread() : getLevel().isClientSide));