Compare commits

..

No commits in common. "4c5b7daf9e971bcea1c04d7f62d57af059a540bb" and "d621e9e089013a440325ef91dec1038e9d36ce87" have entirely different histories.

13 changed files with 43 additions and 140 deletions

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Loggers>
<Logger name="PhyNet">
<AppenderRef ref="SysOut" level="debug"/>
</Logger>
</Loggers>
</Configuration>

View File

@ -1,5 +1,5 @@
plugins {
id "fabric-loom" version "0.6.49"
id "fabric-loom" version "0.5-SNAPSHOT"
id "maven-publish"
id "org.jetbrains.kotlin.jvm" version "1.4.30"
}
@ -12,7 +12,6 @@ version = project.mod_version
group = project.maven_group
minecraft {
log4jConfigs.from "PhyConDebugLogging.xml"
}
repositories {

View File

@ -7,7 +7,6 @@ import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.init.PhyItems
import net.shadowfacts.phycon.init.PhyScreens
import net.shadowfacts.phycon.networking.*
import org.apache.logging.log4j.LogManager
/**
* @author shadowfacts
@ -16,8 +15,6 @@ object PhysicalConnectivity: ModInitializer {
val MODID = "phycon"
val NETWORK_LOGGER = LogManager.getLogger("PhyNet")
override fun onInitialize() {
PhyBlocks.init()
PhyBlockEntities.init()

View File

@ -6,7 +6,6 @@ import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.nbt.CompoundTag
import net.minecraft.util.Tickable
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.PacketSink
import net.shadowfacts.phycon.api.PacketSource
import net.shadowfacts.phycon.api.Interface
@ -53,7 +52,6 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
abstract override fun handle(packet: Packet)
private fun doHandlePacket(packet: Packet) {
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}) received packet: {}", this, ipAddress, packet)
when (packet) {
is DeviceRemovedPacket -> {
arpTable.remove(packet.source)
@ -67,12 +65,13 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
}
override fun receive(frame: EthernetFrame) {
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}, {}) received frame from {}: {}", this, ipAddress, macAddress, frame.source, frame)
println("$this ($ipAddress, ${macAddress}) received frame from ${frame.source}: $frame")
when (frame) {
is ARPQueryFrame -> handleARPQuery(frame)
is ARPResponseFrame -> handleARPResponse(frame)
is PacketFrame -> {
if (frame.packet.destination.isBroadcast || frame.packet.destination == ipAddress) {
println("$this ($ipAddress) received packet: ${frame.packet}")
doHandlePacket(frame.packet)
}
}
@ -80,17 +79,17 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
}
private fun handleARPQuery(frame: ARPQueryFrame) {
PhysicalConnectivity.NETWORK_LOGGER.debug("{}, ({}), received ARP query for {}", this, ipAddress, frame.queryIP)
println("$this ($ipAddress) received ARP query for ${frame.queryIP}")
arpTable[frame.sourceIP] = frame.source
if (frame.queryIP == ipAddress) {
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}) sending ARP response to {} with {}", this, ipAddress, frame.sourceIP, macAddress)
println("$this ($ipAddress) sending ARP response to ${frame.source} with $macAddress")
send(ARPResponseFrame(ipAddress, macAddress, frame.source))
}
}
private fun handleARPResponse(frame: ARPResponseFrame) {
arpTable[frame.query] = frame.source
PhysicalConnectivity.NETWORK_LOGGER.debug("{}, ({}) received ARP response for {} with {}", this, ipAddress, frame.query, frame.source)
println("$this ($ipAddress) received ARP response for ${frame.query} with ${frame.source}")
val toRemove = packetQueue.filter { (packet, _) ->
if (packet.destination == frame.query) {
@ -111,7 +110,7 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>): BlockEntity(type),
} else {
packetQueue.add(PendingPacket(packet, counter))
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}) sending ARP query for {}", this, ipAddress, packet.destination)
println("$this ($ipAddress) sending ARP query for ${packet.destination}")
send(ARPQueryFrame(packet.destination, ipAddress, macAddress))
}
}

View File

@ -1,31 +1,26 @@
package net.shadowfacts.phycon.network.block.extractor
import alexiil.mc.lib.attributes.SearchOptions
import alexiil.mc.lib.attributes.Simulation
import alexiil.mc.lib.attributes.item.FixedItemInv
import alexiil.mc.lib.attributes.item.GroupedItemInv
import alexiil.mc.lib.attributes.item.ItemAttributes
import alexiil.mc.lib.attributes.item.filter.ExactItemStackFilter
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.api.util.IPAddress
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.CheckCapacityPacket
import net.shadowfacts.phycon.network.packet.ItemStackPacket
import net.shadowfacts.phycon.network.packet.RemoteActivationPacket
import net.shadowfacts.phycon.util.ActivationMode
import kotlin.properties.Delegates
/**
* @author shadowfacts
*/
class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
NetworkStackDispatcher<ExtractorBlockEntity.PendingInsertion>,
ActivationController.ActivatableDevice {
companion object {
@ -35,18 +30,17 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
private val facing: Direction
get() = cachedState[ExtractorBlock.FACING]
private var inventory: FixedItemInv? = null
override val pendingInsertions = mutableListOf<PendingInsertion>()
override val dispatchStackTimeout = 40L
private var inventory: GroupedItemInv? = null
private var shouldExtract = false
override val controller = ActivationController(SLEEP_TIME, this)
fun updateInventory() {
val offsetPos = pos.offset(facing)
val option = SearchOptions.inDirection(facing)
inventory = ItemAttributes.FIXED_INV.getFirstOrNull(world, offsetPos, option)
inventory = ItemAttributes.GROUPED_INV.getFirstOrNull(world, offsetPos, option)
}
private fun getInventory(): FixedItemInv? {
private fun getInventory(): GroupedItemInv? {
if (inventory == null) updateInventory()
return inventory
}
@ -54,27 +48,18 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
override fun handle(packet: Packet) {
when (packet) {
is CapacityPacket -> handleCapacity(packet)
is ItemStackPacket -> handleItemStack(packet)
is RemoteActivationPacket -> controller.handleRemoteActivation(packet)
}
}
override fun doHandleItemStack(packet: ItemStackPacket): ItemStack {
// we can't insert things back into the inventory, so just let them spawn
return packet.stack
private fun handleCapacity(packet: CapacityPacket) {
if (shouldExtract && packet.capacity > 0) {
getInventory()?.also { inv ->
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() {
@ -87,17 +72,11 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
override fun activate(): Boolean {
val inventory = getInventory() ?: return false
for (slot in 0 until inventory.slotCount) {
val slotStack = inventory.getInvStack(slot)
if (slotStack.isEmpty) continue
dispatchItemStack(slotStack) { insertion ->
insertion.inventory = inventory
insertion.inventorySlot = slot
}
val stack = inventory.storedStacks.firstOrNull() ?: return false
shouldExtract = true
sendPacket(CheckCapacityPacket(stack, ipAddress, IPAddress.BROADCAST))
return true
}
return false
}
override fun toTag(tag: CompoundTag): CompoundTag {
tag.putString("ActivationMode", controller.activationMode.name)
@ -118,9 +97,4 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
super.fromClientTag(tag)
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>()
}
}

View File

@ -7,7 +7,6 @@ import net.minecraft.entity.ItemEntity
import net.minecraft.nbt.CompoundTag
import net.minecraft.util.Tickable
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.Interface
import net.shadowfacts.phycon.api.frame.EthernetFrame
import net.shadowfacts.phycon.api.frame.PacketFrame
@ -54,10 +53,10 @@ class SwitchBlockEntity: BlockEntity(PhyBlockEntities.SWITCH),
if (frame.destination.type != MACAddress.Type.BROADCAST && macTable.containsKey(frame.destination)) {
val dir = macTable[frame.destination]!!
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}, {}) forwarding {} to side {}", this, fromItf.side, fromItf.macAddress, frame, dir)
println("$this (${fromItf.side}, ${fromItf.macAddress}) forwarding $frame to side $dir")
interfaceForSide(dir).send(frame)
} else {
PhysicalConnectivity.NETWORK_LOGGER.debug("{} ({}, {}) flooding {}", this, fromItf.side, fromItf.macAddress, frame)
println("$this (${fromItf.side}, ${fromItf.macAddress}) flooding $frame")
flood(frame, fromItf)
}
}

View File

@ -33,8 +33,7 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
if (insertion != null) {
insertion.results.add(packet.capacity to packet.stackReceiver)
if (insertion.isFinishable(this)) {
val remaining = finishInsertion(insertion)
// todo: do something with remaining
finishInsertion(insertion)
}
}
}
@ -44,8 +43,7 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
// todo: also sort results by interface priority
val sortedResults = insertion.results.sortedBy { it.first }.toMutableList()
// copy the insertion stack so subclasses that override this method can still see the originally dispatched stack after the super call
val remaining = insertion.stack.copy()
val remaining = insertion.stack
while (!remaining.isEmpty && sortedResults.isNotEmpty()) {
val (capacity, receivingInterface) = sortedResults.removeFirst()
if (capacity <= 0) continue
@ -58,7 +56,7 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
}
open class PendingInsertion<Self: PendingInsertion<Self>>(
var stack: ItemStack,
val stack: ItemStack,
val timestamp: Long
) {
val results = mutableSetOf<Pair<Int, NetworkStackReceiver>>()
@ -79,6 +77,5 @@ fun <Self, Insertion: NetworkStackDispatcher.PendingInsertion<Insertion>> Self.f
val finishable = pendingInsertions.filter { it.isFinishable(this) }
// finishInsertion removes the object from pendingInsertions
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?)
}

View File

@ -7,7 +7,6 @@ import net.minecraft.client.util.math.MatrixStack
import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.component.ActivationController
import net.shadowfacts.phycon.networking.C2SConfigureActivationMode
import net.shadowfacts.phycon.util.ActivationMode
import net.shadowfacts.phycon.util.next
import org.lwjgl.glfw.GLFW
@ -29,7 +28,10 @@ class ActivatableDeviceConsoleScreen<T>(
val minX = (width - backgroundWidth) / 2
val minY = (height - backgroundHeight) / 2
val mode = EnumButton(device.controller::activationMode, minX + 5, minY + 25, 55, 20) {
lateinit var mode: ButtonWidget
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))
}
addButton(mode)

View File

@ -1,49 +0,0 @@
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()
}
}

