Add bus cable block.

This commit is contained in:
Florian Nücke
2020-11-30 18:35:21 +01:00
parent f13042d3d2
commit 656822bbb6
12 changed files with 493 additions and 5 deletions

View File

@@ -2,6 +2,7 @@ package li.cil.oc2;
public final class Constants {
public static final String COMPUTER_BLOCK_NAME = "computer";
public static final String BUS_CABLE_BLOCK_NAME = "bus_cable";
public static final String REDSTONE_INTERFACE_BLOCK_NAME = "redstone_interface";
public static final String SCREEN_BLOCK_NAME = "screen";
}

View File

@@ -4,10 +4,12 @@ import li.cil.ceres.Ceres;
import li.cil.oc2.api.API;
import li.cil.oc2.client.ClientSetup;
import li.cil.oc2.common.CommonSetup;
import li.cil.oc2.common.block.BusCableBlock;
import li.cil.oc2.common.block.ComputerBlock;
import li.cil.oc2.common.block.RedstoneInterfaceBlock;
import li.cil.oc2.common.block.ScreenBlock;
import li.cil.oc2.common.container.ComputerContainer;
import li.cil.oc2.common.tile.BusCableTileEntity;
import li.cil.oc2.common.tile.ComputerTileEntity;
import li.cil.sedna.devicetree.DeviceTreeRegistry;
import net.minecraft.block.Block;
@@ -37,16 +39,19 @@ public final class OpenComputers {
public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, API.MOD_ID);
public static final RegistryObject<Block> COMPUTER_BLOCK = BLOCKS.register(Constants.COMPUTER_BLOCK_NAME, ComputerBlock::new);
public static final RegistryObject<Block> BUS_CABLE_BLOCK = BLOCKS.register(Constants.BUS_CABLE_BLOCK_NAME, BusCableBlock::new);
public static final RegistryObject<Block> REDSTONE_INTERFACE_BLOCK = BLOCKS.register(Constants.REDSTONE_INTERFACE_BLOCK_NAME, RedstoneInterfaceBlock::new);
public static final RegistryObject<Block> SCREEN_BLOCK = BLOCKS.register(Constants.SCREEN_BLOCK_NAME, ScreenBlock::new);
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, API.MOD_ID);
public static final RegistryObject<Item> COMPUTER_ITEM = ITEMS.register(Constants.COMPUTER_BLOCK_NAME, () -> new BlockItem(COMPUTER_BLOCK.get(), new Item.Properties().group(ITEM_GROUP)));
public static final RegistryObject<Item> BUS_CABLE_ITEM = ITEMS.register(Constants.BUS_CABLE_BLOCK_NAME, () -> new BlockItem(BUS_CABLE_BLOCK.get(), new Item.Properties().group(ITEM_GROUP)));
public static final RegistryObject<Item> REDSTONE_INTERFACE_ITEM = ITEMS.register(Constants.REDSTONE_INTERFACE_BLOCK_NAME, () -> new BlockItem(REDSTONE_INTERFACE_BLOCK.get(), new Item.Properties().group(ITEM_GROUP)));
public static final RegistryObject<Item> SCREEN_ITEM = ITEMS.register(Constants.SCREEN_BLOCK_NAME, () -> new BlockItem(SCREEN_BLOCK.get(), new Item.Properties().group(ITEM_GROUP)));
public static final DeferredRegister<TileEntityType<?>> TILES = DeferredRegister.create(ForgeRegistries.TILE_ENTITIES, API.MOD_ID);
public static final RegistryObject<TileEntityType<ComputerTileEntity>> COMPUTER_TILE_ENTITY = TILES.register(Constants.COMPUTER_BLOCK_NAME, () -> TileEntityType.Builder.create(ComputerTileEntity::new, COMPUTER_BLOCK.get()).build(null));
public static final RegistryObject<TileEntityType<BusCableTileEntity>> BUS_CABLE_TILE_ENTITY = TILES.register(Constants.BUS_CABLE_BLOCK_NAME, () -> TileEntityType.Builder.create(BusCableTileEntity::new, BUS_CABLE_BLOCK.get()).build(null));
public static final DeferredRegister<ContainerType<?>> CONTAINERS = DeferredRegister.create(ForgeRegistries.CONTAINERS, API.MOD_ID);
public static final RegistryObject<ContainerType<ComputerContainer>> COMPUTER_CONTAINER = CONTAINERS.register(Constants.COMPUTER_BLOCK_NAME, () -> IForgeContainerType.create((id, inventory, data) -> {

View File

@@ -1,6 +1,8 @@
package li.cil.oc2.common;
import li.cil.oc2.common.capabilities.DeviceBusElementCapability;
import li.cil.oc2.common.device.DeviceMethodParameterTypeAdapters;
import li.cil.oc2.common.device.Providers;
import li.cil.oc2.common.network.Network;
import li.cil.oc2.common.vm.Allocator;
import li.cil.oc2.serialization.BlobStorage;

View File

@@ -0,0 +1,41 @@
package li.cil.oc2.common.block;
import li.cil.oc2.OpenComputers;
import li.cil.oc2.common.tile.BusCableTileEntity;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import javax.annotation.Nullable;
public class BusCableBlock extends Block {
public BusCableBlock() {
super(Properties.create(Material.IRON).sound(SoundType.METAL));
}
@Override
public boolean hasTileEntity(final BlockState state) {
return true;
}
@Nullable
@Override
public TileEntity createTileEntity(final BlockState state, final IBlockReader world) {
return OpenComputers.BUS_CABLE_TILE_ENTITY.get().create();
}
@SuppressWarnings("deprecation")
@Override
public void neighborChanged(final BlockState state, final World world, final BlockPos pos, final Block changedBlock, final BlockPos changedBlockPos, final boolean isMoving) {
final TileEntity tileEntity = world.getTileEntity(pos);
if (tileEntity instanceof BusCableTileEntity) {
final BusCableTileEntity busCable = (BusCableTileEntity) tileEntity;
busCable.handleNeighborChanged(changedBlockPos);
}
}
}

View File

@@ -0,0 +1,161 @@
package li.cil.oc2.common.bus;
import li.cil.oc2.api.bus.DeviceBus;
import li.cil.oc2.api.bus.DeviceBusElement;
import li.cil.oc2.common.ServerScheduler;
import li.cil.oc2.common.device.CompoundDevice;
import li.cil.oc2.common.device.IdentifiableDeviceImpl;
import li.cil.oc2.common.device.Providers;
import li.cil.oc2.common.util.NBTTagIds;
import li.cil.oc2.common.util.TileEntityUtils;
import li.cil.oc2.common.util.WorldUtils;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.util.INBTSerializable;
import net.minecraftforge.common.util.LazyOptional;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
public final class TileEntityDeviceBusElement implements INBTSerializable<CompoundNBT> {
private static final String DEVICE_IDS_NBT_TAG_NAME = "deviceIds";
private static final String DEVICE_ID_NBT_TAG_NAME = "deviceId";
private static final int NEIGHBOR_COUNT = 6;
private final TileEntity tileEntity;
private final DeviceBusElementImpl busElement = new DeviceBusElementImpl();
private final UUID[] deviceIds = new UUID[NEIGHBOR_COUNT];
private final IdentifiableDeviceImpl[] devices = new IdentifiableDeviceImpl[NEIGHBOR_COUNT];
public TileEntityDeviceBusElement(final TileEntity tileEntity) {
this.tileEntity = tileEntity;
for (int i = 0; i < NEIGHBOR_COUNT; i++) {
deviceIds[i] = UUID.randomUUID();
}
}
public DeviceBusElementImpl getBusElement() {
return busElement;
}
public void handleNeighborChanged(final BlockPos pos) {
final World world = tileEntity.getWorld();
if (world == null || world.isRemote()) {
return;
}
final BlockPos toPos = pos.subtract(tileEntity.getPos());
final Direction direction = Direction.byLong(toPos.getX(), toPos.getY(), toPos.getZ());
if (direction == null) {
return;
}
final int index = direction.getIndex();
final LazyOptional<CompoundDevice> device = Providers.getDevice(world, pos, direction);
final IdentifiableDeviceImpl identifiableDevice;
if (device.isPresent()) {
identifiableDevice = new IdentifiableDeviceImpl(device, deviceIds[index]);
device.addListener((ignored) -> handleNeighborChanged(pos));
} else {
identifiableDevice = null;
}
if (Objects.equals(devices[index], identifiableDevice)) {
return;
}
if (devices[index] != null) {
busElement.removeDevice(devices[index]);
}
devices[index] = identifiableDevice;
if (devices[index] != null) {
busElement.addDevice(devices[index]);
}
}
public void initialize() {
final World world = tileEntity.getWorld();
if (world == null || world.isRemote()) {
return;
}
ServerScheduler.schedule(world, () -> {
if (tileEntity.isRemoved()) return;
scanNeighborsForBusElements(tileEntity.getWorld());
scanNeighborsForDevices();
});
}
public void dispose() {
busElement.scheduleScan();
}
@Override
public CompoundNBT serializeNBT() {
final ListNBT deviceIdsNbt = new ListNBT();
for (int i = 0; i < NEIGHBOR_COUNT; i++) {
final CompoundNBT deviceIdNbt = new CompoundNBT();
deviceIdNbt.putUniqueId(DEVICE_ID_NBT_TAG_NAME, deviceIds[i]);
deviceIdsNbt.add(deviceIdNbt);
}
final CompoundNBT compound = new CompoundNBT();
compound.put(DEVICE_IDS_NBT_TAG_NAME, deviceIdsNbt);
return compound;
}
@Override
public void deserializeNBT(final CompoundNBT compound) {
final ListNBT deviceIdsNbt = compound.getList(DEVICE_IDS_NBT_TAG_NAME, NBTTagIds.TAG_COMPOUND);
for (int i = 0; i < Math.min(deviceIdsNbt.size(), NEIGHBOR_COUNT); i++) {
final CompoundNBT deviceIdNbt = deviceIdsNbt.getCompound(i);
if (deviceIdNbt.hasUniqueId(DEVICE_ID_NBT_TAG_NAME)) {
deviceIds[i] = deviceIdNbt.getUniqueId(DEVICE_ID_NBT_TAG_NAME);
}
}
}
public CompoundNBT write(final CompoundNBT compound) {
final ListNBT deviceIdsNbt = new ListNBT();
for (int i = 0; i < NEIGHBOR_COUNT; i++) {
final CompoundNBT deviceIdNbt = new CompoundNBT();
deviceIdNbt.putUniqueId(DEVICE_ID_NBT_TAG_NAME, deviceIds[i]);
deviceIdsNbt.add(deviceIdNbt);
}
compound.put(DEVICE_IDS_NBT_TAG_NAME, deviceIdsNbt);
return compound;
}
private void scanNeighborsForDevices() {
for (final Direction direction : Direction.values()) {
handleNeighborChanged(tileEntity.getPos().offset(direction));
}
}
private void scanNeighborsForBusElements(final World world) {
final BlockPos pos = tileEntity.getPos();
for (final Direction direction : Direction.values()) {
final BlockPos neighborPos = pos.offset(direction);
final TileEntity tileEntity = WorldUtils.getTileEntityIfChunkExists(world, neighborPos);
if (tileEntity == null) {
continue;
}
final Optional<DeviceBusElement> capability = TileEntityUtils.getInterfaceForSide(tileEntity, DeviceBusElement.class, direction.getOpposite());
capability.ifPresent(DeviceBus::scheduleScan);
}
}
}

View File

@@ -0,0 +1,36 @@
package li.cil.oc2.common.device;
import li.cil.oc2.api.device.provider.BlockDeviceQuery;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nullable;
public class BlockDeviceQueryImpl implements BlockDeviceQuery {
private final World world;
private final BlockPos pos;
@Nullable private final Direction side;
public BlockDeviceQueryImpl(final World world, final BlockPos pos, @Nullable final Direction side) {
this.world = world;
this.pos = pos;
this.side = side;
}
@Override
public World getWorld() {
return world;
}
@Override
public BlockPos getQueryPosition() {
return pos;
}
@Nullable
@Override
public Direction getQuerySide() {
return side;
}
}

View File

@@ -0,0 +1,29 @@
package li.cil.oc2.common.device;
import com.google.gson.GsonBuilder;
import li.cil.oc2.api.imc.DeviceMethodParameterTypeAdapter;
import java.util.ArrayList;
public final class DeviceMethodParameterTypeAdapters {
private static final ArrayList<DeviceMethodParameterTypeAdapter> TYPE_ADAPTERS = new ArrayList<>();
public static void addTypeAdapter(final Class<?> type, final Object typeAdapter) {
addTypeAdapter(new DeviceMethodParameterTypeAdapter(type, typeAdapter));
}
public static void addTypeAdapter(final DeviceMethodParameterTypeAdapter value) {
TYPE_ADAPTERS.add(value);
}
public static GsonBuilder beginBuildGson() {
final GsonBuilder builder = new GsonBuilder()
.serializeNulls();
for (final DeviceMethodParameterTypeAdapter value : TYPE_ADAPTERS) {
builder.registerTypeAdapter(value.type, value.typeAdapter);
}
return builder;
}
}

View File

@@ -3,16 +3,23 @@ package li.cil.oc2.common.device;
import li.cil.oc2.api.device.Device;
import li.cil.oc2.api.device.DeviceMethod;
import li.cil.oc2.api.device.IdentifiableDevice;
import li.cil.oc2.common.util.LazyOptionalUtils;
import net.minecraftforge.common.util.LazyOptional;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
public final class IdentifiableDeviceImpl implements IdentifiableDevice {
private final Device device;
private final LazyOptional<? extends Device> device;
private final UUID uuid;
public IdentifiableDeviceImpl(final Device device, final UUID uuid) {
this(LazyOptional.of(() -> device), uuid);
}
public IdentifiableDeviceImpl(final LazyOptional<? extends Device> device, final UUID uuid) {
this.device = device;
this.uuid = uuid;
}
@@ -24,12 +31,12 @@ public final class IdentifiableDeviceImpl implements IdentifiableDevice {
@Override
public List<String> getTypeNames() {
return device.getTypeNames();
return device.map(Device::getTypeNames).orElse(Collections.emptyList());
}
@Override
public List<DeviceMethod> getMethods() {
return device.getMethods();
return device.map(Device::getMethods).orElse(Collections.emptyList());
}
@Override
@@ -38,11 +45,11 @@ public final class IdentifiableDeviceImpl implements IdentifiableDevice {
if (o == null || getClass() != o.getClass()) return false;
final IdentifiableDeviceImpl that = (IdentifiableDeviceImpl) o;
return uuid.equals(that.uuid) &&
device.equals(that.device);
LazyOptionalUtils.equals(device, that.device);
}
@Override
public int hashCode() {
return Objects.hash(uuid, device);
return Objects.hash(uuid, LazyOptionalUtils.hashCode(device));
}
}

View File

@@ -0,0 +1,66 @@
package li.cil.oc2.common.device;
import li.cil.oc2.api.device.Device;
import li.cil.oc2.api.device.provider.DeviceProvider;
import li.cil.oc2.api.device.provider.DeviceQuery;
import li.cil.oc2.common.device.provider.EnergyStorageDeviceProvider;
import li.cil.oc2.common.device.provider.FluidHandlerDeviceProvider;
import li.cil.oc2.common.device.provider.ItemHandlerDeviceProvider;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional;
import java.util.ArrayList;
public final class Providers {
private static final ArrayList<DeviceProvider> DEVICE_PROVIDERS = new ArrayList<>();
public static void initialize() {
addProvider(new EnergyStorageDeviceProvider());
addProvider(new FluidHandlerDeviceProvider());
addProvider(new ItemHandlerDeviceProvider());
}
public static void addProvider(final DeviceProvider provider) {
if (!DEVICE_PROVIDERS.contains(provider)) {
DEVICE_PROVIDERS.add(provider);
}
}
public static LazyOptional<CompoundDevice> getDevice(final TileEntity tileEntity, final Direction side) {
final World world = tileEntity.getWorld();
final BlockPos pos = tileEntity.getPos();
if (world == null) throw new IllegalArgumentException();
return getDevice(world, pos, side);
}
public static LazyOptional<CompoundDevice> getDevice(final World world, final BlockPos pos, final Direction side) {
return getDevice(new BlockDeviceQueryImpl(world, pos, side));
}
public static LazyOptional<CompoundDevice> getDevice(final DeviceQuery query) {
final ArrayList<Device> devices = new ArrayList<>();
final ArrayList<LazyOptional<Device>> optionals = new ArrayList<>();
for (final DeviceProvider provider : DEVICE_PROVIDERS) {
final LazyOptional<Device> optional = provider.getDevice(query);
optional.ifPresent((device) -> {
devices.add(device);
optionals.add(optional);
});
}
if (devices.isEmpty()) {
return LazyOptional.empty();
} else {
final LazyOptional<CompoundDevice> compoundOptional = LazyOptional.of(() -> new CompoundDevice(devices));
for (final LazyOptional<Device> optional : optionals) {
optional.addListener((ignored) -> compoundOptional.invalidate());
}
return compoundOptional;
}
}
}

View File

@@ -0,0 +1,66 @@
package li.cil.oc2.common.tile;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.util.HashMap;
public abstract class AbstractTileEntity extends TileEntity {
protected final HashMap<Capability<?>, LazyOptional<?>> capabilities = new HashMap<>();
protected AbstractTileEntity(final TileEntityType<?> tileEntityType) {
super(tileEntityType);
}
protected <T> void addCapability(final Capability<T> capability, final T value) {
capabilities.put(capability, LazyOptional.of(() -> value));
}
protected void initialize() {
}
protected void dispose() {
}
@NotNull
@Override
public <T> LazyOptional<T> getCapability(@NotNull final Capability<T> capability, @Nullable final Direction side) {
final LazyOptional<?> optional = capabilities.get(capability);
if (optional != null) {
return optional.cast();
} else {
return super.getCapability(capability, side);
}
}
@Override
public void onLoad() {
super.onLoad();
initialize();
}
@Override
public void remove() {
super.remove();
dispose();
}
@Override
public void onChunkUnloaded() {
invalidateCaps();
dispose();
}
@Override
protected void invalidateCaps() {
super.invalidateCaps();
for (final LazyOptional<?> capability : capabilities.values()) {
capability.invalidate();
}
}
}

View File

@@ -0,0 +1,49 @@
package li.cil.oc2.common.tile;
import li.cil.oc2.OpenComputers;
import li.cil.oc2.common.bus.TileEntityDeviceBusElement;
import li.cil.oc2.common.capabilities.Capabilities;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.math.BlockPos;
public class BusCableTileEntity extends AbstractTileEntity {
private static final String BUS_ELEMENT_NBT_TAG_NAME = "busElement";
private final TileEntityDeviceBusElement busElement;
public BusCableTileEntity() {
super(OpenComputers.BUS_CABLE_TILE_ENTITY.get());
busElement = new TileEntityDeviceBusElement(this);
addCapability(Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY, busElement.getBusElement());
}
public void handleNeighborChanged(final BlockPos pos) {
busElement.handleNeighborChanged(pos);
}
@Override
public void initialize() {
super.initialize();
busElement.initialize();
}
@Override
protected void dispose() {
super.dispose();
busElement.dispose();
}
@Override
public CompoundNBT write(CompoundNBT compound) {
compound = super.write(compound);
compound.put(BUS_ELEMENT_NBT_TAG_NAME, busElement.serializeNBT());
return compound;
}
@Override
public void read(final CompoundNBT compound) {
super.read(compound);
busElement.deserializeNBT(compound.getCompound(BUS_ELEMENT_NBT_TAG_NAME));
}
}

View File

@@ -0,0 +1,25 @@
package li.cil.oc2.common.util;
import net.minecraftforge.common.util.LazyOptional;
import java.util.Objects;
public final class LazyOptionalUtils {
public static <T1, T2> boolean equals(final LazyOptional<T1> optionalA, final LazyOptional<T2> optionalB) {
if (optionalA.isPresent() != optionalB.isPresent()) {
return false;
}
if (!optionalA.isPresent()) {
return true;
}
final T1 valueA = optionalA.orElseThrow(AssertionError::new);
final T2 valueB = optionalB.orElseThrow(AssertionError::new);
return Objects.equals(valueA, valueB);
}
public static <T> int hashCode(final LazyOptional<T> optional) {
return optional.map(Objects::hash).orElse(0);
}
}