Compare commits

..

21 Commits

Author SHA1 Message Date
Shadowfacts 385e36918f
Fix weird terminal behavior when inserting multiple equivalent stacks 2021-03-03 23:16:31 -05:00
Shadowfacts 8dbccc541e
Add network stack receiver priority 2021-03-03 23:13:09 -05:00
Shadowfacts 868b0d42f9
Fix screwdrivered block stacking, tweak sound 2021-03-03 22:29:19 -05:00
Shadowfacts 170e50755a
Add network stack provider priorities 2021-03-03 22:00:21 -05:00
Shadowfacts 2d3ac4538d
Drop Terminal items on block break 2021-03-03 19:16:51 -05:00
Shadowfacts 5b314120a7
Add using screwdriver to dismantle devices 2021-03-03 18:43:49 -05:00
Shadowfacts ca090d0924
Add Miner automatic mode 2021-03-03 17:23:57 -05:00
Shadowfacts 2958fa295a
Unify client configuration packets 2021-03-02 22:40:41 -05:00
Shadowfacts 3cd4a7aa0d
Add Redstone Emitter 2021-03-02 22:20:25 -05:00
Shadowfacts 4fe6391e69
Use powered property for Redstone Controller instead of lit 2021-03-01 21:30:36 -05:00
Shadowfacts e2fa31b680
Change screen handler constant names 2021-03-01 21:30:13 -05:00
Shadowfacts 9aa1077977
Add copyWithCount helper 2021-03-01 21:29:14 -05:00
Shadowfacts c976c3f607
Add DeviceBlockEntity common tag helpers 2021-02-28 22:47:54 -05:00
Shadowfacts e4e8dde2fb
Fix inserter screen not drawing world overlay 2021-02-28 22:21:08 -05:00
Shadowfacts ccefb8ae2f
Change Redstone Controller package 2021-02-28 22:03:29 -05:00
Shadowfacts 4c2148b30f
Extract GhostSlot to separate class 2021-02-28 22:02:26 -05:00
Shadowfacts f5268aef51
Fix extractor duplicating items 2021-02-28 19:01:59 -05:00
Shadowfacts 2084a749fb
Add loot tables for everything 2021-02-28 18:56:04 -05:00
Shadowfacts e13154943e
Add Inserter 2021-02-28 18:06:26 -05:00
Shadowfacts 9b4fc548e5
Reorganize code 2021-02-28 13:48:39 -05:00
Shadowfacts a8f0387577
Remove test blocks 2021-02-28 13:45:56 -05:00
108 changed files with 2595 additions and 616 deletions

View File

@ -3,7 +3,7 @@ package net.shadowfacts.phycon.mixin.client;
import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.Slot;
import net.shadowfacts.phycon.network.block.terminal.TerminalScreen; import net.shadowfacts.phycon.block.terminal.TerminalScreen;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;

View File

@ -7,6 +7,7 @@ import net.minecraft.screen.ScreenHandler
import net.minecraft.sound.SoundEvents import net.minecraft.sound.SoundEvents
import net.minecraft.text.Text import net.minecraft.text.Text
import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.util.KeyModifiers
import net.shadowfacts.cacao.util.MouseButton import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.RenderHelper import net.shadowfacts.cacao.util.RenderHelper
import net.shadowfacts.cacao.window.ScreenHandlerWindow import net.shadowfacts.cacao.window.ScreenHandlerWindow
@ -16,7 +17,7 @@ import java.util.*
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class CacaoHandledScreen<Handler: ScreenHandler>( open class CacaoHandledScreen<Handler: ScreenHandler>(
handler: Handler, handler: Handler,
playerInv: PlayerInventory, playerInv: PlayerInventory,
title: Text, title: Text,
@ -57,15 +58,16 @@ class CacaoHandledScreen<Handler: ScreenHandler>(
} }
override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) { override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) {
renderBackground(matrixStack)
}
override fun drawForeground(matrixStack: MatrixStack, mouseX: Int, mouseY: Int) {
// no-op // no-op
} }
override fun render(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) { override fun render(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
renderBackground(matrixStack)
val mouse = Point(mouseX, mouseY) val mouse = Point(mouseX, mouseY)
windows.forEachIndexed { index, it -> windows.forEachIndexed { index, it ->
it.draw(matrixStack, mouse, delta)
if (it is ScreenHandlerWindow) { if (it is ScreenHandlerWindow) {
if (index == windows.size - 1) { if (index == windows.size - 1) {
super.render(matrixStack, mouseX, mouseY, delta) super.render(matrixStack, mouseX, mouseY, delta)
@ -74,7 +76,10 @@ class CacaoHandledScreen<Handler: ScreenHandler>(
super.render(matrixStack, -1, -1, delta) super.render(matrixStack, -1, -1, delta)
} }
} }
it.draw(matrixStack, mouse, delta)
} }
drawMouseoverTooltip(matrixStack, mouseX, mouseY)
} }
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
@ -90,4 +95,20 @@ class CacaoHandledScreen<Handler: ScreenHandler>(
} }
} }
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
val modifiersSet by lazy { KeyModifiers(modifiers) }
if (findResponder { it.keyPressed(keyCode, modifiersSet) }) {
return true
}
return super.keyPressed(keyCode, scanCode, modifiers)
}
override fun charTyped(char: Char, modifiers: Int): Boolean {
val modifiersSet by lazy { KeyModifiers(modifiers) }
if (findResponder { it.charTyped(char, modifiersSet) }) {
return true
}
return super.charTyped(char, modifiers)
}
} }

View File

@ -139,7 +139,9 @@ open class CacaoScreen(title: Text = LiteralText("CacaoScreen")): Screen(title),
return super.charTyped(char, modifiers) return super.charTyped(char, modifiers)
} }
private fun findResponder(fn: (Responder) -> Boolean): Boolean { }
fun AbstractCacaoScreen.findResponder(fn: (Responder) -> Boolean): Boolean {
var responder = windows.lastOrNull()?.firstResponder var responder = windows.lastOrNull()?.firstResponder
while (responder != null) { while (responder != null) {
if (fn(responder)) { if (fn(responder)) {
@ -149,5 +151,3 @@ open class CacaoScreen(title: Text = LiteralText("CacaoScreen")): Screen(title),
} }
return false return false
} }
}

View File

@ -29,6 +29,7 @@ data class Color(val red: Int, val green: Int, val blue: Int, val alpha: Int = 2
val RED = Color(0xff0000) val RED = Color(0xff0000)
val GREEN = Color(0x00ff00) val GREEN = Color(0x00ff00)
val BLUE = Color(0x0000ff) val BLUE = Color(0x0000ff)
val MAGENTA = Color(0xfc46e4)
val TEXT = Color(0x404040) val TEXT = Color(0x404040)
} }

View File

