Skip to content

Commit

Permalink
Make the growth accelerator accept FE directly (#7734)
Browse files Browse the repository at this point in the history
Also fix some sync issues related to powering/unpowering the
accelerator.

Fixes #7694
  • Loading branch information
shartte authored Mar 9, 2024
1 parent f324b67 commit 0685fb2
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 96 deletions.
5 changes: 4 additions & 1 deletion guidebook/items-blocks-machines/growth_accelerator.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ Curiously, it can *also* accelerate the growth of various plants.
<IsometricCamera yaw="195" pitch="30" />
</GameScene>

Power can be provided via the top or bottom, via either AE2's [cables](cables.md), or other mod power cables. It can
accept either AE2's power (AE) or Forge Energy (FE).

To power it manually, place a <ItemLink id="crank" /> on the top or bottom and right-click it.

It only connects to cables on its ends where the pink fluix greebles are.
The top and the bottom can be identified by the pink flux greebles on them.

<GameScene zoom="6" background="transparent">
<ImportStructure src="../assets/assemblies/accelerator_connections.snbt" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;

import appeng.api.config.Actionable;
import appeng.api.config.PowerUnits;
import appeng.api.implementations.blockentities.ICrankable;
import appeng.api.networking.GridHelper;
import appeng.api.networking.IManagedGridNode;
import appeng.api.networking.energy.IAEPowerStorage;
import appeng.api.orientation.BlockOrientation;
import appeng.api.util.AECableType;
import appeng.blockentity.misc.CrankBlockEntity;
import appeng.blockentity.powersink.AEBasePoweredBlockEntity;
import appeng.me.helpers.BlockEntityNodeListener;
import appeng.me.helpers.IGridConnectedBlockEntity;
Expand Down Expand Up @@ -109,4 +113,16 @@ protected void onOrientationChanged(BlockOrientation orientation) {
protected final void onGridConnectableSidesChanged() {
getMainNode().setExposedOnSides(getGridConnectableSides(getOrientation()));
}

public final class Crankable implements ICrankable {
@Override
public boolean canTurn() {
return getInternalCurrentPower() < getInternalMaxPower();
}

@Override
public void applyTurn() {
injectExternalPower(PowerUnits.AE, CrankBlockEntity.POWER_PER_CRANK_TURN, Actionable.MODULATE);
}
}
}
12 changes: 0 additions & 12 deletions src/main/java/appeng/blockentity/misc/ChargerBlockEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -274,16 +274,4 @@ public boolean allowExtract(InternalInventory inv, int slotIndex, int amount) {
return ChargerRecipes.allowExtract(chargerBlockEntity.level, extractedItem);
}
}

