Compare commits

..

12 Commits

Author SHA1 Message Date
Shadowfacts c9bcfd29d5
Add Twisted Pair and Cable recipe 2021-03-06 20:04:32 -05:00
Shadowfacts 2198b5e10d
Fix weird tab appearance at certain resolutions
There's 1 physical pixel of error creeping in somewhere which was being
displayed below the selected tab. Workaround it by using a tab texture
which has transparent pixels below the bottom of the select tabs.
2021-03-06 19:10:24 -05:00
Shadowfacts 1c405f8623
Tweak terminal placement behavior 2021-03-06 19:09:35 -05:00
Shadowfacts 89e91796a5
Fix not being able to open Redstone Emitter GUI 2021-03-06 15:53:25 -05:00
Shadowfacts 219033476c
Fix crash showing tooltip when mouse was on the edge between two tabs 2021-03-06 15:47:27 -05:00
Shadowfacts 32d87fbd9d
Convert Redstone Controller and Emitter to use FaceDeviceModel 2021-03-06 15:36:06 -05:00
Shadowfacts 72b8435834
Add unconnected interface cable 2021-03-06 15:01:22 -05:00
Shadowfacts 5542f088f9
Add programatic model for interface block 2021-03-06 14:23:41 -05:00
Shadowfacts 7cc96d78ad
Delay packets after switching capacity is reached 2021-03-06 14:09:56 -05:00
Shadowfacts da8b600f31
Fix extractors not extracting 2021-03-04 22:24:40 -05:00
Shadowfacts 74aae99b36
Fix screwdriver duplicating terminal buffered items 2021-03-04 22:15:18 -05:00
Shadowfacts f321b2a06a
Add interface priority syncing 2021-03-04 19:44:31 -05:00
35 changed files with 791 additions and 577 deletions

View File

@ -35,7 +35,7 @@ data class Rect(val left: Double, val top: Double, val width: Double, val height
}
operator fun contains(point: Point): Boolean {
return point.x in left..right && point.y in top..bottom
return point.x >= left && point.x < right && point.y >= top && point.y < bottom
}
}

View File

