Skip to content

Commit 2dd4f56

Browse files
committed
feat: Add first apis for coroutines and the entity store as well as a simple example
1 parent d45488a commit 2dd4f56

12 files changed

Lines changed: 325 additions & 4 deletions

File tree

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ Kotale provides two mods for hytale that:
55
1. (kotlin.jar) The shaded stdlib, reflect and coroutine libraries for other mods
66
2. (kotale.jar) Extensions and utilities for modding hytale in kotlin
77

8+
## Included Libraries
9+
10+
* Kotlin StdLib (2.3.0)
11+
* Kotlin Reflect (2.3.0)
12+
* Kotlin Serialization (1.9.0)
13+
* Kotlin Serialization Json (1.9.0)
14+
* Kotlin Serialization Cbor (1.9.0)
15+
* Kotlin Coroutines (1.10.2)
16+
817
## Usage (kotlin.jar)
918

1019
To use the shaded kotlin stdlib in your mod, add Kotale:Kotlin as a dependency in your `manifest.json`:

mods/example/build.gradle.kts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
plugins {
2+
// Apply the shared build logic from a convention plugin.
3+
// The shared code is located in `buildSrc/src/main/kotlin/kotlin-jvm.gradle.kts`.
4+
id("buildsrc.convention.kotlin-jvm")
5+
// Apply Kotlin Serialization plugin from `gradle/libs.versions.toml`.
6+
alias(libs.plugins.kotlinPluginSerialization)
7+
alias(libs.plugins.shadow)
8+
}
9+
10+
dependencies {
11+
// Apply the kotlinx bundle of dependencies from the version catalog (`gradle/libs.versions.toml`).
12+
compileOnly(libs.bundles.kotlinxEcosystem)
13+
compileOnly(libs.hytaleServer)
14+
compileOnly(project(":mods:kotale"))
15+
16+
testImplementation(kotlin("test"))
17+
}
18+
19+
tasks {
20+
shadowJar {
21+
exclude("kotlin/**")
22+
exclude("org/intellij/**")
23+
exclude("org/jetbrains/**")
24+
exclude("META-INF/**")
25+
26+
minimize()
27+
}
28+
29+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package dev.helight.kotale
2+
3+
import com.hypixel.hytale.component.Ref
4+
import com.hypixel.hytale.component.Store
5+
import com.hypixel.hytale.server.core.command.system.CommandContext
6+
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand
7+
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent
8+
import com.hypixel.hytale.server.core.universe.PlayerRef
9+
import com.hypixel.hytale.server.core.universe.world.World
10+
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore
11+
import kotlinx.coroutines.delay
12+
13+
class ExampleCommand(
14+
val plugin: KotlinPlugin
15+
) : AbstractPlayerCommand("kotale-example", "kotale-example") {
16+
17+
override fun execute(
18+
p0: CommandContext,
19+
p1: Store<EntityStore?>,
20+
p2: Ref<EntityStore?>,
21+
p3: PlayerRef,
22+
p4: World
23+
) {
24+
val position = p3.transform.position.toVector3i()
25+
p4.launch(plugin) {
26+
delay(1000) // We sleep here to check if coroutines even in foreign threads work
27+
p4.setBlock(position.x, position.y, position.z, "Rock_Aqua_Cobble")
28+
}
29+
}
30+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package dev.helight.kotale
2+
3+
import com.hypixel.hytale.server.core.plugin.JavaPluginInit
4+
5+
class KotaleExamplePlugin(init: JavaPluginInit) : KotlinPlugin(init) {
6+
7+
override fun setup() {
8+
super.setup()
9+
this.commandRegistry.registerCommand(ExampleCommand(this))
10+
}
11+
}
12+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"Name": "KotaleExample",
3+
"Group": "Kotale",
4+
"Version": "${buildVersion}",
5+
"Authors": [
6+
{
7+
"Name": "Helight",
8+
"Email": "contact@helight.dev",
9+
"Url": "https://helight.dev"
10+
}
11+
],
12+
"Main": "dev.helight.kotale.KotaleExamplePlugin",
13+
"ServerVersion": "*",
14+
"Dependencies": {
15+
"Kotale:Kotale": "*"
16+
}
17+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package dev.helight.kotale
2+
3+
import com.hypixel.hytale.server.core.universe.world.World
4+
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore
5+
import kotlinx.coroutines.CoroutineDispatcher
6+
import kotlinx.coroutines.CoroutineScope
7+
import kotlinx.coroutines.Deferred
8+
import kotlinx.coroutines.Job
9+
import kotlinx.coroutines.Runnable
10+
import kotlinx.coroutines.async
11+
import kotlinx.coroutines.launch
12+
import kotlinx.coroutines.withContext
13+
import java.util.Collections
14+
import java.util.WeakHashMap
15+
import java.util.concurrent.CompletableFuture
16+
import kotlin.coroutines.CoroutineContext
17+
18+
val World.dispatcher: CoroutineDispatcher
19+
get() = WorldThreadDispatcher.get(this)
20+
21+
suspend fun <T> World.context(block: suspend CoroutineScope.() -> T): T = withContext(this.dispatcher) {
22+
block()
23+
}
24+
25+
suspend fun <T> EntityStore.context(block: suspend CoroutineScope.() -> T): T = withContext(world.dispatcher) {
26+
block()
27+
}
28+
29+
fun World.launch(plugin: KotlinPlugin, block: suspend CoroutineScope.() -> Unit): Job =
30+
CoroutineScope(plugin.supervisor + this.dispatcher).launch {
31+
block()
32+
}
33+
34+
fun <T> World.launch(plugin: KotlinPlugin, block: suspend CoroutineScope.() -> T): Deferred<T> =
35+
CoroutineScope(plugin.supervisor + this.dispatcher).async {
36+
block()
37+
}
38+
39+
fun Job.asVoidCompletableFuture(): CompletableFuture<Void?> {
40+
val future = CompletableFuture<Void?>()
41+
invokeOnCompletion { throwable ->
42+
if (throwable != null) {
43+
future.completeExceptionally(throwable)
44+
} else {
45+
future.complete(null)
46+
}
47+
}
48+
return future
49+
}
50+
51+
class WorldThreadDispatcher(
52+
val world: World
53+
) : CoroutineDispatcher() {
54+
55+
override fun isDispatchNeeded(context: CoroutineContext): Boolean {
56+
return !world.isInThread
57+
}
58+
59+
override fun dispatch(context: CoroutineContext, block: Runnable) {
60+
world.execute(block)
61+
}
62+
63+
64+
companion object {
65+
private val dispatchers = Collections.synchronizedMap(
66+
WeakHashMap<World, CoroutineDispatcher>()
67+
)
68+
69+
fun get(world: World): CoroutineDispatcher =
70+
dispatchers.getOrPut(world) { WorldThreadDispatcher(world) }
71+
}
72+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package dev.helight.kotale
2+
3+
import com.hypixel.hytale.component.Ref
4+
import com.hypixel.hytale.component.Store
5+
import com.hypixel.hytale.server.core.entity.Entity
6+
import com.hypixel.hytale.server.core.entity.Frozen
7+
import com.hypixel.hytale.server.core.entity.UUIDComponent
8+
import com.hypixel.hytale.server.core.entity.damage.DamageDataComponent
9+
import com.hypixel.hytale.server.core.entity.entities.Player
10+
import com.hypixel.hytale.server.core.entity.nameplate.Nameplate
11+
import com.hypixel.hytale.server.core.modules.entity.component.*
12+
import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent
13+
import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent
14+
import com.hypixel.hytale.server.core.modules.entity.repulsion.Repulsion
15+
import com.hypixel.hytale.server.core.modules.physics.component.Velocity
16+
import com.hypixel.hytale.server.core.modules.projectile.config.StandardPhysicsProvider
17+
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore
18+
import java.util.*
19+
20+
fun Store<EntityStore>.player(ref: Ref<EntityStore>): Player {
21+
return this.getComponent(ref, Player.getComponentType()) as Player
22+
}
23+
24+
fun Store<EntityStore>.maybePlayer(ref: Ref<EntityStore>): Player? {
25+
return this.getComponent(ref, Player.getComponentType())
26+
}
27+
28+
fun Store<EntityStore>.transform(ref: Ref<EntityStore>): TransformComponent {
29+
return this.getComponent(ref, TransformComponent.getComponentType()) as TransformComponent
30+
}
31+
32+
fun Store<EntityStore>.uuid(ref: Ref<EntityStore>): UUID {
33+
return (this.getComponent(ref, UUIDComponent.getComponentType()) as UUIDComponent).uuid
34+
}
35+
36+
fun Store<EntityStore>.scale(ref: Ref<EntityStore>): Float {
37+
return (this.getComponent(ref, EntityScaleComponent.getComponentType()) as EntityScaleComponent).scale
38+
}
39+
40+
fun Store<EntityStore>.velocity(ref: Ref<EntityStore>): Velocity {
41+
return this.getComponent(ref, Velocity.getComponentType()) as Velocity
42+
}
43+
44+
fun Store<EntityStore>.standardPhysics(ref: Ref<EntityStore>): StandardPhysicsProvider {
45+
return this.getComponent(ref, StandardPhysicsProvider.getComponentType()) as StandardPhysicsProvider
46+
}
47+
48+
fun Store<EntityStore>.boundingBox(ref: Ref<EntityStore>): BoundingBox {
49+
return this.getComponent(ref, BoundingBox.getComponentType()) as BoundingBox
50+
}
51+
52+
fun Store<EntityStore>.headRotation(ref: Ref<EntityStore>): HeadRotation {
53+
return this.getComponent(ref, HeadRotation.getComponentType()) as HeadRotation
54+
}
55+
56+
fun Store<EntityStore>.audio(ref: Ref<EntityStore>): AudioComponent {
57+
return this.getComponent(ref, AudioComponent.getComponentType()) as AudioComponent
58+
}
59+
60+
fun Store<EntityStore>.model(ref: Ref<EntityStore>): ModelComponent {
61+
return this.getComponent(ref, ModelComponent.getComponentType()) as ModelComponent
62+
}
63+
64+
fun Store<EntityStore>.persistentModel(ref: Ref<EntityStore>): PersistentModel {
65+
return this.getComponent(ref, PersistentModel.getComponentType()) as PersistentModel
66+
}
67+
68+
fun Store<EntityStore>.item(ref: Ref<EntityStore>): ItemComponent {
69+
return this.getComponent(ref, ItemComponent.getComponentType()) as ItemComponent
70+
}
71+
72+
fun Store<EntityStore>.nameplate(ref: Ref<EntityStore>): Nameplate {
73+
return this.getComponent(ref, Nameplate.getComponentType()) as Nameplate
74+
}
75+
76+
fun Store<EntityStore>.repulsion(ref: Ref<EntityStore>): Repulsion {
77+
return this.getComponent(ref, Repulsion.getComponentType()) as Repulsion
78+
}
79+
80+
fun Store<EntityStore>.damageData(ref: Ref<EntityStore>): DamageDataComponent {
81+
return this.getComponent(ref, DamageDataComponent.getComponentType()) as DamageDataComponent
82+
}
83+
84+
fun Store<EntityStore>.maybeDeath(ref: Ref<EntityStore>): DeathComponent? {
85+
return this.getComponent(ref, DeathComponent.getComponentType())
86+
}
87+
88+
fun Store<EntityStore>.intangible(ref: Ref<EntityStore>): Boolean {
89+
return this.getComponent(ref, Intangible.getComponentType()) != null
90+
}
91+
92+
fun Store<EntityStore>.invulnerable(ref: Ref<EntityStore>): Boolean {
93+
return this.getComponent(ref, Invulnerable.getComponentType()) != null
94+
}
95+
96+
fun Store<EntityStore>.interactable(ref: Ref<EntityStore>): Boolean {
97+
return this.getComponent(ref, Interactable.getComponentType()) != null
98+
}
99+
100+
fun Store<EntityStore>.frozen(ref: Ref<EntityStore>): Boolean {
101+
return this.getComponent(ref, Frozen.getComponentType()) != null
102+
}
103+
104+
fun Store<EntityStore>.prop(ref: Ref<EntityStore>): Boolean {
105+
return this.getComponent(ref, PropComponent.getComponentType()) != null
106+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package dev.helight.kotale
2+
3+
import com.hypixel.hytale.event.IBaseEvent
4+
import com.hypixel.hytale.event.IEventRegistry
5+
6+
inline fun <reified EventType: IBaseEvent<*>> IEventRegistry.register(crossinline event: (EventType) -> Unit) {
7+
this.register(EventType::class.java) {
8+
event(it)
9+
}
10+
}
11+
12+
inline fun <reified EventType: IBaseEvent<Any>> IEventRegistry.registerGlobal(crossinline event: (EventType) -> Unit) {
13+
this.registerGlobal(EventType::class.java) {
14+
event(it)
15+
}
16+
}

mods/kotale/src/main/kotlin/dev/helight/kotale/KotalePlugin.kt renamed to mods/kotale/src/main/kotlin/dev/helight/kotale/KotalePluginImpl.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ package dev.helight.kotale
33
import com.hypixel.hytale.server.core.plugin.JavaPlugin
44
import com.hypixel.hytale.server.core.plugin.JavaPluginInit
55

6-
class KotalePlugin(init: JavaPluginInit) : JavaPlugin(init) {
6+
class KotalePluginImpl(init: JavaPluginInit) : JavaPlugin(init) {
77

88
override fun setup() {
99
super.setup()
1010
logger.atInfo().log("Kotale Plugin setup!")
1111
}
12-
}
12+
}
13+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package dev.helight.kotale
2+
3+
import com.hypixel.hytale.server.core.plugin.JavaPlugin
4+
import com.hypixel.hytale.server.core.plugin.JavaPluginInit
5+
import kotlinx.coroutines.CoroutineDispatcher
6+
import kotlinx.coroutines.CoroutineScope
7+
import kotlinx.coroutines.Dispatchers
8+
import kotlinx.coroutines.Job
9+
import kotlinx.coroutines.SupervisorJob
10+
import kotlinx.coroutines.launch
11+
12+
abstract class KotlinPlugin(init: JavaPluginInit) : JavaPlugin(init) {
13+
var supervisor = SupervisorJob()
14+
15+
override fun setup() {
16+
super.setup()
17+
supervisor = SupervisorJob()
18+
}
19+
20+
override fun shutdown() {
21+
super.shutdown()
22+
supervisor.cancel()
23+
}
24+
25+
fun launch(dispatcher: CoroutineDispatcher = Dispatchers.Default, block: suspend CoroutineScope.() -> Unit): Job {
26+
return CoroutineScope(supervisor + dispatcher).launch(block = block)
27+
}
28+
}

0 commit comments

Comments
 (0)