diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2f6f3c1 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,625 @@ +# AGENTS.md — Arma 3 KP Liberation Mission Project Specification + +## 1. Project Overview + +**Mission:** CTI 34 KP Liberation v0.96.7a Ruha.ruha +**Type:** Arma 3 CTI (Capture the Island) multiplayer mission +**Max Players:** 34 +**Map:** Ruha +**Framework:** KP Liberation v0.96.7a (fork of GreuhZbug → KillahPotatoes) +**Repository:** https://github.com/KillahPotatoes/KP-Liberation + +This is a Ukraine-themed fork with custom BLUFOR preset using UA_Azov, b_afougf, FA_UAF, UF_ mod classnames. + +### File Inventory + +| File Type | Count | Lines of Code | +|------------|-------|---------------| +| `.sqf` | ~393 | ~47,570 | +| `.hpp` | ~33 | ~6,827 | +| `.xml` | 1 | 7,321 | +| `.sqm` | 1 | 189 | +| `.fsm` | 3 | — | +| Other | ~46 | — | +| **Total** | ~477 | ~62,000+ | + +### Top-level Directory Layout + +``` +/ +├── mission.sqm — Binary editor file (Arma Eden) +├── description.ext — Mission config (configFile syntax) +├── CfgFunctions.hpp — Function registry +├── init.sqf — Entry point +├── kp_liberation_config.sqf — Main gameplay configuration +├── kp_objectInits.sqf — Object-specific triggers +├── onPlayerRespawn.sqf — Respawn handler +├── whitelist.sqf — Commander whitelist +├── stringtable.xml — Locale strings +├── pbo.json — PBO compression config +├── AGENTS.md — This file +├── scripts/ — Shared/client/server scripts +│ ├── shared/ — Runs on all machines +│ ├── client/ — Client-only (40+ files, 14 subdirs) +│ ├── server/ — Server-only (70+ files, 17 subdirs) +│ └── fob_templates/ — FOB building layouts +├── functions/ — Compiled KPLIB functions (84 files) +│ ├── curator/ — Zeus curator handlers +│ └── ui/ — Overlay UI +├── presets/ — Faction unit pricing +│ ├── init_presets.sqf — Preset loader +│ ├── blufor/ — 16 faction presets +│ ├── opfor/ — 16 faction presets +│ ├── resistance/ — 9 faction presets +│ └── civilians/ — 9 faction presets +├── arsenal_presets/ — Arsenal unlock definitions (16 files) +├── ui/ — HUD/dialog definitions (21 files) +├── KP/ — KP modules +│ ├── KPGUI/ — GUI framework (defines, classes) +│ └── KPPLM/ — Player menu (functions, UI) +├── GREUH/ — Legacy GREUH module +│ ├── Scripts/ — 10 legacy scripts +│ └── UI/ — Interface HPP +└── res/ — Assets (PAA, OGG, JPG) +``` + +--- + +## 2. SQF Language Specification + +SQF (Status Quo Function) is Arma 3's primary scripting language — a C-like, preprocessed, operator-based language with no formal keyword grammar for control flow (control structures are operators that accept code blocks as arguments). + +### 2.1 Grammar & Syntax Rules + +**Statements** — every expression MUST end with `;` (semicolon): +```sqf +private _x = 5; +hint str _x; +``` + +**Expressions** — there are only three expression types: +- **Nular** — zero arguments, like a computed variable: `player`, `time`, `side player` +- **Unary** — one argument on the right: `!alive`, `hint "hello"`, `west` +- **Binary** — two arguments: `a + b`, `str _x`, `a isEqualTo b` + +**Operator precedence** follows a strict table (highest 11 → lowest 1): +| Precedence | Operators | +|------------|-----------| +| 11 | `*`, `/`, `%`, `mod`, `atan2` | +| 10 | `+`, `-`, `: (config)` | +| 9 | `select` | +| 8 | `^` | +| 7 | `==`, `!=`, `<`, `>`, `<=`, `>=`, `isEqualTo`, `isNotEqualTo` | +| 6 | `!`, `not` | +| 5 | `&&`, `and` | +| 4 | `||`, `or` | +| 3 | `> (binary)`, `>= (binary)`, `< (binary)`, `<= (binary)` | +| 2 | `then`, `do`, `count`, `apply`, `findIf` | +| 1 | `else`, `exitWith` | + +**Brackets:** +- `()` — override precedence or group expressions +- `[]` — define arrays +- `{}` — code blocks (Code data type) + +**Comments:** +- `//` single-line (preprocessor removes them) +- `/* */` multi-line block +- `comment "text"` runtime comment command (SQF level, not preprocessor) + +### 2.2 Data Types + +| Type | Examples | Notes | +|------|----------|-------| +| **Scalar** (Number) | `42`, `-3.14`, `1e6` | Double precision | +| **Boolean** | `true`, `false` | | +| **String** | `"hello"` | Double quotes only in SQF | +| **Array** | `[1, "a", player]` | 0-indexed, heterogeneous | +| **Code** | `{ hint "hi" }` | Block, assigned or passed | +| **Object** | `player`, `cursorTarget` | In-game entity reference | +| **Side** | `west`, `east`, `independent`, `civilian` | | +| **Group** | `group player` | Unit group | +| **Config** | `configFile >> "CfgVehicles"` | Config tree reference | +| **Display** | `findDisplay 46` | UI display | +| **Control** | `_display displayCtrl 1000` | UI control | +| **Namespace** | `missionNamespace`, `uiNamespace` | Variable containers | +| **Script** | `_handle = _x spawn _code` | Script handle | +| **NaN** | `objNull`, `nil` | Special null values | +| **HashMap** | `createHashMap` | Arma 3 v2.01+ | + +### 2.3 Variables + +**Scope rules:** +- **Global:** no prefix — `KP_liberation_version` +- **Private (local):** `_` prefix — `_myVar` +- **Mission namespace:** `missionNamespace setVariable ["varname", value]` + +**Declaration via `private` keyword (MANDATORY to avoid upper-scope pollution):** +```sqf +private _myVar = 42; +private ["_a", "_b", "_c"]; // array form (legacy) +params ["_param1", "_param2"]; // auto-private via params +``` + +**Naming conventions observed in this project:** +| Pattern | Example | Scope | +|---------|---------|-------| +| `KP_liberation_*` | `KP_liberation_ace` | Global config | +| `GRLIB_*` | `GRLIB_sector_size` | Legacy global | +| `KPLIB_*` | `KPLIB_init` | Mission global | +| `_under_score` | `_sector`, `_nearestFob` | Local variable | +| `_i`, `_x`, `_forEachIndex` | `_i` | Loop iteration | +| `MACRO_NAME` | `COLOR_GREEN` | `#define` constant (UPPER_SNAKE_CASE mandatory) | + +### 2.4 Control Structures (Operator-based, no reserved keywords) + +```sqf +// if-then-else +if (_a > _b) then { hint "bigger" } else { hint "smaller" }; + +// while-do +while { _i < 10 } do { _i = _i + 1 }; + +// for-do (range) +for "_i" from 0 to 9 do { systemChat str _i }; + +// for-do (array iteration — forEach) +{ hint _x } forEach [1,2,3]; + +// switch-do +switch (_var) do { + case 1: { hint "one" }; + case 2: { hint "two" }; + default { hint "other" }; +}; + +// exitWith (early return) +if (_condition) exitWith { _defaultValue }; + +// waitUntil +waitUntil { !isNil "KPLIB_init" }; +``` + +### 2.5 Arrays + +- 0-indexed, dynamic, heterogeneous +- Access: `_arr select 0` or `_arr #0` +- Slicing: `_arr select [0, 3]` +- Range syntax: `_arr # [0, 3]` (Arma 3 v2.14+) +- Common operators: `pushBack`, `append`, `apply`, `select`, `resize`, `deleteAt`, `arrayIntersect`, `in`, `findIf`, `count` + +**Preset arrays (this project):** +```sqf +// Multi-dimensional with resource costs +infantry_units = [ + ["classname", supplies, ammunition, fuel], + ["B_soldier_F", 20, 0, 0] +]; +// Last element MUST NOT have a trailing comma +``` + +### 2.6 Functions + +**Compiled via CfgFunctions.hpp (preferred):** +```hpp +class KPLIB { + class functions { + file = "functions"; + class log {}; + class checkClass {}; + class spawnVehicle {}; + }; +}; +``` +Usage: `[_text, "TAG"] call KPLIB_fnc_log;` + +**Compiled inline (this project's secondary pattern):** +```sqf +kill_manager = compileFinal preprocessFileLineNumbers "scripts\shared\kill_manager.sqf"; +``` +Usage: `[] call kill_manager;` + +**Function parameter conventions:** +```sqf +params [ + ["_text", "", [""] ], // Default: "", Type filter: String + ["_tag", "INFO", [""] ], // Default: "INFO", Type filter: String + ["_debug", false, [true] ] // Default: false, Type filter: Boolean +]; +``` + +### 2.7 Preprocessor (`#` directives, C-like) + +Case-sensitive in Arma 3. + +**`#define` — constant macro:** +```sqf +#define COLOR_GREEN { 0.2, 0.23, 0.18, 0.75 } +#define FACTION_BLUFOR(k) (k == 0) +``` + +**Macro arguments** — whole-word replacement by default. Use `##` for word-part concatenation: +```sqf +#define GET_PARAM(outVar, paramName, paramDefault) \ + outVar = [paramName,paramDefault] call KPLIB_fnc_getSaveableParam; \ + publicVariable #outVar; +``` + +**`#include` — file inclusion:** +```sqf +#include "defines.hpp" +#include // brackets = quotes (equivalent) +``` + +**`__LINE__`, `__FILE__`** — debug directives (added by `preprocessFileLineNumbers`) + +**`#ifdef` / `#ifndef` / `#endif`** — conditional compilation + +**Critical pitfalls:** +- No `__EXEC` or `__EVAL` in SQF scripts (only in configs) +- Macro body swallows spaces around non-space characters; tabs are NOT spaces +- Backslash `\` for multi-line macros MUST be the last character on the line +- Macros do NOT propagate across files (each file preprocessed independently) +- `loadFile` does NOT preprocess; `preprocessFile` and `preprocessFileLineNumbers` DO + +### 2.8 File I/O & Script Execution + +| Method | Preprocesses? | Returns | Use Case | +|--------|:---:|:---:|----------| +| `preprocessFileLineNumbers` | Yes | String (with `#line` info) | Compiling functions | +| `preprocessFile` | Yes | String (no line info) | Loading config text | +| `loadFile` | No | String (raw) | Raw file content | +| `execVM` | Yes | Script handle | Fire-and-forget scripts | +| `compileFinal` | No* | Code | Lock compiled code | +| `call` | — | Any | Synchronous execution | +| `spawn` | — | Script handle | Async execution | + +*`compileFinal` does NOT preprocess; pass it preprocessed string: +```sqf +_code = compileFinal preprocessFileLineNumbers "file.sqf"; +``` + +### 2.9 Networking / Remote Execution + +```sqf +// Execute on all clients +[_arg1, _arg2] remoteExec ["functionName", 0]; + +// Execute on server only +remoteExecCall ["fnc_name", 2]; + +// Execute on specific client (owner ID) +remoteExecCall ["fnc_name", _ownerID]; + +// JIP (Join In Progress) persistent +remoteExecCall ["fnc_name", 0, _jipObject]; +``` + +**This project's pattern:** Server-to-client via `remote_call_*.sqf` compiled scripts, Client-to-server via `remoteExecCall` targeting server (ID 2). + +--- + +## 3. Arma 3 Mission Editing Conventions + +### 3.1 Mission File Types + +| File | Purpose | Format | +|------|---------|--------| +| `mission.sqm` | Editor-placed entities, markers, triggers, waypoints | Binary/text hybrid | +| `description.ext` | Mission config (CfgDebriefing, CfgRespawnInventory, etc.) | Config class syntax | +| `CfgFunctions.hpp` | Function registry (included by description.ext) | SQF preprocessed | +| `init.sqf` | Entry point, runs on all machines | SQF | +| `stringtable.xml` | Multi-language localization | XML | +| `*.hpp` | Header/includes (macros, UI classes, function registries) | SQF preprocessed | +| `*.sqf` | Script files | SQF | +| `*.fsm` | Finite State Machine definitions | FSM format | + +### 3.2 PBO Format + +**Two types:** +- **Mission PBO:** Contains `mission.sqm`, no `config.cpp`, no PBO prefix header. Lives in `mpmissions/`. +- **Addon PBO:** Contains `config.cpp`/`config.bin`, has PBO property header (`prefix=...`). Lives in `addons/`. + +**PBO structure (binary):** +``` +[Header entries (21-byte records)] [Data blocks] [Optional SHA-1 signature] +``` +Each header entry: null-terminated filename (ASCIIZ) + 4-byte MIME type + 4x ulong fields. + +**Building on Linux:** Use `armake2 pack` for missions (no binarization), `armake2 build` for addons. + +### 3.3 description.ext (Config syntax) + +Uses config-class syntax (similar to C++ classes but preprocessed): +```sqf +class CfgDebriefing { + class Victory { + title = "Mission Complete"; + subtitle = "All sectors captured"; + description = "The island has been liberated."; + picture = "bg_victory"; + }; +}; +``` + +Key attributes set in this project's `description.ext`: +- `briefingName`, `overviewText`, `author` — Mission metadata +- `respawn`, `respawnDelay`, `respawnOnStart` — Respawn settings +- `class CfgDebriefing {}` — End-mission screens +- `class Header { gameType = CTI; ... }` — MP lobby info + +### 3.4 Init Order (Lifecycle) + +1. `description.ext` parsed +2. `mission.sqm` loaded (all editor-placed objects, triggers, markers) +3. `init.sqf` runs on all machines (if not using `enableDynamicSimulation`) +4. JIP players run `init.sqf` on join + +**KP Liberation init flow** (`init.sqf:22-64`): +``` +1. Set version, disable saving +2. KPLIB_fnc_initSectors() +3. #include "scripts/shared/defines.hpp" +4. Call fetch_params.sqf (mission params) +5. Call kp_liberation_config.sqf (hardcoded config) +6. Call init_presets.sqf (faction load) +7. Call kp_objectInits.sqf (triggers) +8. KPPLM or GREUH player menu init +9. Call init_shared.sqf (compile shared) +10. Call init_server.sqf (server only) +11. HC manager +12. Client init (if player) +13. Revive system init +``` + +### 3.5 Config Class Syntax (ext / cpp) + +- `class` keyword MUST be lowercase +- Properties use `=` not `:` +- Strings: `"double quotes"` +- Arrays: `{ element, element }` in configs, `[element, element]` in SQF +- Semicolons required after each property +- Inheritance: `class MyClass : BaseClass {}` + +### 3.6 mod.cpp vs description.ext + +- **mod.cpp** — placed in `@modname/` root, defines mod metadata in Steam Workshop +- **description.ext** — placed in mission root, defines mission-specific settings +- **config.cpp** — placed in addons, defines `CfgPatches`, `CfgVehicles`, etc. + +--- + +## 4. KP Liberation Architecture + +### 4.1 Namespace Conventions + +| Prefix | Scope | Definition | +|--------|-------|------------| +| `KPLIB_` | Mission globals | CfgFunctions.hpp namespace (KPLIB_fnc_*) | +| `KP_liberation_` | Configuration/state | kp_liberation_config.sqf | +| `GRLIB_` | Legacy (original Liberation) | Config/system globals | +| `KPPLM_` | Player menu module | KPPLM_functions.hpp | +| `KPGUI_` | GUI framework | KPGUI_defines.hpp | +| `GREUH_` | Legacy GREUH module | GREUH_config.sqf | +| `F_` | Legacy function prefix (sparse) | Inline compiled scripts | + +### 4.2 Init Flags + +| Variable | Description | +|----------|-------------| +| `KPLIB_init` | All init complete | +| `KPLIB_initServer` | Server init complete | +| `KPLIB_initPresets` | Presets loaded | +| `KP_serverParamsFetched` | Mission params received | +| `GRLIB_respawn_marker` | Respawn position set | +| `GRLIB_player_configured` | Player loadout configured | + +### 4.3 Remote Call Architecture + +**Server → Client files** (in `scripts/client/remotecall/`): +- `remote_call_battlegroup.sqf` +- `remote_call_endgame.sqf` +- `remote_call_fob.sqf` +- `remote_call_incoming.sqf` +- `remote_call_intel.sqf` +- `remote_call_prisonner.sqf` +- `remote_call_sector.sqf` + +**Client → Server files** (in `scripts/server/remotecall/`): +- `build_remote_call.sqf` +- `build_fob_remote_call.sqf` +- `cancel_build_remote_call.sqf` +- Plus logistics (add_logi*, del_logi*, save_logi*) + +### 4.4 Subsystem Debug Flags + +Set in `kp_liberation_config.sqf`, enable verbose logging: +```sqf +KP_liberation_kill_debug = false; +KP_liberation_civrep_debug = false; +KP_liberation_asymmetric_debug = false; +KP_liberation_logging = false; // Master switch +``` + +### 4.5 CfgFunctions.hpp Structure + +```hpp +class KPLIB { + // Server-agnostic functions (in functions/) + class functions { + file = "functions"; + class log {}; + class checkClass {}; + class getSaveData {}; + class setSaveData {}; + class getLoadout {}; + class setLoadout {}; + // ... 80 entries + }; + // Zeus curator + class functions_curator { + file = "functions\curator"; + class initCuratorHandlers {}; + class requestZeus {}; + }; + // Overlay UI + class functions_ui { + file = "functions\ui"; + class overlayUpdateResources {}; + }; +}; +#include "scripts\client\CfgFunctions.hpp" // Client tutorials +#include "scripts\server\CfgFunctions.hpp" // Server sector, HC +#include "KP\KPPLM\KPPLM_functions.hpp" // Player menu +``` + +### 4.6 Preset System + +Each preset file exports arrays indexed by build menu page (7 pages): +1. `infantry_units` — Page 1 +2. `light_vehicles` — Page 2 +3. `heavy_vehicles` — Page 3 +4. `air_vehicles` — Page 4 +5. `sea_vehicles` — Page 5 +6. `buildings` — Page 6 +7. `support_vehicles` — Page 7 + +Plus: +- `squads` — AI squad compositions +- `elite_vehicles` — Military-base-unlockable vehicles + +Array format: `["classname", supplies, ammo, fuel]` + +Arsenal presets define `GRLIB_arsenal_weapons`, `GRLIB_arsenal_magazines`, `GRLIB_arsenal_items`, `GRLIB_arsenal_backpacks` arrays of classname strings. + +### 4.7 Logging System + +```sqf +[_message, _tag] call KPLIB_fnc_log; +// Output: [KPLIB] [TAG] message text (via diag_log) +``` +Tags used: `PARAM`, `PRESETS`, `MISSIONSTART`, `STATS`, `KILL`, `MOD`, `CIVREP`, `CIVINFO`, `ASYMMETRIC`, `SECTORSPAWN`, `SUPPORTMODULES`, `ERROR`, `BLACKLIST`, `INFO` + +--- + +## 5. Coding Standards & Conventions (for this project) + +### 5.1 Syntax Rules + +- **Every statement MUST end with `;`** — no exceptions +- **No trailing commas** in arrays — last element before `]` must not have `,` +- **No `00` number literals** — `0` not `00` (SQF parses `00` as octal or breaks) +- **No double semicolons `;;`** — causes SQF parse errors +- **Strings require double quotes** `"..."` — single quotes are only for config paths +- **Round brackets `()` are optional** in control structure conditions but RECOMMENDED for readability and correct precedence + +### 5.2 Naming + +| Construct | Convention | Example | +|-----------|------------|---------| +| Global variables | `Prefix_lowercase_camelCase` | `KP_liberation_ace`, `GRLIB_unitcap` | +| Private variables | `_lowercase_camelCase` | `_sectorPos`, `_nearestFob` | +| Constants (`#define`) | `UPPER_SNAKE_CASE` | `COLOR_GREEN`, `GUI_GRID_W` | +| KPLIB functions | `KPLIB_fnc_descriptiveName` | `KPLIB_fnc_log` | +| Script files | `descriptive_name.sqf` | `kill_manager.sqf`, `do_build.sqf` | +| Function files | `fn_descriptiveName.sqf` | `fn_log.sqf`, `fn_checkClass.sqf` | +| UI HPP files | `liberation_name.hpp` | `liberation_build.hpp` | +| Preset files | `faction_variant.sqf` | `rhs_usaf_wdl.sqf` | + +### 5.3 File Prologue (all `functions/` files) + +```sqf +/* + File: fn_functionName.sqf + Author: KP Liberation Dev Team - https://github.com/KillahPotatoes + Date: 2020-04-16 + Last Update: 2020-04-20 + License: MIT License - http://www.opensource.org/licenses/MIT + + Description: + Brief one-paragraph description. +*/ +``` + +### 5.4 Indentation + +- **4 spaces** per indent level +- Align array elements vertically for readability: +```sqf +blufor_squad_inf_light = [ + ["B_soldier_F", 20, 0, 0], + ["B_soldier_AR_F",25, 0, 0] +]; +``` + +### 5.5 What To Avoid + +- **Do NOT add `0 = ` prefix** to command calls (unnecessary since Arma 3 v2.04) +- **Do NOT use `_this select N`** — use `params` instead +- **Do NOT use SQS** (deprecated); use SQF exclusively +- **Do NOT use `goto`/labels** — use structured control flow +- **Do NOT put code in unit init boxes** in mission.sqm (runs client-side per connection) +- **Do NOT modify GREUH/** files unless explicitly asked (legacy, separate maintainer) +- **Do NOT use `exec`** (SQS); use `execVM` or `call`/`spawn` +- **Do NOT introduce semicolons after closing braces** `};` unless followed by another statement on the same line + +### 5.6 Best Practices + +- `params` with type filtering for ALL function parameters +- `private` for ALL local variables to prevent scope pollution +- `compileFinal preprocessFileLineNumbers` for reusable compiled code +- Debug flags for subsystem verbosity (not commented-out code) +- Use `arrayIntersect` for deduplication, `apply` for transformations +- Use `remoteExecCall` over `remoteExec` when return value is not needed (faster) +- Always include JIP object parameter for persistent remoteExec + +### 5.7 Common Pitfalls & Error Patterns + +| Mistake | Symptom | Fix | +|---------|---------|-----| +| `00` numeric literal | Parse error or octal value | Use `0` instead | +| `;;` double semicolon | Parse error | Remove extra `;` | +| Trailing comma in array | Parse error | Remove last `,` | +| Mismatched brackets | Silent runtime failure | Count `(` vs `)` and `[` vs `]` | +| Non-private variable shadows global | Unexpected behavior | Add `private` keyword | +| Missing `;` | Parse error cascades | Add semicolon | +| Unquoted string macro arg with comma | Macro breaks | Wrap in `""` or use `##` | +| `#include` path starting with `\` | File not found | Use relative path without leading `\` | +| Using `localize` inside `#define` | Hardcoded language | Use `$STR_XXX` reference instead | +| Wrong bracket type in config | Class parse error | Configs use `{}` not `[]` for arrays | +| Semicolon after `}` of control struct | Usually valid, but redundant/wrong when followed by `else` | Remove `;` before `else` | + +--- + +## 6. PBO Build & Deployment + +### 6.1 Local Build +```bash +# Pack mission folder (no binarization) +armake2 pack "CTI 34 KP Liberation v0.96.7a Ruha.ruha" "CTI_34_KP_Liberation_v0.96.7a_Ruha.ruha.pbo" + +# Verify +armake2 inspect "CTI_34_KP_Liberation_v0.96.7a_Ruha.ruha.pbo" +``` + +### 6.2 CI/CD (Gitea Actions) +Workflow file: `.gitea/workflows/build.yml` +- Trigger: push to main/master, tags `v*`, manual +- Uses `arwynfr/armake2:debian` Docker image +- Produces artifact: `CTI_34_KP_Liberation_v0.96.7a_Ruha.ruha.pbo` +- Includes disabled `deploy` job template for SCP-to-server + +--- + +## 7. References + +- **SQF Syntax:** https://community.bistudio.com/wiki/SQF_Syntax +- **Control Structures:** https://community.bistudio.com/wiki/Control_Structures +- **PreProcessor Commands:** https://community.bistudio.com/wiki/PreProcessor_Commands +- **Code Best Practices:** https://community.bistudio.com/wiki/Code_Best_Practices +- **Description.ext:** https://community.bohemia.net/wiki/Description.ext +- **PBO File Format:** https://community.bistudio.com/wiki/PBO_File_Format +- **KP Liberation:** https://github.com/KillahPotatoes/KP-Liberation +- **armake2:** https://github.com/KoffeinFlummi/armake2 +- **Code Optimisation:** https://community.bistudio.com/wiki/Code_Optimisation +- **Multiplayer Scripting:** https://community.bistudio.com/wiki/Multiplayer_Scripting