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.util.math.MatrixStack;
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.injection.At;
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.text.Text
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.util.KeyModifiers
import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.RenderHelper
import net.shadowfacts.cacao.window.ScreenHandlerWindow
@ -16,7 +17,7 @@ import java.util.*
/**
* @author shadowfacts
*/
class CacaoHandledScreen<Handler: ScreenHandler>(
open class CacaoHandledScreen<Handler: ScreenHandler>(
handler: Handler,
playerInv: PlayerInventory,
title: Text,
@ -57,15 +58,16 @@ class CacaoHandledScreen<Handler: ScreenHandler>(
}
override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) {
renderBackground(matrixStack)
}
override fun drawForeground(matrixStack: MatrixStack, mouseX: Int, mouseY: Int) {
// no-op
}
override fun render(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
renderBackground(matrixStack)
val mouse = Point(mouseX, mouseY)
windows.forEachIndexed { index, it ->
it.draw(matrixStack, mouse, delta)
if (it is ScreenHandlerWindow) {
if (index == windows.size - 1) {
super.render(matrixStack, mouseX, mouseY, delta)
@ -74,7 +76,10 @@ class CacaoHandledScreen<Handler: ScreenHandler>(
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 {
@ -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,15 +139,15 @@ open class CacaoScreen(title: Text = LiteralText("CacaoScreen")): Screen(title),
return super.charTyped(char, modifiers)
}
private fun findResponder(fn: (Responder) -> Boolean): Boolean {
var responder = windows.lastOrNull()?.firstResponder
while (responder != null) {
if (fn(responder)) {
return true
}
responder = responder.nextResponder
}
return false
}
}
fun AbstractCacaoScreen.findResponder(fn: (Responder) -> Boolean): Boolean {
var responder = windows.lastOrNull()?.firstResponder
while (responder != null) {
if (fn(responder)) {
return true
}
responder = responder.nextResponder
}
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 GREEN = Color(0x00ff00)
val BLUE = Color(0x0000ff)
val MAGENTA = Color(0xfc46e4)
val TEXT = Color(0x404040)
}

View File

@ -94,7 +94,7 @@ class Label(
for (i in 0 until lines.size) {
val x = when (textAlignment) {
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])
}
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 no.birkett.kiwi.Constraint
import java.lang.RuntimeException
import java.util.*
/**
@ -18,6 +19,7 @@ import java.util.*
* @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
* primary [axis].
* @param spacing The distance between arranged subviews along the primary axis.
*/
open class StackView(
val axis: Axis,
@ -25,7 +27,7 @@ open class StackView(
val spacing: Double = 0.0
): View() {
// the internal mutable, list of arranged subviews
// the internal, mutable list of arranged subviews
private val _arrangedSubviews = LinkedList<View>()
/**
* The list of arranged subviews belonging to this stack view.
@ -57,6 +59,64 @@ open class StackView(
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) {
if (index == 0) {
if (leadingConnection != null) {
@ -209,4 +269,4 @@ open class StackView(
*/
FILL
}
}
}

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
* 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.
* @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) {
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)
}
/**
* 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 var minecraftWidget = ProxyWidget()
init {
minecraftWidget.text = initialText
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.
*/
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 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 visible A function that determines if the tab should currently be visible.
*/
class SimpleTab(
override val tabView: View,
override val tooltip: Text? = null,
override val controller: ViewController,
): Tab
private val visible: (() -> Boolean)? = null
): Tab {
override val isVisible: Boolean
get() = visible?.invoke() ?: true
}
/**
* The currently selected tab.
@ -100,20 +112,7 @@ class TabViewController<T: TabViewController.Tab>(
tabStack = StackView(Axis.HORIZONTAL, StackView.Distribution.FILL)
tabStack.zIndex = 1.0
outerStack.addArrangedSubview(tabStack)
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())
updateTabButtons()
val background = NinePatchView(NinePatchTexture.PANEL_BG)
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
* swaps the content view controller.

View File

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

View File

@ -1,13 +1,12 @@
package net.shadowfacts.phycon
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.screenhandler.v1.ScreenRegistry
import net.minecraft.client.render.RenderLayer
import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.block.inserter.InserterScreen
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterScreen
import net.shadowfacts.phycon.init.PhyScreens
import net.shadowfacts.phycon.network.block.terminal.TerminalScreen
import net.shadowfacts.phycon.block.terminal.TerminalScreen
import net.shadowfacts.phycon.networking.ClientReceiver
import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems
@ -17,9 +16,9 @@ import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems
object PhysicalConnectivityClient: ClientModInitializer {
override fun onInitializeClient() {
BlockRenderLayerMap.INSTANCE.putBlock(PhyBlocks.CABLE, RenderLayer.getTranslucent())
ScreenRegistry.register(PhyScreens.TERMINAL_SCREEN_HANDLER, ::TerminalScreen)
ScreenRegistry.register(PhyScreens.TERMINAL, ::TerminalScreen)
ScreenRegistry.register(PhyScreens.INSERTER, ::InserterScreen)
ScreenRegistry.register(PhyScreens.REDSTONE_EMITTER, ::RedstoneEmitterScreen)
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.entity.player.PlayerEntity
@ -8,7 +8,6 @@ import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.NetworkComponentBlock
import net.shadowfacts.phycon.block.BlockWithEntity
/**
* @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.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.util.IPAddress
import net.shadowfacts.phycon.api.util.MACAddress
import net.shadowfacts.phycon.network.frame.ARPQueryFrame
import net.shadowfacts.phycon.network.frame.ARPResponseFrame
import net.shadowfacts.phycon.network.frame.BasePacketFrame
import net.shadowfacts.phycon.network.packet.*
import net.shadowfacts.phycon.util.NetworkUtil
import net.shadowfacts.phycon.frame.ARPQueryFrame
import net.shadowfacts.phycon.frame.ARPResponseFrame
import net.shadowfacts.phycon.frame.BasePacketFrame
import net.shadowfacts.phycon.packet.*
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.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)
}
override fun fromTag(state: BlockState, tag: CompoundTag) {
super.fromTag(state, tag)
ipAddress = IPAddress(tag.getInt("IPAddress"))
macAddress = MACAddress(tag.getLong("MACAddress"))
fromCommonTag(tag)
}
override fun toClientTag(tag: CompoundTag): CompoundTag {
tag.putInt("IPAddress", ipAddress.address)
tag.putLong("MACAddress", macAddress.address)
toCommonTag(tag)
return tag
}
override fun fromClientTag(tag: CompoundTag) {
ipAddress = IPAddress(tag.getInt("IPAddress"))
macAddress = MACAddress(tag.getLong("MACAddress"))
fromCommonTag(tag)
}
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.BlockState
@ -16,7 +16,7 @@ import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.NetworkComponentBlock
import net.shadowfacts.phycon.network.block.cable.CableBlock
import net.shadowfacts.phycon.block.cable.CableBlock
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.piston.PistonBehavior
@ -31,6 +31,7 @@ import java.util.*
*/
class CableBlock: Block(
Settings.of(CABLE_MATERIAL)
.strength(1f)
.nonOpaque()
), NetworkCableBlock {
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.BlockState
@ -7,6 +7,7 @@ import net.minecraft.block.ShapeContext
import net.minecraft.entity.LivingEntity
import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.state.StateManager
import net.minecraft.state.property.Properties
import net.minecraft.util.Identifier
@ -19,13 +20,17 @@ 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.network.DeviceBlock
import net.shadowfacts.phycon.block.DeviceBlock
import java.util.*
/**
* @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 {
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 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)
}
@ -108,4 +113,4 @@ class ExtractorBlock: DeviceBlock<ExtractorBlockEntity>(Settings.of(Material.MET
}
}
}
}

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.Simulation
@ -11,14 +11,15 @@ import net.minecraft.nbt.CompoundTag
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.component.ActivationController
import net.shadowfacts.phycon.network.component.NetworkStackDispatcher
import net.shadowfacts.phycon.network.component.handleItemStack
import net.shadowfacts.phycon.network.packet.CapacityPacket
import net.shadowfacts.phycon.network.packet.ItemStackPacket
import net.shadowfacts.phycon.network.packet.RemoteActivationPacket
import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.component.ActivationController
import net.shadowfacts.phycon.component.NetworkStackDispatcher
import net.shadowfacts.phycon.component.handleItemStack
import net.shadowfacts.phycon.packet.CapacityPacket
import net.shadowfacts.phycon.packet.ItemStackPacket
import net.shadowfacts.phycon.packet.RemoteActivationPacket
import net.shadowfacts.phycon.util.ActivationMode
import net.shadowfacts.phycon.util.ClientConfigurableDevice
import kotlin.properties.Delegates
/**
@ -26,7 +27,8 @@ import kotlin.properties.Delegates
*/
class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
NetworkStackDispatcher<ExtractorBlockEntity.PendingInsertion>,
ActivationController.ActivatableDevice {
ActivationController.ActivatableDevice,
ClientConfigurableDevice {
companion object {
val SLEEP_TIME = 40L
@ -37,7 +39,7 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
private var inventory: FixedItemInv? = null
override val pendingInsertions = mutableListOf<PendingInsertion>()
override val dispatchStackTimeout = 40L
override val dispatchStackTimeout = 1L
override val controller = ActivationController(SLEEP_TIME, this)
fun updateInventory() {
@ -99,23 +101,21 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
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)
return super.toTag(tag)
}
override fun fromTag(state: BlockState, tag: CompoundTag) {
super.fromTag(state, tag)
controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode"))
}
override fun toClientTag(tag: CompoundTag): CompoundTag {
tag.putString("ActivationMode", controller.activationMode.name)
return super.toClientTag(tag)
}
override fun fromClientTag(tag: CompoundTag) {
super.fromClientTag(tag)
override fun loadDeviceConfiguration(tag: CompoundTag) {
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.BlockState
@ -6,6 +6,7 @@ import net.minecraft.block.Material
import net.minecraft.entity.LivingEntity
import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.state.StateManager
import net.minecraft.state.property.Properties
import net.minecraft.util.Identifier
@ -16,14 +17,18 @@ 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.network.DeviceBlock
import net.shadowfacts.phycon.network.block.extractor.ExtractorBlock
import net.shadowfacts.phycon.block.DeviceBlock
import net.shadowfacts.phycon.block.extractor.ExtractorBlock
import java.util.*
/**
* @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 {
val ID = Identifier(PhysicalConnectivity.MODID, "miner")
@ -66,4 +71,4 @@ class MinerBlock: DeviceBlock<MinerBlockEntity>(Settings.of(Material.METAL)) {
}
}
}
}

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.ItemStackUtil
@ -6,20 +6,25 @@ import alexiil.mc.lib.attributes.item.filter.ItemFilter
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.nbt.CompoundTag
import net.minecraft.server.world.ServerWorld
import net.minecraft.text.TranslatableText
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.world.World
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.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.network.component.NetworkStackDispatcher
import net.shadowfacts.phycon.network.component.NetworkStackProvider
import net.shadowfacts.phycon.network.component.handleItemStack
import net.shadowfacts.phycon.network.component.spawnItemStack
import net.shadowfacts.phycon.network.packet.*
import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.component.ActivationController
import net.shadowfacts.phycon.component.NetworkStackDispatcher
import net.shadowfacts.phycon.component.NetworkStackProvider
import net.shadowfacts.phycon.component.handleItemStack
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
/**
@ -27,7 +32,9 @@ import kotlin.math.min
*/
class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
NetworkStackProvider,
NetworkStackDispatcher<MinerBlockEntity.PendingInsertion> {
NetworkStackDispatcher<MinerBlockEntity.PendingInsertion>,
ActivationController.ActivatableDevice,
ClientConfigurableDevice {
private val facing: Direction
get() = cachedState[MinerBlock.FACING]
@ -36,6 +43,10 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
override val pendingInsertions = mutableListOf<PendingInsertion>()
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) {
when (packet) {
@ -48,10 +59,16 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
}
private fun handleRequestInventory(packet: RequestInventoryPacket) {
if (minerMode != MinerMode.ON_DEMAND) {
return
}
sendPacket(ReadInventoryPacket(invProxy, ipAddress, packet.source))
}
private fun handleLocateStack(packet: LocateStackPacket) {
if (minerMode != MinerMode.ON_DEMAND) {
return
}
val amount = invProxy.getAmount(packet.stack)
if (amount > 0) {
sendPacket(StackLocationPacket(packet.stack, amount, this, ipAddress, packet.source))
@ -59,6 +76,10 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
}
private fun handleExtractStack(packet: ExtractStackPacket) {
if (minerMode != MinerMode.ON_DEMAND) {
return
}
// always recalculate immediately before breaking
val drops = invProxy.getDrops(recalculate = true)
if (invProxy.getAmount(packet.stack) > 0) {
@ -73,14 +94,13 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
if (!ItemStackUtil.areEqualIgnoreAmounts(droppedStack, packet.stack)) {
continue
}
val copy = droppedStack.copy()
val toDecr = min(droppedStack.count, remaining)
val copy = droppedStack.copyWithCount(toDecr)
droppedStack.decrement(toDecr)
remaining -= toDecr
// todo: should this try to combine stacks and send as few packets as possible?
copy.count = toDecr
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 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 {
companion object {
val TOOL = ItemStack(Items.DIAMOND_PICKAXE)
}
private var cachedState: BlockState? = null
private var cachedDrops: List<ItemStack>? = null
@ -117,12 +206,15 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
if (cachedDrops == null || realState != cachedState || recalculate) {
cachedState = realState
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!!
}
override fun getStoredStacks(): Set<ItemStack> {
if (miner.minerMode != MinerMode.ON_DEMAND) {
return setOf()
}
return getDrops().toSet()
}
@ -132,7 +224,7 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
override fun getStatistics(filter: ItemFilter): GroupedItemInvView.ItemInvStatistic {
var totalCount = 0
for (s in getDrops()) {
for (s in storedStacks) {
if (filter.matches(s)) {
totalCount += s.count
}
@ -144,4 +236,4 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
class PendingInsertion(stack: ItemStack, timestamp: Long): NetworkStackDispatcher.PendingInsertion<PendingInsertion>(stack, timestamp) {
}
}
}

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.AttributeProvider
import net.minecraft.block.*
import net.minecraft.entity.LivingEntity
import net.minecraft.item.ItemStack
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
@ -12,12 +13,16 @@ import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.NetworkComponentBlock
import net.shadowfacts.phycon.network.FaceDeviceBlock
import net.shadowfacts.phycon.block.FaceDeviceBlock
/**
* @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,
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.Simulation
import alexiil.mc.lib.attributes.item.GroupedItemInv
import alexiil.mc.lib.attributes.item.ItemAttributes
import net.minecraft.block.BlockState
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.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.FaceDeviceBlock
import net.shadowfacts.phycon.network.component.ItemStackPacketHandler
import net.shadowfacts.phycon.network.component.NetworkStackProvider
import net.shadowfacts.phycon.network.component.NetworkStackReceiver
import net.shadowfacts.phycon.network.component.handleItemStack
import net.shadowfacts.phycon.network.packet.*
import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.block.FaceDeviceBlock
import net.shadowfacts.phycon.component.ItemStackPacketHandler
import net.shadowfacts.phycon.component.NetworkStackProvider
import net.shadowfacts.phycon.component.NetworkStackReceiver
import net.shadowfacts.phycon.component.handleItemStack
import net.shadowfacts.phycon.packet.*
import net.shadowfacts.phycon.util.ClientConfigurableDevice
import kotlin.math.min
/**
@ -23,11 +26,15 @@ import kotlin.math.min
class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE),
ItemStackPacketHandler,
NetworkStackProvider,
NetworkStackReceiver {
NetworkStackReceiver,
ClientConfigurableDevice {
private val facing: Direction
get() = cachedState[FaceDeviceBlock.FACING]
override var providerPriority = 0
override var receiverPriority = 0
// todo: should this be a weak ref?
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.AttributeProvider
import net.minecraft.block.BlockState
import net.minecraft.block.Material
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
@ -19,7 +20,11 @@ import java.util.*
/**
* @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,
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.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.util.MACAddress
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.NetworkUtil
import net.shadowfacts.phycon.network.packet.ItemStackPacket
import net.shadowfacts.phycon.util.NetworkUtil
import net.shadowfacts.phycon.packet.ItemStackPacket
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.BlockState
import net.minecraft.block.Material
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.property.Properties
import net.minecraft.util.Identifier
@ -12,22 +12,23 @@ 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.FaceDeviceBlock
import java.util.*
import net.shadowfacts.phycon.block.FaceDeviceBlock
/**
* @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 {
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 faceShapes = mapOf(
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>) {
super.appendProperties(builder)
builder.add(LIT)
builder.add(POWERED)
}
override fun createBlockEntity(world: BlockView) = RedstoneControllerBlockEntity()
override fun getPlacementState(context: ItemPlacementContext): BlockState {
val state = super.getPlacementState(context)
return state.with(LIT, isPowered(context.world, context.blockPos, state[FACING]))
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) {
// this can't be done in getStateForNeighborUpdate because getEmittedRedstonePower is defined in World not WorldAccess
if (!world.isClient) {
val wasLit = state[LIT]
val wasLit = state[POWERED]
val isLit = isPowered(world, pos, state[FACING])
if (wasLit != isLit) {
toggleLit(state, world, pos)
@ -67,7 +68,7 @@ class RedstoneControllerBlock: FaceDeviceBlock<RedstoneControllerBlockEntity>(Se
}
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()
}

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

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.AttributeProvider
import net.minecraft.block.BlockState
import net.minecraft.block.Material
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
@ -16,13 +17,17 @@ import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.NetworkComponentBlock
import net.shadowfacts.phycon.network.DeviceBlock
import net.shadowfacts.phycon.block.DeviceBlock
import java.util.*
/**
* @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,
AttributeProvider {
@ -41,6 +46,15 @@ class TerminalBlock: DeviceBlock<TerminalBlockEntity>(Settings.of(Material.METAL
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<*>) {
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.ItemStackCollections
import alexiil.mc.lib.attributes.item.ItemStackUtil
import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory
import net.minecraft.block.BlockState
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.inventory.Inventory
import net.minecraft.inventory.InventoryChangedListener
import net.minecraft.item.ItemStack
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.network.PacketByteBuf
import net.minecraft.screen.ScreenHandler
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.util.IPAddress
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.NetworkUtil
import net.shadowfacts.phycon.network.component.*
import net.shadowfacts.phycon.network.packet.*
import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.util.NetworkUtil
import net.shadowfacts.phycon.component.*
import net.shadowfacts.phycon.packet.*
import java.lang.ref.WeakReference
import java.util.*
import kotlin.math.min
@ -43,8 +41,10 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
NetworkStackDispatcher<TerminalBlockEntity.PendingInsertion> {
companion object {
val LOCATE_REQUEST_TIMEOUT: Long = 40 // ticks
val INSERTION_TIMEOUT: Long = 40
// the locate/insertion timeouts are only 1 tick because that's long enough to hear from every device on the network
// 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>()
@ -164,14 +164,15 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
override fun tick() {
super.tick()
if (counter % 20 == 0L) {
if (!world!!.isClient) {
finishPendingRequests()
beginInsertions()
finishTimedOutPendingInsertions()
}
if (!world!!.isClient) {
finishPendingRequests()
finishTimedOutPendingInsertions()
}
if (observers > 0 && !world!!.isClient) {
if (counter % 20 == 0L && !world!!.isClient) {
beginInsertions()
if (observers > 0) {
updateAndSync()
}
}
@ -184,7 +185,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
inventoryCache.clear()
sendPacket(RequestInventoryPacket(ipAddress))
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)
}
@ -210,8 +211,15 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
private fun stackLocateRequestCompleted(request: StackLocateRequest) {
pendingRequests.remove(request)
// todo: also sort results by interface priority
val sortedResults = request.results.sortedByDescending { it.first }.toMutableList()
val sortedResults = request.results.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
while (amountRequested < request.amount && sortedResults.isNotEmpty()) {
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())
return super.toTag(tag)
}
override fun fromTag(state: BlockState, tag: CompoundTag) {
super.fromTag(state, tag)
override fun fromCommonTag(tag: CompoundTag) {
super.fromCommonTag(tag)
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 {
fun netItemsChanged()
}
@ -279,6 +277,8 @@ data class StackLocateRequest(
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
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 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.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 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.SlotActionType
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.init.PhyBlocks
import net.shadowfacts.phycon.init.PhyScreens
import net.shadowfacts.phycon.networking.C2STerminalRequestItem
import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems
import net.shadowfacts.phycon.util.SortMode
import net.shadowfacts.phycon.util.copyWithCount
import java.lang.ref.WeakReference
import kotlin.math.ceil
import kotlin.math.min
/**
* @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 {
companion object {
@ -40,9 +38,7 @@ class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val ter
field = value
if (terminal.world!!.isClient) {
itemsForDisplay = value.map {
val stack = it.stack.copy()
stack.count = it.amount
stack
it.stack.copyWithCount(it.amount)
}
}
}

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.shadowfacts.phycon.network.packet.RemoteActivationPacket
import net.shadowfacts.phycon.packet.RemoteActivationPacket
import net.shadowfacts.phycon.util.ActivationMode
import net.shadowfacts.phycon.util.ClientConfigurableDevice
/**
* @author shadowfacts
@ -50,11 +51,15 @@ class ActivationController<T>(
}
}
interface ActivatableDevice {
interface ActivatableDevice: ClientConfigurableDevice {
val controller: ActivationController<*>
val counter: Long
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.entity.ItemEntity
import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.PacketSink
import net.shadowfacts.phycon.api.PacketSource
import net.shadowfacts.phycon.network.packet.ItemStackPacket
import net.shadowfacts.phycon.packet.ItemStackPacket
/**
* @author shadowfacts
@ -32,4 +32,4 @@ fun <Self> Self.spawnItemStack(stack: ItemStack) where Self: BlockEntity, Self:
// todo: calculate entity spawn point by finding non-obstructed location
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), stack)
world!!.spawnEntity(entity)
}
}

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 net.minecraft.block.entity.BlockEntity
import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.network.packet.CapacityPacket
import net.shadowfacts.phycon.network.packet.CheckCapacityPacket
import net.shadowfacts.phycon.network.packet.ItemStackPacket
import net.shadowfacts.phycon.packet.CapacityPacket
import net.shadowfacts.phycon.packet.CheckCapacityPacket
import net.shadowfacts.phycon.packet.ItemStackPacket
import net.shadowfacts.phycon.util.copyWithCount
import kotlin.math.min
/**
* @author shadowfacts
@ -27,10 +29,10 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
}
fun handleCapacity(packet: CapacityPacket) {
val insertion = pendingInsertions.firstOrNull {
ItemStackUtil.areEqualIgnoreAmounts(packet.stack, it.stack)
}
if (insertion != null) {
pendingInsertions.firstOrNull { insertion ->
ItemStackUtil.areEqualIgnoreAmounts(packet.stack, insertion.stack) &&
insertion.results.none { it.second.ipAddress == packet.source }
}?.also { insertion ->
insertion.results.add(packet.capacity to packet.stackReceiver)
if (insertion.isFinishable(this)) {
val remaining = finishInsertion(insertion)
@ -42,14 +44,22 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
fun finishInsertion(insertion: Insertion): ItemStack {
pendingInsertions.remove(insertion)
// todo: also sort results by interface priority
val sortedResults = insertion.results.sortedBy { it.first }.toMutableList()
val sortedResults = insertion.results.toMutableList()//.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
val remaining = insertion.stack.copy()
while (!remaining.isEmpty && sortedResults.isNotEmpty()) {
val (capacity, receivingInterface) = sortedResults.removeFirst()
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
remaining.count -= capacity
}
@ -67,7 +77,8 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
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.MACAddress
@ -19,4 +19,4 @@ class ARPResponseFrame(
source: MACAddress,
destination: MACAddress,
): BaseFrame(source, destination) {
}
}

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.PacketFrame
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.MACAddress
/**
@ -14,4 +12,4 @@ open class BaseFrame(
): EthernetFrame {
override fun getSource() = source
override fun getDestination() = destination
}
}

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.packet.Packet
@ -13,4 +13,4 @@ class BasePacketFrame(
destination: MACAddress,
): BaseFrame(source, destination), PacketFrame {
override fun getPacket() = packet
}
}

View File

@ -5,22 +5,22 @@ import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry
import net.shadowfacts.phycon.network.block.extractor.ExtractorBlock
import net.shadowfacts.phycon.network.block.extractor.ExtractorBlockEntity
import net.shadowfacts.phycon.network.block.miner.MinerBlock
import net.shadowfacts.phycon.network.block.miner.MinerBlockEntity
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlockEntity
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlockEntity
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlock
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlockEntity
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
import net.shadowfacts.phycon.network.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.network.block.test.DestBlock
import net.shadowfacts.phycon.network.block.test.DestBlockEntity
import net.shadowfacts.phycon.network.block.test.SourceBlock
import net.shadowfacts.phycon.network.block.test.SourceBlockEntity
import net.shadowfacts.phycon.block.extractor.ExtractorBlock
import net.shadowfacts.phycon.block.extractor.ExtractorBlockEntity
import net.shadowfacts.phycon.block.inserter.InserterBlock
import net.shadowfacts.phycon.block.inserter.InserterBlockEntity
import net.shadowfacts.phycon.block.miner.MinerBlock
import net.shadowfacts.phycon.block.miner.MinerBlockEntity
import net.shadowfacts.phycon.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.block.netinterface.InterfaceBlockEntity
import net.shadowfacts.phycon.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.block.netswitch.SwitchBlockEntity
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlockEntity
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlockEntity
import net.shadowfacts.phycon.block.terminal.TerminalBlock
import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
/**
* @author shadowfacts
@ -31,11 +31,10 @@ object PhyBlockEntities {
val TERMINAL = create(::TerminalBlockEntity, PhyBlocks.TERMINAL)
val SWITCH = create(::SwitchBlockEntity, PhyBlocks.SWITCH)
val EXTRACTOR = create(::ExtractorBlockEntity, PhyBlocks.EXTRACTOR)
val INSERTER = create(::InserterBlockEntity, PhyBlocks.INSERTER)
val MINER = create(::MinerBlockEntity, PhyBlocks.MINER)
val REDSTONE_CONTROLLER = create(::RedstoneControllerBlockEntity, PhyBlocks.REDSTONE_CONTROLLER)
val SOURCE = create(::SourceBlockEntity, PhyBlocks.SOURCE)
val DEST = create(::DestBlockEntity, PhyBlocks.DEST)
val REDSTONE_EMITTER = create(::RedstoneEmitterBlockEntity, PhyBlocks.REDSTONE_EMITTER)
private fun <T: BlockEntity> create(builder: () -> T, block: Block): BlockEntityType<T> {
return BlockEntityType.Builder.create(builder, block).build(null)
@ -46,11 +45,10 @@ object PhyBlockEntities {
register(TerminalBlock.ID, TERMINAL)
register(SwitchBlock.ID, SWITCH)
register(ExtractorBlock.ID, EXTRACTOR)
register(InserterBlock.ID, INSERTER)
register(MinerBlock.ID, MINER)
register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER)
register(SourceBlock.ID, SOURCE)
register(DestBlock.ID, DEST)
register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER)
}
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.util.Identifier
import net.minecraft.util.registry.Registry
import net.shadowfacts.phycon.network.block.cable.CableBlock
import net.shadowfacts.phycon.network.block.extractor.ExtractorBlock
import net.shadowfacts.phycon.network.block.miner.MinerBlock
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlock
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
import net.shadowfacts.phycon.network.block.test.DestBlock
import net.shadowfacts.phycon.network.block.test.SourceBlock
import net.shadowfacts.phycon.block.cable.CableBlock
import net.shadowfacts.phycon.block.extractor.ExtractorBlock
import net.shadowfacts.phycon.block.inserter.InserterBlock
import net.shadowfacts.phycon.block.miner.MinerBlock
import net.shadowfacts.phycon.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
import net.shadowfacts.phycon.block.terminal.TerminalBlock
/**
* @author shadowfacts
@ -23,11 +23,10 @@ object PhyBlocks {
val SWITCH = SwitchBlock()
val CABLE = CableBlock()
val EXTRACTOR = ExtractorBlock()
val INSERTER = InserterBlock()
val MINER = MinerBlock()
val REDSTONE_CONTROLLER = RedstoneControllerBlock()
val SOURCE = SourceBlock()
val DEST = DestBlock()
val REDSTONE_EMITTER = RedstoneEmitterBlock()
fun init() {
register(InterfaceBlock.ID, INTERFACE)
@ -35,11 +34,10 @@ object PhyBlocks {
register(SwitchBlock.ID, SWITCH)
register(CableBlock.ID, CABLE)
register(ExtractorBlock.ID, EXTRACTOR)
register(InserterBlock.ID, INSERTER)
register(MinerBlock.ID, MINER)
register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER)
register(SourceBlock.ID, SOURCE)
register(DestBlock.ID, DEST)
register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER)
}
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.shadowfacts.phycon.item.ConsoleItem
import net.shadowfacts.phycon.item.ScrewdriverItem
import net.shadowfacts.phycon.network.block.cable.CableBlock
import net.shadowfacts.phycon.network.block.extractor.ExtractorBlock
import net.shadowfacts.phycon.network.block.miner.MinerBlock
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlock
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
import net.shadowfacts.phycon.network.block.test.DestBlock
import net.shadowfacts.phycon.network.block.test.SourceBlock
import net.shadowfacts.phycon.block.cable.CableBlock
import net.shadowfacts.phycon.block.extractor.ExtractorBlock
import net.shadowfacts.phycon.block.inserter.InserterBlock
import net.shadowfacts.phycon.block.miner.MinerBlock
import net.shadowfacts.phycon.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
import net.shadowfacts.phycon.block.terminal.TerminalBlock
import net.shadowfacts.phycon.item.DeviceBlockItem
/**
* @author shadowfacts
*/
object PhyItems {
val INTERFACE = BlockItem(PhyBlocks.INTERFACE, Item.Settings())
val TERMINAL = BlockItem(PhyBlocks.TERMINAL, Item.Settings())
val INTERFACE = DeviceBlockItem(PhyBlocks.INTERFACE, Item.Settings())
val TERMINAL = DeviceBlockItem(PhyBlocks.TERMINAL, Item.Settings())
val SWITCH = BlockItem(PhyBlocks.SWITCH, Item.Settings())
val CABLE = BlockItem(PhyBlocks.CABLE, Item.Settings())
val EXTRACTOR = BlockItem(PhyBlocks.EXTRACTOR, Item.Settings())
val MINER = BlockItem(PhyBlocks.MINER, Item.Settings())
val REDSTONE_CONTROLLER = BlockItem(PhyBlocks.REDSTONE_CONTROLLER, Item.Settings())
val SOURCE = BlockItem(PhyBlocks.SOURCE, Item.Settings())
val DEST = BlockItem(PhyBlocks.DEST , Item.Settings())
val EXTRACTOR = DeviceBlockItem(PhyBlocks.EXTRACTOR, Item.Settings())
val INSERTER = DeviceBlockItem(PhyBlocks.INSERTER, Item.Settings())
val MINER = DeviceBlockItem(PhyBlocks.MINER, Item.Settings())
val REDSTONE_CONTROLLER = DeviceBlockItem(PhyBlocks.REDSTONE_CONTROLLER, Item.Settings())
val REDSTONE_EMITTER = DeviceBlockItem(PhyBlocks.REDSTONE_EMITTER, Item.Settings())
val SCREWDRIVER = ScrewdriverItem()
val CONSOLE = ConsoleItem()
@ -41,11 +41,10 @@ object PhyItems {
register(SwitchBlock.ID, SWITCH)
register(CableBlock.ID, CABLE)
register(ExtractorBlock.ID, EXTRACTOR)
register(InserterBlock.ID, INSERTER)
register(MinerBlock.ID, MINER)
register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER)
register(SourceBlock.ID, SOURCE)
register(DestBlock.ID, DEST)
register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER)
register(ScrewdriverItem.ID, SCREWDRIVER)
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.minecraft.screen.ScreenHandlerType
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.network.block.terminal.TerminalScreenHandler
import net.shadowfacts.phycon.block.inserter.InserterScreenHandler
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterScreenHandler
import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler
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
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.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.network.DeviceBlock
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.block.DeviceBlock
import net.shadowfacts.phycon.block.DeviceBlockEntity
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
import net.minecraft.block.Blocks
import net.minecraft.entity.ItemEntity
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.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.block.DeviceBlock
/**
* @author shadowfacts
@ -11,4 +19,33 @@ class ScrewdriverItem: Item(Settings()) {
companion object {
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.PacketSender
import net.minecraft.block.entity.BlockEntity
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.Packet
import net.minecraft.network.PacketByteBuf
import net.minecraft.server.MinecraftServer
import net.minecraft.server.network.ServerPlayNetworkHandler
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry
import net.minecraft.util.registry.RegistryKey
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.component.ActivationController
import net.shadowfacts.phycon.util.ActivationMode
import net.shadowfacts.phycon.util.ClientConfigurableDevice
/**
* @author shadowfacts
*/
object C2SConfigureActivationMode: ServerReceiver {
override val CHANNEL = Identifier(PhysicalConnectivity.MODID, "configure_activation_mode")
object C2SConfigureDevice: ServerReceiver {
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()
buf.writeIdentifier(be.world!!.registryKey.value)
buf.writeBlockPos(be.pos)
buf.writeString(be.controller.activationMode.name)
val tag = CompoundTag()
be.writeDeviceConfiguration(tag)
buf.writeCompoundTag(tag)
return createPacket(buf)
}
@ -34,16 +34,16 @@ object C2SConfigureActivationMode: ServerReceiver {
override fun receive(server: MinecraftServer, player: ServerPlayerEntity, handler: ServerPlayNetworkHandler, buf: PacketByteBuf, responseSender: PacketSender) {
val dimID = buf.readIdentifier()
val pos = buf.readBlockPos()
val mode = ActivationMode.valueOf(buf.readString())
val tag = buf.readCompoundTag() ?: return
server.execute {
// todo: check the player is close enough
val key = RegistryKey.of(Registry.DIMENSION, dimID)
val world = server.getWorld(key) ?: return@execute
val device = world.getBlockEntity(pos) ?: return@execute
if (device !is ActivationController.ActivatableDevice) return@execute
device.controller.activationMode = mode
device.markDirty()
// todo: check if the player is close enough
val world = player.world
if (world.registryKey.value != dimID) return@execute
val be = world.getBlockEntity(pos) ?: return@execute
val device = be as? ClientConfigurableDevice ?: return@execute
device.loadDeviceConfiguration(tag)
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.RegistryKey
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
@ -30,9 +31,7 @@ object C2STerminalRequestItem: ServerReceiver {
// 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
// an overflow and be negative on the receiving side.
val stackCopy = stack.copy()
stackCopy.count = 1
buf.writeItemStack(stackCopy)
buf.writeItemStack(stack.copyWithCount(1))
buf.writeVarInt(amount)
return ClientPlayNetworking.createC2SPacket(CHANNEL, buf)
@ -52,4 +51,4 @@ object C2STerminalRequestItem: ServerReceiver {
}
}
}
}

View File

@ -10,8 +10,8 @@ import net.minecraft.server.network.ServerPlayNetworkHandler
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.network.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.network.block.terminal.TerminalScreenHandler
import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler
import net.shadowfacts.phycon.util.SortMode
/**
@ -47,4 +47,4 @@ object C2STerminalUpdateDisplayedItems: ServerReceiver {
screenHandler.sendUpdatedItemsToClient(player, query, sortMode)
}
}
}
}

View File

@ -7,8 +7,8 @@ import net.minecraft.client.MinecraftClient
import net.minecraft.client.network.ClientPlayNetworkHandler
import net.minecraft.network.Packet
import net.minecraft.network.PacketByteBuf
import net.shadowfacts.phycon.network.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.network.block.terminal.TerminalScreenHandler
import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler
import net.shadowfacts.phycon.util.SortMode
/**
@ -54,4 +54,4 @@ object S2CTerminalUpdateDisplayedItems: ClientReceiver {
screenHandler.receivedUpdatedItemsFromServer(entries, query, 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.util.IPAddress
@ -14,4 +14,4 @@ abstract class BasePacket(
override fun getSource() = source
override fun getDestination() = destination
}
}

View File

@ -1,8 +1,8 @@
package net.shadowfacts.phycon.network.packet
package net.shadowfacts.phycon.packet
import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.network.component.NetworkStackReceiver
import net.shadowfacts.phycon.component.NetworkStackReceiver
/**
* @author shadowfacts
@ -14,4 +14,4 @@ class CapacityPacket(
source: IPAddress,
destination: IPAddress
): BasePacket(source, destination) {
}
}

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.packet
package net.shadowfacts.phycon.packet
import net.minecraft.item.ItemStack
import net.shadowfacts.phycon.api.util.IPAddress
@ -7,4 +7,4 @@ import net.shadowfacts.phycon.api.util.IPAddress
* @author shadowfacts
*/
class CheckCapacityPacket(val stack: ItemStack, source: IPAddress, destination: IPAddress): BasePacket(source, destination) {
}
}

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.network.DeviceBlockEntity
import net.shadowfacts.phycon.block.DeviceBlockEntity
/**
* @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.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.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.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 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

View File

@ -1,4 +1,4 @@
package net.shadowfacts.phycon.network.packet
package net.shadowfacts.phycon.packet
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.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.network.component.NetworkStackProvider
import net.shadowfacts.phycon.component.NetworkStackProvider
/**
* @author shadowfacts

View File

@ -2,16 +2,14 @@ package net.shadowfacts.phycon.screen.console
import net.minecraft.client.MinecraftClient
import net.minecraft.text.TranslatableText
import net.shadowfacts.cacao.geometry.Axis
import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.view.Label
import net.shadowfacts.cacao.view.StackView
import net.shadowfacts.cacao.view.button.EnumButton
import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.component.ActivationController
import net.shadowfacts.phycon.networking.C2SConfigureActivationMode
import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.component.ActivationController
import net.shadowfacts.phycon.networking.C2SConfigureDevice
import net.shadowfacts.phycon.util.ActivationMode
/**
@ -32,7 +30,7 @@ class ActivatableDeviceViewController<T>(
val mode = EnumButton(device.controller.activationMode, ActivationMode::friendlyName)
mode.handler = {
device.controller.activationMode = it.value
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureActivationMode(device))
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device))
}
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.window.Window
import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.block.redstone.RedstoneControllerBlockEntity
import net.shadowfacts.phycon.network.component.ActivationController
import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.block.miner.MinerBlockEntity
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
/**
@ -40,7 +43,8 @@ class DeviceConsoleScreen(
intrinsicContentSize = Size(16.0, 16.0)
},
TranslatableText("gui.phycon.console.remote"),
ActivatableDeviceViewController(device)
ActivatableDeviceViewController(device),
device::canConfigureActivationController
))
}
if (device is RedstoneControllerBlockEntity) {
@ -52,7 +56,30 @@ class DeviceConsoleScreen(
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)

View File

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

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.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" }
},
{
"when": { "facing": "down", "lit": "false" },
"when": { "facing": "down", "powered": "false" },
"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 }
},
{
"when": { "facing": "north", "lit": "false" },
"when": { "facing": "north", "powered": "false" },
"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 }
},
{
"when": { "facing": "west", "lit": "false" },
"when": { "facing": "west", "powered": "false" },
"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 }
},
{
"when": { "facing": "down", "lit": "true" },
"when": { "facing": "down", "powered": "true" },
"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 }
},
{
"when": { "facing": "north", "lit": "true" },
"when": { "facing": "north", "powered": "true" },
"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 }
},
{
"when": { "facing": "west", "lit": "true" },
"when": { "facing": "west", "powered": "true" },
"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 }
},

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.cable": "Cable",
"block.phycon.extractor": "Inventory Extractor",
"block.phycon.inserter": "Inventory Inserter",
"block.phycon.miner": "Block Miner",
"block.phycon.redstone_controller": "Redstone Controller",
"block.phycon.redstone_emitter": "Redstone Emitter",
"item.phycon.screwdriver": "Screwdriver",
"item.phycon.console": "Console",
@ -18,11 +20,24 @@
"gui.phycon.console.redstone.devices": "Managed Devices",
"gui.phycon.console.remote": "Remote Management",
"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.low": "Low",
"gui.phycon.redstone_mode.toggle": "Toggle",
"gui.phycon.redstone_mode.rising_edge": "Rising Edge",
"gui.phycon.redstone_mode.falling_edge": "Falling Edge",
"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",
"textures": {
"cable_side": "phycon:block/cable_straight",
"cable_end": "phycon:block/cable_cap_end"
},
"elements": [
{
"from": [0, 0, 0],
"to": [16, 2, 16],
"faces": {
"down": {"texture": "phycon:block/extractor_front"},
"down": {"texture": "phycon:block/extractor_front", "cullface": "down"},
"up": {"texture": "phycon:block/extractor_back"},
"north": {"texture": "phycon:block/extractor_side"},
"south": {"texture": "phycon:block/extractor_side"},
@ -39,12 +43,12 @@
"from": [6, 6, 6],
"to": [10, 16, 10],
"faces": {
"up": {"texture": "phycon:block/cable_side"},
"north": {"texture": "phycon:block/cable_side"},
"south": {"texture": "phycon:block/cable_side"},
"west": {"texture": "phycon:block/cable_side"},
"east": {"texture": "phycon:block/cable_side"}
"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,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