@ -94,7 +94,7 @@ class Label(
for (i in 0 until lines.size) { for (i in 0 until lines.size) {
val x = when (textAlignment) { val x = when (textAlignment) {
TextAlignment.LEFT -> 0.0 TextAlignment.LEFT -> 0.0
TextAlignment.CENTER -> (bounds.width + textRenderer.getWidth(lines[i])) / 2 TextAlignment.CENTER -> (bounds.width - textRenderer.getWidth(lines[i])) / 2
TextAlignment.RIGHT -> bounds.width - textRenderer.getWidth(lines[i]) TextAlignment.RIGHT -> bounds.width - textRenderer.getWidth(lines[i])
} }
val y = i * textRenderer.fontHeight val y = i * textRenderer.fontHeight

View File

@ -6,6 +6,7 @@ import net.shadowfacts.cacao.geometry.Axis
import net.shadowfacts.cacao.geometry.AxisPosition import net.shadowfacts.cacao.geometry.AxisPosition
import net.shadowfacts.cacao.geometry.AxisPosition.* import net.shadowfacts.cacao.geometry.AxisPosition.*
import no.birkett.kiwi.Constraint import no.birkett.kiwi.Constraint
import java.lang.RuntimeException
import java.util.* import java.util.*
/** /**
@ -18,6 +19,7 @@ import java.util.*
* @param axis The primary axis that this stack lays out its children along. * @param axis The primary axis that this stack lays out its children along.
* @param distribution The mode by which this stack lays out its children along the axis perpendicular to the * @param distribution The mode by which this stack lays out its children along the axis perpendicular to the
* primary [axis]. * primary [axis].
* @param spacing The distance between arranged subviews along the primary axis.
*/ */
open class StackView( open class StackView(
val axis: Axis, val axis: Axis,
@ -25,7 +27,7 @@ open class StackView(
val spacing: Double = 0.0 val spacing: Double = 0.0
): View() { ): View() {
// the internal mutable, list of arranged subviews // the internal, mutable list of arranged subviews
private val _arrangedSubviews = LinkedList<View>() private val _arrangedSubviews = LinkedList<View>()
/** /**
* The list of arranged subviews belonging to this stack view. * The list of arranged subviews belonging to this stack view.
@ -57,6 +59,64 @@ open class StackView(
return view return view
} }
/**
* Removes the given arranged subview from this stack view's arranged subviews.
*/
fun removeArrangedSubview(view: View) {
val index = arrangedSubviews.indexOf(view)
if (index < 0) {
throw RuntimeException("Cannot remove view that is not arranged subview")
}
if (index == 0) {
solver.removeConstraint(leadingConnection)
val next = arrangedSubviews.getOrNull(1)
if (next != null) {
solver.dsl {
leadingConnection = anchor(LEADING) equalTo anchor(LEADING, next)
}
} else {
leadingConnection = null
}
}
if (index == arrangedSubviews.size - 1) {
solver.removeConstraint(trailingConnection)
val prev = arrangedSubviews.getOrNull(arrangedSubviews.size - 2)
if (prev != null) {
solver.dsl {
trailingConnection = anchor(TRAILING) equalTo anchor(TRAILING, prev)
}
} else {
trailingConnection = null
}
}
// if the removed view is in the middle
if (arrangedSubviews.size >= 3 && index > 0 && index < arrangedSubviews.size - 1) {
val prev = arrangedSubviews[index - 1]
val next = arrangedSubviews[index + 1]
solver.dsl {
solver.removeConstraint(arrangedSubviewConnections[index - 1])
solver.removeConstraint(arrangedSubviewConnections[index])
// todo: double check me
arrangedSubviewConnections[index - 1] = anchor(TRAILING, prev) equalTo anchor(LEADING, next)
arrangedSubviewConnections.removeAt(index)
}
}
_arrangedSubviews.remove(view)
removeSubview(view)
}
override fun removeSubview(view: View) {
if (arrangedSubviews.contains(view)) {
removeArrangedSubview(view)
} else {
super.removeSubview(view)
}
}
private fun addConstraintsForArrangedView(view: View, index: Int) { private fun addConstraintsForArrangedView(view: View, index: Int) {
if (index == 0) { if (index == 0) {
if (leadingConnection != null) { if (leadingConnection != null) {

View File

@ -197,10 +197,13 @@ open class View(): Responder {
* its children (recursively) to a view outside of the subview's hierarchy. Constraints internal to the subview's * its children (recursively) to a view outside of the subview's hierarchy. Constraints internal to the subview's
* hierarchy (e.g., one between the subview and its child) will be left in place. * hierarchy (e.g., one between the subview and its child) will be left in place.
* *
* This method may be overridden by layout-providing views (such as [StackView]) to update its layout when a managed
* subview is removed.
*
* @param view The view to removed as a child of this view. * @param view The view to removed as a child of this view.
* @throws RuntimeException If the given [view] is not a subview of this view. * @throws RuntimeException If the given [view] is not a subview of this view.
*/ */
fun removeSubview(view: View) { open fun removeSubview(view: View) {
if (view.superview !== this) { if (view.superview !== this) {
throw RuntimeException("Cannot remove subview whose superview is not this view") throw RuntimeException("Cannot remove subview whose superview is not this view")
} }

View File

@ -62,12 +62,22 @@ abstract class AbstractTextField<Impl: AbstractTextField<Impl>>(
minecraftWidget.setMaxLength(value) minecraftWidget.setMaxLength(value)
} }
/**
* Whether the Minecraft builtin black background and border are drawn. Defaults to true.
*/
var drawBackground = true
set(value) {
field = value
minecraftWidget.setHasBorder(value)
}
private lateinit var originInWindow: Point private lateinit var originInWindow: Point
private var minecraftWidget = ProxyWidget() private var minecraftWidget = ProxyWidget()
init { init {
minecraftWidget.text = initialText minecraftWidget.text = initialText
minecraftWidget.setTextPredicate { this.validate(it) } minecraftWidget.setTextPredicate { this.validate(it) }
minecraftWidget.setHasBorder(drawBackground)
} }
/** /**

View File

@ -0,0 +1,46 @@
package net.shadowfacts.cacao.view.textfield
/**
* @author shadowfacts
*/
class NumberField(
initialValue: Int,
handler: ((NumberField) -> Unit)? = null,
): AbstractTextField<NumberField>(initialValue.toString()) {
var number: Int?
get() {
return if (isTextTemporarilyAllowed(text)) {
null
} else {
try {
Integer.parseInt(text)
} catch (e: NumberFormatException) {
null
}
}
}
set(value) {
text = value?.toString() ?: ""
}
var validator: ((Int) -> Boolean)? = null
init {
this.handler = handler
}
override fun validate(proposedText: String): Boolean {
return isTextTemporarilyAllowed(proposedText) || try {
val value = Integer.parseInt(proposedText)
validator?.invoke(value) ?: true
} catch (e: NumberFormatException) {
false
}
}
private fun isTextTemporarilyAllowed(s: String): Boolean {
return s.isEmpty() || s == "-"
}
}

View File

@ -61,6 +61,13 @@ class TabViewController<T: TabViewController.Tab>(
* may be reused or created from scratch each time. * may be reused or created from scratch each time.
*/ */
val controller: ViewController val controller: ViewController
/**
* Used by the tab view controller to determine whether the button for this tab should be displayed.
* If the conditions that control this change, call [TabViewController.visibleTabsChanged].
*/
val isVisible: Boolean
get() = true
} }
/** /**
@ -68,12 +75,17 @@ class TabViewController<T: TabViewController.Tab>(
* @param tabView The view to display on the tab's button. * @param tabView The view to display on the tab's button.
* @param tooltip The tooltip to display when the tab's button is hovered (or `null`, if none). * @param tooltip The tooltip to display when the tab's button is hovered (or `null`, if none).
* @param controller The content view controller for this tab. * @param controller The content view controller for this tab.
* @param visible A function that determines if the tab should currently be visible.
*/ */
class SimpleTab( class SimpleTab(
override val tabView: View, override val tabView: View,
override val tooltip: Text? = null, override val tooltip: Text? = null,
override val controller: ViewController, override val controller: ViewController,
): Tab private val visible: (() -> Boolean)? = null
): Tab {
override val isVisible: Boolean
get() = visible?.invoke() ?: true
}
/** /**
* The currently selected tab. * The currently selected tab.
@ -100,20 +112,7 @@ class TabViewController<T: TabViewController.Tab>(
tabStack = StackView(Axis.HORIZONTAL, StackView.Distribution.FILL) tabStack = StackView(Axis.HORIZONTAL, StackView.Distribution.FILL)
tabStack.zIndex = 1.0 tabStack.zIndex = 1.0
outerStack.addArrangedSubview(tabStack) outerStack.addArrangedSubview(tabStack)
updateTabButtons()
tabButtons = tabs.mapIndexed { index, tab ->
val btn = TabButton(tab)
btn.handler = { selectTab(it.tab) }
if (tab == currentTab) {
btn.setSelected(true)
}
btn
}
// todo: batch calls to addArrangedSubview
tabButtons.forEach(tabStack::addArrangedSubview)
// spacer
tabStack.addArrangedSubview(View())
val background = NinePatchView(NinePatchTexture.PANEL_BG) val background = NinePatchView(NinePatchTexture.PANEL_BG)
outerStack.addArrangedSubview(background) outerStack.addArrangedSubview(background)
@ -137,6 +136,37 @@ class TabViewController<T: TabViewController.Tab>(
} }
} }
private fun updateTabButtons() {
while (tabStack.arrangedSubviews.isNotEmpty()) tabStack.removeArrangedSubview(tabStack.arrangedSubviews.first())
tabButtons = tabs.mapNotNull { tab ->
if (!tab.isVisible) {
return@mapNotNull null
}
val btn = TabButton(tab)
btn.handler = { selectTab(it.tab) }
if (tab == currentTab) {
btn.setSelected(true)
}
btn
}
// todo: batch calls to addArrangedSubview
tabButtons.forEach(tabStack::addArrangedSubview)
// spacer
tabStack.addArrangedSubview(View())
window!!.layout()
}
/**
* Call this method when the conditions that make the configured tabs visible change.
*/
fun visibleTabsChanged() {
updateTabButtons()
}
/** /**
* Sets the provided tab as the currently active tab for this controller. Updates the state of tab bar buttons and * Sets the provided tab as the currently active tab for this controller. Updates the state of tab bar buttons and
* swaps the content view controller. * swaps the content view controller.

View File

@ -26,8 +26,7 @@ object PhysicalConnectivity: ModInitializer {
registerGlobalReceiver(C2STerminalRequestItem) registerGlobalReceiver(C2STerminalRequestItem)
registerGlobalReceiver(C2STerminalUpdateDisplayedItems) registerGlobalReceiver(C2STerminalUpdateDisplayedItems)
registerGlobalReceiver(C2SConfigureActivationMode) registerGlobalReceiver(C2SConfigureDevice)
registerGlobalReceiver(C2SConfigureRedstoneController)
} }
private fun registerGlobalReceiver(receiver: ServerReceiver) { private fun registerGlobalReceiver(receiver: ServerReceiver) {

View File

@ -1,13 +1,12 @@
package net.shadowfacts.phycon package net.shadowfacts.phycon
import net.fabricmc.api.ClientModInitializer import net.fabricmc.api.ClientModInitializer
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap
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.minecraft.client.render.RenderLayer import net.shadowfacts.phycon.block.inserter.InserterScreen
import net.shadowfacts.phycon.init.PhyBlocks import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterScreen
import net.shadowfacts.phycon.init.PhyScreens import net.shadowfacts.phycon.init.PhyScreens
import net.shadowfacts.phycon.network.block.terminal.TerminalScreen import net.shadowfacts.phycon.block.terminal.TerminalScreen
import net.shadowfacts.phycon.networking.ClientReceiver import net.shadowfacts.phycon.networking.ClientReceiver
import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems
@ -17,9 +16,9 @@ import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems
object PhysicalConnectivityClient: ClientModInitializer { object PhysicalConnectivityClient: ClientModInitializer {
override fun onInitializeClient() { override fun onInitializeClient() {
BlockRenderLayerMap.INSTANCE.putBlock(PhyBlocks.CABLE, RenderLayer.getTranslucent()) ScreenRegistry.register(PhyScreens.TERMINAL, ::TerminalScreen)
ScreenRegistry.register(PhyScreens.INSERTER, ::InserterScreen)
ScreenRegistry.register(PhyScreens.TERMINAL_SCREEN_HANDLER, ::TerminalScreen) ScreenRegistry.register(PhyScreens.REDSTONE_EMITTER, ::RedstoneEmitterScreen)
registerGlobalReceiver(S2CTerminalUpdateDisplayedItems) registerGlobalReceiver(S2CTerminalUpdateDisplayedItems)
} }

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network package net.shadowfacts.phycon.block
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerEntity
@ -8,7 +8,6 @@ import net.minecraft.world.World
import net.minecraft.world.WorldAccess import net.minecraft.world.WorldAccess
import net.shadowfacts.phycon.api.Interface import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.NetworkComponentBlock import net.shadowfacts.phycon.api.NetworkComponentBlock
import net.shadowfacts.phycon.block.BlockWithEntity
/** /**
* @author shadowfacts * @author shadowfacts

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network package net.shadowfacts.phycon.block
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
@ -16,10 +16,11 @@ import net.shadowfacts.phycon.api.frame.PacketFrame
import net.shadowfacts.phycon.api.packet.Packet import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.IPAddress 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.network.frame.ARPQueryFrame import net.shadowfacts.phycon.util.NetworkUtil
import net.shadowfacts.phycon.network.frame.ARPResponseFrame import net.shadowfacts.phycon.frame.ARPQueryFrame
import net.shadowfacts.phycon.network.frame.BasePacketFrame import net.shadowfacts.phycon.frame.ARPResponseFrame
import net.shadowfacts.phycon.network.packet.* import net.shadowfacts.phycon.frame.BasePacketFrame
import net.shadowfacts.phycon.packet.*
import java.util.* import java.util.*
/** /**
@ -146,27 +147,33 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
} }
} }
override fun toTag(tag: CompoundTag): CompoundTag { protected open fun toCommonTag(tag: CompoundTag) {
tag.putInt("IPAddress", ipAddress.address) tag.putInt("IPAddress", ipAddress.address)
tag.putLong("MACAddress", macAddress.address) tag.putLong("MACAddress", macAddress.address)
}
protected open fun fromCommonTag(tag: CompoundTag) {
ipAddress = IPAddress(tag.getInt("IPAddress"))
macAddress = MACAddress(tag.getLong("MACAddress"))
}
override fun toTag(tag: CompoundTag): CompoundTag {
toCommonTag(tag)
return super.toTag(tag) return super.toTag(tag)
} }
override fun fromTag(state: BlockState, tag: CompoundTag) { override fun fromTag(state: BlockState, tag: CompoundTag) {
super.fromTag(state, tag) super.fromTag(state, tag)
ipAddress = IPAddress(tag.getInt("IPAddress")) fromCommonTag(tag)
macAddress = MACAddress(tag.getLong("MACAddress"))
} }
override fun toClientTag(tag: CompoundTag): CompoundTag { override fun toClientTag(tag: CompoundTag): CompoundTag {
tag.putInt("IPAddress", ipAddress.address) toCommonTag(tag)
tag.putLong("MACAddress", macAddress.address)
return tag return tag
} }
override fun fromClientTag(tag: CompoundTag) { override fun fromClientTag(tag: CompoundTag) {
ipAddress = IPAddress(tag.getInt("IPAddress")) fromCommonTag(tag)
macAddress = MACAddress(tag.getLong("MACAddress"))
} }
fun onBreak() { fun onBreak() {

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network package net.shadowfacts.phycon.block
import net.minecraft.block.Block import net.minecraft.block.Block
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
@ -16,7 +16,7 @@ import net.minecraft.world.World
import net.minecraft.world.WorldAccess import net.minecraft.world.WorldAccess
import net.shadowfacts.phycon.api.Interface import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.NetworkComponentBlock import net.shadowfacts.phycon.api.NetworkComponentBlock
import net.shadowfacts.phycon.network.block.cable.CableBlock import net.shadowfacts.phycon.block.cable.CableBlock
import java.util.* import java.util.*

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.block.cable package net.shadowfacts.phycon.block.cable
import net.minecraft.block.* import net.minecraft.block.*
import net.minecraft.block.piston.PistonBehavior import net.minecraft.block.piston.PistonBehavior
@ -31,6 +31,7 @@ import java.util.*
*/ */
class CableBlock: Block( class CableBlock: Block(
Settings.of(CABLE_MATERIAL) Settings.of(CABLE_MATERIAL)
.strength(1f)
.nonOpaque() .nonOpaque()
), NetworkCableBlock { ), NetworkCableBlock {
companion object { companion object {

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.block.extractor package net.shadowfacts.phycon.block.extractor
import net.minecraft.block.Block import net.minecraft.block.Block
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
@ -7,6 +7,7 @@ import net.minecraft.block.ShapeContext
import net.minecraft.entity.LivingEntity import net.minecraft.entity.LivingEntity
import net.minecraft.item.ItemPlacementContext import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.state.StateManager import net.minecraft.state.StateManager
import net.minecraft.state.property.Properties import net.minecraft.state.property.Properties
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
@ -19,13 +20,17 @@ import net.minecraft.world.World
import net.minecraft.world.WorldAccess import net.minecraft.world.WorldAccess
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.network.DeviceBlock import net.shadowfacts.phycon.block.DeviceBlock
import java.util.* import java.util.*
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class ExtractorBlock: DeviceBlock<ExtractorBlockEntity>(Settings.of(Material.METAL)) { class ExtractorBlock: DeviceBlock<ExtractorBlockEntity>(
Settings.of(Material.METAL)
.strength(1.5f)
.sounds(BlockSoundGroup.METAL)
) {
companion object { companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "extractor") val ID = Identifier(PhysicalConnectivity.MODID, "extractor")
@ -88,7 +93,7 @@ class ExtractorBlock: DeviceBlock<ExtractorBlockEntity>(Settings.of(Material.MET
override fun createBlockEntity(world: BlockView) = ExtractorBlockEntity() override fun createBlockEntity(world: BlockView) = ExtractorBlockEntity()
override fun getPlacementState(context: ItemPlacementContext): BlockState { override fun getPlacementState(context: ItemPlacementContext): BlockState {
val facing = if (context.player?.isSneaking == true) context.side.opposite else context.playerFacing.opposite val facing = if (context.player?.isSneaking == true) context.side.opposite else context.playerLookDirection.opposite
return defaultState.with(FACING, facing) return defaultState.with(FACING, facing)
} }

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.block.extractor package net.shadowfacts.phycon.block.extractor
import alexiil.mc.lib.attributes.SearchOptions import alexiil.mc.lib.attributes.SearchOptions
import alexiil.mc.lib.attributes.Simulation import alexiil.mc.lib.attributes.Simulation
@ -11,14 +11,15 @@ import net.minecraft.nbt.CompoundTag
import net.minecraft.util.math.Direction import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.api.packet.Packet import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.network.component.ActivationController import net.shadowfacts.phycon.component.ActivationController
import net.shadowfacts.phycon.network.component.NetworkStackDispatcher import net.shadowfacts.phycon.component.NetworkStackDispatcher
import net.shadowfacts.phycon.network.component.handleItemStack import net.shadowfacts.phycon.component.handleItemStack
import net.shadowfacts.phycon.network.packet.CapacityPacket import net.shadowfacts.phycon.packet.CapacityPacket
import net.shadowfacts.phycon.network.packet.ItemStackPacket import net.shadowfacts.phycon.packet.ItemStackPacket
import net.shadowfacts.phycon.network.packet.RemoteActivationPacket import net.shadowfacts.phycon.packet.RemoteActivationPacket
import net.shadowfacts.phycon.util.ActivationMode import net.shadowfacts.phycon.util.ActivationMode
import net.shadowfacts.phycon.util.ClientConfigurableDevice
import kotlin.properties.Delegates import kotlin.properties.Delegates
/** /**
@ -26,7 +27,8 @@ import kotlin.properties.Delegates
*/ */
class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR), class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
NetworkStackDispatcher<ExtractorBlockEntity.PendingInsertion>, NetworkStackDispatcher<ExtractorBlockEntity.PendingInsertion>,
ActivationController.ActivatableDevice { ActivationController.ActivatableDevice,
ClientConfigurableDevice {
companion object { companion object {
val SLEEP_TIME = 40L val SLEEP_TIME = 40L
@ -37,7 +39,7 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
private var inventory: FixedItemInv? = null private var inventory: FixedItemInv? = null
override val pendingInsertions = mutableListOf<PendingInsertion>() override val pendingInsertions = mutableListOf<PendingInsertion>()
override val dispatchStackTimeout = 40L override val dispatchStackTimeout = 1L
override val controller = ActivationController(SLEEP_TIME, this) override val controller = ActivationController(SLEEP_TIME, this)
fun updateInventory() { fun updateInventory() {
@ -99,23 +101,21 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
return false return false
} }
override fun toTag(tag: CompoundTag): CompoundTag { override fun toCommonTag(tag: CompoundTag) {
super.toCommonTag(tag)
writeDeviceConfiguration(tag)
}
override fun fromCommonTag(tag: CompoundTag) {
super.fromCommonTag(tag)
loadDeviceConfiguration(tag)
}
override fun writeDeviceConfiguration(tag: CompoundTag) {
tag.putString("ActivationMode", controller.activationMode.name) tag.putString("ActivationMode", controller.activationMode.name)
return super.toTag(tag)
} }
override fun fromTag(state: BlockState, tag: CompoundTag) { override fun loadDeviceConfiguration(tag: CompoundTag) {
super.fromTag(state, tag)
controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode"))
}
override fun toClientTag(tag: CompoundTag): CompoundTag {
tag.putString("ActivationMode", controller.activationMode.name)
return super.toClientTag(tag)
}
override fun fromClientTag(tag: CompoundTag) {
super.fromClientTag(tag)
controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode")) controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode"))
} }

View File

@ -0,0 +1,148 @@
package net.shadowfacts.phycon.block.inserter
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.Material
import net.minecraft.block.ShapeContext
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.network.PacketByteBuf
import net.minecraft.screen.ScreenHandler
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.state.StateManager
import net.minecraft.state.property.Properties
import net.minecraft.text.Text
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.block.DeviceBlock
import net.shadowfacts.phycon.block.extractor.ExtractorBlock
import java.util.*
/**
* @author shadowfacts
*/
class InserterBlock: DeviceBlock<InserterBlockEntity>(
Settings.of(Material.METAL)
.strength(1.5f)
.sounds(BlockSoundGroup.METAL)
) {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "inserter")
val FACING = Properties.FACING
private val INSERTER_SHAPES = mutableMapOf<Direction, VoxelShape>()
init {
val components = arrayOf(
doubleArrayOf(4.0, 0.0, 4.0, 12.0, 2.0, 12.0),
doubleArrayOf(2.0, 2.0, 2.0, 14.0, 4.0, 14.0),
doubleArrayOf(0.0, 4.0, 0.0, 16.0, 6.0, 16.0),
doubleArrayOf(6.0, 6.0, 6.0, 10.0, 16.0, 10.0)
)
val directions = arrayOf(
Triple(Direction.DOWN, null, false),
Triple(Direction.UP, null, true),
Triple(Direction.NORTH, 2, false),
Triple(Direction.SOUTH, 2, true),
Triple(Direction.WEST, 1, false),
Triple(Direction.EAST, 1, true),
)
for ((dir, rotate, flip) in directions) {
val shapes = components.map { it ->
val arr = it.copyOf()
if (rotate != null) {
for (i in 0 until 3) {
arr[i] = it[(i + rotate) % 3]
arr[3 + i] = it[3 + ((i + rotate) % 3)]
}
}
if (flip) {
for (i in arr.indices) {
arr[i] = 16.0 - arr[i]
}
}
createCuboidShape(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5])
}
INSERTER_SHAPES[dir] = shapes.reduce { a, b -> VoxelShapes.union(a, b) }
}
}
}
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection<Direction> {
return EnumSet.of(state[FACING].opposite)
}
override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: WorldAccess, pos: BlockPos): Interface? {
return if (side == state[FACING].opposite) {
getBlockEntity(world, pos)
} else {
null
}
}
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
super.appendProperties(builder)
builder.add(FACING)
}
override fun createBlockEntity(world: BlockView) = InserterBlockEntity()
override fun getPlacementState(context: ItemPlacementContext): BlockState {
val facing = if (context.player?.isSneaking == true) context.side.opposite else context.playerLookDirection.opposite
return defaultState.with(FACING, facing)
}
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape {
return INSERTER_SHAPES[state[FACING]]!!
}
override fun onPlaced(world: World, pos: BlockPos, state: BlockState, entity: LivingEntity?, stack: ItemStack) {
if (!world.isClient) {
getBlockEntity(world, pos)!!.updateInventory()
}
}
override fun neighborUpdate(state: BlockState, world: World, pos: BlockPos, neighborBlock: Block, neighborPos: BlockPos, bl: Boolean) {
if (!world.isClient) {
getBlockEntity(world, pos)!!.updateInventory()
}
}
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hitResult: BlockHitResult): ActionResult {
if (!world.isClient) {
val be = getBlockEntity(world, pos)!!
be.sync()
val factory = object: ExtendedScreenHandlerFactory {
override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler {
return InserterScreenHandler(syncId, playerInv, be)
}
override fun getDisplayName() = this@InserterBlock.name
override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) {
buf.writeBlockPos(be.pos)
}
}
player.openHandledScreen(factory)
}
return ActionResult.SUCCESS
}
}

View File

@ -0,0 +1,163 @@
package net.shadowfacts.phycon.block.inserter
import alexiil.mc.lib.attributes.SearchOptions
import alexiil.mc.lib.attributes.Simulation
import alexiil.mc.lib.attributes.item.ItemAttributes
import alexiil.mc.lib.attributes.item.ItemInsertable
import alexiil.mc.lib.attributes.item.ItemStackUtil
import net.minecraft.item.ItemStack
import net.minecraft.nbt.CompoundTag
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.component.ActivationController
import net.shadowfacts.phycon.component.ItemStackPacketHandler
import net.shadowfacts.phycon.component.NetworkStackProvider
import net.shadowfacts.phycon.component.handleItemStack
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.packet.*
import net.shadowfacts.phycon.util.ActivationMode
import net.shadowfacts.phycon.util.ClientConfigurableDevice
import kotlin.math.min
/**
* @author shadowfacts
*/
class InserterBlockEntity: DeviceBlockEntity(PhyBlockEntities.INSERTER),
ItemStackPacketHandler,
ActivationController.ActivatableDevice,
ClientConfigurableDevice {
companion object {
val SLEEP_TIME = 40L
val REQUEST_TIMEOUT = 40
}
private val facing: Direction
get() = cachedState[InserterBlock.FACING]
private var inventory: ItemInsertable? = null
private var currentRequest: PendingExtractRequest? = null
var stackToExtract: ItemStack = ItemStack.EMPTY
var amountToExtract = 1
override val controller = ActivationController(SLEEP_TIME, this)
fun updateInventory() {
val offsetPos = pos.offset(facing)
val option = SearchOptions.inDirection(facing)
inventory = ItemAttributes.INSERTABLE.getFirstOrNull(world, offsetPos, option)
}
private fun getInventory(): ItemInsertable? {
if (inventory == null) updateInventory()
return inventory
}
override fun handle(packet: Packet) {
when (packet) {
is RemoteActivationPacket -> controller.handleRemoteActivation(packet)
is StackLocationPacket -> handleStackLocation(packet)
is ItemStackPacket -> handleItemStack(packet)
}
}
override fun doHandleItemStack(packet: ItemStackPacket): ItemStack {
val inventory = getInventory()
return if (inventory != null) {
inventory.attemptInsertion(packet.stack, Simulation.ACTION)
} else {
// no inventory, entire stack remains
packet.stack
}
}
private fun handleStackLocation(packet: StackLocationPacket) {
val request = currentRequest
if (request != null && ItemStackUtil.areEqualIgnoreAmounts(request.stack, packet.stack)) {
request.results.add(packet.amount to packet.stackProvider)
if (request.isFinishable(counter)) {
finishRequest()
}
}
}
override fun tick() {
super.tick()
if (!world!!.isClient) {
controller.tick()
val request = currentRequest
if (request != null) {
if (request.isFinishable(counter)) {
finishRequest()
} else if (counter - request.timestamp >= REQUEST_TIMEOUT && request.totalAmount == 0) {
currentRequest = null
}
}
}
}
override fun activate(): Boolean {
if (currentRequest != null || stackToExtract.isEmpty) {
return false
}
// todo: configure me
currentRequest = PendingExtractRequest(stackToExtract, counter)
sendPacket(LocateStackPacket(stackToExtract, ipAddress))
return true
}
private fun finishRequest() {
val request = currentRequest ?: return
// todo: dedup with TerminalBlockEntity.stackLocateRequestCompleted
val actualAmount = min(min(request.stack.maxCount, request.totalAmount), amountToExtract)
val sortedResults = request.results.sortedByDescending { it.first }.toMutableList()
var amountRequested = 0
while (amountRequested < actualAmount && sortedResults.isNotEmpty()) {
val (sourceAmount, source) = sortedResults.removeAt(0)
val amountToRequest = min(sourceAmount, actualAmount - amountRequested)
amountRequested += amountToRequest
sendPacket(ExtractStackPacket(request.stack, amountToRequest, ipAddress, source.ipAddress))
}
currentRequest = null
}
override fun toCommonTag(tag: CompoundTag) {
super.toCommonTag(tag)
writeDeviceConfiguration(tag)
tag.put("StackToExtract", stackToExtract.toTag(CompoundTag()))
}
override fun fromCommonTag(tag: CompoundTag) {
super.fromCommonTag(tag)
loadDeviceConfiguration(tag)
stackToExtract = ItemStack.fromTag(tag.getCompound("StackToExtract"))
}
override fun writeDeviceConfiguration(tag: CompoundTag) {
tag.putString("ActivationMode", controller.activationMode.name)
tag.putInt("AmountToExtract", amountToExtract)
}
override fun loadDeviceConfiguration(tag: CompoundTag) {
controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode"))
amountToExtract = tag.getInt("AmountToExtract")
}
class PendingExtractRequest(
val stack: ItemStack,
val timestamp: Long,
var results: MutableSet<Pair<Int, NetworkStackProvider>> = mutableSetOf()
) {
val totalAmount: Int
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
fun isFinishable(currentTimestamp: Long): Boolean {
return totalAmount >= stack.maxCount || (currentTimestamp - timestamp >= REQUEST_TIMEOUT && totalAmount > 0)
}
}
}

View File

@ -0,0 +1,124 @@
package net.shadowfacts.phycon.block.inserter
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.gui.screen.ingame.HandledScreen
import net.minecraft.client.gui.widget.TextFieldWidget
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.screen.slot.Slot
import net.minecraft.screen.slot.SlotActionType
import net.minecraft.text.LiteralText
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.networking.C2SConfigureDevice
import java.lang.NumberFormatException
/**
* @author shadowfacts
*/
class InserterScreen(
handler: InserterScreenHandler,
playerInv: PlayerInventory,
title: Text,
): HandledScreen<InserterScreenHandler>(
handler,
playerInv,
title
) {
companion object {
val BACKGROUND = Identifier(PhysicalConnectivity.MODID, "textures/gui/inserter.png")
}
private lateinit var amountField: TextFieldWidget
init {
backgroundWidth = 176
backgroundHeight = 133
playerInventoryTitleY = backgroundHeight - 94
}
override fun init() {
super.init()
amountField = TextFieldWidget(textRenderer, x + 57, y + 24, 80, 9, LiteralText("Amount"))
amountField.text = handler.inserter.amountToExtract.toString()
amountField.setHasBorder(false)
amountField.isVisible = true
amountField.setSelected(true)
amountField.setEditableColor(0xffffff)
amountField.setTextPredicate {
if (it.isEmpty()) {
true
} else {
try {
val value = Integer.parseInt(it)
value in 1..64
} catch (e: NumberFormatException) {
false
}
}
}
addChild(amountField)
}
fun amountUpdated() {
if (amountField.text.isNotEmpty()) {
handler.inserter.amountToExtract = Integer.parseInt(amountField.text)
client!!.player!!.networkHandler.sendPacket(C2SConfigureDevice(handler.inserter))
}
}
override fun tick() {
super.tick()
amountField.tick()
}
override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) {
renderBackground(matrixStack)
RenderSystem.color4f(1f, 1f, 1f, 1f)
client!!.textureManager.bindTexture(BACKGROUND)
val x = (width - backgroundWidth) / 2
val y = (height - backgroundHeight) / 2
drawTexture(matrixStack, x, y, 0, 0, backgroundWidth, backgroundHeight)
}
override fun render(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
super.render(matrixStack, mouseX, mouseY, delta)
amountField.render(matrixStack, mouseX, mouseY, delta)
drawMouseoverTooltip(matrixStack, mouseX, mouseY)
}
override fun onMouseClick(slot: Slot?, invSlot: Int, clickData: Int, slotActionType: SlotActionType?) {
super.onMouseClick(slot, invSlot, clickData, slotActionType)
amountField.setSelected(true)
}
override fun charTyped(c: Char, i: Int): Boolean {
val oldText = amountField.text
if (amountField.charTyped(c, i)) {
if (oldText != amountField.text) {
amountUpdated()
}
return true
}
return super.charTyped(c, i)
}
override fun keyPressed(i: Int, j: Int, k: Int): Boolean {
val oldText = amountField.text
if (amountField.keyPressed(i, j, k)) {
if (oldText != amountField.text) {
amountUpdated()
}
return true
}
return super.keyPressed(i, j, k)
}
}

View File

@ -0,0 +1,83 @@
package net.shadowfacts.phycon.block.inserter
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.item.ItemStack
import net.minecraft.network.PacketByteBuf
import net.minecraft.screen.ScreenHandler
import net.minecraft.screen.slot.Slot
import net.minecraft.screen.slot.SlotActionType
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.init.PhyScreens
import net.shadowfacts.phycon.util.GhostSlot
import net.shadowfacts.phycon.util.copyWithCount
import kotlin.math.min
/**
* @author shadowfacts
*/
class InserterScreenHandler(
syncId: Int,
playerInv: PlayerInventory,
val inserter: InserterBlockEntity,
): ScreenHandler(PhyScreens.INSERTER, syncId) {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "inserter")
}
constructor(syncId: Int, playerInv: PlayerInventory, buf: PacketByteBuf):
this(
syncId,
playerInv,
PhyBlocks.INSERTER.getBlockEntity(playerInv.player.world, buf.readBlockPos())!!
)
init {
// fake slot
addSlot(GhostSlot(inserter::stackToExtract, 31, 20))
// player inv
for (y in 0 until 3) {
for (x in 0 until 9) {
addSlot(Slot(playerInv, x + y * 9 + 9, 8 + x * 18, 51 + y * 18))
}
}
// hotbar
for (x in 0 until 9) {
addSlot(Slot(playerInv, x, 8 + x * 18, 109))
}
}
private fun stackToExtractChanged() {
inserter.amountToExtract = min(inserter.stackToExtract.maxCount, inserter.amountToExtract)
}
override fun canUse(player: PlayerEntity): Boolean {
return true
}
override fun onSlotClick(slotId: Int, clickData: Int, actionType: SlotActionType, player: PlayerEntity): ItemStack {
// fake slot
if (slotId == 0) {
if (player.inventory.cursorStack.isEmpty) {
inserter.stackToExtract = ItemStack.EMPTY
} else {
inserter.stackToExtract = player.inventory.cursorStack.copyWithCount(1)
}
stackToExtractChanged()
}
return super.onSlotClick(slotId, clickData, actionType, player)
}
override fun transferSlot(player: PlayerEntity, slotId: Int): ItemStack {
val slot = slots[slotId]
inserter.stackToExtract = slot.stack.copyWithCount(1)
stackToExtractChanged()
return ItemStack.EMPTY
}
}

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.block.miner package net.shadowfacts.phycon.block.miner
import net.minecraft.block.Block import net.minecraft.block.Block
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
@ -6,6 +6,7 @@ import net.minecraft.block.Material
import net.minecraft.entity.LivingEntity import net.minecraft.entity.LivingEntity
import net.minecraft.item.ItemPlacementContext import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.state.StateManager import net.minecraft.state.StateManager
import net.minecraft.state.property.Properties import net.minecraft.state.property.Properties
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
@ -16,14 +17,18 @@ import net.minecraft.world.World
import net.minecraft.world.WorldAccess import net.minecraft.world.WorldAccess
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.network.DeviceBlock import net.shadowfacts.phycon.block.DeviceBlock
import net.shadowfacts.phycon.network.block.extractor.ExtractorBlock import net.shadowfacts.phycon.block.extractor.ExtractorBlock
import java.util.* import java.util.*
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class MinerBlock: DeviceBlock<MinerBlockEntity>(Settings.of(Material.METAL)) { class MinerBlock: DeviceBlock<MinerBlockEntity>(
Settings.of(Material.METAL)
.strength(1.5f)
.sounds(BlockSoundGroup.METAL)
) {
companion object { companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "miner") val ID = Identifier(PhysicalConnectivity.MODID, "miner")

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.block.miner package net.shadowfacts.phycon.block.miner
import alexiil.mc.lib.attributes.item.GroupedItemInvView import alexiil.mc.lib.attributes.item.GroupedItemInvView
import alexiil.mc.lib.attributes.item.ItemStackUtil import alexiil.mc.lib.attributes.item.ItemStackUtil
@ -6,20 +6,25 @@ import alexiil.mc.lib.attributes.item.filter.ItemFilter
import net.minecraft.block.Block import net.minecraft.block.Block
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.nbt.CompoundTag
import net.minecraft.server.world.ServerWorld import net.minecraft.server.world.ServerWorld
import net.minecraft.text.TranslatableText
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.world.World import net.minecraft.world.World
import net.shadowfacts.phycon.api.packet.Packet import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.network.block.terminal.TerminalBlockEntity import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.network.component.NetworkStackDispatcher import net.shadowfacts.phycon.component.ActivationController
import net.shadowfacts.phycon.network.component.NetworkStackProvider import net.shadowfacts.phycon.component.NetworkStackDispatcher
import net.shadowfacts.phycon.network.component.handleItemStack import net.shadowfacts.phycon.component.NetworkStackProvider
import net.shadowfacts.phycon.network.component.spawnItemStack import net.shadowfacts.phycon.component.handleItemStack
import net.shadowfacts.phycon.network.packet.* import net.shadowfacts.phycon.packet.*
import net.shadowfacts.phycon.util.ActivationMode
import net.shadowfacts.phycon.util.ClientConfigurableDevice
import net.shadowfacts.phycon.util.copyWithCount
import kotlin.math.min import kotlin.math.min
/** /**
@ -27,7 +32,9 @@ import kotlin.math.min
*/ */
class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER), class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
NetworkStackProvider, NetworkStackProvider,
NetworkStackDispatcher<MinerBlockEntity.PendingInsertion> { NetworkStackDispatcher<MinerBlockEntity.PendingInsertion>,
ActivationController.ActivatableDevice,
ClientConfigurableDevice {
private val facing: Direction private val facing: Direction
get() = cachedState[MinerBlock.FACING] get() = cachedState[MinerBlock.FACING]
@ -36,6 +43,10 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
override val pendingInsertions = mutableListOf<PendingInsertion>() override val pendingInsertions = mutableListOf<PendingInsertion>()
override val dispatchStackTimeout = TerminalBlockEntity.INSERTION_TIMEOUT override val dispatchStackTimeout = TerminalBlockEntity.INSERTION_TIMEOUT
override val controller = ActivationController(40L, this)
override var providerPriority = 0
var minerMode = MinerMode.ON_DEMAND
override fun handle(packet: Packet) { override fun handle(packet: Packet) {
when (packet) { when (packet) {
@ -48,10 +59,16 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
} }
private fun handleRequestInventory(packet: RequestInventoryPacket) { private fun handleRequestInventory(packet: RequestInventoryPacket) {
if (minerMode != MinerMode.ON_DEMAND) {
return
}
sendPacket(ReadInventoryPacket(invProxy, ipAddress, packet.source)) sendPacket(ReadInventoryPacket(invProxy, ipAddress, packet.source))
} }
private fun handleLocateStack(packet: LocateStackPacket) { private fun handleLocateStack(packet: LocateStackPacket) {
if (minerMode != MinerMode.ON_DEMAND) {
return
}
val amount = invProxy.getAmount(packet.stack) val amount = invProxy.getAmount(packet.stack)
if (amount > 0) { if (amount > 0) {
sendPacket(StackLocationPacket(packet.stack, amount, this, ipAddress, packet.source)) sendPacket(StackLocationPacket(packet.stack, amount, this, ipAddress, packet.source))
@ -59,6 +76,10 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
} }
private fun handleExtractStack(packet: ExtractStackPacket) { private fun handleExtractStack(packet: ExtractStackPacket) {
if (minerMode != MinerMode.ON_DEMAND) {
return
}
// always recalculate immediately before breaking // always recalculate immediately before breaking
val drops = invProxy.getDrops(recalculate = true) val drops = invProxy.getDrops(recalculate = true)
if (invProxy.getAmount(packet.stack) > 0) { if (invProxy.getAmount(packet.stack) > 0) {
@ -73,14 +94,13 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
if (!ItemStackUtil.areEqualIgnoreAmounts(droppedStack, packet.stack)) { if (!ItemStackUtil.areEqualIgnoreAmounts(droppedStack, packet.stack)) {
continue continue
} }
val copy = droppedStack.copy()
val toDecr = min(droppedStack.count, remaining) val toDecr = min(droppedStack.count, remaining)
val copy = droppedStack.copyWithCount(toDecr)
droppedStack.decrement(toDecr) droppedStack.decrement(toDecr)
remaining -= toDecr remaining -= toDecr
// todo: should this try to combine stacks and send as few packets as possible? // todo: should this try to combine stacks and send as few packets as possible?
copy.count = toDecr
sendPacket(ItemStackPacket(copy, ipAddress, packet.source)) sendPacket(ItemStackPacket(copy, ipAddress, packet.source))
} }
@ -99,7 +119,76 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
override fun createPendingInsertion(stack: ItemStack) = PendingInsertion(stack, counter) override fun createPendingInsertion(stack: ItemStack) = PendingInsertion(stack, counter)
override fun tick() {
super.tick()
if (!world!!.isClient && minerMode == MinerMode.AUTOMATIC) {
controller.tick()
}
}
override fun activate(): Boolean {
if (minerMode == MinerMode.ON_DEMAND) {
return false
}
val drops = invProxy.getDrops(recalculate = true)
if (!world!!.getBlockState(pos.offset(facing)).isAir) {
world!!.breakBlock(pos.offset(facing), false)
for (stack in drops) {
if (stack.isEmpty) continue
dispatchItemStack(stack)
}
return true
} else {
return false
}
}
override fun canConfigureActivationController(): Boolean {
return minerMode == MinerMode.AUTOMATIC
}
override fun canConfigureProviderPriority(): Boolean {
return minerMode == MinerMode.ON_DEMAND
}
override fun toCommonTag(tag: CompoundTag) {
super.toCommonTag(tag)
writeDeviceConfiguration(tag)
}
override fun fromCommonTag(tag: CompoundTag) {
super.fromCommonTag(tag)
loadDeviceConfiguration(tag)
}
override fun writeDeviceConfiguration(tag: CompoundTag) {
tag.putString("MinerMode", minerMode.name)
tag.putString("ActivationMode", controller.activationMode.name)
tag.putInt("ProviderPriority", providerPriority)
}
override fun loadDeviceConfiguration(tag: CompoundTag) {
minerMode = MinerMode.valueOf(tag.getString("MinerMode"))
controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode"))
providerPriority = tag.getInt("ProviderPriority")
}
enum class MinerMode {
ON_DEMAND, AUTOMATIC;
val friendlyName = TranslatableText("gui.phycon.miner_mode.${name.toLowerCase()}")
}
class MinerInvProxy(val miner: MinerBlockEntity): GroupedItemInvView { class MinerInvProxy(val miner: MinerBlockEntity): GroupedItemInvView {
companion object {
val TOOL = ItemStack(Items.DIAMOND_PICKAXE)
}
private var cachedState: BlockState? = null private var cachedState: BlockState? = null
private var cachedDrops: List<ItemStack>? = null private var cachedDrops: List<ItemStack>? = null
@ -117,12 +206,15 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
if (cachedDrops == null || realState != cachedState || recalculate) { if (cachedDrops == null || realState != cachedState || recalculate) {
cachedState = realState cachedState = realState
val be = if (realState.block.hasBlockEntity()) world.getBlockEntity(targetPos) else null val be = if (realState.block.hasBlockEntity()) world.getBlockEntity(targetPos) else null
cachedDrops = Block.getDroppedStacks(realState, world as ServerWorld, targetPos, be) cachedDrops = Block.getDroppedStacks(realState, world as ServerWorld, targetPos, be, null, TOOL)
} }
return cachedDrops!! return cachedDrops!!
} }
override fun getStoredStacks(): Set<ItemStack> { override fun getStoredStacks(): Set<ItemStack> {
if (miner.minerMode != MinerMode.ON_DEMAND) {
return setOf()
}
return getDrops().toSet() return getDrops().toSet()
} }
@ -132,7 +224,7 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
override fun getStatistics(filter: ItemFilter): GroupedItemInvView.ItemInvStatistic { override fun getStatistics(filter: ItemFilter): GroupedItemInvView.ItemInvStatistic {
var totalCount = 0 var totalCount = 0
for (s in getDrops()) { for (s in storedStacks) {
if (filter.matches(s)) { if (filter.matches(s)) {
totalCount += s.count totalCount += s.count
} }

View File

@ -1,10 +1,11 @@
package net.shadowfacts.phycon.network.block.netinterface package net.shadowfacts.phycon.block.netinterface
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.* import net.minecraft.block.*
import net.minecraft.entity.LivingEntity import net.minecraft.entity.LivingEntity
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction import net.minecraft.util.math.Direction
@ -12,12 +13,16 @@ import net.minecraft.world.BlockView
import net.minecraft.world.World import net.minecraft.world.World
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.network.FaceDeviceBlock import net.shadowfacts.phycon.block.FaceDeviceBlock
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class InterfaceBlock: FaceDeviceBlock<InterfaceBlockEntity>(Settings.of(Material.METAL)), class InterfaceBlock: FaceDeviceBlock<InterfaceBlockEntity>(
Settings.of(Material.METAL)
.strength(1.5f)
.sounds(BlockSoundGroup.METAL)
),
NetworkComponentBlock, NetworkComponentBlock,
AttributeProvider { AttributeProvider {

View File

@ -1,20 +1,23 @@
package net.shadowfacts.phycon.network.block.netinterface package net.shadowfacts.phycon.block.netinterface
import alexiil.mc.lib.attributes.SearchOptions import alexiil.mc.lib.attributes.SearchOptions
import alexiil.mc.lib.attributes.Simulation import alexiil.mc.lib.attributes.Simulation
import alexiil.mc.lib.attributes.item.GroupedItemInv import alexiil.mc.lib.attributes.item.GroupedItemInv
import alexiil.mc.lib.attributes.item.ItemAttributes import alexiil.mc.lib.attributes.item.ItemAttributes
import net.minecraft.block.BlockState
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.nbt.CompoundTag
import net.minecraft.util.math.Direction import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.api.packet.Packet import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.network.FaceDeviceBlock import net.shadowfacts.phycon.block.FaceDeviceBlock
import net.shadowfacts.phycon.network.component.ItemStackPacketHandler import net.shadowfacts.phycon.component.ItemStackPacketHandler
import net.shadowfacts.phycon.network.component.NetworkStackProvider import net.shadowfacts.phycon.component.NetworkStackProvider
import net.shadowfacts.phycon.network.component.NetworkStackReceiver import net.shadowfacts.phycon.component.NetworkStackReceiver
import net.shadowfacts.phycon.network.component.handleItemStack import net.shadowfacts.phycon.component.handleItemStack
import net.shadowfacts.phycon.network.packet.* import net.shadowfacts.phycon.packet.*
import net.shadowfacts.phycon.util.ClientConfigurableDevice
import kotlin.math.min import kotlin.math.min
/** /**
@ -23,11 +26,15 @@ import kotlin.math.min
class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE), class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE),
ItemStackPacketHandler, ItemStackPacketHandler,
NetworkStackProvider, NetworkStackProvider,
NetworkStackReceiver { NetworkStackReceiver,
ClientConfigurableDevice {
private val facing: Direction private val facing: Direction
get() = cachedState[FaceDeviceBlock.FACING] get() = cachedState[FaceDeviceBlock.FACING]
override var providerPriority = 0
override var receiverPriority = 0
// todo: should this be a weak ref? // todo: should this be a weak ref?
private var inventory: GroupedItemInv? = null private var inventory: GroupedItemInv? = null
@ -101,4 +108,24 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE),
} }
} }
override fun toCommonTag(tag: CompoundTag) {
super.toCommonTag(tag)
writeDeviceConfiguration(tag)
}
override fun fromCommonTag(tag: CompoundTag) {
super.fromCommonTag(tag)
loadDeviceConfiguration(tag)
}
override fun writeDeviceConfiguration(tag: CompoundTag) {
tag.putInt("ProviderPriority", providerPriority)
tag.putInt("ReceiverPriority", receiverPriority)
}
override fun loadDeviceConfiguration(tag: CompoundTag) {
providerPriority = tag.getInt("ProviderPriority")
receiverPriority = tag.getInt("ReceiverPriority")
}
} }

View File

@ -1,9 +1,10 @@
package net.shadowfacts.phycon.network.block.netswitch package net.shadowfacts.phycon.block.netswitch
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.BlockState import net.minecraft.block.BlockState
import net.minecraft.block.Material import net.minecraft.block.Material
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction import net.minecraft.util.math.Direction
@ -19,7 +20,11 @@ import java.util.*
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class SwitchBlock: BlockWithEntity<SwitchBlockEntity>(Settings.of(Material.METAL)), class SwitchBlock: BlockWithEntity<SwitchBlockEntity>(
Settings.of(Material.METAL)
.strength(1.5f)
.sounds(BlockSoundGroup.METAL)
),
NetworkComponentBlock, NetworkComponentBlock,
AttributeProvider { AttributeProvider {

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.block.netswitch 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
@ -13,8 +13,8 @@ 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.MACAddress import net.shadowfacts.phycon.api.util.MACAddress
import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.NetworkUtil import net.shadowfacts.phycon.util.NetworkUtil
import net.shadowfacts.phycon.network.packet.ItemStackPacket import net.shadowfacts.phycon.packet.ItemStackPacket
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
/** /**

View File

@ -1,10 +1,10 @@
package net.shadowfacts.phycon.network.block.redstone package net.shadowfacts.phycon.block.redstone_controller
import net.minecraft.block.Block 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.item.ItemPlacementContext import net.minecraft.item.ItemPlacementContext
import net.minecraft.server.world.ServerWorld import net.minecraft.sound.BlockSoundGroup
import net.minecraft.state.StateManager import net.minecraft.state.StateManager
import net.minecraft.state.property.Properties import net.minecraft.state.property.Properties
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
@ -12,22 +12,23 @@ import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction import net.minecraft.util.math.Direction
import net.minecraft.world.BlockView import net.minecraft.world.BlockView
import net.minecraft.world.World import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.network.FaceDeviceBlock import net.shadowfacts.phycon.block.FaceDeviceBlock
import java.util.*
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class RedstoneControllerBlock: FaceDeviceBlock<RedstoneControllerBlockEntity>(Settings.of(Material.METAL)) { class RedstoneControllerBlock: FaceDeviceBlock<RedstoneControllerBlockEntity>(
Settings.of(Material.METAL)
.strength(1.5f)
.sounds(BlockSoundGroup.METAL)
) {
companion object { companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "redstone_controller") val ID = Identifier(PhysicalConnectivity.MODID, "redstone_controller")
val LIT = Properties.LIT val POWERED = Properties.POWERED
} }
// todo: don't just copy this from the Interface block
override val faceThickness = 3.0 override val faceThickness = 3.0
override val faceShapes = mapOf( override val faceShapes = mapOf(
Direction.DOWN to createCuboidShape(0.0, 0.0, 0.0, 16.0, 3.0, 16.0), Direction.DOWN to createCuboidShape(0.0, 0.0, 0.0, 16.0, 3.0, 16.0),
@ -40,20 +41,20 @@ class RedstoneControllerBlock: FaceDeviceBlock<RedstoneControllerBlockEntity>(Se
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) { override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
super.appendProperties(builder) super.appendProperties(builder)
builder.add(LIT) builder.add(POWERED)
} }
override fun createBlockEntity(world: BlockView) = RedstoneControllerBlockEntity() override fun createBlockEntity(world: BlockView) = RedstoneControllerBlockEntity()
override fun getPlacementState(context: ItemPlacementContext): BlockState { override fun getPlacementState(context: ItemPlacementContext): BlockState {
val state = super.getPlacementState(context) val state = super.getPlacementState(context)
return state.with(LIT, isPowered(context.world, context.blockPos, state[FACING])) return state.with(POWERED, isPowered(context.world, context.blockPos, state[FACING]))
} }
override fun neighborUpdate(state: BlockState, world: World, pos: BlockPos, neighborBlock: Block, neighborPos: BlockPos, bl: Boolean) { override fun neighborUpdate(state: BlockState, world: World, pos: BlockPos, neighborBlock: Block, neighborPos: BlockPos, bl: Boolean) {
// this can't be done in getStateForNeighborUpdate because getEmittedRedstonePower is defined in World not WorldAccess // this can't be done in getStateForNeighborUpdate because getEmittedRedstonePower is defined in World not WorldAccess
if (!world.isClient) { if (!world.isClient) {
val wasLit = state[LIT] val wasLit = state[POWERED]
val isLit = isPowered(world, pos, state[FACING]) val isLit = isPowered(world, pos, state[FACING])
if (wasLit != isLit) { if (wasLit != isLit) {
toggleLit(state, world, pos) toggleLit(state, world, pos)
@ -67,7 +68,7 @@ class RedstoneControllerBlock: FaceDeviceBlock<RedstoneControllerBlockEntity>(Se
} }
private fun toggleLit(state: BlockState, world: World, pos: BlockPos) { private fun toggleLit(state: BlockState, world: World, pos: BlockPos) {
world.setBlockState(pos, state.cycle(LIT), 2) world.setBlockState(pos, state.cycle(POWERED), 2)
getBlockEntity(world, pos)!!.redstoneStateChanged() getBlockEntity(world, pos)!!.redstoneStateChanged()
} }

View File

@ -1,18 +1,19 @@
package net.shadowfacts.phycon.network.block.redstone package net.shadowfacts.phycon.block.redstone_controller
import net.minecraft.block.BlockState
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.shadowfacts.phycon.api.packet.Packet import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.network.packet.RemoteActivationPacket import net.shadowfacts.phycon.packet.RemoteActivationPacket
import net.shadowfacts.phycon.util.ClientConfigurableDevice
import net.shadowfacts.phycon.util.RedstoneMode import net.shadowfacts.phycon.util.RedstoneMode
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class RedstoneControllerBlockEntity: DeviceBlockEntity(PhyBlockEntities.REDSTONE_CONTROLLER) { class RedstoneControllerBlockEntity: DeviceBlockEntity(PhyBlockEntities.REDSTONE_CONTROLLER),
ClientConfigurableDevice {
var managedDevices = Array<IPAddress?>(5) { null } var managedDevices = Array<IPAddress?>(5) { null }
var redstoneMode = RedstoneMode.HIGH var redstoneMode = RedstoneMode.HIGH
@ -24,7 +25,7 @@ class RedstoneControllerBlockEntity: DeviceBlockEntity(PhyBlockEntities.REDSTONE
fun redstoneStateChanged() { fun redstoneStateChanged() {
val oldPowered = redstonePowered val oldPowered = redstonePowered
redstonePowered = cachedState[RedstoneControllerBlock.LIT] redstonePowered = cachedState[RedstoneControllerBlock.POWERED]
val mode: RemoteActivationPacket.Mode? = when (redstoneMode) { val mode: RemoteActivationPacket.Mode? = when (redstoneMode) {
RedstoneMode.TOGGLE -> if (oldPowered != redstonePowered) RemoteActivationPacket.Mode.SINGLE else null RedstoneMode.TOGGLE -> if (oldPowered != redstonePowered) RemoteActivationPacket.Mode.SINGLE else null
@ -46,27 +47,22 @@ class RedstoneControllerBlockEntity: DeviceBlockEntity(PhyBlockEntities.REDSTONE
} }
} }
override fun toTag(tag: CompoundTag): CompoundTag { override fun toCommonTag(tag: CompoundTag) {
super.toCommonTag(tag)
writeDeviceConfiguration(tag)
}
override fun fromCommonTag(tag: CompoundTag) {
super.fromCommonTag(tag)
loadDeviceConfiguration(tag)
}
override fun writeDeviceConfiguration(tag: CompoundTag) {
tag.putIntArray("ManagedDevices", managedDevices.mapNotNull { it?.address }) tag.putIntArray("ManagedDevices", managedDevices.mapNotNull { it?.address })
tag.putString("RedstoneMode", redstoneMode.name) tag.putString("RedstoneMode", redstoneMode.name)
return super.toTag(tag)
} }
override fun fromTag(state: BlockState, tag: CompoundTag) { override fun loadDeviceConfiguration(tag: CompoundTag) {
super.fromTag(state, tag)
val addresses = tag.getIntArray("ManagedDevices")
managedDevices = (0..4).map { if (it >= addresses.size) null else IPAddress(addresses[it]) }.toTypedArray()
redstoneMode = RedstoneMode.valueOf(tag.getString("RedstoneMode"))
}
override fun toClientTag(tag: CompoundTag): CompoundTag {
tag.putIntArray("ManagedDevices", managedDevices.mapNotNull { it?.address })
tag.putString("RedstoneMode", redstoneMode.name)
return super.toClientTag(tag)
}
override fun fromClientTag(tag: CompoundTag) {
super.fromClientTag(tag)
val addresses = tag.getIntArray("ManagedDevices") val addresses = tag.getIntArray("ManagedDevices")
managedDevices = (0..4).map { if (it >= addresses.size) null else IPAddress(addresses[it]) }.toTypedArray() managedDevices = (0..4).map { if (it >= addresses.size) null else IPAddress(addresses[it]) }.toTypedArray()
redstoneMode = RedstoneMode.valueOf(tag.getString("RedstoneMode")) redstoneMode = RedstoneMode.valueOf(tag.getString("RedstoneMode"))

View File

@ -0,0 +1,88 @@
package net.shadowfacts.phycon.block.redstone_emitter
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory
import net.minecraft.block.BlockState
import net.minecraft.block.Material
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.network.PacketByteBuf
import net.minecraft.screen.ScreenHandler
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.text.Text
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.block.FaceDeviceBlock
/**
* @author shadowfacts
*/
class RedstoneEmitterBlock: FaceDeviceBlock<RedstoneEmitterBlockEntity>(
Settings.of(Material.METAL)
.strength(1.5f)
.sounds(BlockSoundGroup.METAL)
) {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "redstone_emitter")
}
// todo: don't just copy the redstone controller
override val faceThickness = 3.0
override val faceShapes = mapOf(
Direction.DOWN to createCuboidShape(0.0, 0.0, 0.0, 16.0, 3.0, 16.0),
Direction.UP to createCuboidShape(0.0, 13.0, 0.0, 16.0, 16.0, 16.0),
Direction.NORTH to createCuboidShape(0.0, 0.0, 0.0, 16.0, 16.0, 3.0),
Direction.SOUTH to createCuboidShape(0.0, 0.0, 13.0, 16.0, 16.0, 16.0),
Direction.WEST to createCuboidShape(0.0, 0.0, 0.0, 3.0, 16.0, 16.0),
Direction.EAST to createCuboidShape(13.0, 0.0, 0.0, 16.0, 16.0, 16.0)
)
override fun createBlockEntity(world: BlockView) = RedstoneEmitterBlockEntity()
override fun emitsRedstonePower(state: BlockState): Boolean {
return true
}
override fun getStrongRedstonePower(state: BlockState, world: BlockView, pos: BlockPos, receivingSide: Direction): Int {
return if (receivingSide.opposite == state[FACING]) {
getBlockEntity(world, pos)!!.cachedEmittedPower
} else {
0
}
}
override fun getWeakRedstonePower(state: BlockState, world: BlockView, pos: BlockPos, receivingSide: Direction): Int {
return getStrongRedstonePower(state, world, pos, receivingSide)
}
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, result: BlockHitResult): ActionResult {
if (!world.isClient) {
val be = getBlockEntity(world, pos)!!
be.sync()
val factory = object: ExtendedScreenHandlerFactory {
override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler {
return RedstoneEmitterScreenHandler(syncId, playerInv, be)
}
override fun getDisplayName() = this@RedstoneEmitterBlock.name
override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) {
buf.writeBlockPos(be.pos)
}
}
player.openHandledScreen(factory)
}
return ActionResult.SUCCESS
}
}

View File

@ -0,0 +1,107 @@
package net.shadowfacts.phycon.block.redstone_emitter
import alexiil.mc.lib.attributes.item.GroupedItemInvView
import net.minecraft.item.ItemStack
import net.minecraft.nbt.CompoundTag
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.block.FaceDeviceBlock
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.packet.DeviceRemovedPacket
import net.shadowfacts.phycon.packet.ReadInventoryPacket
import net.shadowfacts.phycon.packet.RequestInventoryPacket
import net.shadowfacts.phycon.util.ClientConfigurableDevice
import kotlin.math.round
/**
* @author shadowfacts
*/
class RedstoneEmitterBlockEntity: DeviceBlockEntity(PhyBlockEntities.REDSTONE_EMITTER),
ClientConfigurableDevice {
private val inventoryCache = mutableMapOf<IPAddress, GroupedItemInvView>()
var cachedEmittedPower: Int = 0
private set
var stackToMonitor: ItemStack = ItemStack.EMPTY
var maxAmount = 64
override fun handle(packet: Packet) {
when (packet) {
is ReadInventoryPacket -> handleReadInventory(packet)
is DeviceRemovedPacket -> handleDeviceRemoved(packet)
}
}
private fun handleReadInventory(packet: ReadInventoryPacket) {
inventoryCache[packet.source] = packet.inventory
recalculateRedstone()
}
private fun handleDeviceRemoved(packet: DeviceRemovedPacket) {
inventoryCache.remove(packet.source)
recalculateRedstone()
}
override fun tick() {
super.tick()
if (!world!!.isClient && counter % 20 == 0L) {
if (counter % 80 == 0L) {
updateInventories()
} else if (counter % 20 == 0L) {
recalculateRedstone()
}
}
}
private fun updateInventories() {
sendPacket(RequestInventoryPacket(ipAddress))
}
private fun recalculateRedstone() {
if (stackToMonitor.isEmpty) {
cachedEmittedPower = 0
updateWorld()
return
}
val networkAmount = inventoryCache.values.fold(0) { acc, inv ->
acc + inv.getAmount(stackToMonitor)
}
cachedEmittedPower =
if (networkAmount == 0) {
0
} else {
1 + round(networkAmount / maxAmount.toDouble() * 14).toInt()
}
updateWorld()
}
private fun updateWorld() {
world!!.updateNeighborsAlways(pos, cachedState.block)
world!!.updateNeighborsAlways(pos.offset(cachedState[FaceDeviceBlock.FACING]), cachedState.block)
}
override fun toCommonTag(tag: CompoundTag) {
super.toCommonTag(tag)
tag.putInt("CachedEmittedPower", cachedEmittedPower)
tag.put("StackToMonitor", stackToMonitor.toTag(CompoundTag()))
writeDeviceConfiguration(tag)
}
override fun fromCommonTag(tag: CompoundTag) {
super.fromCommonTag(tag)
cachedEmittedPower = tag.getInt("CachedEmittedPower")
stackToMonitor = ItemStack.fromTag(tag.getCompound("StackToMonitor"))
loadDeviceConfiguration(tag)
}
override fun writeDeviceConfiguration(tag: CompoundTag) {
tag.putInt("MaxAmount", maxAmount)
}
override fun loadDeviceConfiguration(tag: CompoundTag) {
maxAmount = tag.getInt("MaxAmount")
}
}

View File

@ -0,0 +1,155 @@
package net.shadowfacts.phycon.block.redstone_emitter
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.MinecraftClient
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.text.Text
import net.minecraft.text.TranslatableText
import net.minecraft.util.Identifier
import net.shadowfacts.cacao.CacaoHandledScreen
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.textfield.NumberField
import net.shadowfacts.cacao.window.ScreenHandlerWindow
import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.networking.C2SConfigureDevice
import no.birkett.kiwi.Variable
import kotlin.math.ceil
/**
* @author shadowfacts
*/
class RedstoneEmitterScreen(
handler: RedstoneEmitterScreenHandler,
playerInv: PlayerInventory,
title: Text
): CacaoHandledScreen<RedstoneEmitterScreenHandler>(
handler,
playerInv,
title
) {
companion object {
val BACKGROUND = Identifier(PhysicalConnectivity.MODID, "textures/gui/redstone_emitter.png")
}
init {
backgroundWidth = 176
backgroundHeight = 166
addWindow(ScreenHandlerWindow(handler, ViewController(handler.emitter)))
}
override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) {
super.drawBackground(matrixStack, delta, mouseX, mouseY)
RenderSystem.color4f(1f, 1f, 1f, 1f)
client!!.textureManager.bindTexture(BACKGROUND)
val x = (width - backgroundWidth) / 2
val y = (height - backgroundHeight) / 2
drawTexture(matrixStack, x, y, 0, 0, backgroundWidth, backgroundHeight)
}
class ViewController(
private val emitter: RedstoneEmitterBlockEntity,
): net.shadowfacts.cacao.viewcontroller.ViewController() {
lateinit var halfLabel: Label
lateinit var fullLabel: Label
override fun viewDidLoad() {
super.viewDidLoad()
val title = Label(PhyBlocks.REDSTONE_EMITTER.name)
title.textColor = Color.TEXT
view.addSubview(title)
val inv = Label(MinecraftClient.getInstance().player!!.inventory.displayName)
inv.textColor = Color.TEXT
view.addSubview(inv)
val field = NumberField(emitter.maxAmount) {
if (it.number != null) {
emitter.maxAmount = it.number!!
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(emitter))
}
updateLabelTexts()
}
field.validator = { it >= 0 }
field.drawBackground = false
view.addSubview(field)
val hStack = StackView(Axis.HORIZONTAL, StackView.Distribution.FILL, spacing = 2.0)
view.addSubview(hStack)
val zeroStack = hStack.addArrangedSubview(StackView(Axis.VERTICAL))
zeroStack.addArrangedSubview(Label(TranslatableText("gui.phycon.emitter.count", 0), textAlignment = Label.TextAlignment.CENTER)).apply {
textColor = Color.TEXT
}
zeroStack.addArrangedSubview(View())
zeroStack.addArrangedSubview(Label("0", textAlignment = Label.TextAlignment.CENTER)).apply {
textColor = Color.RED
}
val halfStack = hStack.addArrangedSubview(StackView(Axis.VERTICAL))
halfLabel = halfStack.addArrangedSubview(Label("half", textAlignment = Label.TextAlignment.CENTER)).apply {
textColor = Color.TEXT
}
halfStack.addArrangedSubview(View())
halfStack.addArrangedSubview(Label("8", textAlignment = Label.TextAlignment.CENTER)).apply {
textColor = Color.RED
}
val fullStack = hStack.addArrangedSubview(StackView(Axis.VERTICAL))
fullLabel = fullStack.addArrangedSubview(Label("full", textAlignment = Label.TextAlignment.CENTER)).apply {
textColor = Color.TEXT
}
fullStack.addArrangedSubview(View())
fullStack.addArrangedSubview(Label("15", textAlignment = Label.TextAlignment.CENTER)).apply {
textColor = Color.RED
}
updateLabelTexts()
view.solver.dsl {
val minX = Variable("minX")
val minY = Variable("minY")
minX equalTo ((view.widthAnchor - 176) / 2)
minY equalTo ((view.heightAnchor - 166) / 2)
title.leftAnchor equalTo (minX + 8)
title.topAnchor equalTo (minY + 6)
inv.leftAnchor equalTo (minX + 8)
inv.topAnchor equalTo (minY + 72)
// offset by 1 on each edge to account for MC border
field.widthAnchor equalTo 82
field.heightAnchor equalTo 11
field.leftAnchor equalTo (minX + 56)
field.topAnchor equalTo (minY + 23)
hStack.centerXAnchor equalTo view.centerXAnchor
hStack.widthAnchor equalTo (176 - 4)
hStack.topAnchor equalTo (field.bottomAnchor + 8)
hStack.bottomAnchor equalTo inv.topAnchor
zeroStack.widthAnchor equalTo halfStack.widthAnchor
halfStack.widthAnchor equalTo fullStack.widthAnchor
}
}
private fun updateLabelTexts() {
halfLabel.text = TranslatableText("gui.phycon.emitter.count", ceil(emitter.maxAmount / 2.0).toInt())
fullLabel.text = TranslatableText("gui.phycon.emitter.count", emitter.maxAmount)
window!!.layout()
}
}
}

View File

@ -0,0 +1,76 @@
package net.shadowfacts.phycon.block.redstone_emitter
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.item.ItemStack
import net.minecraft.network.PacketByteBuf
import net.minecraft.screen.ScreenHandler
import net.minecraft.screen.slot.Slot
import net.minecraft.screen.slot.SlotActionType
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.init.PhyScreens
import net.shadowfacts.phycon.util.GhostSlot
import net.shadowfacts.phycon.util.copyWithCount
/**
* @author shadowfacts
*/
class RedstoneEmitterScreenHandler(
syncId: Int,
playerInv: PlayerInventory,
val emitter: RedstoneEmitterBlockEntity,
): ScreenHandler(PhyScreens.REDSTONE_EMITTER, syncId) {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "redstone_emitter")
}
constructor(syncId: Int, playerInv: PlayerInventory, buf: PacketByteBuf):
this(
syncId,
playerInv,
PhyBlocks.REDSTONE_EMITTER.getBlockEntity(playerInv.player.world, buf.readBlockPos())!!
)
init {
// fake slot
addSlot(GhostSlot(emitter::stackToMonitor, 31, 20))
// player inv
for (y in 0 until 3) {
for (x in 0 until 9) {
addSlot(Slot(playerInv, x + y * 9 + 9, 8 + x * 18, 84 + y * 18))
}
}
// hotbar
for (x in 0 until 9) {
addSlot(Slot(playerInv, x, 8 + x * 18, 142))
}
}
override fun canUse(player: PlayerEntity): Boolean {
return true
}
override fun onSlotClick(slotId: Int, clickData: Int, slotActionType: SlotActionType, player: PlayerEntity): ItemStack {
// fake slot
if (slotId == 0) {
if (player.inventory.cursorStack.isEmpty) {
emitter.stackToMonitor = ItemStack.EMPTY
} else {
emitter.stackToMonitor = player.inventory.cursorStack.copyWithCount(1)
}
}
return super.onSlotClick(slotId, clickData, slotActionType, player)
}
override fun transferSlot(player: PlayerEntity, slotId: Int): ItemStack {
val slot = slots[slotId]
emitter.stackToMonitor = slot.stack.copyWithCount(1)
return ItemStack.EMPTY
}
}

View File

@ -1,10 +1,11 @@
package net.shadowfacts.phycon.network.block.terminal 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.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.sound.BlockSoundGroup
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
@ -16,13 +17,17 @@ import net.minecraft.world.World
import net.minecraft.world.WorldAccess 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.network.DeviceBlock import net.shadowfacts.phycon.block.DeviceBlock
import java.util.* import java.util.*
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class TerminalBlock: DeviceBlock<TerminalBlockEntity>(Settings.of(Material.METAL)), class TerminalBlock: DeviceBlock<TerminalBlockEntity>(
Settings.of(Material.METAL)
.strength(1.5f)
.sounds(BlockSoundGroup.METAL)
),
NetworkComponentBlock, NetworkComponentBlock,
AttributeProvider { AttributeProvider {
@ -41,6 +46,15 @@ class TerminalBlock: DeviceBlock<TerminalBlockEntity>(Settings.of(Material.METAL
return ActionResult.SUCCESS return ActionResult.SUCCESS
} }
override fun onStateReplaced(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean) {
if (!state.isOf(newState.block)) {
val be = getBlockEntity(world, pos)!!
ItemScatterer.spawn(world, pos, be.internalBuffer)
super.onStateReplaced(state, world, pos, newState, moved)
}
}
override fun addAllAttributes(world: World, pos: BlockPos, state: BlockState, to: AttributeList<*>) { override fun addAllAttributes(world: World, pos: BlockPos, state: BlockState, to: AttributeList<*>) {
to.offer(getBlockEntity(world, pos)) to.offer(getBlockEntity(world, pos))
} }

View File

@ -1,18 +1,16 @@
package net.shadowfacts.phycon.network.block.terminal package net.shadowfacts.phycon.block.terminal
import alexiil.mc.lib.attributes.item.GroupedItemInvView import alexiil.mc.lib.attributes.item.GroupedItemInvView
import alexiil.mc.lib.attributes.item.ItemStackCollections import alexiil.mc.lib.attributes.item.ItemStackCollections
import alexiil.mc.lib.attributes.item.ItemStackUtil import alexiil.mc.lib.attributes.item.ItemStackUtil
import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory
import net.minecraft.block.BlockState
import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory import net.minecraft.entity.player.PlayerInventory
import net.minecraft.inventory.Inventory import net.minecraft.inventory.Inventory
import net.minecraft.inventory.InventoryChangedListener import net.minecraft.inventory.InventoryChangedListener
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.network.PacketByteBuf import net.minecraft.network.PacketByteBuf
import net.minecraft.screen.ScreenHandler import net.minecraft.screen.ScreenHandler
import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.server.network.ServerPlayerEntity
@ -23,10 +21,10 @@ import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.packet.Packet import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.network.NetworkUtil import net.shadowfacts.phycon.util.NetworkUtil
import net.shadowfacts.phycon.network.component.* import net.shadowfacts.phycon.component.*
import net.shadowfacts.phycon.network.packet.* import net.shadowfacts.phycon.packet.*
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.* import java.util.*
import kotlin.math.min import kotlin.math.min
@ -43,8 +41,10 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
NetworkStackDispatcher<TerminalBlockEntity.PendingInsertion> { NetworkStackDispatcher<TerminalBlockEntity.PendingInsertion> {
companion object { companion object {
val LOCATE_REQUEST_TIMEOUT: Long = 40 // ticks // the locate/insertion timeouts are only 1 tick because that's long enough to hear from every device on the network
val INSERTION_TIMEOUT: Long = 40 // in a degraded state (when there's latency in the network), not handling interface priorities correctly is acceptable
val LOCATE_REQUEST_TIMEOUT: Long = 1 // ticks
val INSERTION_TIMEOUT: Long = 1
} }
private val inventoryCache = mutableMapOf<IPAddress, GroupedItemInvView>() private val inventoryCache = mutableMapOf<IPAddress, GroupedItemInvView>()
@ -164,14 +164,15 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
override fun tick() { override fun tick() {
super.tick() super.tick()
if (counter % 20 == 0L) {
if (!world!!.isClient) { if (!world!!.isClient) {
finishPendingRequests() finishPendingRequests()
beginInsertions()
finishTimedOutPendingInsertions() finishTimedOutPendingInsertions()
} }
if (observers > 0 && !world!!.isClient) { if (counter % 20 == 0L && !world!!.isClient) {
beginInsertions()
if (observers > 0) {
updateAndSync() updateAndSync()
} }
} }
@ -184,7 +185,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
inventoryCache.clear() inventoryCache.clear()
sendPacket(RequestInventoryPacket(ipAddress)) sendPacket(RequestInventoryPacket(ipAddress))
val factory = object: ExtendedScreenHandlerFactory { val factory = object: ExtendedScreenHandlerFactory {
override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler? { override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler {
return TerminalScreenHandler(syncId, playerInv, this@TerminalBlockEntity) return TerminalScreenHandler(syncId, playerInv, this@TerminalBlockEntity)
} }
@ -210,8 +211,15 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
private fun stackLocateRequestCompleted(request: StackLocateRequest) { private fun stackLocateRequestCompleted(request: StackLocateRequest) {
pendingRequests.remove(request) pendingRequests.remove(request)
// todo: also sort results by interface priority val sortedResults = request.results.toMutableList()
val sortedResults = request.results.sortedByDescending { it.first }.toMutableList() sortedResults.sortWith { a, b ->
// sort results first by provider priority, and then by the count that it can provide
if (a.second.providerPriority == b.second.providerPriority) {
b.first - a.first
} else {
b.second.providerPriority - a.second.providerPriority
}
}
var amountRequested = 0 var amountRequested = 0
while (amountRequested < request.amount && sortedResults.isNotEmpty()) { while (amountRequested < request.amount && sortedResults.isNotEmpty()) {
val (sourceAmount, sourceInterface) = sortedResults.removeAt(0) val (sourceAmount, sourceInterface) = sortedResults.removeAt(0)
@ -240,25 +248,15 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
} }
} }
override fun toTag(tag: CompoundTag): CompoundTag { override fun toCommonTag(tag: CompoundTag) {
super.toCommonTag(tag)
tag.put("InternalBuffer", internalBuffer.toTag()) tag.put("InternalBuffer", internalBuffer.toTag())
return super.toTag(tag)
} }
override fun fromTag(state: BlockState, tag: CompoundTag) { override fun fromCommonTag(tag: CompoundTag) {
super.fromTag(state, tag) super.fromCommonTag(tag)
internalBuffer.fromTag(tag.getCompound("InternalBuffer")) internalBuffer.fromTag(tag.getCompound("InternalBuffer"))
} }
override fun toClientTag(tag: CompoundTag): CompoundTag {
tag.put("InternalBuffer", internalBuffer.toTag())
return tag
}
override fun fromClientTag(tag: CompoundTag) {
internalBuffer.fromTag(tag.getCompound("InternalBuffer"))
}
interface NetItemObserver { interface NetItemObserver {
fun netItemsChanged() fun netItemsChanged()
} }
@ -279,6 +277,8 @@ data class StackLocateRequest(
get() = results.fold(0) { acc, (amount, _) -> acc + amount } get() = results.fold(0) { acc, (amount, _) -> acc + amount }
fun isFinishable(currentTimestamp: Long): Boolean { fun isFinishable(currentTimestamp: Long): Boolean {
return totalResultAmount >= amount || currentTimestamp - timestamp >= TerminalBlockEntity.LOCATE_REQUEST_TIMEOUT // we can't check totalResultAmount >= amount because we need to hear back from all network stack providers to
// correctly sort by priority
return currentTimestamp - timestamp >= TerminalBlockEntity.LOCATE_REQUEST_TIMEOUT
} }
} }

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.block.terminal package net.shadowfacts.phycon.block.terminal
import alexiil.mc.lib.attributes.item.ItemStackUtil import alexiil.mc.lib.attributes.item.ItemStackUtil
import net.minecraft.inventory.SimpleInventory import net.minecraft.inventory.SimpleInventory

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.block.terminal package net.shadowfacts.phycon.block.terminal
import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.Slot
import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerEntity

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.block.terminal package net.shadowfacts.phycon.block.terminal
import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.MinecraftClient import net.minecraft.client.MinecraftClient

View File

@ -1,6 +1,5 @@
package net.shadowfacts.phycon.network.block.terminal package net.shadowfacts.phycon.block.terminal
import net.minecraft.client.network.ClientPlayerEntity
import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.Slot
import net.minecraft.screen.slot.SlotActionType import net.minecraft.screen.slot.SlotActionType
import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerEntity
@ -14,17 +13,16 @@ import net.minecraft.util.registry.Registry
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.init.PhyBlocks import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.init.PhyScreens import net.shadowfacts.phycon.init.PhyScreens
import net.shadowfacts.phycon.networking.C2STerminalRequestItem
import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems
import net.shadowfacts.phycon.util.SortMode import net.shadowfacts.phycon.util.SortMode
import net.shadowfacts.phycon.util.copyWithCount
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import kotlin.math.ceil
import kotlin.math.min import kotlin.math.min
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val terminal: TerminalBlockEntity): ScreenHandler(PhyScreens.TERMINAL_SCREEN_HANDLER, syncId), class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val terminal: TerminalBlockEntity): ScreenHandler(PhyScreens.TERMINAL, syncId),
TerminalBlockEntity.NetItemObserver { TerminalBlockEntity.NetItemObserver {
companion object { companion object {
@ -40,9 +38,7 @@ class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val ter
field = value field = value
if (terminal.world!!.isClient) { if (terminal.world!!.isClient) {
itemsForDisplay = value.map { itemsForDisplay = value.map {
val stack = it.stack.copy() it.stack.copyWithCount(it.amount)
stack.count = it.amount
stack
} }
} }
} }

View File

@ -1,8 +1,9 @@
package net.shadowfacts.phycon.network.component package net.shadowfacts.phycon.component
import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntity
import net.shadowfacts.phycon.network.packet.RemoteActivationPacket import net.shadowfacts.phycon.packet.RemoteActivationPacket
import net.shadowfacts.phycon.util.ActivationMode import net.shadowfacts.phycon.util.ActivationMode
import net.shadowfacts.phycon.util.ClientConfigurableDevice
/** /**
* @author shadowfacts * @author shadowfacts
@ -50,11 +51,15 @@ class ActivationController<T>(
} }
} }
interface ActivatableDevice { interface ActivatableDevice: ClientConfigurableDevice {
val controller: ActivationController<*> val controller: ActivationController<*>
val counter: Long val counter: Long
fun activate(): Boolean fun activate(): Boolean
fun canConfigureActivationController(): Boolean {
return true
}
} }
} }

View File

@ -1,11 +1,11 @@
package net.shadowfacts.phycon.network.component package net.shadowfacts.phycon.component
import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntity
import net.minecraft.entity.ItemEntity import net.minecraft.entity.ItemEntity
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.PacketSink import net.shadowfacts.phycon.api.PacketSink
import net.shadowfacts.phycon.api.PacketSource import net.shadowfacts.phycon.api.PacketSource
import net.shadowfacts.phycon.network.packet.ItemStackPacket import net.shadowfacts.phycon.packet.ItemStackPacket
/** /**
* @author shadowfacts * @author shadowfacts

View File

@ -1,12 +1,14 @@
package net.shadowfacts.phycon.network.component package net.shadowfacts.phycon.component
import alexiil.mc.lib.attributes.item.ItemStackUtil import alexiil.mc.lib.attributes.item.ItemStackUtil
import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntity
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.network.packet.CapacityPacket import net.shadowfacts.phycon.packet.CapacityPacket
import net.shadowfacts.phycon.network.packet.CheckCapacityPacket import net.shadowfacts.phycon.packet.CheckCapacityPacket
import net.shadowfacts.phycon.network.packet.ItemStackPacket import net.shadowfacts.phycon.packet.ItemStackPacket
import net.shadowfacts.phycon.util.copyWithCount
import kotlin.math.min
/** /**
* @author shadowfacts * @author shadowfacts
@ -27,10 +29,10 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
} }
fun handleCapacity(packet: CapacityPacket) { fun handleCapacity(packet: CapacityPacket) {
val insertion = pendingInsertions.firstOrNull { pendingInsertions.firstOrNull { insertion ->
ItemStackUtil.areEqualIgnoreAmounts(packet.stack, it.stack) ItemStackUtil.areEqualIgnoreAmounts(packet.stack, insertion.stack) &&
} insertion.results.none { it.second.ipAddress == packet.source }
if (insertion != null) { }?.also { insertion ->
insertion.results.add(packet.capacity to packet.stackReceiver) insertion.results.add(packet.capacity to packet.stackReceiver)
if (insertion.isFinishable(this)) { if (insertion.isFinishable(this)) {
val remaining = finishInsertion(insertion) val remaining = finishInsertion(insertion)
@ -42,14 +44,22 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
fun finishInsertion(insertion: Insertion): ItemStack { fun finishInsertion(insertion: Insertion): ItemStack {
pendingInsertions.remove(insertion) pendingInsertions.remove(insertion)
// todo: also sort results by interface priority val sortedResults = insertion.results.toMutableList()//.sortedBy { it.first }.toMutableList()
val sortedResults = insertion.results.sortedBy { it.first }.toMutableList() sortedResults.sortWith { a, b ->
// sort results first by receiver priority, and then by capacity
if (a.second.receiverPriority == b.second.receiverPriority) {
b.first - a.first
} else {
b.second.receiverPriority - a.second.receiverPriority
}
}
// copy the insertion stack so subclasses that override this method can still see the originally dispatched stack after the super call // copy the insertion stack so subclasses that override this method can still see the originally dispatched stack after the super call
val remaining = insertion.stack.copy() val remaining = insertion.stack.copy()
while (!remaining.isEmpty && sortedResults.isNotEmpty()) { while (!remaining.isEmpty && sortedResults.isNotEmpty()) {
val (capacity, receivingInterface) = sortedResults.removeFirst() val (capacity, receivingInterface) = sortedResults.removeFirst()
if (capacity <= 0) continue if (capacity <= 0) continue
sendPacket(ItemStackPacket(remaining.copy(), ipAddress, receivingInterface.ipAddress)) val copy = remaining.copyWithCount(min(capacity, remaining.count))
sendPacket(ItemStackPacket(copy, ipAddress, receivingInterface.ipAddress))
// todo: the destination should confirm how much was actually inserted, in case of race condition // todo: the destination should confirm how much was actually inserted, in case of race condition
remaining.count -= capacity remaining.count -= capacity
} }
@ -67,7 +77,8 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
get() = results.fold(0) { acc, (amount, _) -> acc + amount } get() = results.fold(0) { acc, (amount, _) -> acc + amount }
fun isFinishable(owner: NetworkStackDispatcher<Self>): Boolean { fun isFinishable(owner: NetworkStackDispatcher<Self>): Boolean {
return totalCapacity >= stack.count || owner.counter - timestamp >= owner.dispatchStackTimeout // can't check totalCapacity >= stack.count because we need to wait for all responses to correctly sort by priority
return owner.counter - timestamp >= owner.dispatchStackTimeout
} }
} }
} }

View File

@ -0,0 +1,17 @@
package net.shadowfacts.phycon.component
import net.shadowfacts.phycon.api.NetworkDevice
import net.shadowfacts.phycon.util.ClientConfigurableDevice
/**
* @author shadowfacts
*/
interface NetworkStackProvider: NetworkDevice, ClientConfigurableDevice {
var providerPriority: Int
fun canConfigureProviderPriority(): Boolean {
return true
}
}

View File

@ -0,0 +1,13 @@
package net.shadowfacts.phycon.component
import net.shadowfacts.phycon.api.NetworkDevice
import net.shadowfacts.phycon.util.ClientConfigurableDevice
/**
* @author shadowfacts
*/
interface NetworkStackReceiver: NetworkDevice, ClientConfigurableDevice {
var receiverPriority: Int
}

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.frame package net.shadowfacts.phycon.frame
import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.api.util.MACAddress import net.shadowfacts.phycon.api.util.MACAddress

View File

@ -1,8 +1,6 @@
package net.shadowfacts.phycon.network.frame package net.shadowfacts.phycon.frame
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.packet.Packet
import net.shadowfacts.phycon.api.util.MACAddress import net.shadowfacts.phycon.api.util.MACAddress
/** /**

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.frame package net.shadowfacts.phycon.frame
import net.shadowfacts.phycon.api.frame.PacketFrame import net.shadowfacts.phycon.api.frame.PacketFrame
import net.shadowfacts.phycon.api.packet.Packet import net.shadowfacts.phycon.api.packet.Packet

View File

@ -5,22 +5,22 @@ import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityType import net.minecraft.block.entity.BlockEntityType
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.network.block.extractor.ExtractorBlock import net.shadowfacts.phycon.block.extractor.ExtractorBlock
import net.shadowfacts.phycon.network.block.extractor.ExtractorBlockEntity import net.shadowfacts.phycon.block.extractor.ExtractorBlockEntity
import net.shadowfacts.phycon.network.block.miner.MinerBlock import net.shadowfacts.phycon.block.inserter.InserterBlock
import net.shadowfacts.phycon.network.block.miner.MinerBlockEntity import net.shadowfacts.phycon.block.inserter.InserterBlockEntity
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock import net.shadowfacts.phycon.block.miner.MinerBlock
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlockEntity import net.shadowfacts.phycon.block.miner.MinerBlockEntity
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock import net.shadowfacts.phycon.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlockEntity import net.shadowfacts.phycon.block.netinterface.InterfaceBlockEntity
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlock import net.shadowfacts.phycon.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlockEntity import net.shadowfacts.phycon.block.netswitch.SwitchBlockEntity
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock
import net.shadowfacts.phycon.network.block.terminal.TerminalBlockEntity import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlockEntity
import net.shadowfacts.phycon.network.block.test.DestBlock import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
import net.shadowfacts.phycon.network.block.test.DestBlockEntity import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlockEntity
import net.shadowfacts.phycon.network.block.test.SourceBlock import net.shadowfacts.phycon.block.terminal.TerminalBlock
import net.shadowfacts.phycon.network.block.test.SourceBlockEntity import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
/** /**
* @author shadowfacts * @author shadowfacts
@ -31,11 +31,10 @@ object PhyBlockEntities {
val TERMINAL = create(::TerminalBlockEntity, PhyBlocks.TERMINAL) val TERMINAL = create(::TerminalBlockEntity, PhyBlocks.TERMINAL)
val SWITCH = create(::SwitchBlockEntity, PhyBlocks.SWITCH) val SWITCH = create(::SwitchBlockEntity, PhyBlocks.SWITCH)
val EXTRACTOR = create(::ExtractorBlockEntity, PhyBlocks.EXTRACTOR) val EXTRACTOR = create(::ExtractorBlockEntity, PhyBlocks.EXTRACTOR)
val INSERTER = create(::InserterBlockEntity, PhyBlocks.INSERTER)
val MINER = create(::MinerBlockEntity, PhyBlocks.MINER) val MINER = create(::MinerBlockEntity, PhyBlocks.MINER)
val REDSTONE_CONTROLLER = create(::RedstoneControllerBlockEntity, PhyBlocks.REDSTONE_CONTROLLER) val REDSTONE_CONTROLLER = create(::RedstoneControllerBlockEntity, PhyBlocks.REDSTONE_CONTROLLER)
val REDSTONE_EMITTER = create(::RedstoneEmitterBlockEntity, PhyBlocks.REDSTONE_EMITTER)
val SOURCE = create(::SourceBlockEntity, PhyBlocks.SOURCE)
val DEST = create(::DestBlockEntity, PhyBlocks.DEST)
private fun <T: BlockEntity> create(builder: () -> T, block: Block): BlockEntityType<T> { private fun <T: BlockEntity> create(builder: () -> T, block: Block): BlockEntityType<T> {
return BlockEntityType.Builder.create(builder, block).build(null) return BlockEntityType.Builder.create(builder, block).build(null)
@ -46,11 +45,10 @@ object PhyBlockEntities {
register(TerminalBlock.ID, TERMINAL) register(TerminalBlock.ID, TERMINAL)
register(SwitchBlock.ID, SWITCH) register(SwitchBlock.ID, SWITCH)
register(ExtractorBlock.ID, EXTRACTOR) register(ExtractorBlock.ID, EXTRACTOR)
register(InserterBlock.ID, INSERTER)
register(MinerBlock.ID, MINER) register(MinerBlock.ID, MINER)
register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER) register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER)
register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER)
register(SourceBlock.ID, SOURCE)
register(DestBlock.ID, DEST)
} }
private fun register(id: Identifier, type: BlockEntityType<*>) { private fun register(id: Identifier, type: BlockEntityType<*>) {

View File

@ -3,15 +3,15 @@ package net.shadowfacts.phycon.init
import net.minecraft.block.Block import net.minecraft.block.Block
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.network.block.cable.CableBlock import net.shadowfacts.phycon.block.cable.CableBlock
import net.shadowfacts.phycon.network.block.extractor.ExtractorBlock import net.shadowfacts.phycon.block.extractor.ExtractorBlock
import net.shadowfacts.phycon.network.block.miner.MinerBlock import net.shadowfacts.phycon.block.inserter.InserterBlock
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock import net.shadowfacts.phycon.block.miner.MinerBlock
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock import net.shadowfacts.phycon.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlock import net.shadowfacts.phycon.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock
import net.shadowfacts.phycon.network.block.test.DestBlock import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
import net.shadowfacts.phycon.network.block.test.SourceBlock import net.shadowfacts.phycon.block.terminal.TerminalBlock
/** /**
* @author shadowfacts * @author shadowfacts
@ -23,11 +23,10 @@ object PhyBlocks {
val SWITCH = SwitchBlock() val SWITCH = SwitchBlock()
val CABLE = CableBlock() val CABLE = CableBlock()
val EXTRACTOR = ExtractorBlock() val EXTRACTOR = ExtractorBlock()
val INSERTER = InserterBlock()
val MINER = MinerBlock() val MINER = MinerBlock()
val REDSTONE_CONTROLLER = RedstoneControllerBlock() val REDSTONE_CONTROLLER = RedstoneControllerBlock()
val REDSTONE_EMITTER = RedstoneEmitterBlock()
val SOURCE = SourceBlock()
val DEST = DestBlock()
fun init() { fun init() {
register(InterfaceBlock.ID, INTERFACE) register(InterfaceBlock.ID, INTERFACE)
@ -35,11 +34,10 @@ object PhyBlocks {
register(SwitchBlock.ID, SWITCH) register(SwitchBlock.ID, SWITCH)
register(CableBlock.ID, CABLE) register(CableBlock.ID, CABLE)
register(ExtractorBlock.ID, EXTRACTOR) register(ExtractorBlock.ID, EXTRACTOR)
register(InserterBlock.ID, INSERTER)
register(MinerBlock.ID, MINER) register(MinerBlock.ID, MINER)
register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER) register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER)
register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER)
register(SourceBlock.ID, SOURCE)
register(DestBlock.ID, DEST)
} }
private fun register(id: Identifier, block: Block) { private fun register(id: Identifier, block: Block) {

View File

@ -6,31 +6,31 @@ import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry import net.minecraft.util.registry.Registry
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.network.block.cable.CableBlock import net.shadowfacts.phycon.block.cable.CableBlock
import net.shadowfacts.phycon.network.block.extractor.ExtractorBlock import net.shadowfacts.phycon.block.extractor.ExtractorBlock
import net.shadowfacts.phycon.network.block.miner.MinerBlock import net.shadowfacts.phycon.block.inserter.InserterBlock
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock import net.shadowfacts.phycon.block.miner.MinerBlock
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock import net.shadowfacts.phycon.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlock import net.shadowfacts.phycon.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock
import net.shadowfacts.phycon.network.block.test.DestBlock import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
import net.shadowfacts.phycon.network.block.test.SourceBlock import net.shadowfacts.phycon.block.terminal.TerminalBlock
import net.shadowfacts.phycon.item.DeviceBlockItem
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
object PhyItems { object PhyItems {
val INTERFACE = BlockItem(PhyBlocks.INTERFACE, Item.Settings()) val INTERFACE = DeviceBlockItem(PhyBlocks.INTERFACE, Item.Settings())
val TERMINAL = BlockItem(PhyBlocks.TERMINAL, Item.Settings()) val TERMINAL = DeviceBlockItem(PhyBlocks.TERMINAL, Item.Settings())
val SWITCH = BlockItem(PhyBlocks.SWITCH, Item.Settings()) val SWITCH = BlockItem(PhyBlocks.SWITCH, Item.Settings())
val CABLE = BlockItem(PhyBlocks.CABLE, Item.Settings()) val CABLE = BlockItem(PhyBlocks.CABLE, Item.Settings())
val EXTRACTOR = BlockItem(PhyBlocks.EXTRACTOR, Item.Settings()) val EXTRACTOR = DeviceBlockItem(PhyBlocks.EXTRACTOR, Item.Settings())
val MINER = BlockItem(PhyBlocks.MINER, Item.Settings()) val INSERTER = DeviceBlockItem(PhyBlocks.INSERTER, Item.Settings())
val REDSTONE_CONTROLLER = BlockItem(PhyBlocks.REDSTONE_CONTROLLER, Item.Settings()) val MINER = DeviceBlockItem(PhyBlocks.MINER, Item.Settings())
val REDSTONE_CONTROLLER = DeviceBlockItem(PhyBlocks.REDSTONE_CONTROLLER, Item.Settings())
val SOURCE = BlockItem(PhyBlocks.SOURCE, Item.Settings()) val REDSTONE_EMITTER = DeviceBlockItem(PhyBlocks.REDSTONE_EMITTER, Item.Settings())
val DEST = BlockItem(PhyBlocks.DEST , Item.Settings())
val SCREWDRIVER = ScrewdriverItem() val SCREWDRIVER = ScrewdriverItem()
val CONSOLE = ConsoleItem() val CONSOLE = ConsoleItem()
@ -41,11 +41,10 @@ object PhyItems {
register(SwitchBlock.ID, SWITCH) register(SwitchBlock.ID, SWITCH)
register(CableBlock.ID, CABLE) register(CableBlock.ID, CABLE)
register(ExtractorBlock.ID, EXTRACTOR) register(ExtractorBlock.ID, EXTRACTOR)
register(InserterBlock.ID, INSERTER)
register(MinerBlock.ID, MINER) register(MinerBlock.ID, MINER)
register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER) register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER)
register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER)
register(SourceBlock.ID, SOURCE)
register(DestBlock.ID, DEST)
register(ScrewdriverItem.ID, SCREWDRIVER) register(ScrewdriverItem.ID, SCREWDRIVER)
register(ConsoleItem.ID, CONSOLE) register(ConsoleItem.ID, CONSOLE)

View File

@ -2,17 +2,23 @@ package net.shadowfacts.phycon.init
import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry
import net.minecraft.screen.ScreenHandlerType import net.minecraft.screen.ScreenHandlerType
import net.minecraft.util.Identifier import net.shadowfacts.phycon.block.inserter.InserterScreenHandler
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterScreenHandler
import net.shadowfacts.phycon.network.block.terminal.TerminalScreenHandler import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler
object PhyScreens { object PhyScreens {
lateinit var TERMINAL_SCREEN_HANDLER: ScreenHandlerType<TerminalScreenHandler> lateinit var TERMINAL: ScreenHandlerType<TerminalScreenHandler>
private set
lateinit var INSERTER: ScreenHandlerType<InserterScreenHandler>
private set
lateinit var REDSTONE_EMITTER: ScreenHandlerType<RedstoneEmitterScreenHandler>
private set private set
fun init() { fun init() {
TERMINAL_SCREEN_HANDLER = ScreenHandlerRegistry.registerExtended(Identifier(PhysicalConnectivity.MODID, "terminal"), ::TerminalScreenHandler) TERMINAL = ScreenHandlerRegistry.registerExtended(TerminalScreenHandler.ID, ::TerminalScreenHandler)
INSERTER = ScreenHandlerRegistry.registerExtended(InserterScreenHandler.ID, ::InserterScreenHandler)
REDSTONE_EMITTER = ScreenHandlerRegistry.registerExtended(RedstoneEmitterScreenHandler.ID, ::RedstoneEmitterScreenHandler)
} }
} }

View File

@ -6,8 +6,8 @@ import net.minecraft.item.ItemUsageContext
import net.minecraft.util.ActionResult 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.network.DeviceBlock import net.shadowfacts.phycon.block.DeviceBlock
import net.shadowfacts.phycon.network.DeviceBlockEntity import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.screen.console.DeviceConsoleScreen import net.shadowfacts.phycon.screen.console.DeviceConsoleScreen
/** /**

View File

@ -0,0 +1,35 @@
package net.shadowfacts.phycon.item
import net.minecraft.client.item.TooltipContext
import net.minecraft.item.BlockItem
import net.minecraft.item.ItemStack
import net.minecraft.text.Text
import net.minecraft.world.World
import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.block.DeviceBlock
import net.shadowfacts.phycon.util.text
/**
* @author shadowfacts
*/
class DeviceBlockItem(block: DeviceBlock<*>, settings: Settings = Settings()): BlockItem(block, settings) {
override fun appendTooltip(stack: ItemStack, world: World?, list: MutableList<Text>, context: TooltipContext) {
val beTag = stack.getSubTag("BlockEntityTag")
if (beTag != null) {
val ip = IPAddress(beTag.getInt("IPAddress"))
list.add(text {
withStyle(lightPurple) {
+translate("tooltip.phycon.device.configured")
}
+" ("
+translate("tooltip.phycon.device.ip")
withStyle(darkPurple) {
+ip.toString()
}
+")"
})
}
}
}

View File

@ -1,8 +1,16 @@
package net.shadowfacts.phycon.item package net.shadowfacts.phycon.item
import net.minecraft.block.Blocks
import net.minecraft.entity.ItemEntity
import net.minecraft.item.Item import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.item.ItemUsageContext
import net.minecraft.sound.SoundCategory
import net.minecraft.sound.SoundEvents
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
/** /**
* @author shadowfacts * @author shadowfacts
@ -11,4 +19,33 @@ class ScrewdriverItem: Item(Settings()) {
companion object { companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "screwdriver") val ID = Identifier(PhysicalConnectivity.MODID, "screwdriver")
} }
override fun useOnBlock(context: ItemUsageContext): ActionResult {
val state = context.world.getBlockState(context.blockPos)
val block = state.block
if (block is DeviceBlock<*>) {
if (!context.world.isClient) {
val be = block.getBlockEntity(context.world, context.blockPos)!!
val stack = ItemStack(block)
val beTag = stack.getOrCreateSubTag("BlockEntityTag")
be.toTag(beTag)
// remove x, y, z entries for stacking purposes
beTag.remove("x")
beTag.remove("y")
beTag.remove("z")
val entity = ItemEntity(context.world, context.blockPos.x.toDouble(), context.blockPos.y.toDouble(), context.blockPos.z.toDouble(), stack)
context.world.spawnEntity(entity)
context.world.setBlockState(context.blockPos, Blocks.AIR.defaultState, 3, 512)
}
context.world.playSound(context.player, context.blockPos, SoundEvents.BLOCK_METAL_PLACE, SoundCategory.BLOCKS, 0.8f, 0.65f)
return ActionResult.SUCCESS
} else {
return ActionResult.PASS
}
}
} }

View File

@ -1,47 +0,0 @@
package net.shadowfacts.phycon.network.block.test
import net.minecraft.block.BlockState
import net.minecraft.block.Material
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.network.DeviceBlock
import java.util.*
/**
* @author shadowfacts
*/
class DestBlock: DeviceBlock<DestBlockEntity>(Settings.of(Material.METAL)) {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "dest")
}
override fun createBlockEntity(world: BlockView): DestBlockEntity {
return DestBlockEntity()
}
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): MutableCollection<Direction> {
return EnumSet.allOf(Direction::class.java)
}
override fun onUse(
blockState: BlockState?,
world: World,
pos: BlockPos,
playerEntity: PlayerEntity?,
hand: Hand?,
blockHitResult: BlockHitResult?
): ActionResult {
println("dest IP: ${getBlockEntity(world, pos)!!.ipAddress}")
return ActionResult.SUCCESS
}
}

View File

@ -1,29 +0,0 @@
package net.shadowfacts.phycon.network.block.test
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.NetworkUtil
/**
* @author shadowfacts
*/
class DestBlockEntity: DeviceBlockEntity(PhyBlockEntities.DEST) {
override fun handle(packet: Packet) {
println("$this ($ipAddress) received packet: $packet")
}
override fun findDestination(): Interface? {
for (dir in Direction.values()) {
val itf = NetworkUtil.findConnectedInterface(world!!, pos, dir)
if (itf != null) {
return itf
}
}
return null
}
}

View File

@ -1,31 +0,0 @@
package net.shadowfacts.phycon.network.block.test
import net.minecraft.block.BlockState
import net.minecraft.block.Material
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.world.BlockView
import net.minecraft.world.WorldAccess
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.network.DeviceBlock
import java.util.*
/**
* @author shadowfacts
*/
class SourceBlock: DeviceBlock<SourceBlockEntity>(Settings.of(Material.METAL)) {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "source")
}
override fun createBlockEntity(world: BlockView): SourceBlockEntity {
return SourceBlockEntity()
}
override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): MutableCollection<Direction> {
return EnumSet.allOf(Direction::class.java)
}
}

View File

@ -1,38 +0,0 @@
package net.shadowfacts.phycon.network.block.test
import net.minecraft.util.Tickable
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.NetworkUtil
/**
* @author shadowfacts
*/
class SourceBlockEntity: DeviceBlockEntity(PhyBlockEntities.SOURCE), Tickable {
override fun handle(packet: Packet) {
TODO("Not yet implemented")
}
override fun tick() {
super.tick()
if (!world!!.isClient && counter % 40 == 0L) {
sendPacket(TestPacket(ipAddress, IPAddress(170, 171, 101, 168)))
}
}
override fun findDestination(): Interface? {
for (dir in Direction.values()) {
val itf = NetworkUtil.findConnectedInterface(world!!, pos, dir)
if (itf != null) {
return itf
}
}
return null
}
}

View File

@ -1,10 +0,0 @@
package net.shadowfacts.phycon.network.block.test
import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.network.packet.BasePacket
/**
* @author shadowfacts
*/
class TestPacket(source: IPAddress, destination: IPAddress): BasePacket(source, destination) {
}

View File

@ -1,9 +0,0 @@
package net.shadowfacts.phycon.network.component
import net.shadowfacts.phycon.api.NetworkDevice
/**
* @author shadowfacts
*/
interface NetworkStackProvider: NetworkDevice {
}

View File

@ -1,9 +0,0 @@
package net.shadowfacts.phycon.network.component
import net.shadowfacts.phycon.api.NetworkDevice
/**
* @author shadowfacts
*/
interface NetworkStackReceiver: NetworkDevice {
}

View File

@ -2,31 +2,31 @@ package net.shadowfacts.phycon.networking
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
import net.fabricmc.fabric.api.networking.v1.PacketSender import net.fabricmc.fabric.api.networking.v1.PacketSender
import net.minecraft.block.entity.BlockEntity
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.Packet import net.minecraft.network.Packet
import net.minecraft.network.PacketByteBuf import net.minecraft.network.PacketByteBuf
import net.minecraft.server.MinecraftServer import net.minecraft.server.MinecraftServer
import net.minecraft.server.network.ServerPlayNetworkHandler import net.minecraft.server.network.ServerPlayNetworkHandler
import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry
import net.minecraft.util.registry.RegistryKey
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.network.DeviceBlockEntity import net.shadowfacts.phycon.util.ClientConfigurableDevice
import net.shadowfacts.phycon.network.component.ActivationController
import net.shadowfacts.phycon.util.ActivationMode
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
object C2SConfigureActivationMode: ServerReceiver { object C2SConfigureDevice: ServerReceiver {
override val CHANNEL = Identifier(PhysicalConnectivity.MODID, "configure_activation_mode") override val CHANNEL = Identifier(PhysicalConnectivity.MODID, "configure_device")
operator fun <T> invoke(be: T): Packet<*> where T: DeviceBlockEntity, T: ActivationController.ActivatableDevice { operator fun <T> invoke(be: T): Packet<*> where T: BlockEntity, T: ClientConfigurableDevice {
val buf = PacketByteBufs.create() val buf = PacketByteBufs.create()
buf.writeIdentifier(be.world!!.registryKey.value) buf.writeIdentifier(be.world!!.registryKey.value)
buf.writeBlockPos(be.pos) buf.writeBlockPos(be.pos)
buf.writeString(be.controller.activationMode.name) val tag = CompoundTag()
be.writeDeviceConfiguration(tag)
buf.writeCompoundTag(tag)
return createPacket(buf) return createPacket(buf)
} }
@ -34,16 +34,16 @@ object C2SConfigureActivationMode: ServerReceiver {
override fun receive(server: MinecraftServer, player: ServerPlayerEntity, handler: ServerPlayNetworkHandler, buf: PacketByteBuf, responseSender: PacketSender) { override fun receive(server: MinecraftServer, player: ServerPlayerEntity, handler: ServerPlayNetworkHandler, buf: PacketByteBuf, responseSender: PacketSender) {
val dimID = buf.readIdentifier() val dimID = buf.readIdentifier()
val pos = buf.readBlockPos() val pos = buf.readBlockPos()
val mode = ActivationMode.valueOf(buf.readString()) val tag = buf.readCompoundTag() ?: return
server.execute { server.execute {
// todo: check the player is close enough // todo: check if the player is close enough
val key = RegistryKey.of(Registry.DIMENSION, dimID) val world = player.world
val world = server.getWorld(key) ?: return@execute if (world.registryKey.value != dimID) return@execute
val device = world.getBlockEntity(pos) ?: return@execute val be = world.getBlockEntity(pos) ?: return@execute
if (device !is ActivationController.ActivatableDevice) return@execute val device = be as? ClientConfigurableDevice ?: return@execute
device.controller.activationMode = mode device.loadDeviceConfiguration(tag)
device.markDirty() be.markDirty()
} }
} }
} }

View File

@ -1,59 +0,0 @@
package net.shadowfacts.phycon.networking
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
import net.fabricmc.fabric.api.networking.v1.PacketSender
import net.minecraft.network.Packet
import net.minecraft.network.PacketByteBuf
import net.minecraft.server.MinecraftServer
import net.minecraft.server.network.ServerPlayNetworkHandler
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry
import net.minecraft.util.registry.RegistryKey
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlockEntity
import net.shadowfacts.phycon.util.RedstoneMode
/**
* @author shadowfacts
*/
object C2SConfigureRedstoneController: ServerReceiver {
// todo: it would be nice if there wasn't so much duplication with C2SConfigureActivationMode
override val CHANNEL = Identifier(PhysicalConnectivity.MODID, "configure_redstone_controller")
operator fun invoke(be: RedstoneControllerBlockEntity): Packet<*> {
val buf = PacketByteBufs.create()
buf.writeIdentifier(be.world!!.registryKey.value)
buf.writeBlockPos(be.pos)
buf.writeString(be.redstoneMode.name)
be.managedDevices.forEach {
buf.writeInt(it?.address ?: 0)
}
return createPacket(buf)
}
override fun receive(server: MinecraftServer, player: ServerPlayerEntity, handler: ServerPlayNetworkHandler, buf: PacketByteBuf, responseSender: PacketSender) {
val dimID = buf.readIdentifier()
val pos = buf.readBlockPos()
val mode = RedstoneMode.valueOf(buf.readString())
val managedDevices = Array<IPAddress?>(5) { null }
(0..4).map {
val v = buf.readInt()
managedDevices[it] = if (v == 0) null else IPAddress(v)
}
server.execute {
// todo: check if the player is close enough
val key = RegistryKey.of(Registry.DIMENSION, dimID)
val world = server.getWorld(key) ?: return@execute
val device = world.getBlockEntity(pos) as? RedstoneControllerBlockEntity ?: return@execute
device.redstoneMode = mode
device.managedDevices = managedDevices
device.markDirty()
}
}
}

View File

@ -13,7 +13,8 @@ import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry import net.minecraft.util.registry.Registry
import net.minecraft.util.registry.RegistryKey import net.minecraft.util.registry.RegistryKey
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.network.block.terminal.TerminalBlockEntity import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.util.copyWithCount
/** /**
* @author shadowfacts * @author shadowfacts
@ -30,9 +31,7 @@ object C2STerminalRequestItem: ServerReceiver {
// Force the count of the stack to be 1 before serializing for the network because // Force the count of the stack to be 1 before serializing for the network because
// PacketByteBuf serializes the stack count as a byte. Otherwise counts > 127 will result in // PacketByteBuf serializes the stack count as a byte. Otherwise counts > 127 will result in
// an overflow and be negative on the receiving side. // an overflow and be negative on the receiving side.
val stackCopy = stack.copy() buf.writeItemStack(stack.copyWithCount(1))
stackCopy.count = 1
buf.writeItemStack(stackCopy)
buf.writeVarInt(amount) buf.writeVarInt(amount)
return ClientPlayNetworking.createC2SPacket(CHANNEL, buf) return ClientPlayNetworking.createC2SPacket(CHANNEL, buf)

View File

@ -10,8 +10,8 @@ import net.minecraft.server.network.ServerPlayNetworkHandler
import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.network.block.terminal.TerminalBlockEntity import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.network.block.terminal.TerminalScreenHandler import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler
import net.shadowfacts.phycon.util.SortMode import net.shadowfacts.phycon.util.SortMode
/** /**

View File

@ -7,8 +7,8 @@ import net.minecraft.client.MinecraftClient
import net.minecraft.client.network.ClientPlayNetworkHandler import net.minecraft.client.network.ClientPlayNetworkHandler
import net.minecraft.network.Packet import net.minecraft.network.Packet
import net.minecraft.network.PacketByteBuf import net.minecraft.network.PacketByteBuf
import net.shadowfacts.phycon.network.block.terminal.TerminalBlockEntity import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.network.block.terminal.TerminalScreenHandler import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler
import net.shadowfacts.phycon.util.SortMode import net.shadowfacts.phycon.util.SortMode
/** /**

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.packet package net.shadowfacts.phycon.packet
import net.shadowfacts.phycon.api.packet.Packet import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress

View File

@ -1,8 +1,8 @@
package net.shadowfacts.phycon.network.packet package net.shadowfacts.phycon.packet
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.network.component.NetworkStackReceiver import net.shadowfacts.phycon.component.NetworkStackReceiver
/** /**
* @author shadowfacts * @author shadowfacts

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.packet package net.shadowfacts.phycon.packet
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress

View File

@ -1,7 +1,7 @@
package net.shadowfacts.phycon.network.packet package net.shadowfacts.phycon.packet
import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.network.DeviceBlockEntity import net.shadowfacts.phycon.block.DeviceBlockEntity
/** /**
* @author shadowfacts * @author shadowfacts

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.packet package net.shadowfacts.phycon.packet
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.packet package net.shadowfacts.phycon.packet
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.packet package net.shadowfacts.phycon.packet
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.packet package net.shadowfacts.phycon.packet
import alexiil.mc.lib.attributes.item.GroupedItemInvView import alexiil.mc.lib.attributes.item.GroupedItemInvView
import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.packet package net.shadowfacts.phycon.packet
import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.packet package net.shadowfacts.phycon.packet
import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress

View File

@ -1,8 +1,8 @@
package net.shadowfacts.phycon.network.packet package net.shadowfacts.phycon.packet
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.network.component.NetworkStackProvider import net.shadowfacts.phycon.component.NetworkStackProvider
/** /**
* @author shadowfacts * @author shadowfacts

View File

@ -2,16 +2,14 @@ package net.shadowfacts.phycon.screen.console
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.button.EnumButton import net.shadowfacts.cacao.view.button.EnumButton
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.network.DeviceBlockEntity import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.network.component.ActivationController import net.shadowfacts.phycon.component.ActivationController
import net.shadowfacts.phycon.networking.C2SConfigureActivationMode import net.shadowfacts.phycon.networking.C2SConfigureDevice
import net.shadowfacts.phycon.util.ActivationMode import net.shadowfacts.phycon.util.ActivationMode
/** /**
@ -32,7 +30,7 @@ class ActivatableDeviceViewController<T>(
val mode = EnumButton(device.controller.activationMode, ActivationMode::friendlyName) val mode = EnumButton(device.controller.activationMode, ActivationMode::friendlyName)
mode.handler = { mode.handler = {
device.controller.activationMode = it.value device.controller.activationMode = it.value
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureActivationMode(device)) MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device))
} }
view.addSubview(mode) view.addSubview(mode)

View File

@ -12,9 +12,12 @@ import net.shadowfacts.cacao.viewcontroller.TabViewController
import net.shadowfacts.cacao.viewcontroller.ViewController import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.cacao.window.Window import net.shadowfacts.cacao.window.Window
import net.shadowfacts.kiwidsl.dsl import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.network.DeviceBlockEntity import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlockEntity import net.shadowfacts.phycon.block.miner.MinerBlockEntity
import net.shadowfacts.phycon.network.component.ActivationController import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlockEntity
import net.shadowfacts.phycon.component.ActivationController
import net.shadowfacts.phycon.component.NetworkStackProvider
import net.shadowfacts.phycon.component.NetworkStackReceiver
import org.lwjgl.glfw.GLFW import org.lwjgl.glfw.GLFW
/** /**
@ -40,7 +43,8 @@ class DeviceConsoleScreen(
intrinsicContentSize = Size(16.0, 16.0) intrinsicContentSize = Size(16.0, 16.0)
}, },
TranslatableText("gui.phycon.console.remote"), TranslatableText("gui.phycon.console.remote"),
ActivatableDeviceViewController(device) ActivatableDeviceViewController(device),
device::canConfigureActivationController
)) ))
} }
if (device is RedstoneControllerBlockEntity) { if (device is RedstoneControllerBlockEntity) {
@ -52,7 +56,30 @@ class DeviceConsoleScreen(
RedstoneControllerViewController(device) RedstoneControllerViewController(device)
)) ))
} }
if (device is MinerBlockEntity) {
tabs.add(TabViewController.SimpleTab(
TextureView(Texture(Identifier("textures/item/diamond_pickaxe.png"), 0, 0, 16, 16)).apply {
intrinsicContentSize = Size(16.0, 16.0)
},
TranslatableText("block.phycon.miner"),
MinerViewController(device)
))
}
if (device is NetworkStackProvider) {
tabs.add(TabViewController.SimpleTab(
Label("P").apply { textColor = Color.TEXT },
TranslatableText("gui.phycon.console.provider"),
ProviderViewController(device),
device::canConfigureProviderPriority
))
}
if (device is NetworkStackReceiver) {
tabs.add(TabViewController.SimpleTab(
Label("R").apply { textColor = Color.TEXT },
TranslatableText("gui.phycon.console.receiver"),
ReceiverViewController(device),
))
}
tabController = TabViewController(tabs) tabController = TabViewController(tabs)

View File

@ -7,7 +7,7 @@ import net.shadowfacts.cacao.view.Label
import net.shadowfacts.cacao.view.StackView import net.shadowfacts.cacao.view.StackView
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.network.DeviceBlockEntity import net.shadowfacts.phycon.block.DeviceBlockEntity
/** /**
* @author shadowfacts * @author shadowfacts

View File

@ -0,0 +1,49 @@
package net.shadowfacts.phycon.screen.console
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.EnumButton
import net.shadowfacts.cacao.viewcontroller.TabViewController
import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.block.miner.MinerBlockEntity
import net.shadowfacts.phycon.networking.C2SConfigureDevice
/**
* @author shadowfacts
*/
class MinerViewController(
val device: MinerBlockEntity,
): ViewController() {
override fun viewDidLoad() {
super.viewDidLoad()
val label = Label(TranslatableText("gui.phycon.console.miner.mode")).apply {
textColor = Color.TEXT
}
view.addSubview(label)
val mode = EnumButton(device.minerMode, MinerBlockEntity.MinerMode::friendlyName)
mode.handler = {
device.minerMode = it.value
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device))
(parent as TabViewController<TabViewController.SimpleTab>).visibleTabsChanged()
}
view.addSubview(mode)
view.solver.dsl {
mode.widthAnchor equalTo 100
mode.heightAnchor equalTo 20
mode.topAnchor equalTo view.topAnchor
mode.rightAnchor equalTo view.rightAnchor
label.centerYAnchor equalTo mode.centerYAnchor
label.rightAnchor equalTo (mode.leftAnchor - 4)
}
}
}

View File

@ -0,0 +1,57 @@
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.util.Color
import net.shadowfacts.cacao.view.Label
import net.shadowfacts.cacao.view.textfield.NumberField
import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.component.NetworkStackProvider
import net.shadowfacts.phycon.networking.C2SConfigureDevice
/**
* @author shadowfacts
*/
class ProviderViewController<T>(
private val device: T
): ViewController() where T: BlockEntity, T: NetworkStackProvider {
override fun viewDidLoad() {
super.viewDidLoad()
val label = Label(TranslatableText("gui.phycon.console.provider.priority")).apply {
textColor = Color.TEXT
}
view.addSubview(label)
val field = NumberField(device.providerPriority) {
if (it.number != null) {
device.providerPriority = it.number!!
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device))
}
}
view.addSubview(field)
val desc = Label(TranslatableText("gui.phycon.console.provider.priority_desc")).apply {
textColor = Color.TEXT
}
view.addSubview(desc)
view.solver.dsl {
field.widthAnchor equalTo 100
field.heightAnchor equalTo 20
field.rightAnchor equalTo view.rightAnchor
field.topAnchor equalTo view.topAnchor
label.centerYAnchor equalTo field.centerYAnchor
label.rightAnchor equalTo (field.leftAnchor - 4)
desc.topAnchor equalTo (field.bottomAnchor + 4)
desc.leftAnchor equalTo view.leftAnchor
desc.rightAnchor equalTo view.rightAnchor
}
}
}

View File

@ -0,0 +1,57 @@
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.util.Color
import net.shadowfacts.cacao.view.Label
import net.shadowfacts.cacao.view.textfield.NumberField
import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.component.NetworkStackReceiver
import net.shadowfacts.phycon.networking.C2SConfigureDevice
/**
* @author shadowfacts
*/
class ReceiverViewController<T>(
private val device: T
): ViewController() where T: BlockEntity, T: NetworkStackReceiver {
override fun viewDidLoad() {
super.viewDidLoad()
val label = Label(TranslatableText("gui.phycon.console.receiver.priority")).apply {
textColor = Color.TEXT
}
view.addSubview(label)
val field = NumberField(device.receiverPriority) {
if (it.number != null) {
device.receiverPriority = it.number!!
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device))
}
}
view.addSubview(field)
val desc = Label(TranslatableText("gui.phycon.console.receiver.priority_desc")).apply {
textColor = Color.TEXT
}
view.addSubview(desc)
view.solver.dsl {
field.widthAnchor equalTo 100
field.heightAnchor equalTo 20
field.rightAnchor equalTo view.rightAnchor
field.topAnchor equalTo view.topAnchor
label.centerYAnchor equalTo field.centerYAnchor
label.rightAnchor equalTo (field.leftAnchor - 4)
desc.topAnchor equalTo (field.bottomAnchor + 4)
desc.leftAnchor equalTo view.leftAnchor
desc.rightAnchor equalTo view.rightAnchor
}
}
}

View File

@ -11,8 +11,8 @@ import net.shadowfacts.cacao.view.textfield.TextField
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.api.util.IPAddress import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlockEntity import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlockEntity
import net.shadowfacts.phycon.networking.C2SConfigureRedstoneController import net.shadowfacts.phycon.networking.C2SConfigureDevice
import net.shadowfacts.phycon.util.RedstoneMode import net.shadowfacts.phycon.util.RedstoneMode
/** /**
@ -39,14 +39,14 @@ class RedstoneControllerViewController(val device: RedstoneControllerBlockEntity
val mode = EnumButton(device.redstoneMode, RedstoneMode::friendlyName) val mode = EnumButton(device.redstoneMode, RedstoneMode::friendlyName)
mode.handler = { mode.handler = {
device.redstoneMode = it.value device.redstoneMode = it.value
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureRedstoneController(device)) MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device))
} }
controls.addArrangedSubview(mode) controls.addArrangedSubview(mode)
val textFields = (0 until 5).map { i -> val textFields = (0 until 5).map { i ->
TextField(device.managedDevices[i]?.toString() ?: "") { TextField(device.managedDevices[i]?.toString() ?: "") {
device.managedDevices[i] = IPAddress.parse(it.text) device.managedDevices[i] = IPAddress.parse(it.text)
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureRedstoneController(device)) MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device))
} }
} }
textFields.forEach(controls::addArrangedSubview) textFields.forEach(controls::addArrangedSubview)

View File

@ -0,0 +1,14 @@
package net.shadowfacts.phycon.util
import net.minecraft.nbt.CompoundTag
/**
* @author shadowfacts
*/
interface ClientConfigurableDevice {
fun writeDeviceConfiguration(tag: CompoundTag)
fun loadDeviceConfiguration(tag: CompoundTag)
}

View File

@ -0,0 +1,45 @@
package net.shadowfacts.phycon.util
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.inventory.Inventory
import net.minecraft.item.ItemStack
import net.minecraft.screen.slot.Slot
import kotlin.reflect.KMutableProperty
/**
* @author shadowfacts
*/
class GhostSlot(prop: KMutableProperty<ItemStack>, x: Int, y: Int): Slot(GhostInv(prop), 0, x, y) {
override fun canInsert(stack: ItemStack) = false
override fun setStack(itemStack: ItemStack?) {
}
override fun canTakeItems(player: PlayerEntity) = false
class GhostInv(private val prop: KMutableProperty<ItemStack>): Inventory {
override fun clear() {
prop.setter.call(ItemStack.EMPTY)
}
override fun size() = 1
override fun isEmpty() = prop.getter.call().isEmpty
override fun getStack(i: Int): ItemStack {
return if (i == 0) prop.getter.call()
else ItemStack.EMPTY
}
override fun removeStack(i: Int, j: Int) = ItemStack.EMPTY
override fun removeStack(i: Int) = ItemStack.EMPTY
override fun setStack(i: Int, itemStack: ItemStack) {}
override fun markDirty() {}
override fun canPlayerUse(playerEntity: PlayerEntity?) = true
}
}

View File

@ -0,0 +1,10 @@
package net.shadowfacts.phycon.util
import net.minecraft.item.ItemStack
/**
* @author shadowfacts
*/
fun ItemStack.copyWithCount(count: Int): ItemStack {
return copy().also { it.count = count }
}

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network package net.shadowfacts.phycon.util
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction import net.minecraft.util.math.Direction

View File

@ -0,0 +1,120 @@
package net.shadowfacts.phycon.util
import net.minecraft.text.*
import net.minecraft.util.Formatting
import java.util.*
/**
* @author shadowfacts
*/
fun text(build: TextContext.() -> Unit): Text {
val context = TextContext()
context.build()
return context.toText()
}
class TextContext(
private val texts: MutableList<Text> = mutableListOf(),
private val styles: Deque<Style> = LinkedList()
) {
fun toText(): MutableText {
val text = LiteralText("")
texts.forEach(text::append)
return text
}
operator fun String.unaryPlus() {
+LiteralText(this)
}
operator fun MutableText.unaryPlus() {
if (styles.isNotEmpty()) {
texts.add(this.setStyle(styles.peek()))
} else {
texts.add(this)
}
}
fun translate(key: String, vararg parameters: Any): TranslatableText {
return TranslatableText(key, *parameters)
}
fun withStyle(style: Style, build: TextContext.() -> Unit) {
styles.push(style)
build()
styles.pop()
}
fun withStyle(style: Style.() -> Style, build: TextContext.() -> Unit) {
withStyle(Style.EMPTY.style(), build)
}
fun withStyle(formatting: Formatting, build: TextContext.() -> Unit) {
withStyle(Style.EMPTY.withFormatting(formatting), build)
}
val bold = Formatting.BOLD
fun Style.bold(): Style = withBold(true)
val italic = Formatting.ITALIC
fun Style.italic(): Style = withItalic(true)
val strikethrough = Formatting.STRIKETHROUGH
fun Style.strikethrough(): Style = withFormatting(strikethrough)
val underline = Formatting.UNDERLINE
fun Style.underline(): Style = withFormatting(underline)
val obfuscated = Formatting.OBFUSCATED
fun Style.obfuscated(): Style = withFormatting(obfuscated)
val black = Formatting.BLACK
fun Style.black(): Style = withFormatting(black)
val darkBlue = Formatting.DARK_BLUE
fun Style.darkBlue(): Style = withFormatting(darkBlue)
val darkGreen = Formatting.DARK_GREEN
fun Style.darkGreen(): Style = withFormatting(darkGreen)
val darkAqua = Formatting.DARK_AQUA
fun Style.darkAqua(): Style = withFormatting(darkAqua)
val darkRed = Formatting.DARK_RED
fun Style.darkRed(): Style = withFormatting(darkRed)
val darkPurple = Formatting.DARK_PURPLE
fun Style.darkPurple(): Style = withFormatting(darkPurple)
val gold = Formatting.GOLD
fun Style.gold(): Style = withFormatting(gold)
val gray = Formatting.GRAY
fun Style.gray(): Style = withFormatting(gray)
val darkGray = Formatting.DARK_GRAY
fun Style.darkGray(): Style = withFormatting(darkGray)
val blue = Formatting.BLUE
fun Style.blue(): Style = withFormatting(blue)
val green = Formatting.GREEN
fun Style.green(): Style = withFormatting(green)
val aqua = Formatting.RED
fun Style.aqua(): Style = withFormatting(aqua)
val red = Formatting.RED
fun Style.red(): Style = withFormatting(red)
val lightPurple = Formatting.LIGHT_PURPLE
fun Style.lightPurple(): Style = withFormatting(lightPurple)
val yellow = Formatting.YELLOW
fun Style.yellow(): Style = withFormatting(yellow)
val white = Formatting.WHITE
fun Style.white(): Style = withFormatting(white)
}

View File

@ -0,0 +1,29 @@
{
"variants": {
"facing=down": {
"model": "phycon:block/inserter"
},
"facing=up": {
"model": "phycon:block/inserter",
"x": 180
},
"facing=north": {
"model": "phycon:block/inserter",
"x": 270
},
"facing=south": {
"model": "phycon:block/inserter",
"x": 90
},
"facing=west": {
"model": "phycon:block/inserter",
"x": 90,
"y": 90
},
"facing=east": {
"model": "phycon:block/inserter",
"x": 90,
"y": 270
}
}
}

View File

@ -4,51 +4,51 @@
"apply": { "model": "phycon:block/cable_center" } "apply": { "model": "phycon:block/cable_center" }
}, },
{ {
"when": { "facing": "down", "lit": "false" }, "when": { "facing": "down", "powered": "false" },
"apply": { "model": "phycon:block/redstone_controller_side_off" } "apply": { "model": "phycon:block/redstone_controller_side_off" }
}, },
{ {
"when": { "facing": "up", "lit": "false" }, "when": { "facing": "up", "powered": "false" },
"apply": { "model": "phycon:block/redstone_controller_side_off", "x": 180 } "apply": { "model": "phycon:block/redstone_controller_side_off", "x": 180 }
}, },
{ {
"when": { "facing": "north", "lit": "false" }, "when": { "facing": "north", "powered": "false" },
"apply": { "model": "phycon:block/redstone_controller_side_off", "x": 270 } "apply": { "model": "phycon:block/redstone_controller_side_off", "x": 270 }
}, },
{ {
"when": { "facing": "south", "lit": "false" }, "when": { "facing": "south", "powered": "false" },
"apply": { "model": "phycon:block/redstone_controller_side_off", "x": 90 } "apply": { "model": "phycon:block/redstone_controller_side_off", "x": 90 }
}, },
{ {
"when": { "facing": "west", "lit": "false" }, "when": { "facing": "west", "powered": "false" },
"apply": { "model": "phycon:block/redstone_controller_side_off", "x": 90, "y": 90 } "apply": { "model": "phycon:block/redstone_controller_side_off", "x": 90, "y": 90 }
}, },
{ {
"when": { "facing": "east", "lit": "false" }, "when": { "facing": "east", "powered": "false" },
"apply": { "model": "phycon:block/redstone_controller_side_off", "x": 90, "y": 270 } "apply": { "model": "phycon:block/redstone_controller_side_off", "x": 90, "y": 270 }
}, },
{ {
"when": { "facing": "down", "lit": "true" }, "when": { "facing": "down", "powered": "true" },
"apply": { "model": "phycon:block/redstone_controller_side_on" } "apply": { "model": "phycon:block/redstone_controller_side_on" }
}, },
{ {
"when": { "facing": "up", "lit": "true" }, "when": { "facing": "up", "powered": "true" },
"apply": { "model": "phycon:block/redstone_controller_side_on", "x": 180 } "apply": { "model": "phycon:block/redstone_controller_side_on", "x": 180 }
}, },
{ {
"when": { "facing": "north", "lit": "true" }, "when": { "facing": "north", "powered": "true" },
"apply": { "model": "phycon:block/redstone_controller_side_on", "x": 270 } "apply": { "model": "phycon:block/redstone_controller_side_on", "x": 270 }
}, },
{ {
"when": { "facing": "south", "lit": "true" }, "when": { "facing": "south", "powered": "true" },
"apply": { "model": "phycon:block/redstone_controller_side_on", "x": 90 } "apply": { "model": "phycon:block/redstone_controller_side_on", "x": 90 }
}, },
{ {
"when": { "facing": "west", "lit": "true" }, "when": { "facing": "west", "powered": "true" },
"apply": { "model": "phycon:block/redstone_controller_side_on", "x": 90, "y": 90 } "apply": { "model": "phycon:block/redstone_controller_side_on", "x": 90, "y": 90 }
}, },
{ {
"when": { "facing": "east", "lit": "true" }, "when": { "facing": "east", "powered": "true" },
"apply": { "model": "phycon:block/redstone_controller_side_on", "x": 90, "y": 270 } "apply": { "model": "phycon:block/redstone_controller_side_on", "x": 90, "y": 270 }
}, },

View File

@ -0,0 +1,159 @@
{
"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 }
}
]
}

View File

@ -4,8 +4,10 @@
"block.phycon.terminal": "Terminal", "block.phycon.terminal": "Terminal",
"block.phycon.cable": "Cable", "block.phycon.cable": "Cable",
"block.phycon.extractor": "Inventory Extractor", "block.phycon.extractor": "Inventory Extractor",
"block.phycon.inserter": "Inventory Inserter",
"block.phycon.miner": "Block Miner", "block.phycon.miner": "Block Miner",
"block.phycon.redstone_controller": "Redstone Controller", "block.phycon.redstone_controller": "Redstone Controller",
"block.phycon.redstone_emitter": "Redstone Emitter",
"item.phycon.screwdriver": "Screwdriver", "item.phycon.screwdriver": "Screwdriver",
"item.phycon.console": "Console", "item.phycon.console": "Console",
@ -18,11 +20,24 @@
"gui.phycon.console.redstone.devices": "Managed Devices", "gui.phycon.console.redstone.devices": "Managed Devices",
"gui.phycon.console.remote": "Remote Management", "gui.phycon.console.remote": "Remote Management",
"gui.phycon.console.remote.mode": "Activation Mode", "gui.phycon.console.remote.mode": "Activation Mode",
"gui.phycon.console.miner.mode": "Miner Mode",
"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.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.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",
"gui.phycon.redstone_mode.rising_edge": "Rising Edge", "gui.phycon.redstone_mode.rising_edge": "Rising Edge",
"gui.phycon.redstone_mode.falling_edge": "Falling Edge", "gui.phycon.redstone_mode.falling_edge": "Falling Edge",
"gui.phycon.activation_mode.automatic": "Automatic", "gui.phycon.activation_mode.automatic": "Automatic",
"gui.phycon.activation_mode.managed": "Managed" "gui.phycon.activation_mode.managed": "Managed",
"gui.phycon.emitter.count": "%d Item(s)",
"gui.phycon.miner_mode.automatic": "Automatic",
"gui.phycon.miner_mode.on_demand": "On Demand",
"tooltip.phycon.device.configured": "Configured",
"tooltip.phycon.device.ip": "IP: "
} }

View File

@ -1,11 +1,15 @@
{ {
"parent": "block/block", "parent": "block/block",
"textures": {
"cable_side": "phycon:block/cable_straight",
"cable_end": "phycon:block/cable_cap_end"
},
"elements": [ "elements": [
{ {
"from": [0, 0, 0], "from": [0, 0, 0],
"to": [16, 2, 16], "to": [16, 2, 16],
"faces": { "faces": {
"down": {"texture": "phycon:block/extractor_front"}, "down": {"texture": "phycon:block/extractor_front", "cullface": "down"},
"up": {"texture": "phycon:block/extractor_back"}, "up": {"texture": "phycon:block/extractor_back"},
"north": {"texture": "phycon:block/extractor_side"}, "north": {"texture": "phycon:block/extractor_side"},
"south": {"texture": "phycon:block/extractor_side"}, "south": {"texture": "phycon:block/extractor_side"},
@ -39,11 +43,11 @@
"from": [6, 6, 6], "from": [6, 6, 6],
"to": [10, 16, 10], "to": [10, 16, 10],
"faces": { "faces": {
"up": {"texture": "phycon:block/cable_side"}, "up": {"texture": "#cable_end", "cullface": "up"},
"north": {"texture": "phycon:block/cable_side"}, "north": {"texture": "#cable_side"},
"south": {"texture": "phycon:block/cable_side"}, "south": {"texture": "#cable_side"},
"west": {"texture": "phycon:block/cable_side"}, "west": {"texture": "#cable_side"},
"east": {"texture": "phycon:block/cable_side"} "east": {"texture": "#cable_side"}
} }
} }
] ]

View File

@ -0,0 +1,54 @@
{
"parent": "block/block",
"textures": {
"cable_side": "phycon:block/cable_straight",
"cable_end": "phycon:block/cable_cap_end"
},
"elements": [
{
"from": [4, 0, 4],
"to": [12, 2, 12],
"faces": {
"down": {"texture": "phycon:block/extractor_front", "cullface": "down"},
"north": {"texture": "phycon:block/extractor_side"},
"south": {"texture": "phycon:block/extractor_side"},
"west": {"texture": "phycon:block/extractor_side"},
"east": {"texture": "phycon:block/extractor_side"}
}
},
{
"from": [2, 2, 2],
"to": [14, 4, 14],
"faces": {
"down": {"texture": "phycon:block/extractor_front"},
"north": {"texture": "phycon:block/extractor_side"},
"south": {"texture": "phycon:block/extractor_side"},
"west": {"texture": "phycon:block/extractor_side"},
"east": {"texture": "phycon:block/extractor_side"}
}
},
{
"from": [0, 4, 0],
"to": [16, 6, 16],
"faces": {
"down": {"texture": "phycon:block/extractor_front"},
"up": {"texture": "phycon:block/extractor_back"},
"north": {"texture": "phycon:block/extractor_side"},
"south": {"texture": "phycon:block/extractor_side"},
"west": {"texture": "phycon:block/extractor_side"},
"east": {"texture": "phycon:block/extractor_side"}
}
},
{
"from": [6, 6, 6],
"to": [10, 16, 10],
"faces": {
"up": {"texture": "#cable_end", "cullface": "up"},
"north": {"texture": "#cable_side"},
"south": {"texture": "#cable_side"},
"west": {"texture": "#cable_side"},
"east": {"texture": "#cable_side"}
}
}
]
}

View File

@ -0,0 +1,33 @@
{
"parent": "block/block",
"textures": {
"cable": "phycon:block/cable_straight",
"front": "phycon:block/redstone_emitter_front",
"back": "phycon:block/redstone_emitter_back",
"side": "phycon:block/redstone_emitter_back"
},
"elements": [
{
"from": [0, 0, 0],
"to": [16, 3, 16],
"faces": {
"down": { "texture": "#front" },
"up": { "texture": "#back" },
"north": { "texture": "#side" },
"south": { "texture": "#side" },
"west": { "texture": "#side" },
"east": { "texture": "#side" }
}
},
{
"from": [6, 3, 6],
"to": [10, 6, 10],
"faces": {
"north": { "texture": "#cable" },
"south": { "texture": "#cable" },
"west": { "texture": "#cable" },
"east": { "texture": "#cable" }
}
}
]
}

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Some files were not shown because too many files have changed in this diff Show More