Skip to content

Commit d6c40a7

Browse files
committed
a little bit more mccoroutine extensions
1 parent d5947b7 commit d6c40a7

File tree

4 files changed

+147
-8
lines changed

4 files changed

+147
-8
lines changed

.idea/compiler.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

Lines changed: 0 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ javaVersion=21
77
mcVersion=1.21.4
88

99
group=dev.slne.surf
10-
version=1.21.4-2.1.0-SNAPSHOT
10+
version=1.21.4-2.1.1-SNAPSHOT
1111
relocationPrefix=dev.slne.surf.surfapi.libs

surf-api-bukkit/surf-api-bukkit-api/src/main/kotlin/dev/slne/surf/surfapi/bukkit/api/util/bukkit-util.kt

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,132 @@
22

33
package dev.slne.surf.surfapi.bukkit.api.util
44

5+
import com.github.shynixn.mccoroutine.folia.SuspendingPlugin
6+
import com.github.shynixn.mccoroutine.folia.entityDispatcher
7+
import com.github.shynixn.mccoroutine.folia.regionDispatcher
58
import dev.slne.surf.surfapi.core.api.util.getCallerClass
9+
import kotlinx.coroutines.async
10+
import kotlinx.coroutines.awaitAll
11+
import kotlinx.coroutines.coroutineScope
12+
import kotlinx.coroutines.withContext
613
import org.bukkit.Bukkit
714
import org.bukkit.Chunk
815
import org.bukkit.Location
916
import org.bukkit.NamespacedKey
17+
import org.bukkit.entity.Entity
1018
import org.bukkit.entity.Player
1119
import org.bukkit.plugin.java.JavaPlugin
1220
import java.util.*
1321

22+
/**
23+
* Creates a [NamespacedKey] using the calling plugin and the given name.
24+
*
25+
* @param name The key name.
26+
* @return The created [NamespacedKey].
27+
* @throws IllegalStateException If the calling plugin cannot be determined.
28+
*/
1429
fun key(name: String): NamespacedKey { // TODO: Verify if this works
1530
return NamespacedKey(getCallingPlugin(), name)
1631
}
1732

33+
/**
34+
* Retrieves the [JavaPlugin] that called this method.
35+
*
36+
* @param depth The stack trace depth to determine the caller class. Default is 1.
37+
* @return The calling [JavaPlugin].
38+
* @throws IllegalStateException If the caller class cannot be determined.
39+
*/
1840
fun getCallingPlugin(depth: Int = 1): JavaPlugin {
1941
val caller = getCallerClass(depth) ?: error("Cannot determine caller class")
2042
return JavaPlugin.getProvidingPlugin(caller)
2143
}
2244

45+
/**
46+
* Iterates over all online players and performs the given action.
47+
*
48+
* @param action The action to perform on each player.
49+
*/
2350
fun forEachPlayer(action: (player: Player) -> Unit) {
2451
Bukkit.getOnlinePlayers().forEach(action)
2552
}
2653

54+
/**
55+
* Executes a suspendable action on each online player, optionally concurrently.
56+
*
57+
* @param action The suspendable action to perform on each player.
58+
* @param concurrent If `true`, actions will run concurrently; otherwise, sequentially. Default is `false`.
59+
*/
60+
suspend fun forEachPlayerInRegion(
61+
action: suspend (player: Player) -> Unit,
62+
concurrent: Boolean = false,
63+
) {
64+
if (concurrent) {
65+
coroutineScope {
66+
Bukkit.getOnlinePlayers()
67+
.map {
68+
async {
69+
withContext(it.dispatcher(getCallingSuspendingPlugin())) {
70+
action(it)
71+
}
72+
}
73+
}.awaitAll()
74+
}
75+
} else {
76+
for (player in Bukkit.getOnlinePlayers()) {
77+
withContext(player.dispatcher(getCallingSuspendingPlugin())) {
78+
action(player)
79+
}
80+
}
81+
}
82+
}
83+
84+
/**
85+
* Computes the squared distance between this location and another.
86+
*
87+
* @receiver The starting location.
88+
* @param other The target location.
89+
* @return The squared distance between the two locations.
90+
*/
2791
infix fun Location.distanceSqt(other: Location): Double = distanceSquared(other)
2892

