All code now compiles with Kotlin 2.1.20, all tests pass, development builds work and production builds work.

This commit is contained in:
Daan Vanden Bosch 2025-03-21 13:17:19 +01:00
parent a8b1a00e06
commit 15bc398294
14 changed files with 290 additions and 600 deletions
.github/workflows
README.md
buildSrc
gradle/wrapper
kotlin-js-store
web
assembly-worker/src/jsMain/kotlin/world/phantasmal/web/assemblyWorker
build.gradle.kts
src
jsMain/kotlin/world/phantasmal/web
jsTest/kotlin/world/phantasmal/web/test

View File

@ -17,7 +17,7 @@ jobs:
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '11'
java-version: '17'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v3

View File

@ -18,7 +18,7 @@ jobs:
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '11'
java-version: '17'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v3

View File

@ -24,8 +24,8 @@ See [features](./FEATURES.md) for a list of features, planned features and bugs.
### Getting Started
1. Install Java 11+ ([GraalVM](https://www.graalvm.org/downloads/) is recommended, and necessary if
you want to produce native builds of the PSO server)
1. Install Java 17 ([Temurin](https://adoptium.net/temurin/releases/?version=17&package=jdk) is
recommended)
2. Ensure the JAVA_HOME environment variable is set to JDK's location
Then, for the web application:
@ -95,8 +95,6 @@ Work-in-progress PSO server and fully functional PSO proxy server.
Run the unit tests with `./gradlew check`. JS tests are run with Karma and Mocha, JVM tests with
Junit 5. Tests can also be run per project with e.g. `./gradlew :psolib:check`.
TODO: Figure out why `./gradlew check` runs the cell tests twice since upgrade to Gradle 8.9.
### Code Style and Formatting
The Kotlin [coding conventions](https://kotlinlang.org/docs/coding-conventions.html) are used.

View File

@ -7,6 +7,6 @@ repositories {
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0")
implementation("org.jetbrains.kotlin:kotlin-serialization:1.9.24")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.20")
implementation("org.jetbrains.kotlin:kotlin-serialization:2.1.0")
}

View File

@ -1,8 +1,9 @@
package world.phantasmal
import org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11
import org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinTest
plugins {
kotlin("plugin.serialization") apply false
@ -27,7 +28,7 @@ tasks.withType<KotlinCompilationTask<*>> {
tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
jvmTarget.set(JVM_11)
jvmTarget.set(JVM_17)
freeCompilerArgs.addAll(
"-Xjdk-release=${jvmTarget.get().target}",
"-Xjvm-default=all",
@ -35,12 +36,21 @@ tasks.withType<KotlinCompile>().configureEach {
}
}
// Non-JVM tests.
tasks.withType<KotlinTest>().configureEach {
// Always run all tests.
outputs.upToDateWhen { false }
}
// JVM tests.
tasks.withType<Test>().configureEach {
// Always run all tests.
outputs.upToDateWhen { false }
useJUnitPlatform()
}
project.extra["coroutinesVersion"] = "1.9.0-RC"
project.extra["coroutinesVersion"] = "1.10.1"
project.extra["kotlinLoggingVersion"] = "2.0.11"
project.extra["ktorVersion"] = "2.3.12"
project.extra["log4jVersion"] = "2.14.1"
project.extra["serializationVersion"] = "1.7.1"
project.extra["serializationVersion"] = "1.8.0"

View File

@ -8,7 +8,7 @@ plugins {
val log4jVersion: String by project.extra
kotlin {
jvmToolchain(11)
jvmToolchain(17)
}
dependencies {

View File

@ -11,7 +11,7 @@ val kotlinLoggingVersion: String by project.extra
val log4jVersion: String by project.extra
kotlin {
jvmToolchain(11)
jvmToolchain(17)
jvm {}

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

File diff suppressed because it is too large Load Diff

View File

@ -63,10 +63,6 @@ class AsmServer(
is ClientNotification.UpdateAsm ->
asmAnalyser.updateAsm(message.changes)
else ->
// Should be processed by processMessage.
logger.error { "Unexpected ${message::class.simpleName}." }
}
}

View File

@ -1,8 +1,9 @@
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
plugins {
id("world.phantasmal.js")
}
val ktorVersion: String by project.extra
val serializationVersion: String by project.extra
kotlin {
@ -26,13 +27,11 @@ kotlin {
sourceSets {
getByName("jsMain") {
dependencies {
implementation(kotlin("reflect"))
implementation(project(":psolib"))
implementation(project(":webui"))
implementation(project(":web:shared"))
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.0")
implementation(npm("golden-layout", "^1.5.9"))
@ -54,7 +53,7 @@ kotlin {
}
val copyAssemblyWorkerJsTask = tasks.register<Copy>("copyAssemblyWorkerJs") {
dependsOn(":web:assembly-worker:build")
dependsOn(":web:assembly-worker:jsBrowserDistribution")
val workerDist =
project(":web:assembly-worker").layout.buildDirectory.get().asFile.resolve("dist/js/productionExecutable")

View File

@ -1,12 +1,7 @@
package world.phantasmal.web
import io.ktor.client.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.*
import kotlinx.browser.document
import kotlinx.browser.window
import kotlinx.coroutines.cancel
import kotlinx.datetime.Clock
import mu.KotlinLoggingConfiguration
import mu.KotlinLoggingLevel
@ -22,7 +17,6 @@ import world.phantasmal.web.core.persistence.LocalStorageKeyValueStore
import world.phantasmal.web.core.rendering.DisposableThreeRenderer
import world.phantasmal.web.core.stores.ApplicationUrl
import world.phantasmal.web.externals.three.WebGLRenderer
import world.phantasmal.web.shared.JSON_FORMAT
import world.phantasmal.web.shared.logging.LogAppender
import world.phantasmal.web.shared.logging.LogFormatter
import world.phantasmal.webui.dom.disposableListener
@ -49,18 +43,11 @@ private fun init(): Disposable {
val rootElement = document.body!!.root()
val httpClient = HttpClient {
install(ContentNegotiation) {
serialization(ContentType.Application.Json, JSON_FORMAT)
}
}
disposer.add(disposable { httpClient.cancel() })
disposer.add(
Application(
rootElement,
LocalStorageKeyValueStore(),
AssetLoader(httpClient),
AssetLoader(),
disposer.add(HistoryApplicationUrl()),
::createThreeRenderer,
Clock.System,

View File

@ -1,30 +1,37 @@
package world.phantasmal.web.core.loading
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.util.reflect.*
import io.ktor.utils.io.js.*
import kotlin.reflect.KType
import kotlin.reflect.typeOf
import kotlinx.browser.window
import kotlinx.coroutines.await
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.json.decodeFromDynamic
import kotlinx.serialization.serializer
import org.khronos.webgl.ArrayBuffer
import org.w3c.fetch.Response
import world.phantasmal.core.unsafe.unsafeCast
import world.phantasmal.web.shared.JSON_FORMAT
@OptIn(ExperimentalSerializationApi::class)
class AssetLoader(
private val httpClient: HttpClient,
private val origin: String = window.location.origin,
private val basePath: String = defaultBasePath(),
) {
suspend inline fun <reified T : Any> load(path: String): T =
load(path, typeInfo<T>())
load(path, typeOf<T>())
suspend fun <T : Any> load(path: String, typeInfo: TypeInfo): T =
get(path).body(typeInfo)
suspend fun <T : Any> load(path: String, type: KType) =
JSON_FORMAT.decodeFromDynamic(
unsafeCast<KSerializer<T>>(serializer(type)),
get(path).json().await(),
)
suspend fun loadArrayBuffer(path: String): ArrayBuffer =
get(path).bodyAsChannel().readRemaining().readArrayBuffer()
get(path).arrayBuffer().await()
private suspend fun get(path: String): HttpResponse =
httpClient.get("$origin$basePath$path")
private suspend fun get(path: String): Response =
window.fetch("$origin$basePath$path").await()
companion object {
fun defaultBasePath(): String {

View File

@ -1,14 +1,10 @@
package world.phantasmal.web.test
import io.ktor.client.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.*
import kotlinx.coroutines.cancel
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
import kotlinx.datetime.Clock
import org.w3c.dom.HTMLCanvasElement
import world.phantasmal.core.disposable.Disposable
import world.phantasmal.core.disposable.disposable
import world.phantasmal.testUtils.TestContext
import world.phantasmal.web.core.loading.AssetLoader
import world.phantasmal.web.core.persistence.KeyValueStore
@ -24,35 +20,22 @@ import world.phantasmal.web.questEditor.loading.AreaAssetLoader
import world.phantasmal.web.questEditor.loading.QuestLoader
import world.phantasmal.web.questEditor.stores.AreaStore
import world.phantasmal.web.questEditor.stores.QuestEditorStore
import world.phantasmal.web.shared.JSON_FORMAT
import world.phantasmal.web.viewer.loading.AnimationAssetLoader
import world.phantasmal.web.viewer.loading.CharacterClassAssetLoader
import world.phantasmal.web.viewer.stores.ViewerStore
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/**
* Assigning a disposable to any of the properties in this class will add the assigned value to
* [ctx]'s disposer.
*/
class TestComponents(private val ctx: TestContext) {
var httpClient: HttpClient by default {
HttpClient {
install(ContentNegotiation) {
serialization(ContentType.Application.Json, JSON_FORMAT)
}
}.also {
ctx.disposer.add(disposable { it.cancel() })
}
}
var clock: Clock by default { StubClock() }
var applicationUrl: ApplicationUrl by default { TestApplicationUrl("") }
// Asset Loaders
var assetLoader: AssetLoader by default { AssetLoader(httpClient, basePath = "/assets") }
var assetLoader: AssetLoader by default { AssetLoader(basePath = "/assets") }
var characterClassAssetLoader: CharacterClassAssetLoader by default {
CharacterClassAssetLoader(assetLoader)