class Crankable implements ICrankable {
@Override
public boolean canTurn() {
return getInternalCurrentPower() < getInternalMaxPower();
}

@Override
public void applyTurn() {
injectExternalPower(PowerUnits.AE, CrankBlockEntity.POWER_PER_CRANK_TURN, Actionable.MODULATE);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;

import appeng.api.config.Actionable;
import appeng.api.ids.AETags;
import appeng.api.implementations.IPowerChannelState;
import appeng.api.implementations.blockentities.ICrankable;
import appeng.api.inventories.InternalInventory;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IGridNodeListener;
import appeng.api.networking.ticking.IGridTickable;
Expand All @@ -39,22 +40,21 @@
import appeng.api.orientation.BlockOrientation;
import appeng.api.orientation.RelativeSide;
import appeng.api.util.AECableType;
import appeng.blockentity.grid.AENetworkBlockEntity;
import appeng.block.misc.GrowthAcceleratorBlock;
import appeng.blockentity.grid.AENetworkPowerBlockEntity;
import appeng.core.AEConfig;

public class GrowthAcceleratorBlockEntity extends AENetworkBlockEntity implements IPowerChannelState {
public class GrowthAcceleratorBlockEntity extends AENetworkPowerBlockEntity implements IPowerChannelState {

// Allow storage of up to 10 cranks
public static final int MAX_STORED_POWER = 10 * CrankBlockEntity.POWER_PER_CRANK_TURN;
// AE per tick
private static final int POWER_PER_TICK = 8;

private boolean hasPower = false;

// For cranking!
private float storedPower;

public GrowthAcceleratorBlockEntity(BlockEntityType<?> blockEntityType, BlockPos pos, BlockState blockState) {
super(blockEntityType, pos, blockState);
setInternalMaxPower(MAX_STORED_POWER);
setPowerSides(getGridConnectableSides(getOrientation()));
getMainNode().setFlags();
getMainNode().setIdlePowerUsage(POWER_PER_TICK);
getMainNode().addService(IGridTickable.class, new IGridTickable() {
Expand All @@ -72,23 +72,35 @@ public TickRateModulation tickingRequest(IGridNode node, int ticksSinceLastCall)
});
}

@Override
public InternalInventory getInternalInventory() {
return InternalInventory.empty();
}

@Override
public Set<Direction> getGridConnectableSides(BlockOrientation orientation) {
return orientation.getSides(EnumSet.of(RelativeSide.FRONT, RelativeSide.BACK));
}

@Override
protected void onOrientationChanged(BlockOrientation orientation) {
super.onOrientationChanged(orientation);
setPowerSides(getGridConnectableSides(getOrientation()));
}

private void onTick(int ticksSinceLastCall) {
// We drain local power in *addition* to network power, which is handled via idle power consumption
if (storedPower > 0) {
storedPower -= POWER_PER_TICK * Math.max(1, ticksSinceLastCall);
if (storedPower <= 0) {
storedPower = 0;
markForUpdate();
}
} else if (!getMainNode().isPowered()) {
var powered = isPowered();
if (powered != getBlockState().getValue(GrowthAcceleratorBlock.POWERED)) {
markForUpdate();
}

if (!powered) {
return;
}

// We drain local power in *addition* to network power, which is handled via idle power consumption
extractAEPower(POWER_PER_TICK * ticksSinceLastCall, Actionable.MODULATE);

for (var direction : Direction.values()) {
var adjPos = getBlockPos().relative(direction);
var adjState = getLevel().getBlockState(adjPos);
Expand All @@ -113,70 +125,25 @@ public AECableType getCableConnectionType(Direction dir) {
return AECableType.COVERED;
}

@Override
public boolean readFromStream(FriendlyByteBuf data) {
final boolean c = super.readFromStream(data);
final boolean hadPower = this.isPowered();
this.setPowered(data.readBoolean());
return this.isPowered() != hadPower || c;
}

@Override
public void writeToStream(FriendlyByteBuf data) {
super.writeToStream(data);
data.writeBoolean(getMainNode().isPowered());
}

@Override
public boolean isPowered() {
if (!isClientSide()) {
return getMainNode().isPowered() || storedPower > 0;
return getMainNode().isPowered() || extractAEPower(POWER_PER_TICK, Actionable.SIMULATE) >= POWER_PER_TICK;
}

return this.hasPower;
return this.getBlockState().getValue(GrowthAcceleratorBlock.POWERED);
}

@Override
public boolean isActive() {
return this.isPowered();
}

private void setPowered(boolean hasPower) {
this.hasPower = hasPower;
}

/**
* Allow cranking from the top or bottom.
*/
@org.jetbrains.annotations.Nullable
public ICrankable getCrankable(Direction direction) {
if (direction == getFront() || direction == getFront().getOpposite()) {
if (getPowerSides().contains(direction)) {
return new Crankable();
}
return null;
}

class Crankable implements ICrankable {
@Override
public boolean canTurn() {
return storedPower < MAX_STORED_POWER;
}

@Override
public void applyTurn() {
if (isClientSide()) {
return; // Only apply crank-turns server-side
}

// Cranking will always add enough power for at least one tick,
// so we should send a transition from unpowered to powered to clients.
boolean needsUpdate = !isPowered();

storedPower = Math.min(MAX_STORED_POWER, storedPower + CrankBlockEntity.POWER_PER_CRANK_TURN);

if (needsUpdate) {
markForUpdate();
}
}
}
}
13 changes: 0 additions & 13 deletions src/main/java/appeng/blockentity/misc/InscriberBlockEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@

import appeng.api.config.Actionable;
import appeng.api.config.PowerMultiplier;
import appeng.api.config.PowerUnits;
import appeng.api.config.Setting;
import appeng.api.config.Settings;
import appeng.api.config.YesNo;
Expand Down Expand Up @@ -609,16 +608,4 @@ public boolean allowInsert(InternalInventory inv, int slot, ItemStack stack) {
return !isSmash();
}
}

class Crankable implements ICrankable {
@Override
public boolean canTurn() {
return getInternalCurrentPower() < getInternalMaxPower();
}

@Override
public void applyTurn() {
injectExternalPower(PowerUnits.AE, CrankBlockEntity.POWER_PER_CRANK_TURN, Actionable.MODULATE);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.entity.BlockEntity;

import appeng.api.implementations.IPowerChannelState;
import appeng.api.integrations.igtooltip.TooltipBuilder;
import appeng.api.integrations.igtooltip.TooltipContext;
import appeng.api.integrations.igtooltip.providers.BodyProvider;
Expand All @@ -30,6 +31,13 @@ public void buildTooltip(BlockEntity object, TooltipContext context, TooltipBuil

@Override
public void provideServerData(Player player, BlockEntity object, CompoundTag serverData) {
// Some devices can be powered both externally and through the grid.
// If they are powered externally, they might still be active when the grid itself is down
if (object instanceof IPowerChannelState powerChannelState && powerChannelState.isActive()) {
serverData.putByte(TAG_STATE, (byte) GridNodeState.ONLINE.ordinal());
return;
}

if (object instanceof IGridConnectedBlockEntity gridConnectedBlockEntity) {
var state = GridNodeState.fromNode(gridConnectedBlockEntity.getActionableNode());
serverData.putByte(TAG_STATE, (byte) state.ordinal());
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/appeng/me/energy/StoredEnergyAmount.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public double insert(double amount, boolean commit) {
}

/**
* Increase the amount, and return what would have exceeded the maximum.
* Decrease the amount, and return how much was actually extracted.
*/
public double extract(double amount, boolean commit) {
if (amount < MIN_AMOUNT) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import net.minecraft.world.level.block.state.properties.BlockStateProperties;

import appeng.api.stacks.AEItemKey;
import appeng.block.misc.GrowthAcceleratorBlock;
import appeng.blockentity.misc.GrowthAcceleratorBlockEntity;
import appeng.blockentity.storage.ChestBlockEntity;
import appeng.core.definitions.AEBlocks;
import appeng.core.definitions.AEItems;
Expand Down Expand Up @@ -99,11 +101,11 @@ public static void testGrowthAccelerator(PlotBuilder plot) {
.setValue(BlockStateProperties.FACING, Direction.UP));
plot.test(helper -> helper.startSequence()
.thenWaitUntil(helper::checkAllInitialized)
// NYI
// .thenWaitUntil(() -> {
// var accel = (GrowthAcceleratorBlockEntity) helper.getBlockEntity(ORIGIN);
// helper.check(accel.isPowered(), "should be powered", ORIGIN);
// })
.thenWaitUntil(() -> {
var accel = (GrowthAcceleratorBlockEntity) helper.getBlockEntity(ORIGIN);
helper.check(accel.isPowered(), "should be powered", ORIGIN);
})
.thenWaitUntil(() -> helper.assertBlockProperty(ORIGIN, GrowthAcceleratorBlock.POWERED, true))
// Ensure that after 1 second, the grid still has no energy
.thenExecuteAfter(20, () -> checkGridHasNoEnergy(helper))
.thenSucceed());
Expand Down

0 comments on commit 0685fb2

Please sign in to comment.