From 117aa0d2d29ad50a97e3b238f3fcbc73f8ed87a4 Mon Sep 17 00:00:00 2001 From: Fingercomp Date: Thu, 18 Jan 2024 19:53:29 +0700 Subject: [PATCH] Synchronize access to OcelotCardItem._entries Fixes #129. --- .../inventory/item/OcelotCardItem.scala | 26 +++++++++++-------- .../ocelot/desktop/ui/widget/LogWidget.scala | 8 +++--- .../ui/widget/card/OcelotCardWindow.scala | 10 +++---- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/main/scala/ocelot/desktop/inventory/item/OcelotCardItem.scala b/src/main/scala/ocelot/desktop/inventory/item/OcelotCardItem.scala index c4b2f76..4ea5504 100644 --- a/src/main/scala/ocelot/desktop/inventory/item/OcelotCardItem.scala +++ b/src/main/scala/ocelot/desktop/inventory/item/OcelotCardItem.scala @@ -18,7 +18,7 @@ import totoro.ocelot.brain.nbt.{NBT, NBTBase, NBTTagCompound, NBTTagString} import totoro.ocelot.brain.util.Tier import totoro.ocelot.brain.util.Tier.Tier -import scala.collection.{IndexedSeqView, mutable} +import scala.collection.mutable import scala.jdk.CollectionConverters.BufferHasAsJava class OcelotCardItem(val ocelotCard: OcelotCard) @@ -31,10 +31,10 @@ class OcelotCardItem(val ocelotCard: OcelotCard) private var _messageLimit: Int = 1000 + // NOTE: access must be synchronized! + // ocelot.log() is a direct method, so it may push events even if the tick lock is not acquired private val _entries = mutable.ArrayDeque.empty[LogEntry] - def entries: IndexedSeqView[LogEntry] = _entries.view - eventHandlers += { case BrainEvent(OcelotCard.LogEvent(_, message, OcelotCard.LogDirection.CardToUser)) => addEntry(LogEntry.Rx(message)) @@ -116,7 +116,7 @@ class OcelotCardItem(val ocelotCard: OcelotCard) addEntries(entries.toSeq) } - override def save(nbt: NBTTagCompound): Unit = { + override def save(nbt: NBTTagCompound): Unit = _entries.synchronized { super.save(nbt) nbt.setTagList(EntriesTag, _entries.map(saveEntry(_).asInstanceOf[NBTBase]).asJava) @@ -127,32 +127,36 @@ class OcelotCardItem(val ocelotCard: OcelotCard) def messageLimit: Int = _messageLimit - def messageLimit_=(limit: Int): Unit = { + def messageLimit_=(limit: Int): Unit = _entries.synchronized { require(limit > 0) ensureFreeSpace(_entries.length - limit) _messageLimit = limit } - def clear(): Unit = { + def entryCount: Int = _entries.synchronized { + _entries.length + } + + def clear(): Unit = _entries.synchronized { val count = _entries.length _entries.clear() window.onMessagesRemoved(count) } - private def addEntry(entry: LogEntry): Unit = { + private def addEntry(entry: LogEntry): Unit = _entries.synchronized { ensureFreeSpace(1) _entries += entry - window.onMessagesAdded(1) + window.onMessagesAdded(Some(entry)) } - private def addEntries(entries: Seq[LogEntry]): Unit = { + private def addEntries(entries: Seq[LogEntry]): Unit = _entries.synchronized { ensureFreeSpace(entries.length) val prevCount = _entries.length _entries ++= entries.view.takeRight(messageLimit) - window.onMessagesAdded(_entries.length - prevCount) + window.onMessagesAdded(_entries.view.takeRight(_entries.length - prevCount)) } - private def ensureFreeSpace(n: Int): Unit = { + private def ensureFreeSpace(n: Int): Unit = _entries.synchronized { val prevCount = _entries.length _entries.takeRightInPlace(messageLimit - n) val removedCount = prevCount - _entries.length diff --git a/src/main/scala/ocelot/desktop/ui/widget/LogWidget.scala b/src/main/scala/ocelot/desktop/ui/widget/LogWidget.scala index 9506ba2..b4f8b31 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/LogWidget.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/LogWidget.scala @@ -163,8 +163,6 @@ abstract class LogWidget extends Widget { scrollView.scrollToEnd = value } - protected def entries: Iterable[LogEntry] - protected def textWidth: Int override def minimumSize: Size2D = Size2D( @@ -175,13 +173,13 @@ abstract class LogWidget extends Widget { BorderThickness * 2, // outer border (top + bottom) ) - def onEntryAdded(count: Int): Unit = { - for (entry <- entries.view.takeRight(count)) { + def addEntries(entries: Iterable[LogEntry]): Unit = { + for (entry <- entries) { MessageListWidget.addEntry(entry) } } - def onEntryRemoved(count: Int): Unit = { + def removeFirstEntries(count: Int): Unit = { MessageListWidget.removeFirst(count) } diff --git a/src/main/scala/ocelot/desktop/ui/widget/card/OcelotCardWindow.scala b/src/main/scala/ocelot/desktop/ui/widget/card/OcelotCardWindow.scala index 770b1ca..ab14aef 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/card/OcelotCardWindow.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/card/OcelotCardWindow.scala @@ -12,9 +12,7 @@ class OcelotCardWindow(item: OcelotCardItem) extends PanelWindow { override protected def title: String = s"Ocelot card ${item.component.node.address}" private val logWidget: LogWidget = new LogWidget { - override protected def entries: Iterable[LogEntry] = item.entries override protected def textWidth: Int = 50 - override def minimumSize: Size2D = super.minimumSize.copy(height = 300) override def maximumSize: Size2D = minimumSize } @@ -32,7 +30,7 @@ class OcelotCardWindow(item: OcelotCardItem) extends PanelWindow { } children :+= new Label { - override def text: String = s"Messages: ${item.entries.length} / ${item.messageLimit}" + override def text: String = s"Messages: ${item.entryCount} / ${item.messageLimit}" } children :+= new Widget { @@ -87,12 +85,12 @@ class OcelotCardWindow(item: OcelotCardItem) extends PanelWindow { item.clear() } - def onMessagesAdded(count: Int): Unit = { - logWidget.onEntryAdded(count) + def onMessagesAdded(entries: Iterable[LogEntry]): Unit = { + logWidget.addEntries(entries) } def onMessagesRemoved(count: Int): Unit = { - logWidget.onEntryRemoved(count) + logWidget.removeFirstEntries(count) } def pushLine(line: String): Unit = {