Refactor event bus

This commit is contained in:
LeshaInc 2023-03-26 11:16:47 +00:00
parent 5f257fc602
commit f07c1201ed
8 changed files with 70 additions and 63 deletions

@ -1 +1 @@
Subproject commit a0eea2abcae926908a232fee827ebbd28782a326
Subproject commit e7b477cf4d369d91132275237e72c80ad5440e40

View File

@ -2,8 +2,6 @@ package ocelot.desktop
import buildinfo.BuildInfo
import li.flor.nativejfilechooser.NativeJFileChooser
import ocelot.desktop.audio._
import ocelot.desktop.node.nodes.{IronNoteBlockNode, NoteBlockNode}
import ocelot.desktop.ui.UiHandler
import ocelot.desktop.ui.swing.SplashScreen
import ocelot.desktop.ui.widget._
@ -15,8 +13,6 @@ import ocelot.desktop.util._
import org.apache.commons.io.FileUtils
import org.apache.logging.log4j.LogManager
import totoro.ocelot.brain.Ocelot
import totoro.ocelot.brain.event.FileSystemActivityType.Floppy
import totoro.ocelot.brain.event._
import totoro.ocelot.brain.nbt.ExtendedNBT.{extendNBTTagCompound, extendNBTTagList}
import totoro.ocelot.brain.nbt.{CompressedStreamTools, NBT, NBTTagCompound, NBTTagString}
import totoro.ocelot.brain.user.User
@ -30,7 +26,7 @@ import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.io.Source
import scala.jdk.CollectionConverters._
import scala.util.{Failure, Random, Success, Try}
import scala.util.{Failure, Success, Try}
object OcelotDesktop extends Logging {
private val splashScreen = new SplashScreen()
@ -49,8 +45,6 @@ object OcelotDesktop extends Logging {
private val tickLock: Lock = new ReentrantLock()
private val random: Random = new Random()
def withTickLockAcquired(f: => Unit): Unit = withLockAcquired(tickLock, f)
private def mainInner(args: mutable.HashMap[Argument, Option[String]]): Unit = {
@ -80,8 +74,6 @@ object OcelotDesktop extends Logging {
// loading resources _after_ the UiHandler was initialized, because the native libraries are not available before
ResourceManager.initResources()
setupEventHandlers()
splashScreen.setStatus("Loading workspace...", 0.90f)
val cmdLineWorkspaceArgument = args.get(CommandLine.WorkspacePath).flatten
if (loadRecentWorkspace || cmdLineWorkspaceArgument.isDefined) {
@ -368,38 +360,4 @@ object OcelotDesktop extends Logging {
private def createWorkspace(): Unit = {
workspace = new Workspace(tmpPath)
}
private def setupEventHandlers(): Unit = {
EventBus.subscribe[MachineCrashEvent] { event =>
logger.info(s"[EVENT] Machine crash! (address = ${event.address}, ${event.message})")
}
if (!Audio.isDisabled) {
EventBus.subscribe[BeepEvent] { event =>
BeepGenerator.newBeep(".", event.frequency, event.duration).play()
}
EventBus.subscribe[BeepPatternEvent] { event =>
BeepGenerator.newBeep(event.pattern, 1000, 200).play()
}
EventBus.subscribe[NoteBlockTriggerEvent] { event =>
SoundSource.fromBuffer(SoundBuffers.NoteBlock(event.instrument), SoundCategory.Beep,
pitch = NoteBlockNode.Pitches(event.pitch), volume = event.volume.toFloat.min(1f).max(0f)).play()
val node = UiHandler.root.workspaceView.nodes.find(_.entity.node.address == event.address).get
node match {
case nb: NoteBlockNode => nb.addParticle(event.pitch)
case nb: IronNoteBlockNode => nb.addParticle(event.pitch)
case _ =>
}
}
val soundFloppyAccess = SoundBuffers.MachineFloppyAccess.map(buffer => SoundSource.fromBuffer(buffer, SoundCategory.Environment))
val soundHDDAccess = SoundBuffers.MachineHDDAccess.map(buffer => SoundSource.fromBuffer(buffer, SoundCategory.Environment))
EventBus.subscribe[FileSystemActivityEvent] { event =>
val sound = if (event.activityType == Floppy) soundFloppyAccess else soundHDDAccess
sound(random.nextInt(sound.length)).play()
}
}
}
}

View File

@ -132,7 +132,7 @@ abstract class Node(val entity: Entity with Environment) extends Widget with Dra
menu.addEntry(new ContextMenuEntry("Delete", () => {
dispose()
workspaceView.nodes -= this
workspaceView.nodes = workspaceView.nodes.filter(_ != this)
}))
}
@ -155,6 +155,8 @@ abstract class Node(val entity: Entity with Environment) extends Widget with Dra
def getNodeByPort(port: NodePort): network.Node = entity.node
def shouldReceiveEventsFor(address: String): Boolean = address == entity.node.address
def connections: Iterator[(NodePort, Node, NodePort)] = _connections.iterator
def connect(portA: NodePort, node: Node, portB: NodePort): Unit = {

View File

@ -1,23 +1,26 @@
package ocelot.desktop.node.nodes
import ocelot.desktop.audio.{SoundBuffers, SoundCategory, SoundSource}
import ocelot.desktop.audio._
import ocelot.desktop.color.Color
import ocelot.desktop.graphics.Graphics
import ocelot.desktop.node.Node
import ocelot.desktop.ui.event.sources.KeyEvents
import ocelot.desktop.ui.event.{ClickEvent, MouseEvent}
import ocelot.desktop.ui.event.{BrainEvent, ClickEvent, MouseEvent}
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuSubmenu}
import ocelot.desktop.ui.widget.slot._
import ocelot.desktop.util.TierColor
import ocelot.desktop.util.{Logging, TierColor}
import org.lwjgl.input.Keyboard
import totoro.ocelot.brain.entity.traits.{Entity, Floppy, GenericCPU, Inventory}
import totoro.ocelot.brain.entity.traits.{Entity, Environment, Floppy, GenericCPU, Inventory}
import totoro.ocelot.brain.entity.{CPU, Case, EEPROM, GraphicsCard, HDDManaged, HDDUnmanaged, Memory}
import totoro.ocelot.brain.event.FileSystemActivityType.Floppy
import totoro.ocelot.brain.event.{BeepEvent, BeepPatternEvent, FileSystemActivityEvent, MachineCrashEvent}
import totoro.ocelot.brain.loot.Loot
import totoro.ocelot.brain.util.Tier
import scala.reflect.ClassTag
import scala.util.Random
class ComputerNode(val computer: Case) extends Node(computer) {
class ComputerNode(val computer: Case) extends Node(computer) with Logging {
var eepromSlot: EEPROMSlot = _
var cpuSlot: CPUSlot = _
var memorySlots: Array[MemorySlot] = _
@ -30,6 +33,26 @@ class ComputerNode(val computer: Case) extends Node(computer) {
setupSlots()
refitSlots()
eventHandlers += {
case BrainEvent(event: MachineCrashEvent) =>
logger.info(s"[EVENT] Machine crash! (address = ${event.address}, ${event.message})")
case BrainEvent(event: BeepEvent) if !Audio.isDisabled =>
BeepGenerator.newBeep(".", event.frequency, event.duration).play()
case BrainEvent(event: BeepPatternEvent) if !Audio.isDisabled =>
BeepGenerator.newBeep(event.pattern, 1000, 200).play()
case BrainEvent(event: FileSystemActivityEvent) if !Audio.isDisabled =>
val soundFloppyAccess = SoundBuffers.MachineFloppyAccess.map(buffer => SoundSource.fromBuffer(buffer, SoundCategory.Environment))
val soundHDDAccess = SoundBuffers.MachineHDDAccess.map(buffer => SoundSource.fromBuffer(buffer, SoundCategory.Environment))
val sound = if (event.activityType == Floppy) soundFloppyAccess else soundHDDAccess
sound(Random.between(0, sound.length)).play()
}
override def shouldReceiveEventsFor(address: String): Boolean = super.shouldReceiveEventsFor(address) ||
computer.inventory.entities.exists { case env: Environment => env.node.address == address }
def setup(): ComputerNode = {
cpuSlot.owner.put(new CPU(computer.tier.min(2)))
memorySlots(0).owner.put(new Memory(computer.tier.min(2) * 2 + 1))
@ -42,6 +65,7 @@ class ComputerNode(val computer: Case) extends Node(computer) {
}
override val icon: String = "nodes/Computer"
override def iconColor: Color = TierColor.get(computer.tier)
override protected val canOpen = true

View File

@ -1,15 +1,25 @@
package ocelot.desktop.node.nodes
import ocelot.desktop.ColorScheme
import ocelot.desktop.audio.{SoundBuffers, SoundCategory, SoundSource}
import ocelot.desktop.geometry.{Size2D, Vector2D}
import ocelot.desktop.graphics.Graphics
import ocelot.desktop.node.Node
import ocelot.desktop.ui.UiHandler
import ocelot.desktop.ui.event.BrainEvent
import totoro.ocelot.brain.entity.traits.{Entity, Environment}
import totoro.ocelot.brain.event.NoteBlockTriggerEvent
import scala.collection.mutable
abstract class NoteBlockNodeBase(entity: Entity with Environment) extends Node(entity) {
eventHandlers += {
case BrainEvent(event: NoteBlockTriggerEvent) =>
SoundSource.fromBuffer(SoundBuffers.NoteBlock(event.instrument), SoundCategory.Beep,
pitch = NoteBlockNode.Pitches(event.pitch), volume = event.volume.toFloat.min(1f).max(0f)).play()
addParticle(event.pitch)
}
private val particles = mutable.ArrayBuffer[(Float, Int)]()
def addParticle(pitch: Int): Unit = {

View File

@ -275,6 +275,7 @@ object UiHandler extends Logging {
}
def terminate(): Unit = {
root.workspaceView.dispose()
KeyEvents.destroy()
MouseEvents.destroy()
Display.destroy()

View File

@ -0,0 +1,3 @@
package ocelot.desktop.ui.event
case class BrainEvent(event: totoro.ocelot.brain.event.Event) extends Event

View File

@ -15,16 +15,17 @@ import ocelot.desktop.{ColorScheme, OcelotDesktop, Settings}
import org.lwjgl.input.Keyboard
import totoro.ocelot.brain.entity.traits.{Entity, Environment, SidedEnvironment}
import totoro.ocelot.brain.entity.{Case, Screen}
import totoro.ocelot.brain.event.{EventBus, NodeEvent}
import totoro.ocelot.brain.nbt.ExtendedNBT._
import totoro.ocelot.brain.nbt.{NBT, NBTBase, NBTTagCompound}
import totoro.ocelot.brain.util.{Direction, Tier}
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.collection.{immutable, mutable}
import scala.jdk.CollectionConverters._
class WorkspaceView extends Widget with DragHandler with ClickHandler with HoverHandler {
val nodes: ArrayBuffer[Node] = ArrayBuffer[Node]()
@volatile
var nodes: immutable.Seq[Node] = immutable.ArraySeq[Node]()
var windowPool = new WindowPool
var nodeSelector = new NodeSelector
var profilerWindow = new ProfilerWindow
@ -42,15 +43,26 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
override def hierarchy: Array[Widget] = nodes.toArray
}
private val eventSubscription = EventBus.subscribe { case event: NodeEvent =>
nodes
.filter(_.shouldReceiveEventsFor(event.address))
.foreach(_.handleEvent(BrainEvent(event)))
}
def reset(): Unit = {
nodes.foreach(_.dispose())
nodes.clear()
nodes = nodes.empty
windowPool.closeAll()
nodeSelector = new NodeSelector
profilerWindow = new ProfilerWindow
cameraOffset = Vector2D(0, 0)
}
def dispose(): Unit = {
nodes.foreach(_.dispose())
eventSubscription.cancel()
}
def load(nbt: NBTTagCompound): Unit = {
reset()
cameraOffset = new Vector2D(nbt.getCompoundTag("cameraOffset"))
@ -70,7 +82,7 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
node.workspaceView = this
node.root = _root
node.load(nbt)
nodes += node
nodes = nodes.appended(node)
})
nbt.getTagList("connections", NBT.TAG_COMPOUND).foreach((nbt: NBTTagCompound) => {
@ -97,7 +109,7 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
cameraOffset.save(cameraOffsetTag)
nbt.setTag("cameraOffset", cameraOffsetTag)
nbt.setTagList("nodes", nodes.map(node => {
nbt.setTagList("nodes", nodes.toList.map(node => {
val nbt = new NBTTagCompound
nbt.setString("class", node.getClass.getName)
nbt.setString("entityClass", node.entity.getClass.getName)
@ -146,7 +158,7 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
node.workspaceView = this
node.root = _root
node.parent = Some(this)
nodes += node
nodes = nodes.appended(node)
}
def buildNewConnection(): Unit = {
@ -259,8 +271,7 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
b: Array[(Vector2D, Vector2D)],
checkCollision: Boolean,
forceParallel: Boolean,
clampMin: Boolean): Array[Array[Vector2D]] =
{
clampMin: Boolean): Array[Array[Vector2D]] = {
val product = for (x <- a; y <- b) yield (x, y)
var iter = product.map { case ((aSide, aCenter), (bSide, bCenter)) =>
val (aLen, bLen) = connectorLen(aSide, aCenter, bSide, bCenter, clampMin)
@ -289,8 +300,7 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
private def connectorLen(aSide: Vector2D, aCenter: Vector2D,
bSide: Vector2D, bCenter: Vector2D,
clampMin: Boolean): (Float, Float) =
{
clampMin: Boolean): (Float, Float) = {
val aDir = aSide - aCenter
val bDir = bSide - bCenter
val xDist = (aSide - bSide).x.abs
@ -395,8 +405,7 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
private def drawSelectorConnection(g: Graphics, aRect: Rect2D, bRect: Rect2D,
thickness: Float = 4,
color: Color = RGBAColor(150, 150, 150)): Unit =
{
color: Color = RGBAColor(150, 150, 150)): Unit = {
if (aRect.collides(bRect)) return
val (a, b) = if (aRect.x > bRect.x) (aRect, bRect) else (bRect, aRect)