Compare commits
3 Commits
d621e9e089
...
4c5b7daf9e
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 4c5b7daf9e | |
Shadowfacts | 1f078c671f | |
Shadowfacts | 9252a35dae |
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Configuration>
|
||||||
|
<Loggers>
|
||||||
|
<Logger name="PhyNet">
|
||||||
|
<AppenderRef ref="SysOut" level="debug"/>
|
||||||
|
</Logger>
|
||||||
|
</Loggers>
|
||||||
|
</Configuration>
|
|
@ -1,5 +1,5 @@
|
||||||
plugins {
|
plugins {
|
||||||
id "fabric-loom" version "0.5-SNAPSHOT"
|
id "fabric-loom" version "0.6.49"
|
||||||
id "maven-publish"
|
id "maven-publish"
|
||||||
id "org.jetbrains.kotlin.jvm" version "1.4.30"
|
id "org.jetbrains.kotlin.jvm" version "1.4.30"
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ version = project.mod_version
|
||||||
group = project.maven_group
|
group = project.maven_group
|
||||||
|
|
||||||
minecraft {
|
minecraft {
|
||||||
|
log4jConfigs.from "PhyConDebugLogging.xml"
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import net.shadowfacts.phycon.init.PhyBlocks
|
||||||
import net.shadowfacts.phycon.init.PhyItems
|
import net.shadowfacts.phycon.init.PhyItems
|
||||||
import net.shadowfacts.phycon.init.PhyScreens
|
import net.shadowfacts.phycon.init.PhyScreens
|
||||||
import net.shadowfacts.phycon.networking.*
|
import net.shadowfacts.phycon.networking.*
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
|
@ -15,6 +16,8 @@ object PhysicalConnectivity: ModInitializer {
|
||||||
|
|
||||||
val MODID = "phycon"
|
val MODID = "phycon"
|
||||||
|
|
||||||
|
val NETWORK_LOGGER = LogManager.getLogger("PhyNet")
|
||||||
|
|
||||||
override fun onInitialize() {
|
override fun onInitialize() {
|
||||||
PhyBlocks.init()
|
PhyBlocks.init()
|
||||||
PhyBlockEntities.init()
|
PhyBlockEntities.init()
|
||||||
|
|
|
@ -6,6 +6,7 @@ import net.minecraft.block.entity.BlockEntity
|
||||||
import net.minecraft.block.entity.BlockEntityType
|
import net.minecraft.block.entity.BlockEntityType
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.util.Tickable
|
import net.minecraft.util.Tickable
|
||||||
|
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||||
import net.shadowfacts.phycon.api.PacketSink
|
import net.shadowfacts.phycon.api.PacketSink
|
||||||
import net.shadowfacts.phycon.api.PacketSource
|
import net.shadowfacts.phycon.api.PacketSource
|
||||||
import net.shadowfacts.phycon.api.Interface
|
import net.shadowfacts.phycon.api.Interface
|
||||||
|
@ -52,6 +53,7 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
|
||||||
abstract override fun handle(packet: Packet)
|
abstract override fun handle(packet: Packet)
|
||||||
|
|
||||||
private fun doHandlePacket(packet: Packet) {
|
private fun doHandlePacket(packet: Packet) {
|
||||||
|
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}) received packet: {}", this, ipAddress, packet)
|
||||||
when (packet) {
|
when (packet) {
|
||||||
is DeviceRemovedPacket -> {
|
is DeviceRemovedPacket -> {
|
||||||
arpTable.remove(packet.source)
|
arpTable.remove(packet.source)
|
||||||
|
@ -65,13 +67,12 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun receive(frame: EthernetFrame) {
|
override fun receive(frame: EthernetFrame) {
|
||||||
println("$this ($ipAddress, ${macAddress}) received frame from ${frame.source}: $frame")
|
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}, {}) received frame from {}: {}", this, ipAddress, macAddress, frame.source, frame)
|
||||||
when (frame) {
|
when (frame) {
|
||||||
is ARPQueryFrame -> handleARPQuery(frame)
|
is ARPQueryFrame -> handleARPQuery(frame)
|
||||||
is ARPResponseFrame -> handleARPResponse(frame)
|
is ARPResponseFrame -> handleARPResponse(frame)
|
||||||
is PacketFrame -> {
|
is PacketFrame -> {
|
||||||
if (frame.packet.destination.isBroadcast || frame.packet.destination == ipAddress) {
|
if (frame.packet.destination.isBroadcast || frame.packet.destination == ipAddress) {
|
||||||
println("$this ($ipAddress) received packet: ${frame.packet}")
|
|
||||||
doHandlePacket(frame.packet)
|
doHandlePacket(frame.packet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,17 +80,17 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleARPQuery(frame: ARPQueryFrame) {
|
private fun handleARPQuery(frame: ARPQueryFrame) {
|
||||||
println("$this ($ipAddress) received ARP query for ${frame.queryIP}")
|
PhysicalConnectivity.NETWORK_LOGGER.debug("{}, ({}), received ARP query for {}", this, ipAddress, frame.queryIP)
|
||||||
arpTable[frame.sourceIP] = frame.source
|
arpTable[frame.sourceIP] = frame.source
|
||||||
if (frame.queryIP == ipAddress) {
|
if (frame.queryIP == ipAddress) {
|
||||||
println("$this ($ipAddress) sending ARP response to ${frame.source} with $macAddress")
|
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}) sending ARP response to {} with {}", this, ipAddress, frame.sourceIP, macAddress)
|
||||||
send(ARPResponseFrame(ipAddress, macAddress, frame.source))
|
send(ARPResponseFrame(ipAddress, macAddress, frame.source))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleARPResponse(frame: ARPResponseFrame) {
|
private fun handleARPResponse(frame: ARPResponseFrame) {
|
||||||
arpTable[frame.query] = frame.source
|
arpTable[frame.query] = frame.source
|
||||||
println("$this ($ipAddress) received ARP response for ${frame.query} with ${frame.source}")
|
PhysicalConnectivity.NETWORK_LOGGER.debug("{}, ({}) received ARP response for {} with {}", this, ipAddress, frame.query, frame.source)
|
||||||
|
|
||||||
val toRemove = packetQueue.filter { (packet, _) ->
|
val toRemove = packetQueue.filter { (packet, _) ->
|
||||||
if (packet.destination == frame.query) {
|
if (packet.destination == frame.query) {
|
||||||
|
@ -110,7 +111,7 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
|
||||||
} else {
|
} else {
|
||||||
packetQueue.add(PendingPacket(packet, counter))
|
packetQueue.add(PendingPacket(packet, counter))
|
||||||
|
|
||||||
println("$this ($ipAddress) sending ARP query for ${packet.destination}")
|
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}) sending ARP query for {}", this, ipAddress, packet.destination)
|
||||||
send(ARPQueryFrame(packet.destination, ipAddress, macAddress))
|
send(ARPQueryFrame(packet.destination, ipAddress, macAddress))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,31 @@
|
||||||
package net.shadowfacts.phycon.network.block.extractor
|
package net.shadowfacts.phycon.network.block.extractor
|
||||||
|
|
||||||
import alexiil.mc.lib.attributes.SearchOptions
|
import alexiil.mc.lib.attributes.SearchOptions
|
||||||
import alexiil.mc.lib.attributes.item.GroupedItemInv
|
import alexiil.mc.lib.attributes.Simulation
|
||||||
|
import alexiil.mc.lib.attributes.item.FixedItemInv
|
||||||
import alexiil.mc.lib.attributes.item.ItemAttributes
|
import alexiil.mc.lib.attributes.item.ItemAttributes
|
||||||
|
import alexiil.mc.lib.attributes.item.filter.ExactItemStackFilter
|
||||||
import net.minecraft.block.BlockState
|
import net.minecraft.block.BlockState
|
||||||
|
import net.minecraft.item.ItemStack
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.util.math.Direction
|
import net.minecraft.util.math.Direction
|
||||||
import net.shadowfacts.phycon.api.packet.Packet
|
import net.shadowfacts.phycon.api.packet.Packet
|
||||||
import net.shadowfacts.phycon.api.util.IPAddress
|
|
||||||
import net.shadowfacts.phycon.init.PhyBlockEntities
|
import net.shadowfacts.phycon.init.PhyBlockEntities
|
||||||
import net.shadowfacts.phycon.network.DeviceBlockEntity
|
import net.shadowfacts.phycon.network.DeviceBlockEntity
|
||||||
import net.shadowfacts.phycon.network.component.ActivationController
|
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.CapacityPacket
|
||||||
import net.shadowfacts.phycon.network.packet.CheckCapacityPacket
|
|
||||||
import net.shadowfacts.phycon.network.packet.ItemStackPacket
|
import net.shadowfacts.phycon.network.packet.ItemStackPacket
|
||||||
import net.shadowfacts.phycon.network.packet.RemoteActivationPacket
|
import net.shadowfacts.phycon.network.packet.RemoteActivationPacket
|
||||||
import net.shadowfacts.phycon.util.ActivationMode
|
import net.shadowfacts.phycon.util.ActivationMode
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
|
class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
|
||||||
|
NetworkStackDispatcher<ExtractorBlockEntity.PendingInsertion>,
|
||||||
ActivationController.ActivatableDevice {
|
ActivationController.ActivatableDevice {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -30,17 +35,18 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
|
||||||
private val facing: Direction
|
private val facing: Direction
|
||||||
get() = cachedState[ExtractorBlock.FACING]
|
get() = cachedState[ExtractorBlock.FACING]
|
||||||
|
|
||||||
private var inventory: GroupedItemInv? = null
|
private var inventory: FixedItemInv? = null
|
||||||
private var shouldExtract = false
|
override val pendingInsertions = mutableListOf<PendingInsertion>()
|
||||||
|
override val dispatchStackTimeout = 40L
|
||||||
override val controller = ActivationController(SLEEP_TIME, this)
|
override val controller = ActivationController(SLEEP_TIME, this)
|
||||||
|
|
||||||
fun updateInventory() {
|
fun updateInventory() {
|
||||||
val offsetPos = pos.offset(facing)
|
val offsetPos = pos.offset(facing)
|
||||||
val option = SearchOptions.inDirection(facing)
|
val option = SearchOptions.inDirection(facing)
|
||||||
inventory = ItemAttributes.GROUPED_INV.getFirstOrNull(world, offsetPos, option)
|
inventory = ItemAttributes.FIXED_INV.getFirstOrNull(world, offsetPos, option)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getInventory(): GroupedItemInv? {
|
private fun getInventory(): FixedItemInv? {
|
||||||
if (inventory == null) updateInventory()
|
if (inventory == null) updateInventory()
|
||||||
return inventory
|
return inventory
|
||||||
}
|
}
|
||||||
|
@ -48,18 +54,27 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
|
||||||
override fun handle(packet: Packet) {
|
override fun handle(packet: Packet) {
|
||||||
when (packet) {
|
when (packet) {
|
||||||
is CapacityPacket -> handleCapacity(packet)
|
is CapacityPacket -> handleCapacity(packet)
|
||||||
|
is ItemStackPacket -> handleItemStack(packet)
|
||||||
is RemoteActivationPacket -> controller.handleRemoteActivation(packet)
|
is RemoteActivationPacket -> controller.handleRemoteActivation(packet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleCapacity(packet: CapacityPacket) {
|
override fun doHandleItemStack(packet: ItemStackPacket): ItemStack {
|
||||||
if (shouldExtract && packet.capacity > 0) {
|
// we can't insert things back into the inventory, so just let them spawn
|
||||||
getInventory()?.also { inv ->
|
return packet.stack
|
||||||
shouldExtract = false
|
|
||||||
val extracted = inv.extract(packet.stack, packet.capacity)
|
|
||||||
sendPacket(ItemStackPacket(extracted, ipAddress, packet.stackReceiver.ipAddress))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun createPendingInsertion(stack: ItemStack) = PendingInsertion(stack, counter)
|
||||||
|
|
||||||
|
override fun finishInsertion(insertion: PendingInsertion): ItemStack {
|
||||||
|
val inventory = getInventory() ?: return insertion.stack
|
||||||
|
// if the inventory has changed, the old slot index is meaningless
|
||||||
|
if (inventory !== insertion.inventory) return insertion.stack
|
||||||
|
val extracted = inventory.extractStack(insertion.inventorySlot, ExactItemStackFilter(insertion.stack), ItemStack.EMPTY, insertion.totalCapacity, Simulation.ACTION)
|
||||||
|
if (extracted.isEmpty) return insertion.stack
|
||||||
|
// if we extracted less than expected, make sure super.finishInsertion doesn't send more than we actually have
|
||||||
|
insertion.stack = extracted
|
||||||
|
return super.finishInsertion(insertion)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun tick() {
|
override fun tick() {
|
||||||
|
@ -72,11 +87,17 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
|
||||||
|
|
||||||
override fun activate(): Boolean {
|
override fun activate(): Boolean {
|
||||||
val inventory = getInventory() ?: return false
|
val inventory = getInventory() ?: return false
|
||||||
val stack = inventory.storedStacks.firstOrNull() ?: return false
|
for (slot in 0 until inventory.slotCount) {
|
||||||
shouldExtract = true
|
val slotStack = inventory.getInvStack(slot)
|
||||||
sendPacket(CheckCapacityPacket(stack, ipAddress, IPAddress.BROADCAST))
|
if (slotStack.isEmpty) continue
|
||||||
|
dispatchItemStack(slotStack) { insertion ->
|
||||||
|
insertion.inventory = inventory
|
||||||
|
insertion.inventorySlot = slot
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
override fun toTag(tag: CompoundTag): CompoundTag {
|
override fun toTag(tag: CompoundTag): CompoundTag {
|
||||||
tag.putString("ActivationMode", controller.activationMode.name)
|
tag.putString("ActivationMode", controller.activationMode.name)
|
||||||
|
@ -97,4 +118,9 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
|
||||||
super.fromClientTag(tag)
|
super.fromClientTag(tag)
|
||||||
controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode"))
|
controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PendingInsertion(stack: ItemStack, timestamp: Long): NetworkStackDispatcher.PendingInsertion<PendingInsertion>(stack, timestamp) {
|
||||||
|
lateinit var inventory: FixedItemInv
|
||||||
|
var inventorySlot by Delegates.notNull<Int>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import net.minecraft.entity.ItemEntity
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.util.Tickable
|
import net.minecraft.util.Tickable
|
||||||
import net.minecraft.util.math.Direction
|
import net.minecraft.util.math.Direction
|
||||||
|
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||||
import net.shadowfacts.phycon.api.Interface
|
import net.shadowfacts.phycon.api.Interface
|
||||||
import net.shadowfacts.phycon.api.frame.EthernetFrame
|
import net.shadowfacts.phycon.api.frame.EthernetFrame
|
||||||
import net.shadowfacts.phycon.api.frame.PacketFrame
|
import net.shadowfacts.phycon.api.frame.PacketFrame
|
||||||
|
@ -53,10 +54,10 @@ class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
|
||||||
|
|
||||||
if (frame.destination.type != MACAddress.Type.BROADCAST && macTable.containsKey(frame.destination)) {
|
if (frame.destination.type != MACAddress.Type.BROADCAST && macTable.containsKey(frame.destination)) {
|
||||||
val dir = macTable[frame.destination]!!
|
val dir = macTable[frame.destination]!!
|
||||||
println("$this (${fromItf.side}, ${fromItf.macAddress}) forwarding $frame to side $dir")
|
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}, {}) forwarding {} to side {}", this, fromItf.side, fromItf.macAddress, frame, dir)
|
||||||
interfaceForSide(dir).send(frame)
|
interfaceForSide(dir).send(frame)
|
||||||
} else {
|
} else {
|
||||||
println("$this (${fromItf.side}, ${fromItf.macAddress}) flooding $frame")
|
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}, {}) flooding {}", this, fromItf.side, fromItf.macAddress, frame)
|
||||||
flood(frame, fromItf)
|
flood(frame, fromItf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,8 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
|
||||||
if (insertion != null) {
|
if (insertion != null) {
|
||||||
insertion.results.add(packet.capacity to packet.stackReceiver)
|
insertion.results.add(packet.capacity to packet.stackReceiver)
|
||||||
if (insertion.isFinishable(this)) {
|
if (insertion.isFinishable(this)) {
|
||||||
finishInsertion(insertion)
|
val remaining = finishInsertion(insertion)
|
||||||
|
// todo: do something with remaining
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +44,8 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
|
||||||
|
|
||||||
// todo: also sort results by interface priority
|
// todo: also sort results by interface priority
|
||||||
val sortedResults = insertion.results.sortedBy { it.first }.toMutableList()
|
val sortedResults = insertion.results.sortedBy { it.first }.toMutableList()
|
||||||
val remaining = insertion.stack
|
// 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()) {
|
while (!remaining.isEmpty && sortedResults.isNotEmpty()) {
|
||||||
val (capacity, receivingInterface) = sortedResults.removeFirst()
|
val (capacity, receivingInterface) = sortedResults.removeFirst()
|
||||||
if (capacity <= 0) continue
|
if (capacity <= 0) continue
|
||||||
|
@ -56,7 +58,7 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
|
||||||
}
|
}
|
||||||
|
|
||||||
open class PendingInsertion<Self: PendingInsertion<Self>>(
|
open class PendingInsertion<Self: PendingInsertion<Self>>(
|
||||||
val stack: ItemStack,
|
var stack: ItemStack,
|
||||||
val timestamp: Long
|
val timestamp: Long
|
||||||
) {
|
) {
|
||||||
val results = mutableSetOf<Pair<Int, NetworkStackReceiver>>()
|
val results = mutableSetOf<Pair<Int, NetworkStackReceiver>>()
|
||||||
|
@ -77,5 +79,6 @@ fun <Self, Insertion: NetworkStackDispatcher.PendingInsertion<Insertion>> Self.f
|
||||||
val finishable = pendingInsertions.filter { it.isFinishable(this) }
|
val finishable = pendingInsertions.filter { it.isFinishable(this) }
|
||||||
// finishInsertion removes the object from pendingInsertions
|
// finishInsertion removes the object from pendingInsertions
|
||||||
finishable.forEach(::finishInsertion)
|
finishable.forEach(::finishInsertion)
|
||||||
|
// todo: do something with remaining?
|
||||||
// todo: if a timed-out insertion can't be finished, we should probably retry after some time (exponential backoff?)
|
// todo: if a timed-out insertion can't be finished, we should probably retry after some time (exponential backoff?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import net.minecraft.client.util.math.MatrixStack
|
||||||
import net.shadowfacts.phycon.network.DeviceBlockEntity
|
import net.shadowfacts.phycon.network.DeviceBlockEntity
|
||||||
import net.shadowfacts.phycon.network.component.ActivationController
|
import net.shadowfacts.phycon.network.component.ActivationController
|
||||||
import net.shadowfacts.phycon.networking.C2SConfigureActivationMode
|
import net.shadowfacts.phycon.networking.C2SConfigureActivationMode
|
||||||
|
import net.shadowfacts.phycon.util.ActivationMode
|
||||||
import net.shadowfacts.phycon.util.next
|
import net.shadowfacts.phycon.util.next
|
||||||
import org.lwjgl.glfw.GLFW
|
import org.lwjgl.glfw.GLFW
|
||||||
|
|
||||||
|
@ -28,10 +29,7 @@ class ActivatableDeviceConsoleScreen<T>(
|
||||||
val minX = (width - backgroundWidth) / 2
|
val minX = (width - backgroundWidth) / 2
|
||||||
val minY = (height - backgroundHeight) / 2
|
val minY = (height - backgroundHeight) / 2
|
||||||
|
|
||||||
lateinit var mode: ButtonWidget
|
val mode = EnumButton(device.controller::activationMode, minX + 5, minY + 25, 55, 20) {
|
||||||
mode = ButtonWidget(minX + 5, minY + 25, 55, 20, device.controller.activationMode.friendlyName) {
|
|
||||||
device.controller.activationMode = device.controller.activationMode.next
|
|
||||||
mode.message = device.controller.activationMode.friendlyName
|
|
||||||
client!!.player!!.networkHandler.sendPacket(C2SConfigureActivationMode(device))
|
client!!.player!!.networkHandler.sendPacket(C2SConfigureActivationMode(device))
|
||||||
}
|
}
|
||||||
addButton(mode)
|
addButton(mode)
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package net.shadowfacts.phycon.screen
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.widget.AbstractPressableButtonWidget
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import net.shadowfacts.phycon.util.FriendlyNameable
|
||||||
|
import net.shadowfacts.phycon.util.RotatableEnum
|
||||||
|
import net.shadowfacts.phycon.util.next
|
||||||
|
import net.shadowfacts.phycon.util.prev
|
||||||
|
import kotlin.reflect.KMutableProperty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author shadowfacts
|
||||||
|
*/
|
||||||
|
class EnumButton<E>(
|
||||||
|
val prop: KMutableProperty<E>,
|
||||||
|
x: Int,
|
||||||
|
y: Int,
|
||||||
|
width: Int,
|
||||||
|
height: Int,
|
||||||
|
val onChange: () -> Unit
|
||||||
|
): AbstractPressableButtonWidget(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
prop.getter.call().friendlyName
|
||||||
|
) where E: Enum<E>, E: RotatableEnum, E: FriendlyNameable {
|
||||||
|
|
||||||
|
private var currentButton: Int? = null
|
||||||
|
|
||||||
|
override fun mouseClicked(d: Double, e: Double, button: Int): Boolean {
|
||||||
|
currentButton = button
|
||||||
|
val res = super.mouseClicked(d, e, button)
|
||||||
|
currentButton = null
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isValidClickButton(i: Int): Boolean {
|
||||||
|
return i == 0 || i == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPress() {
|
||||||
|
val newVal = if ((currentButton ?: 0) == 0) prop.getter.call().next else prop.getter.call().prev
|
||||||
|
prop.setter.call(newVal)
|
||||||
|
message = newVal.friendlyName
|
||||||
|
onChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -33,10 +33,7 @@ class RedstoneControllerConsoleScreen(
|
||||||
val minX = (width - backgroundWidth) / 2
|
val minX = (width - backgroundWidth) / 2
|
||||||
val minY = (height - backgroundHeight) / 2
|
val minY = (height - backgroundHeight) / 2
|
||||||
|
|
||||||
lateinit var mode: ButtonWidget
|
val mode = EnumButton(device::redstoneMode, minX + 5, minY + 25, 75, 20) {
|
||||||
mode = ButtonWidget(minX + 5, minY + 25, 75, 20, device.redstoneMode.friendlyName) {
|
|
||||||
device.redstoneMode = device.redstoneMode.next
|
|
||||||
mode.message = device.redstoneMode.friendlyName
|
|
||||||
client!!.player!!.networkHandler.sendPacket(C2SConfigureRedstoneController(device))
|
client!!.player!!.networkHandler.sendPacket(C2SConfigureRedstoneController(device))
|
||||||
}
|
}
|
||||||
addButton(mode)
|
addButton(mode)
|
||||||
|
|
|
@ -6,10 +6,10 @@ import net.minecraft.text.TranslatableText
|
||||||
/**
|
/**
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
enum class ActivationMode: RotatableEnum {
|
enum class ActivationMode: RotatableEnum, FriendlyNameable {
|
||||||
AUTOMATIC,
|
AUTOMATIC,
|
||||||
MANAGED;
|
MANAGED;
|
||||||
|
|
||||||
val friendlyName: Text
|
override val friendlyName: Text
|
||||||
get() = TranslatableText("gui.phycon.activation_mode.${name.toLowerCase()}")
|
get() = TranslatableText("gui.phycon.activation_mode.${name.toLowerCase()}")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package net.shadowfacts.phycon.util
|
||||||
|
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author shadowfacts
|
||||||
|
*/
|
||||||
|
interface FriendlyNameable {
|
||||||
|
val friendlyName: Text
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import net.minecraft.text.TranslatableText
|
||||||
/**
|
/**
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
enum class RedstoneMode: RotatableEnum {
|
enum class RedstoneMode: RotatableEnum, FriendlyNameable {
|
||||||
HIGH,
|
HIGH,
|
||||||
LOW,
|
LOW,
|
||||||
TOGGLE,
|
TOGGLE,
|
||||||
|
@ -25,6 +25,6 @@ enum class RedstoneMode: RotatableEnum {
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
val friendlyName: Text
|
override val friendlyName: Text
|
||||||
get() = TranslatableText("gui.phycon.redstone_mode.${name.toLowerCase()}")
|
get() = TranslatableText("gui.phycon.redstone_mode.${name.toLowerCase()}")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue