Add Redstone Controller
This commit is contained in:
parent
700817919a
commit
d3a0f279da
|
@ -1,8 +1,11 @@
|
|||
package net.shadowfacts.phycon.api.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
|
@ -25,6 +28,20 @@ public final class IPAddress {
|
|||
return random(ipAddressRandom);
|
||||
}
|
||||
|
||||
private static final Pattern IP_PATTERN = Pattern.compile("^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$");
|
||||
@Nullable
|
||||
public static IPAddress parse(String s) {
|
||||
Matcher matcher = IP_PATTERN.matcher(s);
|
||||
if (!matcher.matches()) {
|
||||
return null;
|
||||
}
|
||||
int a = Integer.parseInt(matcher.group(1));
|
||||
int b = Integer.parseInt(matcher.group(2));
|
||||
int c = Integer.parseInt(matcher.group(3));
|
||||
int d = Integer.parseInt(matcher.group(4));
|
||||
return new IPAddress(a, b, c, d);
|
||||
}
|
||||
|
||||
public static final IPAddress BROADCAST = new IPAddress(0xff_ff_ff_ff);
|
||||
|
||||
public final int address;
|
||||
|
|
|
@ -6,9 +6,7 @@ import net.shadowfacts.phycon.init.PhyBlockEntities
|
|||
import net.shadowfacts.phycon.init.PhyBlocks
|
||||
import net.shadowfacts.phycon.init.PhyItems
|
||||
import net.shadowfacts.phycon.init.PhyScreens
|
||||
import net.shadowfacts.phycon.networking.C2STerminalRequestItem
|
||||
import net.shadowfacts.phycon.networking.C2STerminalUpdateDisplayedItems
|
||||
import net.shadowfacts.phycon.networking.ServerReceiver
|
||||
import net.shadowfacts.phycon.networking.*
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
|
@ -25,6 +23,8 @@ object PhysicalConnectivity: ModInitializer {
|
|||
|
||||
registerGlobalReceiver(C2STerminalRequestItem)
|
||||
registerGlobalReceiver(C2STerminalUpdateDisplayedItems)
|
||||
registerGlobalReceiver(C2SConfigureActivationMode)
|
||||
registerGlobalReceiver(C2SConfigureRedstoneController)
|
||||
}
|
||||
|
||||
private fun registerGlobalReceiver(receiver: ServerReceiver) {
|
||||
|
|
|
@ -13,6 +13,8 @@ import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
|
|||
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlockEntity
|
||||
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock
|
||||
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlockEntity
|
||||
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlock
|
||||
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlockEntity
|
||||
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
|
||||
import net.shadowfacts.phycon.network.block.terminal.TerminalBlockEntity
|
||||
import net.shadowfacts.phycon.network.block.test.DestBlock
|
||||
|
@ -30,6 +32,7 @@ object PhyBlockEntities {
|
|||
val SWITCH = create(::SwitchBlockEntity, PhyBlocks.SWITCH)
|
||||
val EXTRACTOR = create(::ExtractorBlockEntity, PhyBlocks.EXTRACTOR)
|
||||
val MINER = create(::MinerBlockEntity, PhyBlocks.MINER)
|
||||
val REDSTONE_CONTROLLER = create(::RedstoneControllerBlockEntity, PhyBlocks.REDSTONE_CONTROLLER)
|
||||
|
||||
val SOURCE = create(::SourceBlockEntity, PhyBlocks.SOURCE)
|
||||
val DEST = create(::DestBlockEntity, PhyBlocks.DEST)
|
||||
|
@ -44,6 +47,7 @@ object PhyBlockEntities {
|
|||
register(SwitchBlock.ID, SWITCH)
|
||||
register(ExtractorBlock.ID, EXTRACTOR)
|
||||
register(MinerBlock.ID, MINER)
|
||||
register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER)
|
||||
|
||||
register(SourceBlock.ID, SOURCE)
|
||||
register(DestBlock.ID, DEST)
|
||||
|
|
|
@ -8,6 +8,7 @@ import net.shadowfacts.phycon.network.block.extractor.ExtractorBlock
|
|||
import net.shadowfacts.phycon.network.block.miner.MinerBlock
|
||||
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
|
||||
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock
|
||||
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlock
|
||||
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
|
||||
import net.shadowfacts.phycon.network.block.test.DestBlock
|
||||
import net.shadowfacts.phycon.network.block.test.SourceBlock
|
||||
|
@ -23,6 +24,7 @@ object PhyBlocks {
|
|||
val CABLE = CableBlock()
|
||||
val EXTRACTOR = ExtractorBlock()
|
||||
val MINER = MinerBlock()
|
||||
val REDSTONE_CONTROLLER = RedstoneControllerBlock()
|
||||
|
||||
val SOURCE = SourceBlock()
|
||||
val DEST = DestBlock()
|
||||
|
@ -34,6 +36,7 @@ object PhyBlocks {
|
|||
register(CableBlock.ID, CABLE)
|
||||
register(ExtractorBlock.ID, EXTRACTOR)
|
||||
register(MinerBlock.ID, MINER)
|
||||
register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER)
|
||||
|
||||
register(SourceBlock.ID, SOURCE)
|
||||
register(DestBlock.ID, DEST)
|
||||
|
|
|
@ -11,6 +11,7 @@ import net.shadowfacts.phycon.network.block.extractor.ExtractorBlock
|
|||
import net.shadowfacts.phycon.network.block.miner.MinerBlock
|
||||
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
|
||||
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock
|
||||
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlock
|
||||
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
|
||||
import net.shadowfacts.phycon.network.block.test.DestBlock
|
||||
import net.shadowfacts.phycon.network.block.test.SourceBlock
|
||||
|
@ -26,6 +27,7 @@ object PhyItems {
|
|||
val CABLE = BlockItem(PhyBlocks.CABLE, Item.Settings())
|
||||
val EXTRACTOR = BlockItem(PhyBlocks.EXTRACTOR, Item.Settings())
|
||||
val MINER = BlockItem(PhyBlocks.MINER, Item.Settings())
|
||||
val REDSTONE_CONTROLLER = BlockItem(PhyBlocks.REDSTONE_CONTROLLER, Item.Settings())
|
||||
|
||||
val SOURCE = BlockItem(PhyBlocks.SOURCE, Item.Settings())
|
||||
val DEST = BlockItem(PhyBlocks.DEST , Item.Settings())
|
||||
|
@ -40,6 +42,7 @@ object PhyItems {
|
|||
register(CableBlock.ID, CABLE)
|
||||
register(ExtractorBlock.ID, EXTRACTOR)
|
||||
register(MinerBlock.ID, MINER)
|
||||
register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER)
|
||||
|
||||
register(SourceBlock.ID, SOURCE)
|
||||
register(DestBlock.ID, DEST)
|
||||
|
|
|
@ -8,7 +8,11 @@ import net.minecraft.util.Identifier
|
|||
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||
import net.shadowfacts.phycon.network.DeviceBlock
|
||||
import net.shadowfacts.phycon.network.DeviceBlockEntity
|
||||
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlockEntity
|
||||
import net.shadowfacts.phycon.network.component.ActivationController
|
||||
import net.shadowfacts.phycon.screen.ActivatableDeviceConsoleScreen
|
||||
import net.shadowfacts.phycon.screen.DeviceConsoleScreen
|
||||
import net.shadowfacts.phycon.screen.RedstoneControllerConsoleScreen
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
|
@ -36,7 +40,11 @@ class ConsoleItem: Item(Settings()) {
|
|||
}
|
||||
|
||||
private fun openScreen(be: DeviceBlockEntity) {
|
||||
val screen = DeviceConsoleScreen(be)
|
||||
val screen = when (be) {
|
||||
is ActivationController.ActivatableDevice -> ActivatableDeviceConsoleScreen(be)
|
||||
is RedstoneControllerBlockEntity -> RedstoneControllerConsoleScreen(be)
|
||||
else -> DeviceConsoleScreen(be)
|
||||
}
|
||||
MinecraftClient.getInstance().openScreen(screen)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,7 @@ import net.shadowfacts.phycon.api.util.MACAddress
|
|||
import net.shadowfacts.phycon.network.frame.ARPQueryFrame
|
||||
import net.shadowfacts.phycon.network.frame.ARPResponseFrame
|
||||
import net.shadowfacts.phycon.network.frame.BasePacketFrame
|
||||
import net.shadowfacts.phycon.network.packet.DeviceRemovedPacket
|
||||
import java.lang.RuntimeException
|
||||
import net.shadowfacts.phycon.network.packet.*
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
|
@ -52,6 +51,15 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
|
|||
|
||||
abstract override fun handle(packet: Packet)
|
||||
|
||||
private fun doHandlePacket(packet: Packet) {
|
||||
when (packet) {
|
||||
is DeviceRemovedPacket -> {
|
||||
arpTable.remove(packet.source)
|
||||
}
|
||||
}
|
||||
handle(packet)
|
||||
}
|
||||
|
||||
override fun send(frame: EthernetFrame) {
|
||||
findDestination()?.receive(frame)
|
||||
}
|
||||
|
@ -64,7 +72,7 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
|
|||
is PacketFrame -> {
|
||||
if (frame.packet.destination.isBroadcast || frame.packet.destination == ipAddress) {
|
||||
println("$this ($ipAddress) received packet: ${frame.packet}")
|
||||
handle(frame.packet)
|
||||
doHandlePacket(frame.packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,18 +71,23 @@ abstract class FaceDeviceBlock<T: DeviceBlockEntity>(settings: Settings): Device
|
|||
builder.add(CABLE_CONNECTION)
|
||||
}
|
||||
|
||||
override fun getPlacementState(context: ItemPlacementContext): BlockState? {
|
||||
val facing = if (context.player?.isSneaking == true) context.side.opposite else context.playerFacing.opposite
|
||||
val cableConnection = getCableConnectedSide(context.world, context.blockPos) ?: facing.opposite
|
||||
override fun getPlacementState(context: ItemPlacementContext): BlockState {
|
||||
val facing = if (context.player?.isSneaking == true) context.side.opposite else context.playerLookDirection.opposite
|
||||
val cableConnection = getCableConnectedSide(context.world, context.blockPos, facing) ?: facing.opposite
|
||||
return defaultState
|
||||
.with(FACING, facing)
|
||||
.with(CABLE_CONNECTION, cableConnection)
|
||||
}
|
||||
|
||||
protected fun getCableConnectedSide(world: World, pos: BlockPos): Direction? {
|
||||
protected fun getCableConnectedSide(world: World, pos: BlockPos, facing: Direction): Direction? {
|
||||
for (side in Direction.values()) {
|
||||
if (side == facing) {
|
||||
continue
|
||||
}
|
||||
val offsetPos = pos.offset(side)
|
||||
if (world.getBlockState(offsetPos) is NetworkComponentBlock) {
|
||||
val state = world.getBlockState(offsetPos)
|
||||
val block = state.block
|
||||
if (block is NetworkComponentBlock && block.getNetworkConnectedSides(state, world, offsetPos).contains(side.opposite)) {
|
||||
return side
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,38 @@
|
|||
package net.shadowfacts.phycon.network.block.extractor
|
||||
|
||||
import alexiil.mc.lib.attributes.SearchOptions
|
||||
import alexiil.mc.lib.attributes.item.FixedItemInv
|
||||
import alexiil.mc.lib.attributes.item.GroupedItemInv
|
||||
import alexiil.mc.lib.attributes.item.ItemAttributes
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.shadowfacts.phycon.api.packet.Packet
|
||||
import net.shadowfacts.phycon.api.util.IPAddress
|
||||
import net.shadowfacts.phycon.init.PhyBlockEntities
|
||||
import net.shadowfacts.phycon.network.DeviceBlockEntity
|
||||
import net.shadowfacts.phycon.network.component.ActivationController
|
||||
import net.shadowfacts.phycon.network.packet.CapacityPacket
|
||||
import net.shadowfacts.phycon.network.packet.CheckCapacityPacket
|
||||
import net.shadowfacts.phycon.network.packet.ItemStackPacket
|
||||
import net.shadowfacts.phycon.network.packet.RemoteActivationPacket
|
||||
import net.shadowfacts.phycon.util.ActivationMode
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR) {
|
||||
class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
|
||||
ActivationController.ActivatableDevice {
|
||||
|
||||
companion object {
|
||||
val SLEEP_TIME = 40L
|
||||
}
|
||||
|
||||
private val facing: Direction
|
||||
get() = cachedState[ExtractorBlock.FACING]
|
||||
|
||||
private var inventory: GroupedItemInv? = null
|
||||
private var shouldExtract = false
|
||||
override val controller = ActivationController(SLEEP_TIME, this)
|
||||
|
||||
fun updateInventory() {
|
||||
val offsetPos = pos.offset(facing)
|
||||
|
@ -36,7 +46,14 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR) {
|
|||
}
|
||||
|
||||
override fun handle(packet: Packet) {
|
||||
if (packet is CapacityPacket && shouldExtract) {
|
||||
when (packet) {
|
||||
is CapacityPacket -> handleCapacity(packet)
|
||||
is RemoteActivationPacket -> controller.handleRemoteActivation(packet)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCapacity(packet: CapacityPacket) {
|
||||
if (shouldExtract && packet.capacity > 0) {
|
||||
getInventory()?.also { inv ->
|
||||
shouldExtract = false
|
||||
val extracted = inv.extract(packet.stack, packet.capacity)
|
||||
|
@ -48,12 +65,36 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR) {
|
|||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
if (!world!!.isClient && counter % 40 == 0L) {
|
||||
getInventory()?.also {
|
||||
val stack = it.storedStacks.firstOrNull() ?: return
|
||||
shouldExtract = true
|
||||
sendPacket(CheckCapacityPacket(stack, ipAddress, IPAddress.BROADCAST))
|
||||
}
|
||||
if (!world!!.isClient) {
|
||||
controller.tick()
|
||||
}
|
||||
}
|
||||
|
||||
override fun activate(): Boolean {
|
||||
val inventory = getInventory() ?: return false
|
||||
val stack = inventory.storedStacks.firstOrNull() ?: return false
|
||||
shouldExtract = true
|
||||
sendPacket(CheckCapacityPacket(stack, ipAddress, IPAddress.BROADCAST))
|
||||
return true
|
||||
}
|
||||
|
||||
override fun toTag(tag: CompoundTag): CompoundTag {
|
||||
tag.putString("ActivationMode", controller.activationMode.name)
|
||||
return super.toTag(tag)
|
||||
}
|
||||
|
||||
override fun fromTag(state: BlockState, tag: CompoundTag) {
|
||||
super.fromTag(state, tag)
|
||||
controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode"))
|
||||
}
|
||||
|
||||
override fun toClientTag(tag: CompoundTag): CompoundTag {
|
||||
tag.putString("ActivationMode", controller.activationMode.name)
|
||||
return super.toClientTag(tag)
|
||||
}
|
||||
|
||||
override fun fromClientTag(tag: CompoundTag) {
|
||||
super.fromClientTag(tag)
|
||||
controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode"))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package net.shadowfacts.phycon.network.block.redstone
|
||||
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.Material
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.Properties
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||
import net.shadowfacts.phycon.network.FaceDeviceBlock
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
class RedstoneControllerBlock: FaceDeviceBlock<RedstoneControllerBlockEntity>(Settings.of(Material.METAL)) {
|
||||
|
||||
companion object {
|
||||
val ID = Identifier(PhysicalConnectivity.MODID, "redstone_controller")
|
||||
val LIT = Properties.LIT
|
||||
}
|
||||
|
||||
// todo: don't just copy this from the Interface block
|
||||
override val faceThickness = 2.0
|
||||
override val faceShapes = mapOf(
|
||||
Direction.DOWN to createCuboidShape(0.0, 0.0, 0.0, 16.0, 2.0, 16.0),
|
||||
Direction.UP to createCuboidShape(0.0, 14.0, 0.0, 16.0, 16.0, 16.0),
|
||||
Direction.NORTH to createCuboidShape(0.0, 0.0, 0.0, 16.0, 16.0, 2.0),
|
||||
Direction.SOUTH to createCuboidShape(0.0, 0.0, 14.0, 16.0, 16.0, 16.0),
|
||||
Direction.WEST to createCuboidShape(0.0, 0.0, 0.0, 2.0, 16.0, 16.0),
|
||||
Direction.EAST to createCuboidShape(14.0, 0.0, 0.0, 16.0, 16.0, 16.0)
|
||||
)
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
super.appendProperties(builder)
|
||||
builder.add(LIT)
|
||||
}
|
||||
|
||||
override fun createBlockEntity(world: BlockView) = RedstoneControllerBlockEntity()
|
||||
|
||||
override fun getPlacementState(context: ItemPlacementContext): BlockState {
|
||||
val state = super.getPlacementState(context)
|
||||
return state.with(LIT, isPowered(context.world, context.blockPos, state[FACING]))
|
||||
}
|
||||
|
||||
// todo: does this need to be separate from getStateForNeighborUpdate?
|
||||
override fun neighborUpdate(state: BlockState, world: World, pos: BlockPos, neighborBlock: Block, neighborPos: BlockPos, bl: Boolean) {
|
||||
if (!world.isClient) {
|
||||
val wasLit = state[LIT]
|
||||
val isLit = isPowered(world, pos, state[FACING])
|
||||
if (wasLit != isLit) {
|
||||
if (wasLit) {
|
||||
world.blockTickScheduler.schedule(pos, this, 4)
|
||||
} else {
|
||||
toggleLit(state, world, pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun scheduledTick(state: BlockState, world: ServerWorld, pos: BlockPos, random: Random) {
|
||||
if (state[LIT] && !isPowered(world, pos, state[FACING])) {
|
||||
toggleLit(state, world, pos)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isPowered(world: World, pos: BlockPos, facing: Direction): Boolean {
|
||||
val offset = pos.offset(facing)
|
||||
return world.getEmittedRedstonePower(offset, facing) > 0
|
||||
}
|
||||
|
||||
private fun toggleLit(state: BlockState, world: World, pos: BlockPos) {
|
||||
world.setBlockState(pos, state.cycle(LIT), 2)
|
||||
getBlockEntity(world, pos)!!.redstoneStateChanged()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package net.shadowfacts.phycon.network.block.redstone
|
||||
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.shadowfacts.phycon.api.packet.Packet
|
||||
import net.shadowfacts.phycon.api.util.IPAddress
|
||||
import net.shadowfacts.phycon.init.PhyBlockEntities
|
||||
import net.shadowfacts.phycon.network.DeviceBlockEntity
|
||||
import net.shadowfacts.phycon.network.packet.RemoteActivationPacket
|
||||
import net.shadowfacts.phycon.util.RedstoneMode
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
class RedstoneControllerBlockEntity: DeviceBlockEntity(PhyBlockEntities.REDSTONE_CONTROLLER) {
|
||||
|
||||
var managedDevices = Array<IPAddress?>(5) { null }
|
||||
var redstoneMode = RedstoneMode.HIGH
|
||||
|
||||
private var redstonePowered = false
|
||||
|
||||
override fun handle(packet: Packet) {
|
||||
}
|
||||
|
||||
fun redstoneStateChanged() {
|
||||
val oldPowered = redstonePowered
|
||||
redstonePowered = cachedState[RedstoneControllerBlock.LIT]
|
||||
|
||||
val mode: RemoteActivationPacket.Mode? = when (redstoneMode) {
|
||||
RedstoneMode.TOGGLE -> if (oldPowered != redstonePowered) RemoteActivationPacket.Mode.SINGLE else null
|
||||
RedstoneMode.RISING_EDGE -> if (!oldPowered && redstonePowered) RemoteActivationPacket.Mode.SINGLE else null
|
||||
RedstoneMode.FALLING_EDGE -> if (oldPowered && !redstonePowered) RemoteActivationPacket.Mode.SINGLE else null
|
||||
RedstoneMode.HIGH -> if (redstonePowered) RemoteActivationPacket.Mode.ENABLE else RemoteActivationPacket.Mode.DISABLE
|
||||
RedstoneMode.LOW -> if (redstonePowered) RemoteActivationPacket.Mode.DISABLE else RemoteActivationPacket.Mode.ENABLE
|
||||
}
|
||||
|
||||
if (mode != null) {
|
||||
sendActivatePacket(mode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendActivatePacket(mode: RemoteActivationPacket.Mode) {
|
||||
for (ip in managedDevices) {
|
||||
if (ip == null) continue
|
||||
sendPacket(RemoteActivationPacket(mode, ipAddress, ip))
|
||||
}
|
||||
}
|
||||
|
||||
override fun toTag(tag: CompoundTag): CompoundTag {
|
||||
tag.putIntArray("ManagedDevices", managedDevices.mapNotNull { it?.address })
|
||||
tag.putString("RedstoneMode", redstoneMode.name)
|
||||
return super.toTag(tag)
|
||||
}
|
||||
|
||||
override fun fromTag(state: BlockState, tag: CompoundTag) {
|
||||
super.fromTag(state, tag)
|
||||
val addresses = tag.getIntArray("ManagedDevices")
|
||||
managedDevices = (0..4).map { if (it >= addresses.size) null else IPAddress(addresses[it]) }.toTypedArray()
|
||||
redstoneMode = RedstoneMode.valueOf(tag.getString("RedstoneMode"))
|
||||
}
|
||||
|
||||
override fun toClientTag(tag: CompoundTag): CompoundTag {
|
||||
tag.putIntArray("ManagedDevices", managedDevices.mapNotNull { it?.address })
|
||||
tag.putString("RedstoneMode", redstoneMode.name)
|
||||
return super.toClientTag(tag)
|
||||
}
|
||||
|
||||
override fun fromClientTag(tag: CompoundTag) {
|
||||
super.fromClientTag(tag)
|
||||
val addresses = tag.getIntArray("ManagedDevices")
|
||||
managedDevices = (0..4).map { if (it >= addresses.size) null else IPAddress(addresses[it]) }.toTypedArray()
|
||||
redstoneMode = RedstoneMode.valueOf(tag.getString("RedstoneMode"))
|
||||
}
|
||||
|
||||
}
|
|
@ -20,6 +20,8 @@ import net.shadowfacts.phycon.PhysicalConnectivity
|
|||
import net.shadowfacts.phycon.networking.C2STerminalRequestItem
|
||||
import net.shadowfacts.phycon.networking.C2STerminalUpdateDisplayedItems
|
||||
import net.shadowfacts.phycon.util.SortMode
|
||||
import net.shadowfacts.phycon.util.next
|
||||
import net.shadowfacts.phycon.util.prev
|
||||
import org.lwjgl.glfw.GLFW
|
||||
import java.lang.NumberFormatException
|
||||
import kotlin.math.ceil
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package net.shadowfacts.phycon.network.component
|
||||
|
||||
import net.minecraft.block.entity.BlockEntity
|
||||
import net.shadowfacts.phycon.network.packet.RemoteActivationPacket
|
||||
import net.shadowfacts.phycon.util.ActivationMode
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
class ActivationController<T>(
|
||||
private val sleepInterval: Long,
|
||||
private val device: T
|
||||
) where T: ActivationController.ActivatableDevice, T: BlockEntity {
|
||||
|
||||
var activationMode = ActivationMode.AUTOMATIC
|
||||
set(value) {
|
||||
field = value
|
||||
// when the activation mode changes, reset the remote enabled status
|
||||
remotelyEnabled = false
|
||||
}
|
||||
|
||||
private var lastActivation = -sleepInterval
|
||||
private var remotelyEnabled = false
|
||||
|
||||
fun tick() {
|
||||
if (activationMode == ActivationMode.AUTOMATIC || remotelyEnabled) {
|
||||
tryActivate()
|
||||
}
|
||||
}
|
||||
|
||||
fun handleRemoteActivation(packet: RemoteActivationPacket) {
|
||||
if (activationMode != ActivationMode.MANAGED) {
|
||||
return
|
||||
}
|
||||
|
||||
when (packet.mode) {
|
||||
RemoteActivationPacket.Mode.SINGLE -> tryActivate()
|
||||
RemoteActivationPacket.Mode.ENABLE -> remotelyEnabled = true
|
||||
RemoteActivationPacket.Mode.DISABLE -> remotelyEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryActivate() {
|
||||
assert(!device.world!!.isClient)
|
||||
if ((device.counter - lastActivation) < sleepInterval) {
|
||||
return
|
||||
}
|
||||
if (device.activate()) {
|
||||
lastActivation = device.counter
|
||||
}
|
||||
}
|
||||
|
||||
interface ActivatableDevice {
|
||||
val controller: ActivationController<*>
|
||||
|
||||
val counter: Long
|
||||
|
||||
fun activate(): Boolean
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package net.shadowfacts.phycon.network.packet
|
||||
|
||||
import net.shadowfacts.phycon.api.util.IPAddress
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
class RemoteActivationPacket(val mode: Mode, source: IPAddress, destination: IPAddress): BasePacket(source, destination) {
|
||||
enum class Mode {
|
||||
SINGLE,
|
||||
ENABLE,
|
||||
DISABLE
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package net.shadowfacts.phycon.networking
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender
|
||||
import net.minecraft.network.Packet
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.registry.Registry
|
||||
import net.minecraft.util.registry.RegistryKey
|
||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||
import net.shadowfacts.phycon.network.DeviceBlockEntity
|
||||
import net.shadowfacts.phycon.network.component.ActivationController
|
||||
import net.shadowfacts.phycon.util.ActivationMode
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
object C2SConfigureActivationMode: ServerReceiver {
|
||||
override val CHANNEL = Identifier(PhysicalConnectivity.MODID, "configure_activation_mode")
|
||||
|
||||
operator fun <T> invoke(be: T): Packet<*> where T: DeviceBlockEntity, T: ActivationController.ActivatableDevice {
|
||||
val buf = PacketByteBufs.create()
|
||||
|
||||
buf.writeIdentifier(be.world!!.registryKey.value)
|
||||
buf.writeBlockPos(be.pos)
|
||||
buf.writeString(be.controller.activationMode.name)
|
||||
|
||||
return createPacket(buf)
|
||||
}
|
||||
|
||||
override fun receive(server: MinecraftServer, player: ServerPlayerEntity, handler: ServerPlayNetworkHandler, buf: PacketByteBuf, responseSender: PacketSender) {
|
||||
val dimID = buf.readIdentifier()
|
||||
val pos = buf.readBlockPos()
|
||||
val mode = ActivationMode.valueOf(buf.readString())
|
||||
|
||||
server.execute {
|
||||
// todo: check the player is close enough
|
||||
val key = RegistryKey.of(Registry.DIMENSION, dimID)
|
||||
val world = server.getWorld(key) ?: return@execute
|
||||
val device = world.getBlockEntity(pos) ?: return@execute
|
||||
if (device !is ActivationController.ActivatableDevice) return@execute
|
||||
device.controller.activationMode = mode
|
||||
device.markDirty()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package net.shadowfacts.phycon.networking
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender
|
||||
import net.minecraft.network.Packet
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.registry.Registry
|
||||
import net.minecraft.util.registry.RegistryKey
|
||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||
import net.shadowfacts.phycon.api.util.IPAddress
|
||||
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlockEntity
|
||||
import net.shadowfacts.phycon.util.RedstoneMode
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
object C2SConfigureRedstoneController: ServerReceiver {
|
||||
// todo: it would be nice if there wasn't so much duplication with C2SConfigureActivationMode
|
||||
|
||||
override val CHANNEL = Identifier(PhysicalConnectivity.MODID, "configure_redstone_controller")
|
||||
|
||||
operator fun invoke(be: RedstoneControllerBlockEntity): Packet<*> {
|
||||
val buf = PacketByteBufs.create()
|
||||
|
||||
buf.writeIdentifier(be.world!!.registryKey.value)
|
||||
buf.writeBlockPos(be.pos)
|
||||
buf.writeString(be.redstoneMode.name)
|
||||
be.managedDevices.forEach {
|
||||
buf.writeInt(it?.address ?: 0)
|
||||
}
|
||||
|
||||
return createPacket(buf)
|
||||
}
|
||||
|
||||
override fun receive(server: MinecraftServer, player: ServerPlayerEntity, handler: ServerPlayNetworkHandler, buf: PacketByteBuf, responseSender: PacketSender) {
|
||||
val dimID = buf.readIdentifier()
|
||||
val pos = buf.readBlockPos()
|
||||
val mode = RedstoneMode.valueOf(buf.readString())
|
||||
val managedDevices = Array<IPAddress?>(5) { null }
|
||||
(0..4).map {
|
||||
val v = buf.readInt()
|
||||
managedDevices[it] = if (v == 0) null else IPAddress(v)
|
||||
}
|
||||
|
||||
server.execute {
|
||||
// todo: check if the player is close enough
|
||||
val key = RegistryKey.of(Registry.DIMENSION, dimID)
|
||||
val world = server.getWorld(key) ?: return@execute
|
||||
val device = world.getBlockEntity(pos) as? RedstoneControllerBlockEntity ?: return@execute
|
||||
device.redstoneMode = mode
|
||||
device.managedDevices = managedDevices
|
||||
device.markDirty()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
package net.shadowfacts.phycon.networking
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking
|
||||
import net.minecraft.network.Packet
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
|
@ -8,4 +11,8 @@ import net.minecraft.util.Identifier
|
|||
*/
|
||||
interface ServerReceiver: ServerPlayNetworking.PlayChannelHandler {
|
||||
val CHANNEL: Identifier
|
||||
|
||||
fun createPacket(buf: PacketByteBuf): Packet<*> {
|
||||
return ClientPlayNetworking.createC2SPacket(CHANNEL, buf)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package net.shadowfacts.phycon.screen
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.client.gui.widget.ButtonWidget
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.text.LiteralText
|
||||
import net.minecraft.text.Text
|
||||
import net.shadowfacts.phycon.network.DeviceBlockEntity
|
||||
import net.shadowfacts.phycon.network.component.ActivationController
|
||||
import net.shadowfacts.phycon.networking.C2SConfigureActivationMode
|
||||
import net.shadowfacts.phycon.util.ActivationMode
|
||||
import net.shadowfacts.phycon.util.next
|
||||
import org.lwjgl.glfw.GLFW
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
class ActivatableDeviceConsoleScreen<T>(
|
||||
val device: T
|
||||
): Screen(device.cachedState.block.name) where T: DeviceBlockEntity, T: ActivationController.ActivatableDevice {
|
||||
|
||||
private val backgroundWidth = 252
|
||||
private val backgroundHeight = 222
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
buttons.clear()
|
||||
|
||||
val minX = (width - backgroundWidth) / 2
|
||||
val minY = (height - backgroundHeight) / 2
|
||||
|
||||
lateinit var mode: ButtonWidget
|
||||
mode = ButtonWidget(minX + 5, minY + 25, 55, 20, device.controller.activationMode.friendlyName) {
|
||||
device.controller.activationMode = device.controller.activationMode.next
|
||||
mode.message = device.controller.activationMode.friendlyName
|
||||
client!!.player!!.networkHandler.sendPacket(C2SConfigureActivationMode(device))
|
||||
}
|
||||
addButton(mode)
|
||||
}
|
||||
|
||||
override fun isPauseScreen() = false
|
||||
|
||||
override fun keyPressed(key: Int, j: Int, k: Int): Boolean {
|
||||
if (key == GLFW.GLFW_KEY_E) {
|
||||
onClose()
|
||||
return true
|
||||
}
|
||||
return super.keyPressed(key, j, k)
|
||||
}
|
||||
|
||||
override fun render(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
renderBackground(matrixStack)
|
||||
|
||||
val minX = (width - backgroundWidth) / 2
|
||||
val minY = (height - backgroundHeight) / 2
|
||||
|
||||
RenderSystem.color4f(1f, 1f, 1f, 1f)
|
||||
client!!.textureManager.bindTexture(DeviceConsoleScreen.BACKGROUND)
|
||||
drawTexture(matrixStack, minX, minY, 0, 0, backgroundWidth, backgroundHeight)
|
||||
|
||||
super.render(matrixStack, mouseX, mouseY, delta)
|
||||
|
||||
textRenderer.draw(matrixStack, "IP Address: ${device.ipAddress}", minX + 5f, minY + 5f, 0x404040)
|
||||
textRenderer.draw(matrixStack, "MAC Address: ${device.macAddress}", minX + 5f, minY + 15f, 0x404040)
|
||||
}
|
||||
|
||||
private val ActivationMode.friendlyName: Text
|
||||
get() = when (this) {
|
||||
ActivationMode.AUTOMATIC -> LiteralText("Automatic")
|
||||
ActivationMode.MANAGED -> LiteralText("Managed")
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,8 @@ package net.shadowfacts.phycon.screen
|
|||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.text.TranslatableText
|
||||
import net.minecraft.util.Identifier
|
||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||
import net.shadowfacts.phycon.network.DeviceBlockEntity
|
||||
import org.lwjgl.glfw.GLFW
|
||||
|
||||
|
@ -11,18 +13,18 @@ import org.lwjgl.glfw.GLFW
|
|||
*/
|
||||
class DeviceConsoleScreen(
|
||||
val device: DeviceBlockEntity,
|
||||
): Screen(TranslatableText("item.phycon.onsole")) {
|
||||
): Screen(TranslatableText("item.phycon.console")) {
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
companion object {
|
||||
val BACKGROUND = Identifier(PhysicalConnectivity.MODID, "textures/gui/console.png")
|
||||
}
|
||||
|
||||
override fun isPauseScreen() = false
|
||||
|
||||
override fun keyPressed(key: Int, j: Int, k: Int): Boolean {
|
||||
if (key == GLFW.GLFW_KEY_E) {
|
||||
onClose();
|
||||
return true;
|
||||
onClose()
|
||||
return true
|
||||
}
|
||||
return super.keyPressed(key, j, k)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
package net.shadowfacts.phycon.screen
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import net.minecraft.client.font.TextRenderer
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.client.gui.widget.ButtonWidget
|
||||
import net.minecraft.client.gui.widget.TextFieldWidget
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.text.LiteralText
|
||||
import net.minecraft.text.Text
|
||||
import net.shadowfacts.phycon.api.util.IPAddress
|
||||
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlockEntity
|
||||
import net.shadowfacts.phycon.networking.C2SConfigureRedstoneController
|
||||
import net.shadowfacts.phycon.util.RedstoneMode
|
||||
import net.shadowfacts.phycon.util.next
|
||||
import org.lwjgl.glfw.GLFW
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
class RedstoneControllerConsoleScreen(
|
||||
val device: RedstoneControllerBlockEntity
|
||||
): Screen(device.cachedState.block.name) {
|
||||
|
||||
private val backgroundWidth = 252
|
||||
private val backgroundHeight = 222
|
||||
|
||||
private val ipAddressTextFields = mutableListOf<TextFieldWidget>()
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
buttons.clear()
|
||||
ipAddressTextFields.clear()
|
||||
|
||||
val minX = (width - backgroundWidth) / 2
|
||||
val minY = (height - backgroundHeight) / 2
|
||||
|
||||
lateinit var mode: ButtonWidget
|
||||
mode = ButtonWidget(minX + 5, minY + 25, 75, 20, device.redstoneMode.friendlyName) {
|
||||
device.redstoneMode = device.redstoneMode.next
|
||||
mode.message = device.redstoneMode.friendlyName
|
||||
client!!.player!!.networkHandler.sendPacket(C2SConfigureRedstoneController(device))
|
||||
}
|
||||
addButton(mode)
|
||||
|
||||
for (i in 0 until 5) {
|
||||
// todo: field name
|
||||
val field = TextFieldWidget(textRenderer, minX + 5, minY + 50 + 22 * i, backgroundWidth / 2, 20, LiteralText(""))
|
||||
field.setMaxLength(15)
|
||||
field.setHasBorder(true)
|
||||
field.isVisible = true
|
||||
field.setEditableColor(0xffffff)
|
||||
field.text = device.managedDevices[i]?.toString()
|
||||
field.setChangedListener { newVal ->
|
||||
device.managedDevices[i] = IPAddress.parse(newVal)
|
||||
client!!.player!!.networkHandler.sendPacket(C2SConfigureRedstoneController(device))
|
||||
}
|
||||
addChild(field)
|
||||
ipAddressTextFields.add(field)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isPauseScreen() = false
|
||||
|
||||
override fun keyPressed(key: Int, j: Int, k: Int): Boolean {
|
||||
if (key == GLFW.GLFW_KEY_E) {
|
||||
onClose()
|
||||
return true
|
||||
}
|
||||
return super.keyPressed(key, j, k)
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
val clickedField = ipAddressTextFields.find { it.x <= mouseX && it.x + it.width >= mouseX && it.y <= mouseY && it.y + it.height >= mouseY }
|
||||
if (clickedField != null) {
|
||||
ipAddressTextFields.forEach {
|
||||
if (it !== clickedField) it.setSelected(false)
|
||||
}
|
||||
}
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
override fun render(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
renderBackground(matrixStack)
|
||||
|
||||
val minX = (width - backgroundWidth) / 2
|
||||
val minY = (height - backgroundHeight) / 2
|
||||
|
||||
RenderSystem.color4f(1f, 1f, 1f, 1f)
|
||||
client!!.textureManager.bindTexture(DeviceConsoleScreen.BACKGROUND)
|
||||
drawTexture(matrixStack, minX, minY, 0, 0, backgroundWidth, backgroundHeight)
|
||||
|
||||
super.render(matrixStack, mouseX, mouseY, delta)
|
||||
|
||||
ipAddressTextFields.forEach { it.render(matrixStack, mouseX, mouseY, delta) }
|
||||
|
||||
textRenderer.draw(matrixStack, "IP Address: ${device.ipAddress}", minX + 5f, minY + 5f, 0x404040)
|
||||
textRenderer.draw(matrixStack, "MAC Address: ${device.macAddress}", minX + 5f, minY + 15f, 0x404040)
|
||||
}
|
||||
|
||||
private val RedstoneMode.friendlyName: Text
|
||||
get() = LiteralText(when (this) {
|
||||
RedstoneMode.HIGH -> "High"
|
||||
RedstoneMode.LOW -> "Low"
|
||||
RedstoneMode.TOGGLE -> "Toggle"
|
||||
RedstoneMode.RISING_EDGE -> "Rising Edge"
|
||||
RedstoneMode.FALLING_EDGE -> "Falling Edge"
|
||||
})
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package net.shadowfacts.phycon.util
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
enum class ActivationMode: RotatableEnum {
|
||||
AUTOMATIC,
|
||||
MANAGED,
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package net.shadowfacts.phycon.util
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
enum class RedstoneMode: RotatableEnum {
|
||||
HIGH,
|
||||
LOW,
|
||||
TOGGLE,
|
||||
RISING_EDGE,
|
||||
FALLING_EDGE;
|
||||
|
||||
val isDiscrete: Boolean
|
||||
get() = when (this) {
|
||||
TOGGLE, RISING_EDGE, FALLING_EDGE -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
val isContinuous: Boolean
|
||||
get() = when (this) {
|
||||
HIGH, LOW -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package net.shadowfacts.phycon.util
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
interface RotatableEnum {
|
||||
}
|
||||
|
||||
val <E> E.prev: E where E: Enum<E>, E: RotatableEnum
|
||||
get() = javaClass.enumConstants[(ordinal - 1 + javaClass.enumConstants.size) % javaClass.enumConstants.size]
|
||||
|
||||
val <E> E.next: E where E: Enum<E>, E: RotatableEnum
|
||||
get() = javaClass.enumConstants[(ordinal + 1) % javaClass.enumConstants.size]
|
|
@ -6,17 +6,11 @@ import net.minecraft.text.Text
|
|||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
enum class SortMode {
|
||||
enum class SortMode: RotatableEnum {
|
||||
COUNT_HIGH_FIRST,
|
||||
COUNT_LOW_FIRST,
|
||||
ALPHABETICAL;
|
||||
|
||||
val prev: SortMode
|
||||
get() = values()[(ordinal - 1 + values().size) % values().size]
|
||||
|
||||
val next: SortMode
|
||||
get() = values()[(ordinal + 1) % values().size]
|
||||
|
||||
val tooltip: Text
|
||||
get() = when (this) {
|
||||
COUNT_HIGH_FIRST -> LiteralText("Count, highest first")
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
{
|
||||
"multipart": [
|
||||
{
|
||||
"apply": { "model": "phycon:block/cable_center" }
|
||||
},
|
||||
{
|
||||
"when": { "facing": "down", "lit": "true|false" },
|
||||
"apply": { "model": "phycon:block/interface_side" }
|
||||
},
|
||||
{
|
||||
"when": { "facing": "up", "lit": "true|false" },
|
||||
"apply": { "model": "phycon:block/interface_side", "x": 180 }
|
||||
},
|
||||
{
|
||||
"when": { "facing": "north", "lit": "true|false" },
|
||||
"apply": { "model": "phycon:block/interface_side", "x": 270 }
|
||||
},
|
||||
{
|
||||
"when": { "facing": "south", "lit": "true|false" },
|
||||
"apply": { "model": "phycon:block/interface_side", "x": 90 }
|
||||
},
|
||||
{
|
||||
"when": { "facing": "west", "lit": "true|false" },
|
||||
"apply": { "model": "phycon:block/interface_side", "x": 90, "y": 90 }
|
||||
},
|
||||
{
|
||||
"when": { "facing": "east", "lit": "true|false" },
|
||||
"apply": { "model": "phycon:block/interface_side", "x": 90, "y": 270 }
|
||||
},
|
||||
|
||||
{
|
||||
"when": { "cable_connection": "up", "facing": "down" },
|
||||
"apply": { "model": "phycon:block/interface_cable_straight" }
|
||||
},
|
||||
{
|
||||
"when": { "cable_connection": "down", "facing": "up" },
|
||||
"apply": { "model": "phycon:block/interface_cable_straight", "x": 180 }
|
||||
},
|
||||
{
|
||||
"when": { "cable_connection": "north", "facing": "south" },
|
||||
"apply": { "model": "phycon:block/interface_cable_straight", "x": 90 }
|
||||
},
|
||||
{
|
||||
"when": { "cable_connection": "south", "facing": "north" },
|
||||
"apply": { "model": "phycon:block/interface_cable_straight", "x": 270 }
|
||||
},
|
||||
{
|
||||
"when": { "cable_connection": "west", "facing": "east" },
|
||||
"apply": { "model": "phycon:block/interface_cable_straight", "x": 90, "y": 270 }
|
||||
},
|
||||
{
|
||||
"when": { "cable_connection": "east", "facing": "west" },
|
||||
"apply": { "model": "phycon:block/interface_cable_straight", "x": 90, "y": 90 }
|
||||
},
|
||||
|
||||
{
|
||||
"when": {"cable_connection": "north", "facing": "down"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner" }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "east", "facing": "down"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner", "y": 90 }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "south", "facing": "down"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner", "y": 180 }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "west", "facing": "down"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner", "y": 270 }
|
||||
},
|
||||
|
||||
{
|
||||
"when": {"cable_connection": "north", "facing": "up"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner", "x": 180, "y": 180 }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "east", "facing": "up"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner", "x": 180, "y": 270 }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "south", "facing": "up"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner", "x": 180 }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "west", "facing": "up"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner", "x": 180, "y": 90 }
|
||||
},
|
||||
|
||||
{
|
||||
"when": {"cable_connection": "down", "facing": "north"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner", "x": 90, "y": 180 }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "up", "facing": "north"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner", "x": 270 }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "west", "facing": "north"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner_2" }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "east", "facing": "north"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner_2", "x": 180, "y": 180 }
|
||||
},
|
||||
|
||||
{
|
||||
"when": {"cable_connection": "down", "facing": "south"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner", "x": 90 }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "up", "facing": "south"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner", "x": 270, "y": 180 }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "west", "facing": "south"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner_3" }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "east", "facing": "south"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner_3", "x": 180, "y": 180 }
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
"when": {"cable_connection": "down", "facing": "west"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner", "x": 90, "y": 90 }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "up", "facing": "west"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner", "x": 270, "y": 270 }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "north", "facing": "west"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner_3", "y": 90 }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "south", "facing": "west"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner_3", "x": 180, "y": 270 }
|
||||
},
|
||||
|
||||
{
|
||||
"when": {"cable_connection": "down", "facing": "east"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner", "x": 90, "y": 270 }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "up", "facing": "east"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner", "x": 270, "y": 90 }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "north", "facing": "east"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner_2", "y": 90 }
|
||||
},
|
||||
{
|
||||
"when": {"cable_connection": "south", "facing": "east"},
|
||||
"apply": { "model": "phycon:block/interface_cable_corner_2", "x": 180, "y": 270 }
|
||||
}
|
||||
]
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
"block.phycon.cable": "Cable",
|
||||
"block.phycon.extractor": "Inventory Extractor",
|
||||
"block.phycon.miner": "Block Miner",
|
||||
"block.phycon.redstone_controller": "Redstone Controller",
|
||||
|
||||
"item.phycon.screwdriver": "Screwdriver",
|
||||
"item.phycon.console": "Console"
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
Loading…
Reference in New Issue