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 { 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.geometry.Size
import net.shadowfacts.cacao.util.Color import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.util.RenderHelper 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 * 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( class Label(
text: Text, text: Text,
val shadow: Boolean = false, var shadow: Boolean = false,
val maxLines: Int = 0, val maxLines: Int = 0,
val wrappingMode: WrappingMode = WrappingMode.WRAP, val wrappingMode: WrappingMode = WrappingMode.WRAP,
val textAlignment: TextAlignment = TextAlignment.LEFT var textAlignment: TextAlignment = TextAlignment.LEFT
): View() { ): View() {
companion object { companion object {
@ -59,7 +60,8 @@ class Label(
var text: Text = text var text: Text = text
set(value) { set(value) {
field = value field = value
updateIntrinsicContentSize() // todo: uhhhh
updateIntrinsicContentSize(true)
// todo: setNeedsLayout instead of force unwrapping window // todo: setNeedsLayout instead of force unwrapping window
window!!.layout() window!!.layout()
} }
@ -75,15 +77,23 @@ class Label(
override fun wasAdded() { override fun wasAdded() {
super.wasAdded() super.wasAdded()
updateIntrinsicContentSize() updateIntrinsicContentSize(false)
} }
private fun updateIntrinsicContentSize() { private fun updateIntrinsicContentSize(canWrap: Boolean): Boolean {
if (RenderHelper.disabled) return if (RenderHelper.disabled) return false
val width = textRenderer.getWidth(text) val oldSize = intrinsicContentSize
val height = textRenderer.fontHeight if (wrappingMode == WrappingMode.WRAP && canWrap && hasSolver) {
intrinsicContentSize = Size(width.toDouble(), height.toDouble()) 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) { override fun drawContent(matrixStack: MatrixStack, mouse: Point, delta: Float) {
@ -110,14 +120,22 @@ class Label(
super.didLayout() super.didLayout()
computeLines() computeLines()
if (updateIntrinsicContentSize(true)) {
// if the intrinsic content size changes, relayout
window!!.layout()
}
} }
private fun computeLines() { private fun computeLines() {
var lines = textRenderer.wrapLines(text, bounds.width.toInt()) if (wrappingMode == WrappingMode.WRAP) {
if (maxLines > 0 && maxLines < lines.size) { var lines = textRenderer.wrapLines(text, bounds.width.toInt())
lines = lines.dropLast(lines.size - maxLines) 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 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. * 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). * 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. * Otherwise, this must be set manually.
* Setting this property updates the [bounds]. * 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. * 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. * 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. * 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 * 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. * 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. * the size.
*/ */
var intrinsicContentSize: Size? = null var intrinsicContentSize: Size? = null
@ -182,7 +190,7 @@ open class View(): Responder {
subviewsSortedByZIndex = subviews.sortedBy(View::zIndex) subviewsSortedByZIndex = subviews.sortedBy(View::zIndex)
view.superview = this view.superview = this
if (solverDelegate.isInitialized) { if (hasSolver) {
view.solver = solver view.solver = solver
} }
view.window = window view.window = window
@ -293,7 +301,7 @@ open class View(): Responder {
} }
private fun updateIntrinsicContentSizeConstraints(old: Size?, new: Size?) { private fun updateIntrinsicContentSizeConstraints(old: Size?, new: Size?) {
if (!usesConstraintBasedLayout || !solverDelegate.isInitialized) return if (!usesConstraintBasedLayout || !hasSolver) return
if (old != null) { if (old != null) {
solver.removeConstraint(intrinsicContentSizeWidthConstraint!!) solver.removeConstraint(intrinsicContentSizeWidthConstraint!!)

View File

@ -1,25 +1,31 @@
package net.shadowfacts.cacao.view.button package net.shadowfacts.cacao.view.button
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Size import net.shadowfacts.cacao.geometry.Size
import net.shadowfacts.cacao.util.MouseButton import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.texture.Texture import net.shadowfacts.cacao.util.texture.Texture
import net.shadowfacts.cacao.view.TextureView import net.shadowfacts.cacao.view.TextureView
import net.shadowfacts.cacao.view.View
/** /**
* A button for toggling between on/off states. * A button for toggling between on/off states.
* *
* @author shadowfacts * @author shadowfacts
* @param initialState Whether the button starts as on or off. * @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 { class ToggleButton(
intrinsicContentSize = Size(19.0, 19.0) initialState: Boolean,
}, padding = 0.0) { handler: ((ToggleButton) -> Unit)? = null,
): AbstractButton<ToggleButton>(TextureView(if (initialState) ON else OFF), padding = 0.0) {
companion object { companion object {
val ON = Texture(Identifier("asmr", "textures/gui/toggle.png"), 0, 0) val OFF = Texture(Identifier("textures/gui/checkbox.png"), 0, 0, 64, 64)
val OFF = Texture(Identifier("asmr", "textures/gui/toggle.png"), 0, 19) 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 private val textureView: TextureView
@ -30,10 +36,15 @@ class ToggleButton(initialState: Boolean): AbstractButton<ToggleButton>(TextureV
* Updating this property updates the button's texture. * Updating this property updates the button's texture.
*/ */
var state: Boolean = initialState var state: Boolean = initialState
set(value) {
field = value init {
textureView.texture = if (value) ON else OFF this.handler = handler
} intrinsicContentSize = Size(20.0, 20.0)
background = null
disabledBackground = null
hoveredBackground = null
}
override fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean { override fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean {
if (!disabled && (mouseButton == MouseButton.LEFT || mouseButton == MouseButton.RIGHT)) { if (!disabled && (mouseButton == MouseButton.LEFT || mouseButton == MouseButton.RIGHT)) {
@ -43,4 +54,17 @@ class ToggleButton(initialState: Boolean): AbstractButton<ToggleButton>(TextureV
return super.mouseClicked(point, mouseButton) 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.Point
import net.shadowfacts.cacao.geometry.Rect import net.shadowfacts.cacao.geometry.Rect
import net.shadowfacts.cacao.geometry.Size import net.shadowfacts.cacao.geometry.Size
import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.util.MouseButton import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.texture.NinePatchTexture import net.shadowfacts.cacao.util.texture.NinePatchTexture
import net.shadowfacts.cacao.util.texture.Texture import net.shadowfacts.cacao.util.texture.Texture
@ -97,6 +98,7 @@ class TabViewController<T: TabViewController.Tab>(
private lateinit var outerStack: StackView private lateinit var outerStack: StackView
private lateinit var tabStack: StackView private lateinit var tabStack: StackView
private lateinit var currentTabController: ViewController
// todo: this shouldn't be public, use layout guides // todo: this shouldn't be public, use layout guides
lateinit var tabVCContainer: View lateinit var tabVCContainer: View
private set private set
@ -121,7 +123,11 @@ class TabViewController<T: TabViewController.Tab>(
tabVCContainer.zIndex = 1.0 tabVCContainer.zIndex = 1.0
view.addSubview(tabVCContainer) 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 { view.solver.dsl {
outerStack.leftAnchor equalTo view.leftAnchor 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() { private fun updateTabButtons() {
while (tabStack.arrangedSubviews.isNotEmpty()) tabStack.removeArrangedSubview(tabStack.arrangedSubviews.first()) while (tabStack.arrangedSubviews.isNotEmpty()) tabStack.removeArrangedSubview(tabStack.arrangedSubviews.first())
@ -186,8 +212,20 @@ class TabViewController<T: TabViewController.Tab>(
tabButtons.forEach { tabButtons.forEach {
it.setSelected(it.tab === tab) it.setSelected(it.tab === tab)
} }
oldTab.controller.removeFromParent() currentTabController.viewWillDisappear()
embedChild(currentTab.controller, tabVCContainer) 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) onTabChange?.invoke(currentTab)
@ -202,7 +240,7 @@ class TabViewController<T: TabViewController.Tab>(
padding = 2.0 padding = 2.0
) { ) {
companion object { companion object {
val BACKGROUND = Identifier("textures/gui/container/creative_inventory/tabs.png") val BACKGROUND = Identifier("phycon:textures/gui/tabs.png")
} }
private var selected = false private var selected = false
@ -256,7 +294,7 @@ class TabViewController<T: TabViewController.Tab>(
val u = when { val u = when {
superview == null -> 0 superview == null -> 0
frame.left == 0.0 -> 0 frame.left == 0.0 -> 0
frame.right == superview!!.bounds.right -> 140 frame.right == superview!!.bounds.right -> 56
else -> 28 else -> 28
} }
backgroundView.texture = Texture(BACKGROUND, u, v) backgroundView.texture = Texture(BACKGROUND, u, v)

View File

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

View File

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

View File

@ -7,6 +7,7 @@ import net.minecraft.item.ItemPlacementContext
import net.minecraft.state.StateManager import net.minecraft.state.StateManager
import net.minecraft.state.property.EnumProperty import net.minecraft.state.property.EnumProperty
import net.minecraft.state.property.Properties import net.minecraft.state.property.Properties
import net.minecraft.util.StringIdentifiable
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape import net.minecraft.util.shape.VoxelShape
@ -26,7 +27,36 @@ import java.util.*
abstract class FaceDeviceBlock<T: DeviceBlockEntity>(settings: Settings): DeviceBlock<T>(settings) { abstract class FaceDeviceBlock<T: DeviceBlockEntity>(settings: Settings): DeviceBlock<T>(settings) {
companion object { companion object {
val FACING = Properties.FACING 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 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) 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) { return shapeCache.getOrPut(facing to cableConnection) {
VoxelShapes.union( if (cableConnection == FaceCableConnection.NONE) {
faceShapes[facing], VoxelShapes.union(faceShapes[facing], centerShapes[facing])
centerShapes[facing], } else {
CableBlock.SIDE_SHAPES[cableConnection] VoxelShapes.union(faceShapes[facing], centerShapes[facing], CableBlock.SIDE_SHAPES[cableConnection.direction])
) }
} }
} }
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection<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? { 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 { override fun getPlacementState(context: ItemPlacementContext): BlockState {
val facing = if (context.player?.isSneaking == true) context.side.opposite else context.playerLookDirection.opposite 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 return defaultState
.with(FACING, facing) .with(FACING, facing)
.with(CABLE_CONNECTION, cableConnection) .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()) { for (side in Direction.values()) {
if (side == facing) { if (side == facing) {
continue 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 { 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) { val current = state[CABLE_CONNECTION]
return state.with(CABLE_CONNECTION, side) 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 { 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.block.DeviceBlockEntity
import net.shadowfacts.phycon.component.ActivationController import net.shadowfacts.phycon.component.ActivationController
import net.shadowfacts.phycon.component.NetworkStackDispatcher import net.shadowfacts.phycon.component.NetworkStackDispatcher
import net.shadowfacts.phycon.component.finishTimedOutPendingInsertions
import net.shadowfacts.phycon.component.handleItemStack import net.shadowfacts.phycon.component.handleItemStack
import net.shadowfacts.phycon.packet.CapacityPacket import net.shadowfacts.phycon.packet.CapacityPacket
import net.shadowfacts.phycon.packet.ItemStackPacket import net.shadowfacts.phycon.packet.ItemStackPacket
@ -84,6 +85,8 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
if (!world!!.isClient) { if (!world!!.isClient) {
controller.tick() controller.tick()
finishTimedOutPendingInsertions()
} }
} }

View File

@ -34,6 +34,7 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE),
override var providerPriority = 0 override var providerPriority = 0
override var receiverPriority = 0 override var receiverPriority = 0
var syncPriorities = true
// todo: should this be a weak ref? // todo: should this be a weak ref?
private var inventory: GroupedItemInv? = null private var inventory: GroupedItemInv? = null
@ -121,11 +122,13 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE),
override fun writeDeviceConfiguration(tag: CompoundTag) { override fun writeDeviceConfiguration(tag: CompoundTag) {
tag.putInt("ProviderPriority", providerPriority) tag.putInt("ProviderPriority", providerPriority)
tag.putInt("ReceiverPriority", receiverPriority) tag.putInt("ReceiverPriority", receiverPriority)
tag.putBoolean("SyncPriorities", syncPriorities)
} }
override fun loadDeviceConfiguration(tag: CompoundTag) { override fun loadDeviceConfiguration(tag: CompoundTag) {
providerPriority = tag.getInt("ProviderPriority") providerPriority = tag.getInt("ProviderPriority")
receiverPriority = tag.getInt("ReceiverPriority") 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.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.block.entity.BlockEntity 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.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.util.Tickable import net.minecraft.util.Tickable
import net.minecraft.util.math.Direction import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.Interface import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.frame.EthernetFrame import net.shadowfacts.phycon.api.frame.EthernetFrame
import net.shadowfacts.phycon.api.frame.PacketFrame 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.api.util.MACAddress
import net.shadowfacts.phycon.frame.BasePacketFrame
import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.util.NetworkUtil
import net.shadowfacts.phycon.packet.ItemStackPacket import net.shadowfacts.phycon.packet.ItemStackPacket
import net.shadowfacts.phycon.util.NetworkUtil
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.Deque
import java.util.LinkedList
/** /**
* @author shadowfacts * @author shadowfacts
@ -25,13 +30,14 @@ class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
Tickable { Tickable {
companion object { 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()) } val interfaces = Direction.values().map { SwitchInterface(it, WeakReference(this), MACAddress.random()) }
private val macTable = mutableMapOf<MACAddress, Direction>() 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 { fun interfaceForSide(side: Direction): SwitchInterface {
return interfaces.find { it.side == side }!! return interfaces.find { it.side == side }!!
@ -40,18 +46,20 @@ class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
private fun handle(frame: EthernetFrame, fromItf: SwitchInterface) { private fun handle(frame: EthernetFrame, fromItf: SwitchInterface) {
macTable[frame.source] = fromItf.side macTable[frame.source] = fromItf.side
if (frame is PacketFrame && frame.packet is ItemStackPacket) { if (frame is PacketFrame) {
val packet = frame.packet as ItemStackPacket if (packetsHandledThisTick > SWITCHING_CAPACITY) {
if (itemsHandledThisTick + packet.stack.count > SWITCHING_CAPACITY) { PhysicalConnectivity.NETWORK_LOGGER.debug("{} reached capacity, delaying forwarding {}", this, frame)
// todo: calculate entity spawn point by finding non-obstructed location delayedPackets.addLast(frame to fromItf)
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), packet.stack)
world!!.spawnEntity(entity)
return return
} else { } 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)) { if (frame.destination.type != MACAddress.Type.BROADCAST && macTable.containsKey(frame.destination)) {
val dir = macTable[frame.destination]!! val dir = macTable[frame.destination]!!
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}, {}) forwarding {} to side {}", this, fromItf.side, fromItf.macAddress, frame, dir) 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() { 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 { override fun toTag(tag: CompoundTag): CompoundTag {
tag.putLongArray("InterfaceAddresses", interfaces.map { it.macAddress.address }) 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) return super.toTag(tag)
} }
@ -88,6 +115,21 @@ class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
tag.getLongArray("InterfaceAddresses")?.forEachIndexed { i, l -> tag.getLongArray("InterfaceAddresses")?.forEachIndexed { i, l ->
interfaces[i].macAddress = MACAddress(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 { 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.AttributeList
import alexiil.mc.lib.attributes.AttributeProvider import alexiil.mc.lib.attributes.AttributeProvider
import net.minecraft.block.Block
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.block.Material import net.minecraft.block.Material
import net.minecraft.entity.player.PlayerEntity 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.sound.BlockSoundGroup
import net.minecraft.state.StateManager
import net.minecraft.state.property.Properties
import net.minecraft.util.ActionResult import net.minecraft.util.ActionResult
import net.minecraft.util.Hand import net.minecraft.util.Hand
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.ItemScatterer
import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction import net.minecraft.util.math.Direction
@ -18,7 +25,7 @@ import net.minecraft.world.WorldAccess
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.NetworkComponentBlock import net.shadowfacts.phycon.api.NetworkComponentBlock
import net.shadowfacts.phycon.block.DeviceBlock import net.shadowfacts.phycon.block.DeviceBlock
import java.util.* import java.util.EnumSet
/** /**
* @author shadowfacts * @author shadowfacts
@ -33,10 +40,22 @@ class TerminalBlock: DeviceBlock<TerminalBlockEntity>(
companion object { companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "terminal") val ID = Identifier(PhysicalConnectivity.MODID, "terminal")
val FACING = Properties.FACING
} }
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection<Direction> { 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() 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.item.Item
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry import net.minecraft.util.registry.Registry
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.item.ConsoleItem import net.shadowfacts.phycon.item.ConsoleItem
import net.shadowfacts.phycon.item.ScrewdriverItem import net.shadowfacts.phycon.item.ScrewdriverItem
import net.shadowfacts.phycon.block.cable.CableBlock import net.shadowfacts.phycon.block.cable.CableBlock
@ -34,6 +35,7 @@ object PhyItems {
val SCREWDRIVER = ScrewdriverItem() val SCREWDRIVER = ScrewdriverItem()
val CONSOLE = ConsoleItem() val CONSOLE = ConsoleItem()
val TWISTED_PAIR = Item(Item.Settings())
fun init() { fun init() {
register(InterfaceBlock.ID, INTERFACE) register(InterfaceBlock.ID, INTERFACE)
@ -48,6 +50,7 @@ object PhyItems {
register(ScrewdriverItem.ID, SCREWDRIVER) register(ScrewdriverItem.ID, SCREWDRIVER)
register(ConsoleItem.ID, CONSOLE) register(ConsoleItem.ID, CONSOLE)
register(Identifier(PhysicalConnectivity.MODID, "twisted_pair"), TWISTED_PAIR)
} }
private fun register(id: Identifier, item: Item) { 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.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.block.DeviceBlock import net.shadowfacts.phycon.block.DeviceBlock
import net.shadowfacts.phycon.init.PhyBlocks
/** /**
* @author shadowfacts * @author shadowfacts
@ -35,6 +36,11 @@ class ScrewdriverItem: Item(Settings()) {
beTag.remove("y") beTag.remove("y")
beTag.remove("z") 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) val entity = ItemEntity(context.world, context.blockPos.x.toDouble(), context.blockPos.y.toDouble(), context.blockPos.z.toDouble(), stack)
context.world.spawnEntity(entity) 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.block.entity.BlockEntity
import net.minecraft.client.MinecraftClient import net.minecraft.client.MinecraftClient
import net.minecraft.text.TranslatableText import net.minecraft.text.TranslatableText
import net.shadowfacts.cacao.geometry.Axis
import net.shadowfacts.cacao.util.Color import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.view.Label 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.view.textfield.NumberField
import net.shadowfacts.cacao.viewcontroller.ViewController import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.kiwidsl.dsl import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.block.netinterface.InterfaceBlockEntity
import net.shadowfacts.phycon.component.NetworkStackProvider import net.shadowfacts.phycon.component.NetworkStackProvider
import net.shadowfacts.phycon.networking.C2SConfigureDevice import net.shadowfacts.phycon.networking.C2SConfigureDevice
@ -18,6 +23,9 @@ class ProviderViewController<T>(
private val device: T private val device: T
): ViewController() where T: BlockEntity, T: NetworkStackProvider { ): ViewController() where T: BlockEntity, T: NetworkStackProvider {
private lateinit var field: NumberField
private var syncButton: ToggleButton? = null
override fun viewDidLoad() { override fun viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -26,9 +34,14 @@ class ProviderViewController<T>(
} }
view.addSubview(label) view.addSubview(label)
val field = NumberField(device.providerPriority) { field = NumberField(device.providerPriority) {
if (it.number != null) { if (it.number != null) {
device.providerPriority = it.number!! device.providerPriority = it.number!!
if (device is InterfaceBlockEntity && device.syncPriorities) {
device.receiverPriority = it.number!!
}
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device)) MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device))
} }
} }
@ -39,6 +52,30 @@ class ProviderViewController<T>(
} }
view.addSubview(desc) 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 { view.solver.dsl {
field.widthAnchor equalTo 100 field.widthAnchor equalTo 100
field.heightAnchor equalTo 20 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.minecraft.text.TranslatableText
import net.shadowfacts.cacao.util.Color import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.view.Label import net.shadowfacts.cacao.view.Label
import net.shadowfacts.cacao.view.button.ToggleButton
import net.shadowfacts.cacao.view.textfield.NumberField import net.shadowfacts.cacao.view.textfield.NumberField
import net.shadowfacts.cacao.viewcontroller.ViewController import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.kiwidsl.dsl import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.block.netinterface.InterfaceBlockEntity
import net.shadowfacts.phycon.component.NetworkStackReceiver import net.shadowfacts.phycon.component.NetworkStackReceiver
import net.shadowfacts.phycon.networking.C2SConfigureDevice import net.shadowfacts.phycon.networking.C2SConfigureDevice
@ -18,6 +20,9 @@ class ReceiverViewController<T>(
private val device: T private val device: T
): ViewController() where T: BlockEntity, T: NetworkStackReceiver { ): ViewController() where T: BlockEntity, T: NetworkStackReceiver {
private lateinit var field: NumberField
private var syncButton: ToggleButton? = null
override fun viewDidLoad() { override fun viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -26,9 +31,14 @@ class ReceiverViewController<T>(
} }
view.addSubview(label) view.addSubview(label)
val field = NumberField(device.receiverPriority) { field = NumberField(device.receiverPriority) {
if (it.number != null) { if (it.number != null) {
device.receiverPriority = it.number!! device.receiverPriority = it.number!!
if (device is InterfaceBlockEntity && device.syncPriorities) {
device.providerPriority = it.number!!
}
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device)) MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device))
} }
} }
@ -39,6 +49,30 @@ class ReceiverViewController<T>(
} }
view.addSubview(desc) 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 { view.solver.dsl {
field.widthAnchor equalTo 100 field.widthAnchor equalTo 100
field.heightAnchor equalTo 20 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": [ "multipart": [
{ {
"apply": { "model": "phycon:block/cable_center" } "_comment": "see SimpleFaceDeviceModel",
}, "apply": { "model": "phycon:block/network_interface" }
{
"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 }
} }
] ]
} }

View File

@ -1,183 +1,8 @@
{ {
"multipart": [ "multipart": [
{ {
"apply": { "model": "phycon:block/cable_center" } "_comment": "see RedstoneControllerModel",
}, "apply": { "model": "phycon:block/redstone_controller" }
{
"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 }
} }
] ]
} }

View File

@ -1,159 +1,7 @@
{ {
"multipart": [ "multipart": [
{ {
"apply": { "model": "phycon:block/cable_center" } "apply": { "model": "phycon:block/redstone_emitter" }
},
{
"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 }
} }
] ]
} }

View File

@ -11,6 +11,7 @@
"item.phycon.screwdriver": "Screwdriver", "item.phycon.screwdriver": "Screwdriver",
"item.phycon.console": "Console", "item.phycon.console": "Console",
"item.phycon.twisted_pair": "Twisted Pair",
"gui.phycon.terminal_buffer": "Buffer", "gui.phycon.terminal_buffer": "Buffer",
"gui.phycon.console.details": "Device Details", "gui.phycon.console.details": "Device Details",
@ -24,9 +25,11 @@
"gui.phycon.console.provider": "Item Provider", "gui.phycon.console.provider": "Item Provider",
"gui.phycon.console.provider.priority": "Provider Priority", "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.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": "Item Receiver",
"gui.phycon.console.receiver.priority": "Receiver Priority", "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.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.high": "High",
"gui.phycon.redstone_mode.low": "Low", "gui.phycon.redstone_mode.low": "Low",
"gui.phycon.redstone_mode.toggle": "Toggle", "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", "cable": "phycon:block/cable_straight",
"front": "phycon:block/redstone_controller_front_off", "front": "phycon:block/redstone_controller_front_off",
"back": "phycon:block/redstone_controller_back", "back": "phycon:block/redstone_controller_back",
"side": "phycon:block/redstone_controller_back" "side": "phycon:block/redstone_controller_back",
"particle": "#front"
}, },
"elements": [ "elements": [
{ {

View File

@ -4,7 +4,8 @@
"cable": "phycon:block/cable_straight", "cable": "phycon:block/cable_straight",
"front": "phycon:block/redstone_controller_front_on", "front": "phycon:block/redstone_controller_front_on",
"back": "phycon:block/redstone_controller_back", "back": "phycon:block/redstone_controller_back",
"side": "phycon:block/redstone_controller_back" "side": "phycon:block/redstone_controller_back",
"particle": "#front"
}, },
"elements": [ "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"
}
}