93+
/**
94+
* Gets the chunk X coordinate of this location.
95+
*/
96+
val Location.chunkX get() = blockX shr 4
97+
98+
/**
99+
* Gets the chunk Z coordinate of this location.
100+
*/
101+
val Location.chunkZ get() = blockZ shr 4
102+
103+
/**
104+
* Gets the chunk key of this location.
105+
*/
106+
val Location.chunkKey get() = Chunk.getChunkKey(this)
107+
108+
/**
109+
* Converts an iterable of UUIDs to a list of online [Player] instances.
110+
*
111+
* @receiver The collection of UUIDs.
112+
* @return A list of [Player] instances corresponding to the UUIDs, excluding offline players.
113+
*/
29114
fun Iterable<UUID>.toPlayers() = mapNotNull { Bukkit.getPlayer(it) }
115+
116+
/**
117+
* Converts a sequence of UUIDs to a sequence of online [Player] instances.
118+
*
119+
* @receiver The sequence of UUIDs.
120+
* @return A list of [Player] instances corresponding to the UUIDs, excluding offline players.
121+
*/
122+
fun Sequence<UUID>.toPlayers() = mapNotNull { Bukkit.getPlayer(it) }
123+
124+
/**
125+
* Checks if the player can see the specified location.
126+
*
127+
* @receiver The player.
128+
* @param location The location to check.
129+
* @return `true` if the player can see the location, `false` otherwise.
130+
*/
30131
fun Player.seesLocation(location: Location): Boolean {
31132
val sameWorld = world == location.world
32133
val chunkSent = isChunkSent(Chunk.getChunkKey(location))
@@ -35,3 +136,47 @@ fun Player.seesLocation(location: Location): Boolean {
35136

36137
return this.world == location.world && this.isChunkSent(Chunk.getChunkKey(location))
37138
}
139+
140+
/**
141+
* Checks if the player can see the specified location based on chunk visibility.
142+
*
143+
* @receiver The player.
144+
* @param location The location to check.
145+
* @return `true` if the player can see the chunk containing the location, `false` otherwise.
146+
*/
147+
fun Player.seesLocation2(location: Location): Boolean =
148+
this.world == location.world && location.world.getPlayersSeeingChunk(
149+
location.chunkX,
150+
location.chunkZ
151+
).contains(this)
152+
153+
/**
154+
* Retrieves the coroutine dispatcher for this entity.
155+
*
156+
* @receiver The entity.
157+
* @param plugin The suspending plugin instance. Defaults to the calling suspending plugin.
158+
* @return The entity's coroutine dispatcher.
159+
*/
160+
fun Entity.dispatcher(
161+
plugin: SuspendingPlugin = getCallingSuspendingPlugin(),
162+
) = plugin.entityDispatcher(this)
163+
164+
/**
165+
* Retrieves the coroutine dispatcher for this location.
166+
*
167+
* @receiver The location.
168+
* @param plugin The suspending plugin instance. Defaults to the calling suspending plugin.
169+
* @return The region's coroutine dispatcher.
170+
*/
171+
fun Location.dispatcher(
172+
plugin: SuspendingPlugin = getCallingSuspendingPlugin(),
173+
) = plugin.regionDispatcher(this)
174+
175+
/**
176+
* Retrieves the calling suspending plugin.
177+
*
178+
* @return The calling [SuspendingPlugin].
179+
* @throws IllegalStateException If the calling plugin cannot be determined.
180+
*/
181+
private fun getCallingSuspendingPlugin() = getCallingPlugin(2) as? SuspendingPlugin
182+
?: error("Cannot determine plugin")

0 commit comments

Comments
 (0)