mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-09 18:14:06 +09:00
token ring stuff wip
This commit is contained in:
@@ -0,0 +1,166 @@
|
|||||||
|
package net.torvald.terrarum.modulecomputers.gameactors
|
||||||
|
|
||||||
|
import com.badlogic.gdx.utils.Queue
|
||||||
|
import net.torvald.random.HQRNG
|
||||||
|
import net.torvald.terrarum.INGAME
|
||||||
|
import net.torvald.terrarum.langpack.Lang
|
||||||
|
import net.torvald.terrarum.modulebasegame.gameactors.BlockBox
|
||||||
|
import net.torvald.terrarum.modulebasegame.gameactors.Electric
|
||||||
|
import net.torvald.terrarum.modulebasegame.gameworld.IngameNetPacket
|
||||||
|
import net.torvald.terrarum.modulebasegame.gameworld.PacketRunner
|
||||||
|
import net.torvald.terrarum.serialise.Common
|
||||||
|
import org.dyn4j.geometry.Vector2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by minjaesong on 2025-03-01.
|
||||||
|
*/
|
||||||
|
class FixtureRingBusExerciser : Electric {
|
||||||
|
|
||||||
|
@Transient private val rng = HQRNG()
|
||||||
|
|
||||||
|
private val mac = rng.nextInt()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val SIGNAL_TOO_WEAK_THRESHOLD = 1.0 / 16.0
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() : super(
|
||||||
|
BlockBox(BlockBox.NO_COLLISION, 2, 2),
|
||||||
|
nameFun = { Lang["ITEM_JUKEBOX"] },
|
||||||
|
mainUI = UIRingBusExerciser()
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun setEmitterAndSink() {
|
||||||
|
clearStatus()
|
||||||
|
setWireEmitterAt(0, 0, "10base2")
|
||||||
|
setWireSinkAt(1, 0, "10base2")
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
setEmitterAndSink()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun reload() {
|
||||||
|
super.reload()
|
||||||
|
setEmitterAndSink()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val msgQueue = Queue<Pair<Int, String>>()
|
||||||
|
|
||||||
|
|
||||||
|
private var statusAbort = false // will "eat away" any receiving packets unless the packet is a ballot packet
|
||||||
|
private var activeMonitorStatus = 0 // 0: unknown, 1: known and not me, 2: known and it's me
|
||||||
|
|
||||||
|
override fun updateSignal() {
|
||||||
|
// monitor the input port
|
||||||
|
val inn = getWireStateAt(1, 0, "10base2")
|
||||||
|
|
||||||
|
// if a signal is there
|
||||||
|
if (inn.x >= SIGNAL_TOO_WEAK_THRESHOLD) {
|
||||||
|
val packetNumber = (inn.y + 0.5).toInt()
|
||||||
|
|
||||||
|
if (packetNumber != 0) { // packet number must be non-zero
|
||||||
|
// if not in abort state, process the incoming packets
|
||||||
|
if (!statusAbort) {
|
||||||
|
// fetch packet from the world
|
||||||
|
try {
|
||||||
|
val packet = getPacketByNumber(packetNumber)
|
||||||
|
|
||||||
|
if (msgQueue.notEmpty() || packet.shouldIintercept(mac)) {
|
||||||
|
val newPacket = doSomethingWithPacket(packet) ?: packetNumber
|
||||||
|
setWireEmissionAt(0, 0, Vector2(1.0, newPacket.toDouble()))
|
||||||
|
|
||||||
|
// mark the old packet as to be destroyed
|
||||||
|
if (newPacket != packetNumber) {
|
||||||
|
packet.discardPacket()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setWireEmissionAt(0, 0, Vector2(1.0, packetNumber.toDouble()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// packet lost due to poor savegame migration or something: send out ABORT signal
|
||||||
|
catch (e: NullPointerException) {
|
||||||
|
val abortPacket = IngameNetPacket.makeAbort(mac)
|
||||||
|
emitNewPacket(abortPacket)
|
||||||
|
statusAbort = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// else, still watch for the new valid token
|
||||||
|
else {
|
||||||
|
// fetch packet from the world
|
||||||
|
try {
|
||||||
|
val packet = getPacketByNumber(packetNumber)
|
||||||
|
|
||||||
|
// not an Active Monitor
|
||||||
|
if (activeMonitorStatus < 2) {
|
||||||
|
if (packet.getFrameType() == "token") {
|
||||||
|
// unlock myself and pass the token
|
||||||
|
statusAbort = false
|
||||||
|
setWireEmissionAt(0, 0, Vector2(1.0, packetNumber.toDouble()))
|
||||||
|
}
|
||||||
|
else if (packet.getFrameType() == "abort") {
|
||||||
|
// unlock myself (just in case) and pass the token
|
||||||
|
statusAbort = true
|
||||||
|
setWireEmissionAt(0, 0, Vector2(1.0, packetNumber.toDouble()))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// discard anything that is not a token
|
||||||
|
setWireEmissionAt(0, 0, Vector2())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// am Active Monitor
|
||||||
|
else {
|
||||||
|
if (packet.getFrameType() == "abort") {
|
||||||
|
// send out a new token
|
||||||
|
emitNewPacket(IngameNetPacket.makeToken(mac))
|
||||||
|
statusAbort = false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// discard anything that is not a token
|
||||||
|
setWireEmissionAt(0, 0, Vector2())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// packet lost due to poor savegame migration or something: discard token
|
||||||
|
catch (e: NullPointerException) {
|
||||||
|
setWireEmissionAt(0, 0, Vector2())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if a signal is not there
|
||||||
|
else {
|
||||||
|
setWireEmissionAt(0, 0, Vector2())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun doSomethingWithPacket(incomingPacket: IngameNetPacket): Int? {
|
||||||
|
return when (incomingPacket.getFrameType()) {
|
||||||
|
"token" -> doSomethingWithToken(incomingPacket)
|
||||||
|
"data" -> doSomethingWithData(incomingPacket)
|
||||||
|
"ack" -> doSomethingWithAck(incomingPacket)
|
||||||
|
"ballot" -> doSomethingWithBallot(incomingPacket)
|
||||||
|
"abort" -> 0
|
||||||
|
else -> null /* returns the packet untouched */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPacketByNumber(number: Int) = (INGAME.world.extraFields["tokenring"] as PacketRunner)[number]
|
||||||
|
|
||||||
|
private fun emitNewPacket(packet: IngameNetPacket): Int {
|
||||||
|
return (INGAME.world.extraFields["tokenring"] as PacketRunner).addPacket(packet).also {
|
||||||
|
setWireEmissionAt(0, 0, Vector2(1.0, it.toDouble()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun doSomethingWithToken(incomingPacket: IngameNetPacket): Int? {
|
||||||
|
if (msgQueue.isEmpty) return null
|
||||||
|
|
||||||
|
val (recipient, msgStr) = msgQueue.removeFirst()
|
||||||
|
val msgByte = msgStr.toByteArray(Common.CHARSET)
|
||||||
|
|
||||||
|
val newPacket = IngameNetPacket.makeData(mac, recipient, msgByte)
|
||||||
|
return emitNewPacket(newPacket)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -98,6 +98,7 @@ open class Electric : FixtureBase {
|
|||||||
/** Triggered when 'digital_bit' is held low. This function WILL NOT be triggered simultaneously with the falling edge. Level detection only considers the real component (labeled as 'x') of the vector */
|
/** Triggered when 'digital_bit' is held low. This function WILL NOT be triggered simultaneously with the falling edge. Level detection only considers the real component (labeled as 'x') of the vector */
|
||||||
//open fun onSignalLow(readFrom: BlockBoxIndex) {}
|
//open fun onSignalLow(readFrom: BlockBoxIndex) {}
|
||||||
|
|
||||||
|
// called right after `onRisingEdge` and `onFallingEdge` in the `updateImpl`
|
||||||
open fun updateSignal() {}
|
open fun updateSignal() {}
|
||||||
|
|
||||||
fun getWireStateAt(offsetX: Int, offsetY: Int, sinkType: WireEmissionType): Vector2 {
|
fun getWireStateAt(offsetX: Int, offsetY: Int, sinkType: WireEmissionType): Vector2 {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import java.util.zip.CRC32
|
|||||||
* ## The Header
|
* ## The Header
|
||||||
*
|
*
|
||||||
* - (Byte1) Frame Type
|
* - (Byte1) Frame Type
|
||||||
* - 00 : invalid
|
* - 00 : invalid (mark the packet as "to be destroyed" by game)
|
||||||
* - FF : token (an "empty" packet for a Token Ring)
|
* - FF : token (an "empty" packet for a Token Ring)
|
||||||
* - AA : data
|
* - AA : data
|
||||||
* - EE : abort
|
* - EE : abort
|
||||||
@@ -107,6 +107,10 @@ data class IngameNetPacket(val byteArray: ByteArray) {
|
|||||||
return byteArray.toBigInt32(6)
|
return byteArray.toBigInt32(6)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun discardPacket() {
|
||||||
|
byteArray[0] = 0
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun ByteArray.makeHeader(frameType: Int, mac: Int): ByteArray {
|
private fun ByteArray.makeHeader(frameType: Int, mac: Int): ByteArray {
|
||||||
this[0] = frameType.toByte()
|
this[0] = frameType.toByte()
|
||||||
@@ -114,26 +118,26 @@ data class IngameNetPacket(val byteArray: ByteArray) {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun makeToken(mac: Int) = ByteArray(5).makeHeader(0xff, mac)
|
fun makeToken(mac: Int) = IngameNetPacket(ByteArray(5).makeHeader(0xff, mac))
|
||||||
|
|
||||||
fun makeAbort(mac: Int) = ByteArray(5).makeHeader(0xee, mac)
|
fun makeAbort(mac: Int) = IngameNetPacket(ByteArray(5).makeHeader(0xee, mac))
|
||||||
|
|
||||||
fun makeBallot(mac: Int) = ByteArray(9).makeHeader(0x99, mac)
|
fun makeBallot(mac: Int) = IngameNetPacket(ByteArray(9).makeHeader(0x99, mac))
|
||||||
|
|
||||||
fun makeData(sender: Int, recipient: Int, data: ByteArray) = ByteArray(18 + data.size).also {
|
fun makeData(sender: Int, recipient: Int, data: ByteArray) = IngameNetPacket(ByteArray(18 + data.size).also {
|
||||||
it.makeHeader(0xaa, sender)
|
it.makeHeader(0xaa, sender)
|
||||||
it.writeBigInt32(recipient, 6)
|
it.writeBigInt32(recipient, 6)
|
||||||
it.writeBigInt32(data.size, 10)
|
it.writeBigInt32(data.size, 10)
|
||||||
System.arraycopy(data, 0, it, 14, data.size)
|
System.arraycopy(data, 0, it, 14, data.size)
|
||||||
val crc = CRC32().also { it.update(data) }.value.toInt()
|
val crc = CRC32().also { it.update(data) }.value.toInt()
|
||||||
it.writeBigInt32(crc, 14 + data.size)
|
it.writeBigInt32(crc, 14 + data.size)
|
||||||
}
|
})
|
||||||
|
|
||||||
fun makeAck(sender: Int, recipient: Int, status: Int = 0) = ByteArray(12).also {
|
fun makeAck(sender: Int, recipient: Int, status: Int = 0) = IngameNetPacket(ByteArray(12).also {
|
||||||
it.makeHeader(0x55, sender)
|
it.makeHeader(0x55, sender)
|
||||||
it.writeBigInt32(recipient, 6)
|
it.writeBigInt32(recipient, 6)
|
||||||
it.writeBigInt16(status, 10)
|
it.writeBigInt16(status, 10)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package net.torvald.terrarum.modulebasegame.gameworld
|
package net.torvald.terrarum.modulebasegame.gameworld
|
||||||
|
|
||||||
|
import net.torvald.random.HQRNG
|
||||||
import net.torvald.terrarum.gameworld.TerrarumSavegameExtrafieldSerialisable
|
import net.torvald.terrarum.gameworld.TerrarumSavegameExtrafieldSerialisable
|
||||||
import java.util.TreeMap
|
import java.util.TreeMap
|
||||||
|
|
||||||
@@ -10,6 +11,8 @@ import java.util.TreeMap
|
|||||||
*/
|
*/
|
||||||
class PacketRunner : TerrarumSavegameExtrafieldSerialisable {
|
class PacketRunner : TerrarumSavegameExtrafieldSerialisable {
|
||||||
|
|
||||||
|
@Transient private val rng = HQRNG()
|
||||||
|
|
||||||
private val ledger = TreeMap<Int, IngameNetPacket>()
|
private val ledger = TreeMap<Int, IngameNetPacket>()
|
||||||
|
|
||||||
operator fun set(id: Int, packet: IngameNetPacket) {
|
operator fun set(id: Int, packet: IngameNetPacket) {
|
||||||
@@ -18,5 +21,15 @@ class PacketRunner : TerrarumSavegameExtrafieldSerialisable {
|
|||||||
|
|
||||||
operator fun get(id: Int) = ledger[id]!!
|
operator fun get(id: Int) = ledger[id]!!
|
||||||
|
|
||||||
|
fun addPacket(packet: IngameNetPacket): Int {
|
||||||
|
var i = rng.nextInt()
|
||||||
|
while (ledger.containsKey(i)) {
|
||||||
|
i = rng.nextInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
ledger[i] = packet
|
||||||
|
|
||||||
|
return i
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user