Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions core/common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ dependencies {
testImplementation("com.willfp:eco:${findProperty("eco-version")}")
testImplementation(kotlin("stdlib", version = "2.3.0"))

// eco's placeholder types reference Bukkit classes, which the server provides at runtime.
testImplementation("org.purpurmc.purpur:purpur-api:1.21.4-R0.1-SNAPSHOT")

// Reads the bundled holidays.yml in tests; the server provides SnakeYAML at runtime.
testImplementation("org.yaml:snakeyaml:2.2")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.willfp.libreforge

import com.willfp.eco.core.registry.KRegistrable
import com.willfp.eco.core.registry.Registry
import java.util.concurrent.ConcurrentHashMap

interface Aliased : KRegistrable {
/**
Expand All @@ -16,7 +17,7 @@ interface Aliased : KRegistrable {
* When an ID and an alias clash, the most recently registered element wins.
*/
abstract class AliasedRegistry<T : Aliased> : Registry<T>() {
private val aliasRegistry = mutableMapOf<String, T>()
private val aliasRegistry = ConcurrentHashMap<String, T>()

override fun get(id: String): T? {
return aliasRegistry[id] ?: super.get(id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ interface BlockBreaker {
/**
* Break [blocks] on behalf of this dispatcher.
*
* Called on the thread that owns the blocks: on Folia, blocks in other regions are left out.
*
* @param data The trigger the breaking effect is running for, which carries
* the drop event the drops belong in, where one exists.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import com.willfp.libreforge.slot.ItemHolderFinder
import org.bukkit.Location
import org.bukkit.event.Event
import org.bukkit.event.HandlerList
import java.util.Collections
import java.util.IdentityHashMap
import java.util.UUID
import java.util.concurrent.CopyOnWriteArrayList


/**
Expand Down Expand Up @@ -178,12 +180,14 @@ data class ProvidedEffectBlock(
}
}

private val providers = mutableListOf<HolderProvider>()
private val providers = CopyOnWriteArrayList<HolderProvider>()

private val providerIds = IdentityHashMap<HolderProvider, String>()
private val providerIds: MutableMap<HolderProvider, String> = Collections.synchronizedMap(IdentityHashMap())

private val providerIdCounts = mutableMapOf<String, Int>()

private val providerRegistrationLock = Any()

/**
* The registered providers, in registration order.
*/
Expand All @@ -200,16 +204,19 @@ internal fun registeredProviderId(provider: HolderProvider): String =
* Register a new holder provider.
*/
fun registerHolderProvider(provider: HolderProvider): Boolean {
if (providerIds.containsKey(provider)) {
return false
}
synchronized(providerRegistrationLock) {
if (providerIds.containsKey(provider)) {
return false
}

val baseId = provider.id
val count = providerIdCounts.getOrDefault(baseId, 0)
providerIdCounts[baseId] = count + 1
providerIds[provider] = if (count == 0) baseId else "$baseId#$count"
val baseId = provider.id
val count = providerIdCounts.getOrDefault(baseId, 0)
providerIdCounts[baseId] = count + 1
providerIds[provider] = if (count == 0) baseId else "$baseId#$count"

providers.add(provider)
}

providers.add(provider)
HolderSignals.registerProvider(provider)
HolderStates.markProviderEverywhere(provider)
return true
Expand All @@ -219,10 +226,12 @@ fun registerHolderProvider(provider: HolderProvider): Boolean {
* Remove holder providers, used when the plugin that registered them is disabled.
*/
internal fun unregisterHolderProviders(filter: (HolderProvider) -> Boolean): List<HolderProvider> {
val removed = providers.filter(filter)
providers.removeAll(removed)
removed.forEach { providerIds.remove(it) }
return removed
synchronized(providerRegistrationLock) {
val removed = providers.filter(filter)
providers.removeAll(removed)
removed.forEach { providerIds.remove(it) }
return removed
}
}

/**
Expand Down Expand Up @@ -312,7 +321,7 @@ inline fun <reified T> registerSpecificHolderProvider(
fun registerSlotHolderFinderAsProvider(finder: ItemHolderFinder<*>) =
registerHolderProvider(finder.toHolderProvider())

private val refreshFunctions = mutableListOf<(Dispatcher<*>) -> Unit>()
private val refreshFunctions = CopyOnWriteArrayList<(Dispatcher<*>) -> Unit>()

/**
* Register a function to be called before a dispatcher's providers are re-asked.
Expand Down Expand Up @@ -376,7 +385,7 @@ fun Dispatcher<*>.refreshHolders() =
fun Dispatcher<*>.forceRefreshHolders() =
HolderStates.forceMarkAllProviders(this)

private val holderPlaceholderProviders = mutableListOf<(ProvidedHolder, Dispatcher<*>) -> Collection<NamedValue>>()
private val holderPlaceholderProviders = CopyOnWriteArrayList<(ProvidedHolder, Dispatcher<*>) -> Collection<NamedValue>>()

/**
* Register a function to generate placeholders for a holder.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,26 @@ import org.bukkit.event.Listener
import org.bukkit.plugin.EventExecutor
import java.util.Collections
import java.util.IdentityHashMap
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.CopyOnWriteArraySet

/**
* Routes change signals to the providers and conditions that declared them.
*/
internal object HolderSignals {
private val providersBySignal = HashMap<HolderChange, MutableList<HolderProvider>>()
private val providersBySignal = ConcurrentHashMap<HolderChange, CopyOnWriteArrayList<HolderProvider>>()

private val conditionsBySignal = HashMap<HolderChange, MutableSet<Condition<*>>>()
private val conditionsBySignal = ConcurrentHashMap<HolderChange, CopyOnWriteArraySet<Condition<*>>>()

private class CustomHandler(
val owner: Any,
val action: (Event) -> Unit
)

private val customHandlers = HashMap<Class<out Event>, MutableList<CustomHandler>>()
private val customHandlers = ConcurrentHashMap<Class<out Event>, CopyOnWriteArrayList<CustomHandler>>()

private val listening = mutableSetOf<Class<out Event>>()
private val listening = ConcurrentHashMap.newKeySet<Class<out Event>>()

private val registeredConditions: MutableSet<Condition<*>> = Collections.newSetFromMap(IdentityHashMap())

Expand All @@ -36,16 +39,18 @@ internal object HolderSignals {
fun conditionsFor(change: HolderChange): Set<Condition<*>> =
conditionsBySignal[change] ?: emptySet()

@Synchronized
fun registerProvider(provider: HolderProvider) {
for (change in provider.invalidatedBy) {
if (change is HolderChange.Custom<*>) {
addCustom(change, provider) { dispatcher, scope -> HolderStates.markProvider(dispatcher, provider, scope) }
} else {
providersBySignal.getOrPut(change) { mutableListOf() } += provider
providersBySignal.computeIfAbsent(change) { CopyOnWriteArrayList() } += provider
}
}
}

@Synchronized
fun registerCondition(condition: Condition<*>) {
// Registration re-runs on every reload.
if (!registeredConditions.add(condition)) {
Expand All @@ -58,14 +63,15 @@ internal object HolderSignals {
if (change is HolderChange.Custom<*>) {
addCustom(change, condition) { dispatcher, scope -> HolderStates.markCondition(dispatcher, condition, scope) }
} else {
conditionsBySignal.getOrPut(change) { mutableSetOf() } += condition
conditionsBySignal.computeIfAbsent(change) { CopyOnWriteArraySet() } += condition
}
}
}

/**
* Stop routing signals to [providers] and to the conditions loaded by [classLoader].
*/
@Synchronized
fun unregisterOwnedBy(classLoader: ClassLoader, providers: Collection<HolderProvider>) {
val removed: MutableSet<Any> = Collections.newSetFromMap(IdentityHashMap())
removed.addAll(providers)
Expand All @@ -92,7 +98,7 @@ internal object HolderSignals {
@Suppress("UNCHECKED_CAST")
val scopeOf = change.scopeOf as ((Event) -> SignalScope?)?

customHandlers.getOrPut(change.event) { mutableListOf() } += CustomHandler(owner) { event ->
customHandlers.computeIfAbsent(change.event) { CopyOnWriteArrayList() } += CustomHandler(owner) { event ->
dispatcherOf(event)?.let { mark(it, scopeOf?.invoke(event)) }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ internal enum class StateKind {
}

/**
* Everything libreforge tracks for one dispatcher. Accessed only on the main thread.
* Everything libreforge tracks for one dispatcher. Accessed only on the thread that owns the
* dispatcher (the main thread off Folia); [admitted] and [isRemoved] are also read by the engine.
*/
internal class HolderState(
val dispatcher: Dispatcher<*>,
Expand Down Expand Up @@ -261,13 +262,15 @@ internal class HolderState(
private var clickScopes: ArrayList<SignalScope>? = ArrayList()

// Set when the state is dropped, possibly by a handler in the middle of its own update.
@Volatile
var isRemoved = false

var admissionAttempts = 0

var bypassCooldown = false

// Non-player states wait for the new-state budget before their first flush.
@Volatile
var admitted = false
var needsCleanup = false

Expand Down
Loading
Loading