@ -10,6 +10,7 @@ import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Size
import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.util.RenderHelper
import kotlin.math.min
/**
* A simple View that displays text. Allows for controlling the color and shadow of the text. Label cannot be used
@ -26,10 +27,10 @@ import net.shadowfacts.cacao.util.RenderHelper
*/
class Label(
text: Text,
val shadow: Boolean = false,
var shadow: Boolean = false,
val maxLines: Int = 0,
val wrappingMode: WrappingMode = WrappingMode.WRAP,
val textAlignment: TextAlignment = TextAlignment.LEFT
var textAlignment: TextAlignment = TextAlignment.LEFT
): View() {
companion object {
@ -59,7 +60,8 @@ class Label(
var text: Text = text
set(value) {
field = value
updateIntrinsicContentSize()
// todo: uhhhh
updateIntrinsicContentSize(true)
// todo: setNeedsLayout instead of force unwrapping window
window!!.layout()
}
@ -75,15 +77,23 @@ class Label(
override fun wasAdded() {
super.wasAdded()
updateIntrinsicContentSize()
updateIntrinsicContentSize(false)
}
private fun updateIntrinsicContentSize() {
if (RenderHelper.disabled) return
private fun updateIntrinsicContentSize(canWrap: Boolean): Boolean {
if (RenderHelper.disabled) return false
val width = textRenderer.getWidth(text)
val height = textRenderer.fontHeight
intrinsicContentSize = Size(width.toDouble(), height.toDouble())
val oldSize = intrinsicContentSize
if (wrappingMode == WrappingMode.WRAP && canWrap && hasSolver) {
val lines = textRenderer.wrapLines(text, bounds.width.toInt())
val height = (if (maxLines == 0) lines.size else min(lines.size, maxLines)) * textRenderer.fontHeight
intrinsicContentSize = Size(bounds.width, height.toDouble())
} else {
val width = textRenderer.getWidth(text)
val height = textRenderer.fontHeight
intrinsicContentSize = Size(width.toDouble(), height.toDouble())
}
return oldSize != intrinsicContentSize
}
override fun drawContent(matrixStack: MatrixStack, mouse: Point, delta: Float) {
@ -110,14 +120,22 @@ class Label(
super.didLayout()
computeLines()
if (updateIntrinsicContentSize(true)) {
// if the intrinsic content size changes, relayout
window!!.layout()
}
}
private fun computeLines() {
var lines = textRenderer.wrapLines(text, bounds.width.toInt())
if (maxLines > 0 && maxLines < lines.size) {
lines = lines.dropLast(lines.size - maxLines)
if (wrappingMode == WrappingMode.WRAP) {
var lines = textRenderer.wrapLines(text, bounds.width.toInt())
if (maxLines > 0 && maxLines < lines.size) {
lines = lines.dropLast(lines.size - maxLines)
}
this.lines = lines
} else {
this.lines = listOf(text.asOrderedText())
}
this.lines = lines
}
}

View File

@ -48,6 +48,9 @@ open class View(): Responder {
*/
var solver: Solver by solverDelegate
val hasSolver: Boolean
get() = solverDelegate.isInitialized
/**
* Layout anchor for the left edge of this view in the window's coordinate system.
*/
@ -92,17 +95,22 @@ open class View(): Responder {
/**
* The rectangle for this view in the coordinate system of its superview view (or the window, if there is no superview).
* If using constraint based layout, this property is not initialized until [didLayout] called.
* If using constraint based layout, this property has zero dimensions until [didLayout] called.
* Otherwise, this must be set manually.
* Setting this property updates the [bounds].
*/
var frame: Rect by ObservableLateInitProperty { this.bounds = Rect(Point.ORIGIN, it.size) }
var frame = Rect(0.0, 0.0, 0.0, 0.0)
set(value) {
field = value
bounds = Rect(Point.ORIGIN, value.size)
}
/**
* The rectangle for this view in its own coordinate system.
* If using constraint based layout, this property is not initialized until [didLayout] called.
* If using constraint based layout, this property has zero dimensions until [didLayout] called.
* Otherwise, this will be initialized when [frame] is set.
*/
lateinit var bounds: Rect
var bounds = Rect(0.0, 0.0, 0.0, 0.0)
/**
* The position on the Z-axis of this view.
@ -114,7 +122,7 @@ open class View(): Responder {
* The intrinsic size of this view's content. May be null if the view doesn't have any content or there is no
* intrinsic size.
*
* Setting this creates/updates [no.birkett.kiwi.Strength.WEAK] constraints on this view's width/height using
* Setting this creates/updates [no.birkett.kiwi.Strength.MEDIUM] constraints on this view's width/height using
* the size.
*/
var intrinsicContentSize: Size? = null
@ -182,7 +190,7 @@ open class View(): Responder {
subviewsSortedByZIndex = subviews.sortedBy(View::zIndex)
view.superview = this
if (solverDelegate.isInitialized) {
if (hasSolver) {
view.solver = solver
}
view.window = window
@ -293,7 +301,7 @@ open class View(): Responder {
}
private fun updateIntrinsicContentSizeConstraints(old: Size?, new: Size?) {
if (!usesConstraintBasedLayout || !solverDelegate.isInitialized) return
if (!usesConstraintBasedLayout || !hasSolver) return
if (old != null) {
solver.removeConstraint(intrinsicContentSizeWidthConstraint!!)

View File

@ -1,25 +1,31 @@
package net.shadowfacts.cacao.view.button
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.util.Identifier
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Size
import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.texture.Texture
import net.shadowfacts.cacao.view.TextureView
import net.shadowfacts.cacao.view.View
/**
* A button for toggling between on/off states.
*
* @author shadowfacts
* @param initialState Whether the button starts as on or off.
* @param handler The handler function to invoke when this button is pressed.
*/
class ToggleButton(initialState: Boolean): AbstractButton<ToggleButton>(TextureView(if (initialState) ON else OFF).apply {
intrinsicContentSize = Size(19.0, 19.0)
}, padding = 0.0) {
class ToggleButton(
initialState: Boolean,
handler: ((ToggleButton) -> Unit)? = null,
): AbstractButton<ToggleButton>(TextureView(if (initialState) ON else OFF), padding = 0.0) {
companion object {
val ON = Texture(Identifier("asmr", "textures/gui/toggle.png"), 0, 0)
val OFF = Texture(Identifier("asmr", "textures/gui/toggle.png"), 0, 19)
val OFF = Texture(Identifier("textures/gui/checkbox.png"), 0, 0, 64, 64)
val OFF_HOVERED = Texture(Identifier("textures/gui/checkbox.png"), 20, 0, 64, 64)
val ON = Texture(Identifier("textures/gui/checkbox.png"), 0, 20, 64, 64)
val ON_HOVERED = Texture(Identifier("textures/gui/checkbox.png"), 20, 20, 64, 64)
}
private val textureView: TextureView
@ -30,10 +36,15 @@ class ToggleButton(initialState: Boolean): AbstractButton<ToggleButton>(TextureV
* Updating this property updates the button's texture.
*/
var state: Boolean = initialState
set(value) {
field = value
textureView.texture = if (value) ON else OFF
}
init {
this.handler = handler
intrinsicContentSize = Size(20.0, 20.0)
background = null
disabledBackground = null
hoveredBackground = null
}
override fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean {
if (!disabled && (mouseButton == MouseButton.LEFT || mouseButton == MouseButton.RIGHT)) {
@ -43,4 +54,17 @@ class ToggleButton(initialState: Boolean): AbstractButton<ToggleButton>(TextureV
return super.mouseClicked(point, mouseButton)
}
override fun draw(matrixStack: MatrixStack, mouse: Point, delta: Float) {
val hovered = mouse in bounds
textureView.texture = if (state) {
if (hovered) ON_HOVERED else ON
} else {
if (hovered) OFF_HOVERED else OFF
}
super.draw(matrixStack, mouse, delta)
}
override fun getCurrentBackground(mouse: Point) = null
}

View File

@ -7,6 +7,7 @@ import net.shadowfacts.cacao.geometry.Axis
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Rect
import net.shadowfacts.cacao.geometry.Size
import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.texture.NinePatchTexture
import net.shadowfacts.cacao.util.texture.Texture
@ -97,6 +98,7 @@ class TabViewController<T: TabViewController.Tab>(
private lateinit var outerStack: StackView
private lateinit var tabStack: StackView
private lateinit var currentTabController: ViewController
// todo: this shouldn't be public, use layout guides
lateinit var tabVCContainer: View
private set
@ -121,7 +123,11 @@ class TabViewController<T: TabViewController.Tab>(
tabVCContainer.zIndex = 1.0
view.addSubview(tabVCContainer)
embedChild(currentTab.controller, tabVCContainer)
currentTabController = currentTab.controller
currentTabController.willMoveTo(this)
embedChild(currentTabController, tabVCContainer)
currentTabController.didMoveTo(this)
// will/did appear events for the initial VC are provided by this class' implementations of those
view.solver.dsl {
outerStack.leftAnchor equalTo view.leftAnchor
@ -136,6 +142,26 @@ class TabViewController<T: TabViewController.Tab>(
}
}
override fun viewWillAppear() {
super.viewWillAppear()
currentTabController.viewWillAppear()
}
override fun viewDidAppear() {
super.viewDidAppear()
currentTabController.viewDidAppear()
}
override fun viewWillDisappear() {
super.viewWillDisappear()
currentTabController.viewWillDisappear()
}
override fun viewDidDisappear() {
super.viewDidDisappear()
currentTabController.viewDidDisappear()
}
private fun updateTabButtons() {
while (tabStack.arrangedSubviews.isNotEmpty()) tabStack.removeArrangedSubview(tabStack.arrangedSubviews.first())
@ -186,8 +212,20 @@ class TabViewController<T: TabViewController.Tab>(
tabButtons.forEach {
it.setSelected(it.tab === tab)
}
oldTab.controller.removeFromParent()
embedChild(currentTab.controller, tabVCContainer)
currentTabController.viewWillDisappear()
currentTabController.view.removeFromSuperview()
currentTabController.viewDidDisappear()
currentTabController.willMoveTo(null)
currentTabController.removeFromParent()
currentTabController.didMoveTo(null)
currentTabController = currentTab.controller
currentTabController.willMoveTo(this)
embedChild(currentTabController, tabVCContainer)
currentTabController.didMoveTo(this)
currentTabController.viewWillAppear()
currentTabController.viewDidAppear()
onTabChange?.invoke(currentTab)
@ -202,7 +240,7 @@ class TabViewController<T: TabViewController.Tab>(
padding = 2.0
) {
companion object {
val BACKGROUND = Identifier("textures/gui/container/creative_inventory/tabs.png")
val BACKGROUND = Identifier("phycon:textures/gui/tabs.png")
}
private var selected = false
@ -256,7 +294,7 @@ class TabViewController<T: TabViewController.Tab>(
val u = when {
superview == null -> 0
frame.left == 0.0 -> 0
frame.right == superview!!.bounds.right -> 140
frame.right == superview!!.bounds.right -> 56
else -> 28
}
backgroundView.texture = Texture(BACKGROUND, u, v)

View File

@ -1,12 +1,14 @@
package net.shadowfacts.phycon
import net.fabricmc.api.ClientModInitializer
import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
import net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry
import net.shadowfacts.phycon.block.inserter.InserterScreen
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterScreen
import net.shadowfacts.phycon.init.PhyScreens
import net.shadowfacts.phycon.block.terminal.TerminalScreen
import net.shadowfacts.phycon.client.PhyModelProvider
import net.shadowfacts.phycon.networking.ClientReceiver
import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems
@ -16,6 +18,8 @@ import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems
object PhysicalConnectivityClient: ClientModInitializer {
override fun onInitializeClient() {
ModelLoadingRegistry.INSTANCE.registerResourceProvider(::PhyModelProvider)
ScreenRegistry.register(PhyScreens.TERMINAL, ::TerminalScreen)
ScreenRegistry.register(PhyScreens.INSERTER, ::InserterScreen)
ScreenRegistry.register(PhyScreens.REDSTONE_EMITTER, ::RedstoneEmitterScreen)

View File

@ -119,10 +119,11 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
open fun findDestination(): Interface? {
val sides = (cachedState.block as NetworkComponentBlock).getNetworkConnectedSides(cachedState, world!!, pos)
if (sides.size != 1) {
throw RuntimeException("DeviceBlockEntity.findDestination must be overridden by devices which have more than 1 network connected side")
return when (sides.size) {
0 -> null
1 -> NetworkUtil.findConnectedInterface(world!!, pos, sides.first())
else -> throw RuntimeException("DeviceBlockEntity.findDestination must be overridden by devices which have more than 1 network connected side")
}
return NetworkUtil.findConnectedInterface(world!!, pos, sides.first())
}
override fun tick() {

View File

@ -7,6 +7,7 @@ import net.minecraft.item.ItemPlacementContext
import net.minecraft.state.StateManager
import net.minecraft.state.property.EnumProperty
import net.minecraft.state.property.Properties
import net.minecraft.util.StringIdentifiable
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape
@ -26,7 +27,36 @@ import java.util.*
abstract class FaceDeviceBlock<T: DeviceBlockEntity>(settings: Settings): DeviceBlock<T>(settings) {
companion object {
val FACING = Properties.FACING
val CABLE_CONNECTION = EnumProperty.of("cable_connection", Direction::class.java)
val CABLE_CONNECTION = EnumProperty.of("cable_connection", FaceCableConnection::class.java)
}
enum class FaceCableConnection : StringIdentifiable {
NONE, DOWN, UP, NORTH, SOUTH, WEST, EAST;
companion object {
fun from(dir: Direction?) = when (dir) {
null -> NONE
Direction.DOWN -> DOWN
Direction.UP -> UP
Direction.NORTH -> NORTH
Direction.SOUTH -> SOUTH
Direction.WEST -> WEST
Direction.EAST -> EAST
}
}
val direction: Direction?
get() = when (this) {
NONE -> null
DOWN -> Direction.DOWN
UP -> Direction.UP
NORTH -> Direction.NORTH
SOUTH -> Direction.SOUTH
WEST -> Direction.WEST
EAST -> Direction.EAST
}
override fun asString() = name.toLowerCase()
}
protected abstract val faceThickness: Double
@ -41,20 +71,21 @@ abstract class FaceDeviceBlock<T: DeviceBlockEntity>(settings: Settings): Device
Direction.EAST to createCuboidShape(6.0, 6.0, 6.0, 16.0 - faceThickness, 10.0, 10.0)
)
}
private val shapeCache = mutableMapOf<Pair<Direction, Direction>, VoxelShape>()
private val shapeCache = mutableMapOf<Pair<Direction, FaceCableConnection>, VoxelShape>()
fun getShape(facing: Direction, cableConnection: Direction): VoxelShape {
private fun getShape(facing: Direction, cableConnection: FaceCableConnection): VoxelShape {
return shapeCache.getOrPut(facing to cableConnection) {
VoxelShapes.union(
faceShapes[facing],
centerShapes[facing],
CableBlock.SIDE_SHAPES[cableConnection]
)
if (cableConnection == FaceCableConnection.NONE) {
VoxelShapes.union(faceShapes[facing], centerShapes[facing])
} else {
VoxelShapes.union(faceShapes[facing], centerShapes[facing], CableBlock.SIDE_SHAPES[cableConnection.direction])
}
}
}
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection<Direction> {
return EnumSet.of(state[CABLE_CONNECTION])
val direction = state[CABLE_CONNECTION].direction
return if (direction != null) EnumSet.of(direction) else setOf()
}
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: WorldAccess, pos: BlockPos): Interface? {
@ -73,13 +104,13 @@ abstract class FaceDeviceBlock<T: DeviceBlockEntity>(settings: Settings): Device
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
val cableConnection = FaceCableConnection.from(getCableConnectedSide(context.world, context.blockPos, facing))
return defaultState
.with(FACING, facing)
.with(CABLE_CONNECTION, cableConnection)
}
protected fun getCableConnectedSide(world: World, pos: BlockPos, facing: Direction): Direction? {
private fun getCableConnectedSide(world: WorldAccess, pos: BlockPos, facing: Direction): Direction? {
for (side in Direction.values()) {
if (side == facing) {
continue
@ -95,10 +126,21 @@ abstract class FaceDeviceBlock<T: DeviceBlockEntity>(settings: Settings): Device
}
override fun getStateForNeighborUpdate(state: BlockState, side: Direction, neighborState: BlockState, world: WorldAccess, pos: BlockPos, neighborPos: BlockPos): BlockState {
if (neighborState.block is NetworkComponentBlock && world.getBlockState(pos.offset(state[CABLE_CONNECTION])).block !is NetworkComponentBlock) {
return state.with(CABLE_CONNECTION, side)
val current = state[CABLE_CONNECTION]
var newConnection = current
if (current == FaceCableConnection.NONE) {
if (neighborState.block is NetworkComponentBlock) {
newConnection = FaceCableConnection.from(side)
}
} else {
val currentConnectedPos = pos.offset(current.direction)
if (neighborPos == currentConnectedPos && neighborState.block !is NetworkComponentBlock) {
// the old cable connection is no longer correct, try to find another
newConnection = FaceCableConnection.from(getCableConnectedSide(world, pos, state[FACING]))
}
}
return state
return state.with(CABLE_CONNECTION, newConnection)
}
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape {

View File

@ -14,6 +14,7 @@ import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.component.ActivationController
import net.shadowfacts.phycon.component.NetworkStackDispatcher
import net.shadowfacts.phycon.component.finishTimedOutPendingInsertions
import net.shadowfacts.phycon.component.handleItemStack
import net.shadowfacts.phycon.packet.CapacityPacket
import net.shadowfacts.phycon.packet.ItemStackPacket
@ -84,6 +85,8 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
if (!world!!.isClient) {
controller.tick()
finishTimedOutPendingInsertions()
}
}

View File

@ -34,6 +34,7 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE),
override var providerPriority = 0
override var receiverPriority = 0
var syncPriorities = true
// todo: should this be a weak ref?
private var inventory: GroupedItemInv? = null
@ -121,11 +122,13 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE),
override fun writeDeviceConfiguration(tag: CompoundTag) {
tag.putInt("ProviderPriority", providerPriority)
tag.putInt("ReceiverPriority", receiverPriority)
tag.putBoolean("SyncPriorities", syncPriorities)
}
override fun loadDeviceConfiguration(tag: CompoundTag) {
providerPriority = tag.getInt("ProviderPriority")
receiverPriority = tag.getInt("ReceiverPriority")
syncPriorities = tag.getBoolean("SyncPriorities")
}
}

View File

@ -3,19 +3,24 @@ package net.shadowfacts.phycon.block.netswitch
import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable
import net.minecraft.block.BlockState
import net.minecraft.block.entity.BlockEntity
import net.minecraft.entity.ItemEntity
import net.minecraft.item.ItemStack
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.util.Tickable
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.frame.EthernetFrame
import net.shadowfacts.phycon.api.frame.PacketFrame
import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.api.util.MACAddress
import net.shadowfacts.phycon.frame.BasePacketFrame
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.util.NetworkUtil
import net.shadowfacts.phycon.packet.ItemStackPacket
import net.shadowfacts.phycon.util.NetworkUtil
import java.lang.ref.WeakReference
import java.util.Deque
import java.util.LinkedList
/**
* @author shadowfacts
@ -25,13 +30,14 @@ class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
Tickable {
companion object {
var SWITCHING_CAPACITY = 256 // 256 items/tick
var SWITCHING_CAPACITY = 256 // 256 packets/tick
}
val interfaces = Direction.values().map { SwitchInterface(it, WeakReference(this), MACAddress.random()) }
private val macTable = mutableMapOf<MACAddress, Direction>()
private var itemsHandledThisTick = 0
private var packetsHandledThisTick = 0
private var delayedPackets: Deque<Pair<PacketFrame, SwitchInterface>> = LinkedList()
fun interfaceForSide(side: Direction): SwitchInterface {
return interfaces.find { it.side == side }!!
@ -40,18 +46,20 @@ class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
private fun handle(frame: EthernetFrame, fromItf: SwitchInterface) {
macTable[frame.source] = fromItf.side
if (frame is PacketFrame && frame.packet is ItemStackPacket) {
val packet = frame.packet as ItemStackPacket
if (itemsHandledThisTick + packet.stack.count > SWITCHING_CAPACITY) {
// todo: calculate entity spawn point by finding non-obstructed location
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), packet.stack)
world!!.spawnEntity(entity)
if (frame is PacketFrame) {
if (packetsHandledThisTick > SWITCHING_CAPACITY) {
PhysicalConnectivity.NETWORK_LOGGER.debug("{} reached capacity, delaying forwarding {}", this, frame)
delayedPackets.addLast(frame to fromItf)
return
} else {
itemsHandledThisTick += packet.stack.count
packetsHandledThisTick++
}
}
resend(frame, fromItf)
}
private fun resend(frame: EthernetFrame, fromItf: SwitchInterface) {
if (frame.destination.type != MACAddress.Type.BROADCAST && macTable.containsKey(frame.destination)) {
val dir = macTable[frame.destination]!!
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}, {}) forwarding {} to side {}", this, fromItf.side, fromItf.macAddress, frame, dir)
@ -75,11 +83,30 @@ class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
}
override fun tick() {
itemsHandledThisTick = 0
packetsHandledThisTick = 0
while (delayedPackets.isNotEmpty() && packetsHandledThisTick <= SWITCHING_CAPACITY) {
val (frame, fromItf) = delayedPackets.pop()
resend(frame, fromItf)
}
}
override fun toTag(tag: CompoundTag): CompoundTag {
tag.putLongArray("InterfaceAddresses", interfaces.map { it.macAddress.address })
val list = ListTag()
for ((frame, fromItf) in delayedPackets) {
val packet = frame.packet
if (packet !is ItemStackPacket) continue
val compound = CompoundTag()
compound.putInt("FromItfSide", fromItf.side.ordinal)
compound.putInt("SourceIP", packet.source.address)
compound.putInt("DestinationIP", packet.destination.address)
compound.putLong("SourceMAC", frame.source.address)
compound.putLong("DestinationMAC", frame.destination.address)
compound.put("Stack", packet.stack.toTag(CompoundTag()))
list.add(compound)
}
tag.put("DelayedStackPackets", list)
return super.toTag(tag)
}
@ -88,6 +115,21 @@ class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
tag.getLongArray("InterfaceAddresses")?.forEachIndexed { i, l ->
interfaces[i].macAddress = MACAddress(l)
}
tag.getList("DelayedStackPackets", 10).forEach { it ->
val compound = it as CompoundTag
val fromItfSide = Direction.values()[compound.getInt("FromItfSide")]
val fromItf = interfaces.find { it.side == fromItfSide }!!
val sourceIP = IPAddress(compound.getInt("SourceIP"))
val destinationIP = IPAddress(compound.getInt("DestinationIP"))
val sourceMAC = MACAddress(compound.getLong("SourceMAC"))
val destinationMAC = MACAddress(compound.getLong("DestinationMAC"))
val stack = ItemStack.fromTag(compound.getCompound("Stack"))
if (!stack.isEmpty) {
val packet = ItemStackPacket(stack, sourceIP, destinationIP)
val frame = BasePacketFrame(packet, sourceMAC, destinationMAC)
delayedPackets.addLast(frame to fromItf)
}
}
}
override fun toClientTag(tag: CompoundTag): CompoundTag {

View File

@ -2,13 +2,20 @@ package net.shadowfacts.phycon.block.terminal
import alexiil.mc.lib.attributes.AttributeList
import alexiil.mc.lib.attributes.AttributeProvider
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.Material
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.server.world.ServerWorld
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.state.StateManager
import net.minecraft.state.property.Properties
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.ItemScatterer
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
@ -18,7 +25,7 @@ import net.minecraft.world.WorldAccess
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.NetworkComponentBlock
import net.shadowfacts.phycon.block.DeviceBlock
import java.util.*
import java.util.EnumSet
/**
* @author shadowfacts
@ -33,10 +40,22 @@ class TerminalBlock: DeviceBlock<TerminalBlockEntity>(
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "terminal")
val FACING = Properties.FACING
}
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection<Direction> {
return EnumSet.allOf(Direction::class.java)
val set = EnumSet.allOf(Direction::class.java)
set.remove(state[FACING])
return set
}
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
super.appendProperties(builder)
builder.add(FACING)
}
override fun getPlacementState(context: ItemPlacementContext): BlockState {
return defaultState.with(FACING, context.playerLookDirection.opposite)
}
override fun createBlockEntity(world: BlockView) = TerminalBlockEntity()

View File

@ -0,0 +1,32 @@
package net.shadowfacts.phycon.client
import net.fabricmc.fabric.api.client.model.ModelProviderContext
import net.fabricmc.fabric.api.client.model.ModelResourceProvider
import net.minecraft.client.render.model.UnbakedModel
import net.minecraft.resource.ResourceManager
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.client.model.RedstoneControllerModel
import net.shadowfacts.phycon.client.model.SimpleFaceDeviceModel
/**
* @author shadowfacts
*/
class PhyModelProvider(resourceManager: ResourceManager) : ModelResourceProvider {
companion object {
val INTERFACE = Identifier(PhysicalConnectivity.MODID, "block/network_interface")
val INTERFACE_SIDE = Identifier(PhysicalConnectivity.MODID, "block/interface_side")
val REDSTONE_CONTROLLER = Identifier(PhysicalConnectivity.MODID, "block/redstone_controller")
val REDSTONE_EMITTER = Identifier(PhysicalConnectivity.MODID, "block/redstone_emitter")
val REDSTONE_EMITTER_SIDE = Identifier(PhysicalConnectivity.MODID, "block/redstone_emitter_side")
}
override fun loadModelResource(resourceId: Identifier, context: ModelProviderContext): UnbakedModel? {
return when (resourceId) {
INTERFACE -> SimpleFaceDeviceModel(INTERFACE_SIDE)
REDSTONE_CONTROLLER -> RedstoneControllerModel
REDSTONE_EMITTER -> SimpleFaceDeviceModel(REDSTONE_EMITTER_SIDE)
else -> null
}
}
}

View File

@ -0,0 +1,167 @@
package net.shadowfacts.phycon.client.model
import net.minecraft.block.BlockState
import net.minecraft.client.render.model.*
import net.minecraft.client.texture.Sprite
import net.minecraft.client.util.SpriteIdentifier
import net.minecraft.util.Identifier
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.block.FaceDeviceBlock
import net.shadowfacts.phycon.block.FaceDeviceBlock.FaceCableConnection
import java.util.Random
import java.util.function.Function
/**
* @author shadowfacts
*/
abstract class FaceDeviceModel: UnbakedModel, BakedModel {
private val interfaceCableStraightID = Identifier(PhysicalConnectivity.MODID, "block/interface_cable_straight")
private val interfaceCableCornerID = Identifier(PhysicalConnectivity.MODID, "block/interface_cable_corner")
private val interfaceCableCorner2ID = Identifier(PhysicalConnectivity.MODID, "block/interface_cable_corner_2")
private val interfaceCableCapID = Identifier(PhysicalConnectivity.MODID, "block/interface_cable_cap")
private var interfaceCableStraight = Array<BakedModel?>(6) { null }
private var interfaceCableCap = Array<BakedModel?>(6) { null }
private var interfaceCableCorner = mutableMapOf<ModelRotation, BakedModel>()
private var interfaceCableCorner2 = mutableMapOf<ModelRotation, BakedModel>()
protected val defaultRotations = listOf(
ModelRotation.X0_Y0,
ModelRotation.X180_Y0,
ModelRotation.X270_Y0,
ModelRotation.X90_Y0,
ModelRotation.X90_Y90,
ModelRotation.X90_Y270,
)
abstract fun getSideModelIDs(): Collection<Identifier>
abstract fun bakeSideModels(loader: ModelLoader)
abstract fun getSideModel(state: BlockState): BakedModel?
override fun getModelDependencies(): Collection<Identifier> {
return getSideModelIDs() + listOf(
interfaceCableStraightID,
interfaceCableCornerID,
interfaceCableCorner2ID,
interfaceCableCapID
)
}
override fun getTextureDependencies(
unbakedModelGetter: Function<Identifier, UnbakedModel>,
unresolvedTextureReferences: MutableSet<com.mojang.datafixers.util.Pair<String, String>>
): Collection<SpriteIdentifier> {
return modelDependencies.map(unbakedModelGetter::apply).flatMap { it.getTextureDependencies(unbakedModelGetter, unresolvedTextureReferences) }
}
override fun bake(loader: ModelLoader, textureGetter: Function<SpriteIdentifier, Sprite>, rotationContainer: ModelBakeSettings, modelId: Identifier): BakedModel {
bakeSideModels(loader)
defaultRotations.forEachIndexed { i, rot ->
interfaceCableStraight[i] = loader.bake(interfaceCableStraightID, rot)
interfaceCableCap[i] = loader.bake(interfaceCableCapID, rot)
}
mapOf(
interfaceCableCorner to interfaceCableCornerID to ModelRotation.values().toList(),
interfaceCableCorner2 to interfaceCableCorner2ID to listOf(
ModelRotation.X0_Y0,
ModelRotation.X0_Y90,
ModelRotation.X0_Y180,
ModelRotation.X0_Y270,
ModelRotation.X180_Y0,
ModelRotation.X180_Y90,
ModelRotation.X180_Y180,
ModelRotation.X180_Y270,
),
).forEach { (k, rotations) ->
val (map, id) = k
map.clear()
rotations.forEach { rot ->
val model = loader.bake(id, rot)
if (model == null) map.remove(rot)
else map[rot] = model
}
}
return this
}
override fun getQuads(state: BlockState?, face: Direction?, random: Random): List<BakedQuad> {
if (state == null) return listOf()
val facing = state[FaceDeviceBlock.FACING]
val connection = state[FaceDeviceBlock.CABLE_CONNECTION]
val sideQuads = getSideModel(state)?.getQuads(state, face, random) ?: listOf()
val cableQuads = if (connection.direction == facing.opposite) {
interfaceCableStraight[facing.ordinal]?.getQuads(state, face, random) ?: listOf()
} else if (connection == FaceCableConnection.NONE) {
interfaceCableCap[facing.ordinal]?.getQuads(state, face, random) ?: listOf()
} else {
val model = when (facing) {
Direction.DOWN -> when (connection) {
FaceCableConnection.NORTH -> interfaceCableCorner[ModelRotation.X0_Y0]
FaceCableConnection.EAST -> interfaceCableCorner[ModelRotation.X0_Y90]
FaceCableConnection.SOUTH -> interfaceCableCorner[ModelRotation.X0_Y180]
FaceCableConnection.WEST -> interfaceCableCorner[ModelRotation.X0_Y270]
else -> null
}
Direction.UP -> when (connection) {
FaceCableConnection.NORTH -> interfaceCableCorner[ModelRotation.X180_Y180]
FaceCableConnection.EAST -> interfaceCableCorner[ModelRotation.X180_Y270]
FaceCableConnection.SOUTH -> interfaceCableCorner[ModelRotation.X180_Y0]
FaceCableConnection.WEST -> interfaceCableCorner[ModelRotation.X180_Y90]
else -> null
}
Direction.NORTH -> when (connection) {
FaceCableConnection.UP -> interfaceCableCorner[ModelRotation.X270_Y0]
FaceCableConnection.EAST -> interfaceCableCorner2[ModelRotation.X180_Y180]
FaceCableConnection.DOWN -> interfaceCableCorner[ModelRotation.X90_Y180]
FaceCableConnection.WEST -> interfaceCableCorner2[ModelRotation.X0_Y0]
else -> null
}
Direction.SOUTH -> when (connection) {
FaceCableConnection.UP -> interfaceCableCorner[ModelRotation.X270_Y180]
FaceCableConnection.WEST -> interfaceCableCorner2[ModelRotation.X180_Y0]
FaceCableConnection.DOWN -> interfaceCableCorner[ModelRotation.X90_Y0]
FaceCableConnection.EAST -> interfaceCableCorner2[ModelRotation.X0_Y180]
else -> null
}
Direction.WEST -> when (connection) {
FaceCableConnection.UP -> interfaceCableCorner[ModelRotation.X270_Y270]
FaceCableConnection.NORTH -> interfaceCableCorner2[ModelRotation.X180_Y90]
FaceCableConnection.DOWN -> interfaceCableCorner[ModelRotation.X90_Y90]
FaceCableConnection.SOUTH -> interfaceCableCorner2[ModelRotation.X0_Y270]
else -> null
}
Direction.EAST -> when (connection) {
FaceCableConnection.UP -> interfaceCableCorner[ModelRotation.X270_Y90]
FaceCableConnection.SOUTH -> interfaceCableCorner2[ModelRotation.X180_Y270]
FaceCableConnection.DOWN -> interfaceCableCorner[ModelRotation.X90_Y270]
FaceCableConnection.NORTH -> interfaceCableCorner2[ModelRotation.X0_Y90]
else -> null
}
else -> null
}
model?.getQuads(state, face, random) ?: listOf()
}
return sideQuads + cableQuads
}
override fun useAmbientOcclusion() = true
override fun hasDepth() = false
override fun isSideLit() = false
override fun isBuiltin() = false
abstract override fun getSprite(): Sprite
override fun getTransformation() = null
override fun getOverrides() = null
}

View File

@ -0,0 +1,45 @@
package net.shadowfacts.phycon.client.model
import net.minecraft.block.BlockState
import net.minecraft.client.render.model.BakedModel
import net.minecraft.client.render.model.ModelLoader
import net.minecraft.client.texture.Sprite
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.block.FaceDeviceBlock
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock
/**
* @author shadowfacts
*/
object RedstoneControllerModel: FaceDeviceModel() {
private val ON = Identifier(PhysicalConnectivity.MODID, "block/redstone_controller_side_on")
private val OFF = Identifier(PhysicalConnectivity.MODID, "block/redstone_controller_side_off")
private val onModels = Array<BakedModel?>(6) { null }
private val offModels = Array<BakedModel?>(6) { null }
override fun getSideModelIDs(): Collection<Identifier> {
return listOf(ON, OFF)
}
override fun bakeSideModels(loader: ModelLoader) {
defaultRotations.forEachIndexed { i, rot ->
onModels[i] = loader.bake(ON, rot)
offModels[i] = loader.bake(OFF, rot)
}
}
override fun getSideModel(state: BlockState): BakedModel? {
return if (state[RedstoneControllerBlock.POWERED]) {
onModels[state[FaceDeviceBlock.FACING].ordinal]
} else {
offModels[state[FaceDeviceBlock.FACING].ordinal]
}
}
override fun getSprite(): Sprite {
return offModels.first()!!.sprite
}
}

View File

@ -0,0 +1,35 @@
package net.shadowfacts.phycon.client.model
import net.minecraft.block.BlockState
import net.minecraft.client.render.model.BakedModel
import net.minecraft.client.render.model.ModelLoader
import net.minecraft.client.texture.Sprite
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.block.FaceDeviceBlock
/**
* @author shadowfacts
*/
class SimpleFaceDeviceModel(
private val sideModelID: Identifier,
): FaceDeviceModel() {
private val sideModels = Array<BakedModel?>(6) { null }
override fun getSideModelIDs(): Collection<Identifier> {
return listOf(sideModelID)
}
override fun bakeSideModels(loader: ModelLoader) {
defaultRotations.forEachIndexed { i, rot ->
sideModels[i] = loader.bake(sideModelID, rot)
}
}
override fun getSideModel(state: BlockState): BakedModel? {
return sideModels[state[FaceDeviceBlock.FACING].ordinal]
}
override fun getSprite(): Sprite {
return sideModels.first()!!.sprite
}
}

View File

@ -4,6 +4,7 @@ import net.minecraft.item.BlockItem
import net.minecraft.item.Item
import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.item.ConsoleItem
import net.shadowfacts.phycon.item.ScrewdriverItem
import net.shadowfacts.phycon.block.cable.CableBlock
@ -34,6 +35,7 @@ object PhyItems {
val SCREWDRIVER = ScrewdriverItem()
val CONSOLE = ConsoleItem()
val TWISTED_PAIR = Item(Item.Settings())
fun init() {
register(InterfaceBlock.ID, INTERFACE)
@ -48,6 +50,7 @@ object PhyItems {
register(ScrewdriverItem.ID, SCREWDRIVER)
register(ConsoleItem.ID, CONSOLE)
register(Identifier(PhysicalConnectivity.MODID, "twisted_pair"), TWISTED_PAIR)
}
private fun register(id: Identifier, item: Item) {

View File

@ -11,6 +11,7 @@ import net.minecraft.util.ActionResult
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.block.DeviceBlock
import net.shadowfacts.phycon.init.PhyBlocks
/**
* @author shadowfacts
@ -35,6 +36,11 @@ class ScrewdriverItem: Item(Settings()) {
beTag.remove("y")
beTag.remove("z")
if (block === PhyBlocks.TERMINAL) {
// remove the terminal's internal buffer since it drops its items
beTag.remove("InternalBuffer")
}
val entity = ItemEntity(context.world, context.blockPos.x.toDouble(), context.blockPos.y.toDouble(), context.blockPos.z.toDouble(), stack)
context.world.spawnEntity(entity)

View File

@ -3,11 +3,16 @@ package net.shadowfacts.phycon.screen.console
import net.minecraft.block.entity.BlockEntity
import net.minecraft.client.MinecraftClient
import net.minecraft.text.TranslatableText
import net.shadowfacts.cacao.geometry.Axis
import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.view.Label
import net.shadowfacts.cacao.view.StackView
import net.shadowfacts.cacao.view.View
import net.shadowfacts.cacao.view.button.ToggleButton
import net.shadowfacts.cacao.view.textfield.NumberField
import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.block.netinterface.InterfaceBlockEntity
import net.shadowfacts.phycon.component.NetworkStackProvider
import net.shadowfacts.phycon.networking.C2SConfigureDevice
@ -18,6 +23,9 @@ class ProviderViewController<T>(
private val device: T
): ViewController() where T: BlockEntity, T: NetworkStackProvider {
private lateinit var field: NumberField
private var syncButton: ToggleButton? = null
override fun viewDidLoad() {
super.viewDidLoad()
@ -26,9 +34,14 @@ class ProviderViewController<T>(
}
view.addSubview(label)
val field = NumberField(device.providerPriority) {
field = NumberField(device.providerPriority) {
if (it.number != null) {
device.providerPriority = it.number!!
if (device is InterfaceBlockEntity && device.syncPriorities) {
device.receiverPriority = it.number!!
}
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device))
}
}
@ -39,6 +52,30 @@ class ProviderViewController<T>(
}
view.addSubview(desc)
if (device is InterfaceBlockEntity) {
val syncLabel = Label(TranslatableText("gui.phycon.console.provider.sync")).apply {
textColor = Color.TEXT
textAlignment = Label.TextAlignment.RIGHT
}
view.addSubview(syncLabel)
val syncButton = ToggleButton(device.syncPriorities) {
device.syncPriorities = it.state
device.receiverPriority = device.providerPriority
}
this.syncButton = syncButton
view.addSubview(syncButton)
view.solver.dsl {
syncButton.topAnchor equalTo (desc.bottomAnchor + 4)
syncButton.leftAnchor equalTo field.leftAnchor
syncLabel.centerYAnchor equalTo syncButton.centerYAnchor
syncLabel.leftAnchor equalTo view.leftAnchor
syncLabel.rightAnchor equalTo (syncButton.leftAnchor - 4)
}
}
view.solver.dsl {
field.widthAnchor equalTo 100
field.heightAnchor equalTo 20
@ -54,4 +91,13 @@ class ProviderViewController<T>(
}
}
override fun viewWillAppear() {
super.viewWillAppear()
field.number = device.providerPriority
if (device is InterfaceBlockEntity) {
syncButton!!.state = device.syncPriorities
}
}
}

View File

@ -5,9 +5,11 @@ import net.minecraft.client.MinecraftClient
import net.minecraft.text.TranslatableText
import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.view.Label
import net.shadowfacts.cacao.view.button.ToggleButton
import net.shadowfacts.cacao.view.textfield.NumberField
import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.block.netinterface.InterfaceBlockEntity
import net.shadowfacts.phycon.component.NetworkStackReceiver
import net.shadowfacts.phycon.networking.C2SConfigureDevice
@ -18,6 +20,9 @@ class ReceiverViewController<T>(
private val device: T
): ViewController() where T: BlockEntity, T: NetworkStackReceiver {
private lateinit var field: NumberField
private var syncButton: ToggleButton? = null
override fun viewDidLoad() {
super.viewDidLoad()
@ -26,9 +31,14 @@ class ReceiverViewController<T>(
}
view.addSubview(label)
val field = NumberField(device.receiverPriority) {
field = NumberField(device.receiverPriority) {
if (it.number != null) {
device.receiverPriority = it.number!!
if (device is InterfaceBlockEntity && device.syncPriorities) {
device.providerPriority = it.number!!
}
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device))
}
}
@ -39,6 +49,30 @@ class ReceiverViewController<T>(
}
view.addSubview(desc)
if (device is InterfaceBlockEntity) {
val syncLabel = Label(TranslatableText("gui.phycon.console.receiver.sync")).apply {
textColor = Color.TEXT
textAlignment = Label.TextAlignment.RIGHT
}
view.addSubview(syncLabel)
val syncButton = ToggleButton(device.syncPriorities) {
device.syncPriorities = it.state
device.providerPriority = device.receiverPriority
}
this.syncButton = syncButton
view.addSubview(syncButton)
view.solver.dsl {
syncButton.topAnchor equalTo (desc.bottomAnchor + 4)
syncButton.leftAnchor equalTo field.leftAnchor
syncLabel.centerYAnchor equalTo syncButton.centerYAnchor
syncLabel.leftAnchor equalTo view.leftAnchor
syncLabel.rightAnchor equalTo (syncButton.leftAnchor - 4)
}
}
view.solver.dsl {
field.widthAnchor equalTo 100
field.heightAnchor equalTo 20
@ -54,4 +88,13 @@ class ReceiverViewController<T>(
}
}
override fun viewWillAppear() {
super.viewWillAppear()
field.number = device.receiverPriority
if (device is InterfaceBlockEntity) {
syncButton!!.state = device.syncPriorities
}
}
}

View File

@ -1,159 +1,8 @@
{
"multipart": [
{
"apply": { "model": "phycon:block/cable_center" }
},
{
"when": { "facing": "down" },
"apply": { "model": "phycon:block/interface_side" }
},
{
"when": { "facing": "up" },
"apply": { "model": "phycon:block/interface_side", "x": 180 }
},
{
"when": { "facing": "north" },
"apply": { "model": "phycon:block/interface_side", "x": 270 }
},
{
"when": { "facing": "south" },
"apply": { "model": "phycon:block/interface_side", "x": 90 }
},
{
"when": { "facing": "west" },
"apply": { "model": "phycon:block/interface_side", "x": 90, "y": 90 }
},
{
"when": { "facing": "east" },
"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 }
"_comment": "see SimpleFaceDeviceModel",
"apply": { "model": "phycon:block/network_interface" }
}
]
}

View File

@ -1,183 +1,8 @@
{
"multipart": [
{
"apply": { "model": "phycon:block/cable_center" }
},
{
"when": { "facing": "down", "powered": "false" },
"apply": { "model": "phycon:block/redstone_controller_side_off" }
},
{
"when": { "facing": "up", "powered": "false" },
"apply": { "model": "phycon:block/redstone_controller_side_off", "x": 180 }
},
{
"when": { "facing": "north", "powered": "false" },
"apply": { "model": "phycon:block/redstone_controller_side_off", "x": 270 }
},
{
"when": { "facing": "south", "powered": "false" },
"apply": { "model": "phycon:block/redstone_controller_side_off", "x": 90 }
},
{
"when": { "facing": "west", "powered": "false" },
"apply": { "model": "phycon:block/redstone_controller_side_off", "x": 90, "y": 90 }
},
{
"when": { "facing": "east", "powered": "false" },
"apply": { "model": "phycon:block/redstone_controller_side_off", "x": 90, "y": 270 }
},
{
"when": { "facing": "down", "powered": "true" },
"apply": { "model": "phycon:block/redstone_controller_side_on" }
},
{
"when": { "facing": "up", "powered": "true" },
"apply": { "model": "phycon:block/redstone_controller_side_on", "x": 180 }
},
{
"when": { "facing": "north", "powered": "true" },
"apply": { "model": "phycon:block/redstone_controller_side_on", "x": 270 }
},
{
"when": { "facing": "south", "powered": "true" },
"apply": { "model": "phycon:block/redstone_controller_side_on", "x": 90 }
},
{
"when": { "facing": "west", "powered": "true" },
"apply": { "model": "phycon:block/redstone_controller_side_on", "x": 90, "y": 90 }
},
{
"when": { "facing": "east", "powered": "true" },
"apply": { "model": "phycon:block/redstone_controller_side_on", "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 }
"_comment": "see RedstoneControllerModel",
"apply": { "model": "phycon:block/redstone_controller" }
}
]
}

View File

@ -1,159 +1,7 @@
{
"multipart": [
{
"apply": { "model": "phycon:block/cable_center" }
},
{
"when": { "facing": "down" },
"apply": { "model": "phycon:block/redstone_emitter_side" }
},
{
"when": { "facing": "up" },
"apply": { "model": "phycon:block/redstone_emitter_side", "x": 180 }
},
{
"when": { "facing": "north" },
"apply": { "model": "phycon:block/redstone_emitter_side", "x": 270 }
},
{
"when": { "facing": "south" },
"apply": { "model": "phycon:block/redstone_emitter_side", "x": 90 }
},
{
"when": { "facing": "west" },
"apply": { "model": "phycon:block/redstone_emitter_side", "x": 90, "y": 90 }
},
{
"when": { "facing": "east" },
"apply": { "model": "phycon:block/redstone_emitter_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 }
"apply": { "model": "phycon:block/redstone_emitter" }
}
]
}

View File

@ -11,6 +11,7 @@
"item.phycon.screwdriver": "Screwdriver",
"item.phycon.console": "Console",
"item.phycon.twisted_pair": "Twisted Pair",
"gui.phycon.terminal_buffer": "Buffer",
"gui.phycon.console.details": "Device Details",
@ -24,9 +25,11 @@
"gui.phycon.console.provider": "Item Provider",
"gui.phycon.console.provider.priority": "Provider Priority",
"gui.phycon.console.provider.priority_desc": "When a device requests items from the network, it send requests to providers (e.g., interfaces) with higher priorities first. Priorities can be negative.",
"gui.phycon.console.provider.sync": "Sync with Receiver Priority",
"gui.phycon.console.receiver": "Item Receiver",
"gui.phycon.console.receiver.priority": "Receiver Priority",
"gui.phycon.console.receiver.priority_desc": "When a device puts items into the network, it starts with receiver (e.g., interfaces) with higher priorities. Priorities can be negative.",
"gui.phycon.console.receiver.sync": "Sync with Provider Priority",
"gui.phycon.redstone_mode.high": "High",
"gui.phycon.redstone_mode.low": "Low",
"gui.phycon.redstone_mode.toggle": "Toggle",

View File

@ -0,0 +1,29 @@
{
"textures": {
"cap": "phycon:block/cable_cap_end",
"straight": "phycon:block/cable_straight"
},
"elements": [
{
"from": [6, 6, 6],
"to": [10, 9, 10],
"faces": {
"north": {"texture": "#straight"},
"south": {"texture": "#straight"},
"west": {"texture": "#straight"},
"east": {"texture": "#straight"}
}
},
{
"from": [6, 9, 6],
"to": [10, 10, 10],
"faces": {
"up": {"texture": "#cap"},
"north": {"texture": "#cap"},
"south": {"texture": "#cap"},
"west": {"texture": "#cap"},
"east": {"texture": "#cap"}
}
}
]
}

View File

@ -1,22 +0,0 @@
{
"textures": {
"straight": "phycon:block/cable_straight_rotated",
"cap": "phycon:block/cable_cap_end",
"corner_down": "phycon:block/interface_cable_corner_l_up",
"corner_up": "phycon:block/interface_cable_corner_r"
},
"elements": [
{
"from": [0, 6, 6],
"to": [10, 10, 10],
"faces": {
"down": {"texture": "#corner_down"},
"up": {"texture": "#corner_up"},
"north": {"texture": "#straight"},
"south": {"texture": "#straight"},
"west": {"texture": "#cap", "cullface": "west"},
"east": {"texture": "#straight"}
}
}
]
}

View File

@ -4,7 +4,8 @@
"cable": "phycon:block/cable_straight",
"front": "phycon:block/redstone_controller_front_off",
"back": "phycon:block/redstone_controller_back",
"side": "phycon:block/redstone_controller_back"
"side": "phycon:block/redstone_controller_back",
"particle": "#front"
},
"elements": [
{

View File

@ -4,7 +4,8 @@
"cable": "phycon:block/cable_straight",
"front": "phycon:block/redstone_controller_front_on",
"back": "phycon:block/redstone_controller_back",
"side": "phycon:block/redstone_controller_back"
"side": "phycon:block/redstone_controller_back",
"particle": "#front"
},
"elements": [
{

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "phycon:item/twisted_pair"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 709 B

View File

@ -0,0 +1,32 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [
"phycon:cable"
]
},
"criteria": {
"has_twisted_pair": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"item": "phycon:twisted_pair"
}
]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": {
"recipe": "phycon:cable"
}
}
},
"requirements": [
[
"has_twisted_pair",
"has_the_recipe"
]
]
}

View File

@ -0,0 +1,32 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [
"phycon:twisted_pair"
]
},
"criteria": {
"has_copper_nuggets": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"tag": "c:copper_nuggets"
}
]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": {
"recipe": "phycon:twisted_pair"
}
}
},
"requirements": [
[
"has_copper_nuggets",
"has_the_recipe"
]
]
}

View File

@ -0,0 +1,17 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"CCC",
"TRT",
"CCC"
],
"key": {
"C": {"tag": "minecraft:carpets"},
"T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"}
},
"result": {
"item": "phycon:cable",
"count": 3
}
}

View File

@ -0,0 +1,14 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"NNN",
" ",
"NNN"
],
"key": {
"N": {"tag": "c:copper_nuggets"}
},
"result": {
"item": "phycon:twisted_pair"
}
}