View File

@ -33,7 +33,10 @@ class RedstoneControllerConsoleScreen(
val minX = (width - backgroundWidth) / 2
val minY = (height - backgroundHeight) / 2
val mode = EnumButton(device::redstoneMode, minX + 5, minY + 25, 75, 20) {
lateinit var mode: ButtonWidget
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))
}
addButton(mode)

View File

@ -6,10 +6,10 @@ import net.minecraft.text.TranslatableText
/**
* @author shadowfacts
*/
enum class ActivationMode: RotatableEnum, FriendlyNameable {
enum class ActivationMode: RotatableEnum {
AUTOMATIC,
MANAGED;
override val friendlyName: Text
val friendlyName: Text
get() = TranslatableText("gui.phycon.activation_mode.${name.toLowerCase()}")
}

View File

@ -1,10 +0,0 @@
package net.shadowfacts.phycon.util
import net.minecraft.text.Text
/**
* @author shadowfacts
*/
interface FriendlyNameable {
val friendlyName: Text
}

View File

@ -6,7 +6,7 @@ import net.minecraft.text.TranslatableText
/**
* @author shadowfacts
*/
enum class RedstoneMode: RotatableEnum, FriendlyNameable {
enum class RedstoneMode: RotatableEnum {
HIGH,
LOW,
TOGGLE,
@ -25,6 +25,6 @@ enum class RedstoneMode: RotatableEnum, FriendlyNameable {
else -> false
}
override val friendlyName: Text
val friendlyName: Text
get() = TranslatableText("gui.phycon.redstone_mode.${name.toLowerCase()}")
}