Skip to content

Commit

Permalink
[SYNC] Adding dynamic extensions feature. (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
Neloreck authored Jul 20, 2023
2 parents 2710cf2 + 2381253 commit f335938
Show file tree
Hide file tree
Showing 74 changed files with 1,623 additions and 409 deletions.
132 changes: 66 additions & 66 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: build-and-test

on:
push:
branches: [ "main", "dev" ]
branches: ["main", "dev"]
pull_request:
branches: [ "main", "dev" ]
branches: ["main", "dev"]

jobs:
test:
Expand All @@ -24,7 +24,7 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
cache: "npm"

- run: npm ci
- run: npm run setup
Expand All @@ -45,20 +45,20 @@ jobs:
node-version: [19.x]

steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- uses: actions/checkout@v3
with:
submodules: recursive

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: "npm"

- run: npm ci
- run: npm run setup
- run: npm run typecheck
- run: npm run lint
- run: npm ci
- run: npm run setup
- run: npm run typecheck
- run: npm run lint

build:
runs-on: ubuntu-latest
Expand All @@ -68,61 +68,61 @@ jobs:
node-version: [19.x]

steps:
- uses: actions/checkout@v3
with:
submodules: recursive

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

- run: npm ci
- run: npm run setup
- run: npm run pack:mod

- name: Install zip
uses: montudor/action-zip@v1

- name: Move build statics
run: |
mv target/xrf_build.log target/mod_package/xrf_build.log
mv target/README.md target/mod_package/README.md
- name: Archive build artifacts
working-directory: target/mod_package
run: zip -qq -r gamedata.release.zip gamedata/ bin/ xrf_build.log README.md

- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
if-no-files-found: error
name: gamedata-release
path: target/mod_package/gamedata.release.zip
retention-days: 1
- uses: actions/checkout@v3
with:
submodules: recursive

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: "npm"

- run: npm ci
- run: npm run setup
- run: npm run pack:mod

- name: Install zip
uses: montudor/action-zip@v1

- name: Move build statics
run: |
mv target/xrf_build.log target/mod_package/xrf_build.log
mv target/README.md target/mod_package/README.md
- name: Archive build artifacts
working-directory: target/mod_package
run: zip -qq -r gamedata.release.zip gamedata/ bin/ xrf_build.log README.md

- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
if-no-files-found: error
name: gamedata-release
path: target/mod_package/gamedata.release.zip
retention-days: 1

upload-nightly:
if: ${{ github.ref_name == 'dev' && github.event_name != 'pull_request' }}
needs: [test, check, build]
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- uses: actions/download-artifact@master
with:
name: gamedata-release

- name: Update nightly tag
uses: EndBug/latest-tag@latest
if: github.ref_name == 'dev'
id: update-nightly-tag
with:
ref: nightly

- name: Publish nightly gamedata build
if: ${{ steps.update-nightly-tag.outcome == 'success' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release upload --clobber nightly gamedata.release.zip
- uses: actions/checkout@v3

- uses: actions/download-artifact@master
with:
name: gamedata-release

- name: Update nightly tag
uses: EndBug/latest-tag@latest
if: github.ref_name == 'dev'
id: update-nightly-tag
with:
ref: nightly

- name: Publish nightly gamedata build
if: ${{ steps.update-nightly-tag.outcome == 'success' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release upload --clobber nightly gamedata.release.zip
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Template for stalker mods and modded game packages. <br/>
- Game profiling / debugging tools
- Creation of custom modded game package
- Unit tests coverage
- [WIP] [Modular extensions](./src/engine/extensions/README.md)

## 📍 Purpose

Expand Down Expand Up @@ -117,3 +118,8 @@ Additional assets repository can be cloned manually or with shortcut command: <b
- UA locale assets: [https://gitlab.com/xray-forge/stalker-xrf-resources-locale-ukr](https://gitlab.com/xray-forge/stalker-xrf-resources-locale-ukr)
- RU locale assets: [https://gitlab.com/xray-forge/stalker-xrf-resources-locale-rus](https://gitlab.com/xray-forge/stalker-xrf-resources-locale-rus)

## 🧰 Bugs

It took 3 months just to rewrite all the 20 years LUA codebase to typescript and create custom transformers to support luabind. <br/>
Further game testing and re-architecture produces new bugs and issues which are easier to prevent with unit tests. <br/>
As for now, main focus is separation and clarification of logics and unit testing coverage.
10 changes: 3 additions & 7 deletions cli/build/plugins/from_cast_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { isIdentifier, SyntaxKind } from "typescript";
import { Plugin } from "typescript-to-lua";
import { createErrorDiagnosticFactory } from "./utils/diagnostics";

const FROM_ARRAY_IDENTIFIER: string = "$fromArray";
const FROM_OBJECT_IDENTIFIER: string = "$fromObject";
const FROM_CAST_METHODS: Array<string> = ["$fromObject", "$fromArray", "$fromLuaArray", "$fromLuaTable"];

/**
* Push generic error to notify about usage issue.
Expand All @@ -13,16 +12,13 @@ const createInvalidFunctionCallError = createErrorDiagnosticFactory((name?: stri
});

/**
* Plugin for transformation of $fromObject and $fromArray calls.
* Plugin for transformation of casting methods.
* Simplifies TS/Lua testing and interoperation.
*/
const plugin: Plugin = {
visitors: {
[SyntaxKind.CallExpression]: (node, context) => {
if (
isIdentifier(node.expression) &&
(node.expression.text === FROM_ARRAY_IDENTIFIER || node.expression.text === FROM_OBJECT_IDENTIFIER)
) {
if (isIdentifier(node.expression) && FROM_CAST_METHODS.includes(node.expression.text)) {
if (node.arguments.length !== 1) {
context.diagnostics.push(createInvalidFunctionCallError(node));
}
Expand Down
7 changes: 3 additions & 4 deletions cli/build/tsconfig.scripts.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"include": [
"../../src/typedefs",
"../../src/engine/lib",
"../../src/engine/scripts"
"../../src/engine/scripts",
"../../src/engine/extensions",
],
"exclude": [
"../../**/*.test.ts"
Expand All @@ -20,9 +21,7 @@
"luaTarget": "JIT",
"noImplicitGlobalVariables": true,
"noImplicitSelf": true,
"noResolvePaths": [
"xray16"
],
"noResolvePaths": ["xray16"],
"luaPlugins": [
{ "name": "./plugins/transform_luabind_class/plugin.ts" },
{ "name": "./plugins/global_declarations_transform.ts" },
Expand Down
9 changes: 4 additions & 5 deletions cli/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
"available_locales": ["ukr", "eng", "pol", "rus"],
"resources": {
"mod_assets_base_folder": "..\\src\\resources\\",
"mod_assets_override_folders": [
"..\\..\\stalker-xrf-resources-extended"
],
"mod_assets_override_folders": ["..\\..\\stalker-xrf-resources-extended"],
"mod_assets_locales": {
"eng": ["..\\..\\stalker-xrf-resources-locale-eng"],
"ukr": ["..\\..\\stalker-xrf-resources-locale-ukr"],
Expand All @@ -18,6 +16,7 @@
"configs": "../src/engine/configs",
"scripts": "../src/engine/scripts",
"translations": "../src/engine/translations",
"extensions": "../src/engine/extensions",
"ui": "../src/engine/forms"
},
"compression": {
Expand All @@ -30,7 +29,7 @@
".\\bin\\package\\fsgame.ltx",
".\\bin\\package\\user.ltx"
],
"gamedata": ["lualib_bundle.script", "scripts", "core", "lib"]
"gamedata": ["lualib_bundle.script", "scripts", "core", "extensions", "lib"]
},
"targets": {
"stalker_game_steam_id": 41700,
Expand All @@ -42,7 +41,7 @@
"url": "https://gitlab.com/xray-forge/stalker-xrf-resources-extended.git",
"description": "Full base gamedata assets for custom game repacks"
},
"locale-eng":{
"locale-eng": {
"url": "https://gitlab.com/xray-forge/stalker-xrf-resources-locale-en.git",
"description": "English locale files, mainly game character voices"
},
Expand Down
1 change: 1 addition & 0 deletions cli/globals/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const CLI_CONFIG: string = path.resolve(CLI_DIR, "config.json");

export const GAME_DATA_LTX_CONFIGS_DIR: string = path.resolve(CLI_DIR, config.build.configs);
export const GAME_DATA_SCRIPTS_DIR: string = path.resolve(CLI_DIR, config.build.scripts);
export const GAME_DATA_EXTENSIONS_DIR: string = path.resolve(CLI_DIR, config.build.extensions);
export const GAME_DATA_TRANSLATIONS_DIR: string = path.resolve(CLI_DIR, config.build.translations);
export const GAME_DATA_UI_DIR: string = path.resolve(CLI_DIR, config.build.ui);

Expand Down
10 changes: 8 additions & 2 deletions src/engine/configs/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# [XRF](../../../) / SRC / ENGINE / INI
# [XRF](../../../) / SRC / ENGINE / CONFIGS

## LTX configurations TS variant
## LTX configurations

- Used to configure game parameters and overwrite existing ones

## LTX vs TS

There is no major differences what to use for configuration files, both sides have pros/cons. <br/>
With `typescript` variant you can generate different variations on build time, check types and share parts. <br/>
With `ini` variant it is less verbose and standard across modding community.
39 changes: 39 additions & 0 deletions src/engine/core/database/extensions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { beforeEach, describe, expect, it } from "@jest/globals";

import { haveExtensions, registerExtension } from "@/engine/core/database/extensions";
import { registry } from "@/engine/core/database/registry";
import { IExtensionsDescriptor } from "@/engine/core/utils/extensions";

describe("'extensions' module of the database", () => {
beforeEach(() => {
registry.extensions = new LuaTable();
});

it("should correctly register extensions", () => {
expect(() => registerExtension(null as unknown as IExtensionsDescriptor)).toThrow();
expect(() => registerExtension({} as IExtensionsDescriptor)).toThrow();

const first: IExtensionsDescriptor = { name: "first", module: {} } as IExtensionsDescriptor;
const second: IExtensionsDescriptor = { name: "second", module: {} } as IExtensionsDescriptor;

expect(registry.extensions.length()).toBe(0);

expect(registerExtension(first)).toBe(first);
expect(registry.extensions.length()).toBe(1);

expect(registerExtension(second)).toBe(second);
expect(registry.extensions.length()).toBe(2);

expect(registry.extensions.has("first")).toBe(true);
expect(registry.extensions.has("second")).toBe(true);
});

it("should correctly check if have extensions", () => {
const first: IExtensionsDescriptor = { name: "first", module: {} } as IExtensionsDescriptor;

expect(haveExtensions()).toBe(false);

expect(registerExtension(first)).toBe(first);
expect(haveExtensions()).toBe(true);
});
});
33 changes: 33 additions & 0 deletions src/engine/core/database/extensions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { registry } from "@/engine/core/database/registry";
import { assert } from "@/engine/core/utils/assertion";
import { IExtensionsDescriptor } from "@/engine/core/utils/extensions";
import { LuaLogger } from "@/engine/core/utils/logging";

const logger: LuaLogger = new LuaLogger($filename);

/**
* Register game mod extension in the registry.
*
* @param extension - extension descriptor to register in the database
* @returns registered extension descriptor
*/
export function registerExtension(extension: IExtensionsDescriptor): IExtensionsDescriptor {
assert(extension.name, "Expected extension to have name when registering.");
assert(extension.module, "Expected extension to have valid lua module object when registering.");
assert(!registry.extensions.has(extension.name), "Declaring duplicated extension: '%s'.", extension.name);

logger.info("Register extension:", extension.name);

registry.extensions.set(extension.name, extension);

return extension;
}

/**
* Check whether game have any extensions activated.
*
* @returns if have at least one extension active
*/
export function haveExtensions(): boolean {
return registry.extensions.length() > 0;
}
1 change: 1 addition & 0 deletions src/engine/core/database/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "@/engine/core/database/actor";
export * from "@/engine/core/database/extensions";
export * from "@/engine/core/database/anomalies";
export * from "@/engine/core/database/doors";
export * from "@/engine/core/database/helicopters";
Expand Down
5 changes: 3 additions & 2 deletions src/engine/core/database/registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { registry } from "@/engine/core/database/registry";

describe("registry storage", () => {
it("storage to contain all fields", () => {
expect(Object.keys(registry)).toHaveLength(37);
expect(Object.keys(registry)).toHaveLength(38);
});

it("storage to initialize with correct data", () => {
Expand Down Expand Up @@ -58,6 +58,7 @@ describe("registry storage", () => {
expect(registry.sounds.themes instanceof LuaTable).toBeTruthy();
expect(registry.sounds.managers instanceof LuaTable).toBeTruthy();
expect(registry.noCombatZones instanceof LuaTable).toBeTruthy();
expect(registry.noCombatZones instanceof LuaTable).toBeTruthy();
expect(registry.noCombatSmartTerrains instanceof LuaTable).toBeTruthy();
expect(registry.extensions instanceof LuaTable).toBeTruthy();
});
});
Loading

0 comments on commit f335938

Please sign in to comment.