diff --git a/FEATURES.md b/FEATURES.md index 2ccd4b99..4e6bc7eb 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -156,8 +156,6 @@ Features that are in ***bold italics*** are planned but not yet implemented. ## Bugs -- [Script Assembly Editor](#script-assembly-editor): Go to definition doesn't work in RT (#231), PW2 -(#234) and Magnitude of Metal (#1, can jump to label 207 from line 433, but not from line 465) - When a modal dialog is open, global keybindings should be disabled - Entities with rendering issues: - Caves 4 Button door @@ -175,4 +173,3 @@ Features that are in ***bold italics*** are planned but not yet implemented. - Desert Fixed Type Box (Breakable Crystals) - Merissa A - Merissa AA -- [Event Actions](#Event Actions): Editing event actions can't be undone diff --git a/README.md b/README.md index 53251ad6..1faec076 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ ## Developers -TODO: This entire section is out of date since porting PW to Kotlin. +Phantasmal World is written in [Kotlin](https://kotlinlang.org/) and uses +the [Gradle](https://gradle.org/) build tool. Much of the code +is [multiplatform](https://kotlinlang.org/docs/multiplatform.html) and reusable as a library. Tests status @@ -20,82 +22,66 @@ See [features](./FEATURES.md) for a list of features, planned features and bugs. ### Getting Started -1. Install Node.js ([https://nodejs.org/](https://nodejs.org/)) -2. Install Yarn ([https://yarnpkg.com/](https://yarnpkg.com/)) -3. `cd` to the project directory -4. Install dependencies with `yarn` -5. Launch server on [http://localhost:1623/](http://localhost:1623/) with `yarn start` -6. [src/index.ts](src/index.ts) is the application's entry point +1. Install Java 11+ (e.g. [AdoptOpenJDK](https://adoptopenjdk.net/) + or [GraalVM](https://www.graalvm.org/downloads/)) +2. `cd` to the project directory +3. Launch webpack server on [http://localhost:1623/](http://localhost:1623/) + with `./gradlew :web:run --continuous` +4. [web/src/main/kotlin/world/phantasmal/web/Main.kt](web/src/main/kotlin/world/phantasmal/web/Main.kt) + is the application's entry point + +[IntelliJ IDEA](https://www.jetbrains.com/idea/download/) is recommended for development. IntelliJ +setup: + +1. Use Ctrl-Alt-Shift-S to open the Project Structure window and select a JDK (you can let IntelliJ + download a JDK if you don't have a compatible one installed) +2. Configure the Gradle run task: + 1. In the Gradle window, right click web -> Tasks -> other -> run + 2. Click "Modify Run Configuration..." + 3. Add `--continuous` to the arguments field + 4. Click OK + 5. You can now start the webpack server from the main toolbar ### Exploring the Code Base -The code base is divided up into a [core](src/core) module, an [application](src/application) module -and a module per tool (e.g. [quest_editor](src/quest_editor)). The core module contains the base -code that the other modules depend on. The application module contains the -[main application view](src/application/gui/ApplicationView.ts) that provides navigation between the -different tools. The application view lazily loads and initializes the necessary modules. Each other -module represents a tool such as the quest editor or the hunt optimizer. +The code base is divided up into the following gradle subprojects. -#### Submodules +#### core -All modules have an index.ts file that contains an initialization function. They then have several -common submodules such as controllers, gui, model and stores and some module-specific submodules. +Core contains the basic utilities that all other subprojects directly or indirectly depend on. -##### GUI +#### lib -The gui submodule contains views with minimal logic. They simply display what their controller -provides and forward user input to it. Their only dependency is the DOM and a single controller. -Keeping logic out of the views makes the UI easier to test. We don't really need to test the views -as they don't contain complex code, just testing the controller layer gives us confidence that the -UI works. The only automatic tests for the gui layer are -[snapshot tests](https://jestjs.io/docs/en/snapshot-testing). +Lib contains PSO file format parsers, compression/decompression code, a PSO script +assembler/disassembler and a work-in-progress script engine/VM. It also has a model of the PSO +scripting bytecode and data flow analysis for it. This subproject can be used as a library in other +projects. -##### Controllers +#### observable -The controllers submodule contains the [controllers](src/core/controllers/Controller.ts) on which -views depend. Usually the view-controller relationship is one-to-one, sometimes it's many-to-one -(e.g. when a view has many subviews that work with the same data). A controller usually extracts -data from a shared store and transforms it into a format which the view can easily consume. +A full-fledged multiplatform implementation of the observer pattern. -##### Model +#### test-utils -The model submodule contains observable model objects. Models expose read-only observable properties -and allow their properties to be changed via setters which validate their inputs. +Test utilities used by the other subprojects. -##### Stores +#### [web](web/README.md) -The stores submodule contains shared data [stores](src/core/stores/Store.ts). Stores ensure that -data is loaded when necessary and that the data is deduplicated. Stores also contain ephemeral -shared state such as the currently selected entity in the quest editor. +The actual Phantasmal World web application. -#### Some Interesting Parts of the Code Base +#### webgui -Phantasmal contains parsers for many of the client's formats in -[src/core/data_formats](src/core/data_formats). A model of the PSO scripting byte code and data flow -analysis for it can be found in [src/core/data_formats/asm](src/core/data_formats/asm). The -[src/quest_editor/scripting](src/quest_editor/scripting) directory contains an assembler, -disassembler and (partly implemented) virtual machine. +Web GUI toolkit used by Phantasmal World. ### Unit Tests -Run the unit tests with `yarn test` or `yarn test --watch` if you want the relevant tests to be -re-run whenever a file is changed. The testing framework used is Jest. +Run the unit tests with `./gradlew test`. JS tests are run with Karma and Mocha, JVM tests with +Junit 5. -### Code Style, Linting and Formatting +### Code Style and Formatting -Class/interface/type names are in `PascalCase` and all other identifiers are in `snake_case`. - -ESLint and Prettier are used for linting and formatting. Run with `yarn lint` and/or configure your -editor to use the ESLint/Prettier configuration. +The Kotlin [coding conventions](https://kotlinlang.org/docs/coding-conventions.html) are used. ### Production Build -Create an optimized production build with `yarn build`. - -### Optional Modules - -### prs-rs - -Provides faster PRS routines using WebAssembly. Build for WebPack with `yarn build_prs_rs_browser`. -Build for Jest with `yarn build_prs_rs_testing`. Building requires -[wasm-pack](https://github.com/rustwasm/wasm-pack). +Create an optimized production build with `./gradlew :web:browserDistribution`. diff --git a/web/README.md b/web/README.md new file mode 100644 index 00000000..3971e0dc --- /dev/null +++ b/web/README.md @@ -0,0 +1,71 @@ +# web + +This is the main Phantasmal World web application. It consists of several tools, each in their own +package. Beside these packages there's also an application, core and externals package. + +## Main Packages + +### application + +The application package contains the main application view that provides navigation between the +different tools. The application view lazily loads and initializes the necessary tools. + +### core + +Contains code that is reused throughout the web project. + +### externals + +External declarations for NPM dependencies. + +### huntOptimizer, questEditor, viewer + +One main package per tool. Each tool is encapsulated in a PwTool implementation. + +## Common Structure + +The main packages all follow the same structure except for the externals package. + +### widgets + +The widgets package contains views with minimal logic. They simply display the models their +controller provides and forward user input to their controller. Their only dependency is the DOM and +a single controller. + +Keeping logic out of the views makes the UI easier to test. We don't really need to have unit tests +for the views as they don't contain complex code, just having unit tests from controller layer down +and manually smoke testing the GUI layer gives us enough confidence that everything works. + +### controllers + +The controllers package contains the controllers on which views depend. Usually the view-controller +relationship is one-to-one, sometimes it's many-to-one +(e.g. when a view has many subviews that work with the same data). A controller usually extracts +data from a shared store and transforms it into a format which the view can easily consume. A +controller has no knowledge of the GUI layer. + +### models + +The models package contains observable model objects. Models expose read-only observable properties +and allow their properties to be changed via setters which validate their inputs. + +### stores + +The stores package contains shared data stores. Stores ensure that data is loaded when necessary and +that the data is deduplicated. Stores also contain ephemeral shared state such as the currently +selected entity in the quest editor. + +## Subprojects + +### web:assembly-worker + +Does analysis of the script assembly code and runs in a worker thread. + +### web:assets-generation + +This code is manually run to generate various assets used by web such as item lists, drop tables, +quest lists, etc. + +### web:shared + +Contains code used by web, web:assembly-worker and web:assets-generation. diff --git a/web/src/main/kotlin/world/phantasmal/web/viewer/Viewer.kt b/web/src/main/kotlin/world/phantasmal/web/viewer/Viewer.kt index 053ff494..06c0bb43 100644 --- a/web/src/main/kotlin/world/phantasmal/web/viewer/Viewer.kt +++ b/web/src/main/kotlin/world/phantasmal/web/viewer/Viewer.kt @@ -6,13 +6,13 @@ import world.phantasmal.web.core.PwToolType import world.phantasmal.web.core.loading.AssetLoader import world.phantasmal.web.core.rendering.DisposableThreeRenderer import world.phantasmal.web.core.widgets.RendererWidget -import world.phantasmal.web.viewer.controller.CharacterClassOptionsController -import world.phantasmal.web.viewer.controller.ViewerController -import world.phantasmal.web.viewer.controller.ViewerToolbarController +import world.phantasmal.web.viewer.controllers.CharacterClassOptionsController +import world.phantasmal.web.viewer.controllers.ViewerController +import world.phantasmal.web.viewer.controllers.ViewerToolbarController import world.phantasmal.web.viewer.loading.CharacterClassAssetLoader import world.phantasmal.web.viewer.rendering.MeshRenderer import world.phantasmal.web.viewer.rendering.TextureRenderer -import world.phantasmal.web.viewer.store.ViewerStore +import world.phantasmal.web.viewer.stores.ViewerStore import world.phantasmal.web.viewer.widgets.CharacterClassOptionsWidget import world.phantasmal.web.viewer.widgets.ViewerToolbar import world.phantasmal.web.viewer.widgets.ViewerWidget diff --git a/web/src/main/kotlin/world/phantasmal/web/viewer/controller/CharacterClassOptionsController.kt b/web/src/main/kotlin/world/phantasmal/web/viewer/controllers/CharacterClassOptionsController.kt similarity index 89% rename from web/src/main/kotlin/world/phantasmal/web/viewer/controller/CharacterClassOptionsController.kt rename to web/src/main/kotlin/world/phantasmal/web/viewer/controllers/CharacterClassOptionsController.kt index 93810d42..841e8f2a 100644 --- a/web/src/main/kotlin/world/phantasmal/web/viewer/controller/CharacterClassOptionsController.kt +++ b/web/src/main/kotlin/world/phantasmal/web/viewer/controllers/CharacterClassOptionsController.kt @@ -1,9 +1,9 @@ -package world.phantasmal.web.viewer.controller +package world.phantasmal.web.viewer.controllers import world.phantasmal.observable.value.Val import world.phantasmal.observable.value.plus import world.phantasmal.web.shared.dto.SectionId -import world.phantasmal.web.viewer.store.ViewerStore +import world.phantasmal.web.viewer.stores.ViewerStore import world.phantasmal.webui.controllers.Controller class CharacterClassOptionsController(private val store: ViewerStore) : Controller() { diff --git a/web/src/main/kotlin/world/phantasmal/web/viewer/controller/ViewerController.kt b/web/src/main/kotlin/world/phantasmal/web/viewer/controllers/ViewerController.kt similarity index 88% rename from web/src/main/kotlin/world/phantasmal/web/viewer/controller/ViewerController.kt rename to web/src/main/kotlin/world/phantasmal/web/viewer/controllers/ViewerController.kt index 46a4a788..0a0d2693 100644 --- a/web/src/main/kotlin/world/phantasmal/web/viewer/controller/ViewerController.kt +++ b/web/src/main/kotlin/world/phantasmal/web/viewer/controllers/ViewerController.kt @@ -1,8 +1,8 @@ -package world.phantasmal.web.viewer.controller +package world.phantasmal.web.viewer.controllers import world.phantasmal.observable.value.Val import world.phantasmal.web.viewer.models.CharacterClass -import world.phantasmal.web.viewer.store.ViewerStore +import world.phantasmal.web.viewer.stores.ViewerStore import world.phantasmal.webui.controllers.Tab import world.phantasmal.webui.controllers.TabContainerController diff --git a/web/src/main/kotlin/world/phantasmal/web/viewer/controller/ViewerToolbarController.kt b/web/src/main/kotlin/world/phantasmal/web/viewer/controllers/ViewerToolbarController.kt similarity index 98% rename from web/src/main/kotlin/world/phantasmal/web/viewer/controller/ViewerToolbarController.kt rename to web/src/main/kotlin/world/phantasmal/web/viewer/controllers/ViewerToolbarController.kt index 2b52d864..17663301 100644 --- a/web/src/main/kotlin/world/phantasmal/web/viewer/controller/ViewerToolbarController.kt +++ b/web/src/main/kotlin/world/phantasmal/web/viewer/controllers/ViewerToolbarController.kt @@ -1,4 +1,4 @@ -package world.phantasmal.web.viewer.controller +package world.phantasmal.web.viewer.controllers import mu.KotlinLogging import org.w3c.files.File @@ -11,7 +11,7 @@ import world.phantasmal.lib.fileFormats.ninja.* import world.phantasmal.lib.fileFormats.parseAfs import world.phantasmal.observable.value.Val import world.phantasmal.observable.value.mutableVal -import world.phantasmal.web.viewer.store.ViewerStore +import world.phantasmal.web.viewer.stores.ViewerStore import world.phantasmal.webui.controllers.Controller import world.phantasmal.webui.extension import world.phantasmal.webui.readFile diff --git a/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/MeshRenderer.kt b/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/MeshRenderer.kt index ef6c74a6..ce59fc90 100644 --- a/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/MeshRenderer.kt +++ b/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/MeshRenderer.kt @@ -9,7 +9,7 @@ import world.phantasmal.web.core.rendering.conversion.createAnimationClip import world.phantasmal.web.core.rendering.conversion.ninjaObjectToSkinnedMesh import world.phantasmal.web.core.times import world.phantasmal.web.externals.three.* -import world.phantasmal.web.viewer.store.ViewerStore +import world.phantasmal.web.viewer.stores.ViewerStore import kotlin.math.tan class MeshRenderer( @@ -58,11 +58,11 @@ class MeshRenderer( super.render() animation?.let { - val action = it.mixer.clipAction(it.clip) - - if (!action.paused) { - // TODO: Update current animation frame in store. - } + // TODO: Update current animation frame in store. +// val action = it.mixer.clipAction(it.clip) +// +// if (!action.paused) { +// } } } diff --git a/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/TextureRenderer.kt b/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/TextureRenderer.kt index 9fe89588..b9bff163 100644 --- a/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/TextureRenderer.kt +++ b/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/TextureRenderer.kt @@ -9,7 +9,7 @@ import world.phantasmal.web.core.rendering.* import world.phantasmal.web.core.rendering.Renderer import world.phantasmal.web.core.rendering.conversion.xvrTextureToThree import world.phantasmal.web.externals.three.* -import world.phantasmal.web.viewer.store.ViewerStore +import world.phantasmal.web.viewer.stores.ViewerStore import world.phantasmal.webui.obj import kotlin.math.ceil import kotlin.math.max diff --git a/web/src/main/kotlin/world/phantasmal/web/viewer/store/ViewerStore.kt b/web/src/main/kotlin/world/phantasmal/web/viewer/stores/ViewerStore.kt similarity index 99% rename from web/src/main/kotlin/world/phantasmal/web/viewer/store/ViewerStore.kt rename to web/src/main/kotlin/world/phantasmal/web/viewer/stores/ViewerStore.kt index 287783b0..fe667ab9 100644 --- a/web/src/main/kotlin/world/phantasmal/web/viewer/store/ViewerStore.kt +++ b/web/src/main/kotlin/world/phantasmal/web/viewer/stores/ViewerStore.kt @@ -1,4 +1,4 @@ -package world.phantasmal.web.viewer.store +package world.phantasmal.web.viewer.stores import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch diff --git a/web/src/main/kotlin/world/phantasmal/web/viewer/widgets/CharacterClassOptionsWidget.kt b/web/src/main/kotlin/world/phantasmal/web/viewer/widgets/CharacterClassOptionsWidget.kt index 6fe0b09f..bf529741 100644 --- a/web/src/main/kotlin/world/phantasmal/web/viewer/widgets/CharacterClassOptionsWidget.kt +++ b/web/src/main/kotlin/world/phantasmal/web/viewer/widgets/CharacterClassOptionsWidget.kt @@ -4,7 +4,7 @@ import kotlinx.coroutines.launch import org.w3c.dom.Node import world.phantasmal.observable.value.value import world.phantasmal.web.shared.dto.SectionId -import world.phantasmal.web.viewer.controller.CharacterClassOptionsController +import world.phantasmal.web.viewer.controllers.CharacterClassOptionsController import world.phantasmal.webui.dom.div import world.phantasmal.webui.dom.table import world.phantasmal.webui.dom.td diff --git a/web/src/main/kotlin/world/phantasmal/web/viewer/widgets/ViewerToolbar.kt b/web/src/main/kotlin/world/phantasmal/web/viewer/widgets/ViewerToolbar.kt index 557f7bde..7484ce60 100644 --- a/web/src/main/kotlin/world/phantasmal/web/viewer/widgets/ViewerToolbar.kt +++ b/web/src/main/kotlin/world/phantasmal/web/viewer/widgets/ViewerToolbar.kt @@ -2,7 +2,7 @@ package world.phantasmal.web.viewer.widgets import kotlinx.coroutines.launch import org.w3c.dom.Node -import world.phantasmal.web.viewer.controller.ViewerToolbarController +import world.phantasmal.web.viewer.controllers.ViewerToolbarController import world.phantasmal.webui.dom.Icon import world.phantasmal.webui.dom.div import world.phantasmal.webui.widgets.* diff --git a/web/src/main/kotlin/world/phantasmal/web/viewer/widgets/ViewerWidget.kt b/web/src/main/kotlin/world/phantasmal/web/viewer/widgets/ViewerWidget.kt index 19636902..146f7521 100644 --- a/web/src/main/kotlin/world/phantasmal/web/viewer/widgets/ViewerWidget.kt +++ b/web/src/main/kotlin/world/phantasmal/web/viewer/widgets/ViewerWidget.kt @@ -2,8 +2,8 @@ package world.phantasmal.web.viewer.widgets import kotlinx.coroutines.launch import org.w3c.dom.Node -import world.phantasmal.web.viewer.controller.ViewerController -import world.phantasmal.web.viewer.controller.ViewerTab +import world.phantasmal.web.viewer.controllers.ViewerController +import world.phantasmal.web.viewer.controllers.ViewerTab import world.phantasmal.webui.dom.div import world.phantasmal.webui.widgets.TabContainer import world.phantasmal.webui.widgets.Widget