Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/main/java/li/cil/oc2/api/capabilities/BundledEmitter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: MIT */

package li.cil.oc2.api.capabilities;

/**
* This interface may be provided as a capability by item components to signal
* to the containing {@link net.minecraft.world.level.block.entity.BlockEntity} that they wish
* to emit redstone signals via a Project Red bundled cable. This is used by the built-in
* redstone interface card, for example.
*/
public interface BundledEmitter {
/**
* Returns the bundled output levels for the side this interface was returned for.
*
* @return the bundled output levels.
*/
byte[] getBundledOutput();
}
2 changes: 2 additions & 0 deletions src/main/java/li/cil/oc2/common/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public final class Constants {
public static final Direction.Axis[] AXES = Direction.Axis.values();
public static final int BLOCK_FACE_COUNT = DIRECTIONS.length;

public static final int BUNDLE_COLOR_COUNT = 16;

///////////////////////////////////////////////////////////////////

public static final String BLOCK_ENTITY_TAG_NAME_IN_ITEM = "BlockEntityTag";
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/li/cil/oc2/common/block/ComputerBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import li.cil.oc2.common.integration.Wrenches;
import li.cil.oc2.common.item.Items;
import li.cil.oc2.common.tags.ItemTags;
import li.cil.oc2.common.util.HorizontalBlockUtils;
import li.cil.oc2.common.util.TooltipUtils;
import li.cil.oc2.common.util.VoxelShapeUtils;
import net.minecraft.core.BlockPos;
Expand Down Expand Up @@ -107,8 +108,9 @@ public int getSignal(final BlockState state, final BlockGetter blockGetter, fina
var level = blockEntity.getLevel();
if (level != null) {
// Redstone requests info for faces with external perspective. Capabilities treat
// the Direction from internal perspective, so flip it.
var cap = level.getCapability(Capabilities.RedstoneEmitter.BLOCK, blockEntity.getBlockPos(), null, blockEntity, side.getOpposite());
// the Direction from an internal and local perspective, so flip it, and transform it from global to
// local.
var cap = level.getCapability(Capabilities.RedstoneEmitter.BLOCK, blockEntity.getBlockPos(), null, blockEntity, HorizontalBlockUtils.toLocal(state, side.getOpposite()));
return Optional.ofNullable(cap)
.map(RedstoneEmitter::getRedstoneOutput)
.orElse(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
import li.cil.oc2.api.bus.device.Device;
import li.cil.oc2.api.bus.device.DeviceTypes;
import li.cil.oc2.api.bus.device.provider.ItemDeviceQuery;
import li.cil.oc2.api.capabilities.BundledEmitter;
import li.cil.oc2.api.capabilities.RedstoneEmitter;
import li.cil.oc2.api.capabilities.TerminalUserProvider;
import li.cil.oc2.client.audio.LoopingSoundManager;
import li.cil.oc2.common.Constants;
import li.cil.oc2.common.block.Blocks;
import li.cil.oc2.common.components.DataComponents;
import li.cil.oc2.common.components.RestrictedContainer;
Expand Down Expand Up @@ -368,6 +371,18 @@ protected void unloadServer(final boolean isRemove) {
busElement.scheduleScan();
}

@Nullable
public byte[] getBundledSignal(Direction direction) {
if (level != null) {
var cap = level.getCapability(Capabilities.BundledEmitter.BLOCK, getBlockPos(), null, this, HorizontalBlockUtils.toLocal(getBlockState(), direction));
return Optional.ofNullable(cap)
.map(BundledEmitter::getBundledOutput)
.orElse(new byte[Constants.BUNDLE_COLOR_COUNT]);
}

return new byte[Constants.BUNDLE_COLOR_COUNT];
}

///////////////////////////////////////////////////////////////////

private void sendToClientsTrackingComputer(final CustomPacketPayload message) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public final class RedstoneInterfaceBlockEntity extends ModBlockEntity implement
///////////////////////////////////////////////////////////////////

private final byte[] output = new byte[Constants.BLOCK_FACE_COUNT];
private final byte[][] bundled_output = new byte[Constants.BLOCK_FACE_COUNT][16];
private final byte[][] bundled_output = new byte[Constants.BLOCK_FACE_COUNT][Constants.BUNDLE_COLOR_COUNT];

///////////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -146,10 +146,12 @@ public byte[] getBundledInput(@Parameter(SIDE) @Nullable final Side side) {

BundledRedstone bundledRedstone = BundledRedstone.getInstance();
if (bundledRedstone.isAvailable()) {
return bundledRedstone.getBundledInput(this.level, this.getBlockPos(), side.getDirection().getOpposite());
} else {
return new byte[Constants.BLOCK_FACE_COUNT];
final Direction direction = HorizontalBlockUtils.toGlobal(getBlockState(), side);
final byte[] input = bundledRedstone.getBundledInput(this.level, this.getBlockPos(), direction);
if (input != null) return input;
}

return new byte[Constants.BUNDLE_COLOR_COUNT];
}

@Callback(name = GET_BUNDLED_OUTPUT)
Expand All @@ -167,7 +169,7 @@ public void setBundledOutput(@Parameter(SIDE) @Nullable final Side side, @Parame
if (side == null) throw new IllegalArgumentException();

boolean changed = false;
final int index = side.getDirection().getOpposite().get3DDataValue();
final int index = side.getDirection().get3DDataValue();
final byte clampedValue = (byte) Mth.clamp(value, 0, 255);
final byte clampedColor = (byte) Mth.clamp(color, 0, 15);
/*for (int i=0; i < values.length; i++) {
Expand Down Expand Up @@ -199,7 +201,7 @@ public void setBundledOutputs(@Parameter(SIDE) @Nullable final Side side, @Param
if (side == null) throw new IllegalArgumentException();

boolean changed = false;
final int index = side.getDirection().getOpposite().get3DDataValue();
final int index = side.getDirection().get3DDataValue();
for (int i=0; i < values.length; i++) {
final byte clampedValue = (byte) Mth.clamp(values[i], 0, 255);
if (clampedValue != bundled_output[index][i]) {
Expand Down Expand Up @@ -285,7 +287,9 @@ private void notifyNeighbor(final Direction direction) {

@Nullable
public byte[] getBundledSignal(Direction direction) {
final int index = direction.get3DDataValue();
final Direction local_direction = HorizontalBlockUtils.toLocal(getBlockState(), direction);
assert local_direction != null;
final int index = local_direction.get3DDataValue();
return this.bundled_output[index];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
import li.cil.oc2.api.bus.device.object.Callback;
import li.cil.oc2.api.bus.device.object.DocumentedDevice;
import li.cil.oc2.api.bus.device.object.Parameter;
import li.cil.oc2.api.capabilities.BundledEmitter;
import li.cil.oc2.api.capabilities.RedstoneEmitter;
import li.cil.oc2.api.util.Side;
import li.cil.oc2.common.Constants;
import li.cil.oc2.common.block.Blocks;
import li.cil.oc2.common.blockentity.ComputerBlockEntity;
import li.cil.oc2.common.capabilities.Capabilities;
import li.cil.oc2.common.integration.util.BundledRedstone;
import li.cil.oc2.common.util.HorizontalBlockUtils;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
Expand All @@ -23,6 +25,7 @@
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.ModList;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;

Expand All @@ -32,29 +35,40 @@
@EventBusSubscriber(modid = API.MOD_ID)
public final class RedstoneInterfaceCardItemDevice extends AbstractItemRPCDevice implements DocumentedDevice {
private static final String OUTPUT_TAG_NAME = "output";
private static final String BUNDLED_TAG_NAME = "bundled";

private static final String GET_REDSTONE_INPUT = "getRedstoneInput";
private static final String GET_REDSTONE_OUTPUT = "getRedstoneOutput";
private static final String SET_REDSTONE_OUTPUT = "setRedstoneOutput";
private static final String GET_BUNDLED_INPUT = "getBundledInput";
private static final String GET_BUNDLED_OUTPUT = "getBundledOutput";
private static final String SET_BUNDLED_OUTPUT = "setBundledOutput";
private static final String SET_BUNDLED_OUTPUTS = "setBundledOutputs";
private static final String SIDE = "side";
private static final String VALUE = "value";
private static final String VALUES = "values";
private static final String COLOUR = "colour";

///////////////////////////////////////////////////////////////////

private final BlockEntity blockEntity;
private final RedstoneEmitter[] capabilities;
private final RedstoneEmitter[] re_capabilities;
private final BundledEmitter[] be_capabilities;
private final byte[] output = new byte[Constants.BLOCK_FACE_COUNT];
private final byte[][] bundled_output = new byte[Constants.BLOCK_FACE_COUNT][Constants.BUNDLE_COLOR_COUNT];

///////////////////////////////////////////////////////////////////

public RedstoneInterfaceCardItemDevice(final ItemStack identity, final BlockEntity blockEntity) {
super(identity, "redstone");
this.blockEntity = blockEntity;

capabilities = new RedstoneEmitter[Constants.BLOCK_FACE_COUNT];
re_capabilities = new RedstoneEmitter[Constants.BLOCK_FACE_COUNT];
be_capabilities = new BundledEmitter[Constants.BLOCK_FACE_COUNT];
for (int i = 0; i < Constants.BLOCK_FACE_COUNT; i++) {
final int indexForClosure = i;
capabilities[i] = () -> output[indexForClosure];
re_capabilities[i] = () -> output[indexForClosure];
be_capabilities[i] = () -> bundled_output[indexForClosure];
}
}

Expand All @@ -72,7 +86,24 @@ public static void registerCapabilities(RegisterCapabilitiesEvent event) {
if (self == null) return null;

final int index = side.get3DDataValue();
return self.capabilities[index];
return self.re_capabilities[index];
}
return null;
},
Blocks.COMPUTER.get()
);

event.registerBlock(
Capabilities.BundledEmitter.BLOCK,
(level, pos, state, be, side) -> {
if (side == null) return null;

if (be instanceof final ComputerBlockEntity computer) {
RedstoneInterfaceCardItemDevice self = computer.getFirstDevice(RedstoneInterfaceCardItemDevice.class);
if (self == null) return null;

final int index = side.get3DDataValue();
return self.be_capabilities[index];
}
return null;
},
Expand All @@ -84,13 +115,26 @@ public static void registerCapabilities(RegisterCapabilitiesEvent event) {
public CompoundTag serializeNBT(HolderLookup.Provider provider) {
final CompoundTag tag = new CompoundTag();
tag.putByteArray(OUTPUT_TAG_NAME, output);
CompoundTag tag_bundled_output = new CompoundTag();
for (Direction dir : Direction.values()) {
tag_bundled_output.putByteArray(dir.getName(), bundled_output[dir.get3DDataValue()]);
}
tag.put(BUNDLED_TAG_NAME, tag_bundled_output);

return tag;
}

@Override
public void deserializeNBT(HolderLookup.Provider provider, final CompoundTag tag) {
final byte[] serializedOutput = tag.getByteArray(OUTPUT_TAG_NAME);
System.arraycopy(serializedOutput, 0, output, 0, Math.min(serializedOutput.length, output.length));

final CompoundTag tag_bundled_output = tag.getCompound(BUNDLED_TAG_NAME);
for (Direction dir : Direction.values()) {
final byte[] serializedBundledOutput = tag_bundled_output.getByteArray(dir.getName());
byte[] dest_output = bundled_output[dir.get3DDataValue()];
System.arraycopy(serializedBundledOutput, 0, dest_output, 0, Math.min(serializedBundledOutput.length, dest_output.length));
}
}

@Callback(name = GET_REDSTONE_INPUT)
Expand Down Expand Up @@ -141,6 +185,87 @@ public void setRedstoneOutput(@Parameter(SIDE) @Nullable final Side side, @Param
}
}

@Nullable
@Callback(name = GET_BUNDLED_INPUT)
public byte[] getBundledInput(@Parameter(SIDE) @Nullable final Side side) {
if(!ModList.get().isLoaded("projectred_transmission")) throw new IllegalStateException();
if (side == null) throw new IllegalArgumentException();

final Level level = blockEntity.getLevel();
if (level != null) {
BundledRedstone bundledRedstone = BundledRedstone.getInstance();
if (bundledRedstone.isAvailable()) {
final Direction direction = HorizontalBlockUtils.toGlobal(blockEntity.getBlockState(), side);
final byte[] input = bundledRedstone.getBundledInput(level, blockEntity.getBlockPos(), direction);
if (input != null) return input;
}
}

return new byte[Constants.BUNDLE_COLOR_COUNT];
}

@Callback(name = GET_BUNDLED_OUTPUT)
public byte[] getBundledOutput(@Parameter(SIDE) @Nullable final Side side) {
if(!ModList.get().isLoaded("projectred_transmission")) throw new IllegalStateException();
if (side == null) throw new IllegalArgumentException();

final int index = side.getDirection().get3DDataValue();
return bundled_output[index];
}

@Callback(name = SET_BUNDLED_OUTPUT)
public void setBundledOutput(@Parameter(SIDE) @Nullable final Side side, @Parameter(VALUE) final int value, @Parameter(COLOUR) final int color) {
if(!ModList.get().isLoaded("projectred_transmission")) throw new IllegalStateException();
if (side == null) throw new IllegalArgumentException();

boolean changed = false;
final int index = side.getDirection().get3DDataValue();
final byte clampedValue = (byte) Mth.clamp(value, 0, 255);
final byte clampedColor = (byte) Mth.clamp(color, 0, 15);
/*for (int i=0; i < values.length; i++) {
final byte clampedValue = (byte) Mth.clamp(values[i], 0, 255);
if (clampedValue != bundled_output[index][i]) {
bundled_output[index][i] = clampedValue;
changed = true;
}
}*/

if (bundled_output[index][clampedColor] != clampedValue) {
changed = true;
bundled_output[index][clampedColor] = clampedValue;
}

if (changed) {
final Direction direction = HorizontalBlockUtils.toGlobal(blockEntity.getBlockState(), side);
if (direction != null) {
notifyNeighbor(direction);
}
}
}

@Callback(name = SET_BUNDLED_OUTPUTS)
public void setBundledOutputs(@Parameter(SIDE) @Nullable final Side side, @Parameter(VALUES) final int[] values) {
if(!ModList.get().isLoaded("projectred_transmission")) throw new IllegalStateException();
if (side == null) throw new IllegalArgumentException();

boolean changed = false;
final int index = side.getDirection().get3DDataValue();
for (int i=0; i < values.length; i++) {
final byte clampedValue = (byte) Mth.clamp(values[i], 0, 255);
if (clampedValue != bundled_output[index][i]) {
bundled_output[index][i] = clampedValue;
changed = true;
}
}

if (changed) {
final Direction direction = HorizontalBlockUtils.toGlobal(blockEntity.getBlockState(), side);
if (direction != null) {
notifyNeighbor(direction);
}
}
}

@Override
public void getDeviceDocumentation(final DocumentedDevice.DeviceVisitor visitor) {
visitor.visitCallback(GET_REDSTONE_INPUT)
Expand All @@ -165,6 +290,29 @@ public void getDeviceDocumentation(final DocumentedDevice.DeviceVisitor visitor)
"the side depends on the orientation of the device's container.")
.parameterDescription(SIDE, "the side to write the output level to.")
.parameterDescription(VALUE, "the output level to set, will be clamped to [0, 15].");

if(ModList.get().isLoaded("projectred_transmission"))
{
visitor.visitCallback(GET_BUNDLED_INPUT)
.description("Get the current bundled level received on the specified side.")
.parameterDescription(SIDE, "the side to read the bundled input level from");
visitor.visitCallback(GET_BUNDLED_OUTPUT)
.description("Get the current bundled level sent out on the specified side.")
.parameterDescription(SIDE, "the side to read the bundled output level from");
visitor.visitCallback(SET_BUNDLED_OUTPUT)
.description("Set the new bundled level transmitted for a specific color on the specified side.\n" +
"Sides may be specified by name or zero-based index. Please note that " +
"the side depends on the orientation of the device.")
.parameterDescription(SIDE, "the side to write the output level to.")
.parameterDescription(VALUE, "the output level to set, will be clamped to [0, 255].")
.parameterDescription(COLOUR, "the colour wire this sets, as int [0, 15]");
visitor.visitCallback(SET_BUNDLED_OUTPUTS)
.description("Set the new bundled levels transmitted on the specified side.\n" +
"Sides may be specified by name or zero-based index. Please note that " +
"the side depends on the orientation of the device.")
.parameterDescription(SIDE, "the side to write the output level to.")
.parameterDescription(VALUES, "the output levels to set in array form, each value will be clamped to [0, 255], 16 entries.");
}
}

///////////////////////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ public static final class Device {
public static final class RedstoneEmitter {
public static final BlockCapability<li.cil.oc2.api.capabilities.RedstoneEmitter, @Nullable Direction> BLOCK = BlockCapability.createSided(ResourceLocation.fromNamespaceAndPath(API.MOD_ID, "redstone_emitter"), li.cil.oc2.api.capabilities.RedstoneEmitter.class);
}
public static final class BundledEmitter {
public static final BlockCapability<li.cil.oc2.api.capabilities.BundledEmitter, @Nullable Direction> BLOCK = BlockCapability.createSided(ResourceLocation.fromNamespaceAndPath(API.MOD_ID, "bundled_emitter"), li.cil.oc2.api.capabilities.BundledEmitter.class);
}
public static final class NetworkInterface {
public static final BlockCapability<li.cil.oc2.api.capabilities.NetworkInterface, @Nullable Direction> BLOCK = BlockCapability.createSided(ResourceLocation.fromNamespaceAndPath(API.MOD_ID, "network_interface"), li.cil.oc2.api.capabilities.NetworkInterface.class);
}
Expand Down
Loading