From c4a801843c63cdc7d59d72da347f5250d9fe53f9 Mon Sep 17 00:00:00 2001 From: Ben Forge <74168521+BenCheung0422@users.noreply.github.com> Date: Mon, 19 May 2025 01:46:33 +0800 Subject: [PATCH 1/3] Update EFP 5 --- efp/efp005/main.xml | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/efp/efp005/main.xml b/efp/efp005/main.xml index 6e3789c..2bbed05 100644 --- a/efp/efp005/main.xml +++ b/efp/efp005/main.xml @@ -21,10 +21,11 @@

- Since the Rust library would be directly compiled into native dynamic libraries to be loaded by - Java Virtual Machine (JVM) Java Native Interface (JNI), the code must be supported by the targetted - platforms. Therefore, when all modern supported platform versions of Windows, macOS and Linux are - targetted, the list of variations that should at least fully be supported is: + Since the Rust library would be directly compiled into native dynamic libraries to be used by + the Kotlin layer through Java Virtual Machine (JVM), the code must be supported by the targetted + platforms. Therefore, platform support would typically be aligned with + Java Runtime Environment (JRE) binary; when all modern supported platform versions of Windows, + macOS and Linux are targetted, the list of variations that should at least fully be supported is:

  • Linux 5+ x64/arm64
  • @@ -67,6 +68,33 @@

    +
    + +

    + Since the Engine would eventually be compiled into native libraries, which are not directly + compatible with JVM without an extra layer of transition, which is typical JNI. + Despite the emergence of modern projects like Project Panama, UniFFI, and JNR-FFI, which offer + alternative tools for bridging native code with JVM-managed code, JNI is still adopted + to maintain the neatness of the overall project framework. It helps preserve the clarity of the + project's overall framework while streamlining development workflows by relying on the + traditional toolset. Detailed workflow and process are still subject to consideration. +

    +

    + On the Kotlin layer, several classes wrapping the Engine's functionalities exist, stored in + terramodulus.engine.ferricia package. Those classes load the same generated dynamic + library built by the Engine project with specified function signatures. In the package of + terramodulus.engine, several classes would be made to directly use the classes + and functions in terramodulus.engine.ferricia. Please note that all the usages within + such classes would be low level, meaning that its code style should not be subject to the high + level code style guidelines. +

    +

    + To reduce JNI invoking overheads, the exposed functions should contain only essential code with + using the least call sites of exposed functions as far as possible for a single scope in + abstraction. +

    +
    +

    From de7c6474d8205d2b9498bcc8ebced9c540bdc056 Mon Sep 17 00:00:00 2001 From: Ben Forge <74168521+BenCheung0422@users.noreply.github.com> Date: Wed, 21 May 2025 07:40:30 +0800 Subject: [PATCH 2/3] Update EFP 5 --- efp/efp005/main.xml | 53 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/efp/efp005/main.xml b/efp/efp005/main.xml index 2bbed05..b3430d8 100644 --- a/efp/efp005/main.xml +++ b/efp/efp005/main.xml @@ -4,6 +4,7 @@ + @@ -47,7 +48,7 @@

    -
    +

    @@ -66,6 +67,52 @@ decoding, using native libraries would enhance the efficiency effectively; this includes Opus and Free Lossless Audio Codec (FLAC), which would be used in the Engine instead of the Kotlin layer.

    +

    + For distribution, native library dependencies may be handled by having them dynamically linked, + unlike Rust dependencies that are mostly statically linked. Also, keeping them dynamically linked + could ensure modularity and size efficiency. On Linux and macOS, when those could be the ones that + are system-wide installed or already installed on the system, they could be skipped by the Launcher + and the system libraries could be included them in the library path. To load the native + dependencies of the Engine, System.loadLibrary could be used, but could be + troublesome, thus, leveraging the system library loader to include the paths may be better. + In general, Windows searches libraries in the file's parent directory but on Linux and macOS, + either LD_LIBRARY_PATH/DYLD_LIBRARY_PATH of the game's native library + path has to be set as an environment variable (by process::Command::env) before + launching or setting RPATH to the Engine library. The latter may be the most flexible. + If there exist some existing libraries or widespread system libraries like libopus, + those libraries could be ignored when downloading onto Linux or macOS clients, installing those + globally via package manager if not installed and letting system's loader handle. + The soname system on Linux and macOS is adopted for library versioning, such that follow a major + version should ensure compatibility with future versions with the major version, but except that + not all libraries may follow this rule; this is handled for system-installed libraries when + required, so bundling them with the Engine without relying on system libraries would avoid this + potential incompatibility when such libraries are not widely adapted. + Common libraries like libopus and libFLAC both follow Semantic Versioning + and Linux's SONAME-based versioning, so they may be used from system libraries. + It is also important to check whether the required version is the same or older than the one + installed on the system with the same major version and perform update via the system's + package manager. + This could also ensure common practice on using dynamic libraries on UNIX-like platforms and size + efficiency of the Launcher's game native library directory, though not applicable on Windows. +

    +

    + For some dependencies of the Engine, there may exist some native dependencies. We may either + statically or dynamically link them to the Engine, but the latter might be more favorable to + manage better for memory usage and efficiency, as well as the aforementioned modularity + allowing system-installed libraries to be used. To distribute the native libraries, we may + only either copy the builds built from source during the Engine build process or build separately + from the Engine build. For the former method, it seems to be no other ways to locate the built + binaries within the build script as build scripts seem to be isolated from other crates' builds; + it is only possible to fork an existing *-sys crate or have a custom crate for + bindings and native library builds, with code that copies the resultant binaries to the target + directory near the Engine binary in the same directory. + For the latter method, there must be a workflow to ensure that the source version of the library + used by the binding crate is the same as the one separately built or obtained for distribution; + as long as the versions are the same, there should be no gap of implementation between the binaries. + Since the built binaries use soname, it is necessary to locate the binary with the filename matched + exactly the same as the soname of the library when copying binaries to output by following symlinks, + to ensure the library could be loaded by library loader. +

    @@ -77,7 +124,9 @@ alternative tools for bridging native code with JVM-managed code, JNI is still adopted to maintain the neatness of the overall project framework. It helps preserve the clarity of the project's overall framework while streamlining development workflows by relying on the - traditional toolset. Detailed workflow and process are still subject to consideration. + traditional toolset. Detailed workflow and process are still subject to consideration. Moreover, + further researches and investigation may have to be employed to consider whether the certain + toolset is suitable for this project architecture.

    On the Kotlin layer, several classes wrapping the Engine's functionalities exist, stored in From 6c9a8b0025b4828e755369432477716bf59af475 Mon Sep 17 00:00:00 2001 From: Ben Forge <74168521+BenCheung0422@users.noreply.github.com> Date: Fri, 23 May 2025 08:09:04 +0800 Subject: [PATCH 3/3] Update EFP 5 --- efp/efp005/main.xml | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/efp/efp005/main.xml b/efp/efp005/main.xml index b3430d8..893e3a4 100644 --- a/efp/efp005/main.xml +++ b/efp/efp005/main.xml @@ -96,10 +96,10 @@ efficiency of the Launcher's game native library directory, though not applicable on Windows.

    - For some dependencies of the Engine, there may exist some native dependencies. We may either + For some dependencies of the Engine, there may exist some native dependencies. One may either statically or dynamically link them to the Engine, but the latter might be more favorable to manage better for memory usage and efficiency, as well as the aforementioned modularity - allowing system-installed libraries to be used. To distribute the native libraries, we may + allowing system-installed libraries to be used. To distribute the native libraries, one may only either copy the builds built from source during the Engine build process or build separately from the Engine build. For the former method, it seems to be no other ways to locate the built binaries within the build script as build scripts seem to be isolated from other crates' builds; @@ -131,11 +131,17 @@

    On the Kotlin layer, several classes wrapping the Engine's functionalities exist, stored in terramodulus.engine.ferricia package. Those classes load the same generated dynamic - library built by the Engine project with specified function signatures. In the package of - terramodulus.engine, several classes would be made to directly use the classes - and functions in terramodulus.engine.ferricia. Please note that all the usages within - such classes would be low level, meaning that its code style should not be subject to the high - level code style guidelines. + library built by the Engine project with specified function signatures. All these functions must + be used internally, so they should be marked with internal visibility in their + signatures. In the package ofterramodulus.engine, several classes would be made to + directly use the classes and functions in terramodulus.engine.ferricia. + Please note that all the usages within such classes would be low level, meaning that its code style + should not be subject to the high level code style guidelines. It is good to separate client-only + and server-only classes into different modules so that they could be loaded with the built binary + with crate feature selected. Moreover, it may eventually come to the situation that the Kotlin + abstraction of the Engine itself could be made an isolated module named internal with + public interface exposed to other modules, to hide the low level components directly accessing + the Engine.

    To reduce JNI invoking overheads, the exposed functions should contain only essential code with @@ -179,6 +185,18 @@ or crashes happen, modern operating systems may still be allowed to free them up in the system level.

    +

    + When there exist some encapsulated, hidden or opaque native objects in the Engine, that are + supposed to be persisted out of the scope of single exported native function invocation, + the specific data structures may be defined within the Engine code without having the abstraction + form of the data objects defined in the Kotlin layer. The allocated objects should be + converted into a form of pointer by initializing a Box and casting to a raw pointer + as long via Box::into_raw; intermedia value read and write may be + performed from a dereference with borrow to the raw pointer passed from the Kotlin layer; + destruction of the objects should be invoked with Box::from_raw. All the object + management is handled within the internal part of the Kotlin communication layer with + the low level interface of the Engine. +