@@ -15,7 +15,10 @@ import com.noxcrew.interfaces.properties.Trigger
15
15
import com.noxcrew.interfaces.transform.AppliedTransform
16
16
import com.noxcrew.interfaces.utilities.CollapsablePaneMap
17
17
import com.noxcrew.interfaces.utilities.forEachInGrid
18
+ import kotlinx.coroutines.Job
19
+ import kotlinx.coroutines.async
18
20
import kotlinx.coroutines.launch
21
+ import kotlinx.coroutines.sync.Mutex
19
22
import kotlinx.coroutines.sync.Semaphore
20
23
import kotlinx.coroutines.withTimeout
21
24
import net.kyori.adventure.text.Component
@@ -27,7 +30,7 @@ import org.bukkit.inventory.Inventory
27
30
import org.bukkit.inventory.ItemStack
28
31
import org.slf4j.LoggerFactory
29
32
import java.util.WeakHashMap
30
- import java.util.concurrent.ConcurrentHashMap
33
+ import java.util.concurrent.ConcurrentLinkedQueue
31
34
import java.util.concurrent.atomic.AtomicBoolean
32
35
import java.util.concurrent.atomic.AtomicInteger
33
36
import kotlin.time.Duration
@@ -68,8 +71,9 @@ public abstract class AbstractInterfaceView<I : InterfacesInventory, T : Interfa
68
71
private val shouldBeOpened = AtomicBoolean (false )
69
72
private val openIfClosed = AtomicBoolean (false )
70
73
71
- private val pendingTransforms = ConcurrentHashMap .newKeySet<AppliedTransform <P >>()
72
- private val debouncedTransforms = ConcurrentHashMap .newKeySet<AppliedTransform <P >>()
74
+ private val pendingTransforms = ConcurrentLinkedQueue <AppliedTransform <P >>()
75
+ private var transformingJob: Job ? = null
76
+ private val transformMutex = Mutex ()
73
77
74
78
private val panes = CollapsablePaneMap .create(backing.createPane())
75
79
internal lateinit var pane: CompletedPane
@@ -221,55 +225,49 @@ public abstract class AbstractInterfaceView<I : InterfacesInventory, T : Interfa
221
225
}
222
226
223
227
private fun applyTransforms (transforms : Collection <AppliedTransform <P >>): Boolean {
224
- // Remove all these from the debounced transforms so we can try running
225
- // them again!
226
- debouncedTransforms - = transforms.toSet()
228
+ // Ignore if the transforms are empty
229
+ if (transforms.isEmpty()) return true
227
230
228
231
// Check if the player is offline or the server stopping
229
232
if (Bukkit .isStopping() || ! player.isOnline) return false
230
233
231
- transforms.forEach { transform ->
232
- // If the transform is already pending we debounce it
233
- if (transform in pendingTransforms) {
234
- debouncedTransforms + = transform
235
- return @forEach
236
- }
237
-
238
- // Indicate this transform is running which prevents the menu
239
- // from rendering until all transforms are done!
240
- pendingTransforms + = transform
241
-
242
- SCOPE .launch {
243
- try {
244
- // Don't run transforms for an offline player!
245
- if (! Bukkit .isStopping() && player.isOnline) {
246
- withTimeout(6 .seconds) {
247
- runTransformAndApplyToPanes(transform)
248
- }
249
- }
250
- } catch (exception: Exception ) {
251
- logger.error(" Failed to run and apply transform: $transform " , exception)
252
- } finally {
253
- // Update that this transform has finished and check if
254
- // we are ready to draw the screen finally!
255
- pendingTransforms - = transform
256
-
257
- if (transform in debouncedTransforms && applyTransforms(listOf (transform))) {
258
- // Simply run the transform again here and do nothing else
259
- } else {
260
- // If all transforms are done we can finally draw and open the menu
261
- if (pendingTransforms.isEmpty()) {
262
- renderAndOpen()
234
+ // Queue up the transforms
235
+ pendingTransforms.addAll(transforms)
236
+
237
+ // Check if the job is already running
238
+ SCOPE .launch {
239
+ try {
240
+ transformMutex.lock()
241
+
242
+ // Start the job if it's not running currently!
243
+ if (transformingJob == null || transformingJob?.isCompleted == true ) {
244
+ transformingJob = SCOPE .async {
245
+ // Go through all pending transforms one at a time until
246
+ // we're fully done with all of them. Other threads may
247
+ // add additional ones as we go through the queue.
248
+ while (pendingTransforms.isNotEmpty()) {
249
+ // Removes the first pending transform
250
+ val transform = pendingTransforms.remove()
251
+
252
+ try {
253
+ // Don't run transforms for an offline player!
254
+ if (! Bukkit .isStopping() && player.isOnline) {
255
+ withTimeout(6 .seconds) {
256
+ runTransformAndApplyToPanes(transform)
257
+ }
258
+ }
259
+ } catch (exception: Exception ) {
260
+ logger.error(" Failed to run and apply transform: $transform " , exception)
261
+ }
263
262
}
263
+
264
+ // After we have finished running all transforms we render and open
265
+ // the menu before ending this job.
266
+ renderAndOpen()
264
267
}
265
268
}
266
- }
267
- }
268
-
269
- // In the case that transforms was empty we might be able to open the menu already
270
- if (pendingTransforms.isEmpty()) {
271
- SCOPE .launch {
272
- renderAndOpen()
269
+ } finally {
270
+ transformMutex.unlock()
273
271
}
274
272
}
275
273
return true
0 commit comments