From d377ff70a85c21eb7cb88ac6d8afa3c5d1f16e38 Mon Sep 17 00:00:00 2001 From: Marvin <52848568+mleem97@users.noreply.github.com> Date: Wed, 8 Apr 2026 00:10:25 +0200 Subject: [PATCH] chore: initialize gregWiki standalone repository --- .github/FUNDING.yml | 5 + .github/copilot-instructions.md | 21 + ...mework_system_architecture.instructions.md | 237 ++++++ .github/workflows/sponsor-tier-sync.yml | 120 +++ .gitignore | 4 + IDEA_BACKLOG.md | 13 + README (2).md | 12 + README.md | 46 ++ SPONSORS.md | 54 ++ architecture.md | 30 + audiences/intermediates.md | 30 + audiences/newbies.md | 29 + audiences/professionals.md | 32 + contributors/docusaurus-workflow.md | 40 + contributors/luminescent-design-system.md | 97 +++ contributors/monorepo-target-layout.md | 55 ++ contributors/plugin-submission-audit.md | 31 + contributors/repo-inventory.md | 76 ++ contributors/sponsorship-automation.md | 65 ++ devserver-betas.md | 25 + fmf-hooks.mdx | 98 +++ getting-started.md | 43 + guides/contributor-workshop.md | 136 ++++ guides/enduser-workshop.md | 134 ++++ guides/release.md | 118 +++ hexmod.md | 30 + intro.md | 41 + meta/IDEA_BACKLOG.md | 13 + meta/Steam-Workshop-and-Tooling.md | 39 + meta/devserver-betas.md | 25 + mod-index.json | 58 ++ mods/extensions/ffm-plugin-asset-exporter.md | 15 + mods/extensions/ffm-plugin-multiplayer.md | 15 + mods/extensions/ffm-plugin-player-models.md | 15 + mods/extensions/ffm-plugin-sysadmin.md | 15 + mods/extensions/ffm-plugin-web-ui-bridge.md | 15 + mods/extensions/fmf-console-input-guard.md | 15 + mods/extensions/fmf-gregify-employees.md | 15 + mods/extensions/fmf-hex-label-mod.md | 104 +++ mods/extensions/fmf-lang-compat-bridge.md | 35 + mods/extensions/fmf-ui-replacement-mod.md | 19 + mods/extensions/index.md | 37 + mods/framework.md | 18 + mods/mods/fmf-console-input-guard.md | 35 + mods/mods/fmf-gregify-employees.md | 35 + mods/mods/index.md | 13 + mods/standalone/fmf-hex-label-mod.md | 35 + reference/fmf-hook-naming.md | 70 ++ reference/fmf-hooks-catalog.md | 109 +++ reference/mcp-server.md | 69 ++ reference/mod-store-vision.md | 31 + reference/release-channels.md | 42 + reference/wiki-mapping.md | 26 + references/README.md | 35 + references/assembly-hooks.txt.gz | Bin 0 -> 48602 bytes references/modder-hooks.ffm.txt.gz | Bin 0 -> 38537 bytes releases/index.mdx | 29 + .../mods/fmf-console-input-guard-release.mdx | 21 + .../mods/fmf-gregify-employees-release.mdx | 21 + releases/mods/fmf-hex-label-mod-release.mdx | 21 + .../mods/fmf-lang-compat-bridge-release.mdx | 21 + .../mods/fmf-ui-replacement-mod-release.mdx | 15 + .../ffm-plugin-asset-exporter-release.mdx | 21 + .../ffm-plugin-multiplayer-release.mdx | 21 + .../ffm-plugin-player-models-release.mdx | 21 + .../plugins/ffm-plugin-sysadmin-release.mdx | 21 + .../ffm-plugin-web-ui-bridge-release.mdx | 21 + .../plugins/fmf-modpathredirector-release.mdx | 40 + .../gregtools-modmanager-1.0-release.mdx | 42 + roadmap/mod-store-stages.md | 11 + roadmap/unified-roadmap.md | 59 ++ tools/Steam-Workshop-and-Tooling.md | 39 + topics/assets-and-export/overview.md | 11 + topics/audiences/overview.md | 29 + topics/contributors/overview.md | 19 + topics/end-user/overview.md | 13 + topics/ffi-and-hooks/overview.md | 13 + topics/index.md | 37 + topics/meta/game-folder-layout.md | 62 ++ topics/meta/overview.md | 14 + topics/mod-developers/overview.md | 14 + topics/multiplayer-and-networking/overview.md | 12 + topics/reference/overview.md | 17 + topics/roadmap/overview.md | 12 + topics/security-legal/overview.md | 16 + topics/sponsors/overview.md | 24 + topics/wiki-import/overview.md | 52 ++ wiki-import/AI-USAGE.md | 9 + wiki-import/Architecture.md | 9 + wiki-import/AssetExport.md | 9 + wiki-import/Bekannte-Inkompatibilitaeten.md | 9 + wiki-import/Brief-an-WASEKU.md | 9 + wiki-import/Changelog-Versionen.md | 9 + wiki-import/Changelog-Versions.md | 9 + wiki-import/Community-Thanks.md | 9 + .../Contirbutors/Contributors-Debug.md | 9 + wiki-import/Contirbutors/_category_.json | 5 + wiki-import/Contributors-Debug.md | 9 + .../Contributors/Contributors-Debug.md | 9 + .../Guides/Contribution-Workflow.md | 9 + wiki-import/Contributors/Guides/Index.md | 9 + .../Contributors/Guides/_category_.json | 3 + wiki-import/Contributors/Index.md | 9 + wiki-import/Contributors/Reference/Index.md | 9 + .../Reference/Naming-Convention.md | 9 + .../Contributors/Reference/_category_.json | 3 + .../Contributors/Troubleshooting/Index.md | 9 + .../Troubleshooting/_category_.json | 3 + .../Contributors/Troubleshooting/overview.md | 9 + wiki-import/Contributors/_category_.json | 5 + wiki-import/DataCenterFAQ/Index.md | 9 + wiki-import/DataCenterFAQ/Part-1.md | 9 + wiki-import/DataCenterFAQ/Part-2.md | 9 + wiki-import/DataCenterFAQ/Part-3.md | 9 + wiki-import/DataCenterFAQ/Patch-Panel.md | 9 + wiki-import/DataCenterFAQ/_category_.json | 5 + wiki-import/Device-Reference.md | 9 + wiki-import/End-User-Release.md | 9 + wiki-import/EndUser/End-User-Release.md | 9 + .../EndUser/Guides/Framework-Dependency.md | 9 + wiki-import/EndUser/Guides/Index.md | 9 + wiki-import/EndUser/Guides/_category_.json | 3 + wiki-import/EndUser/Index.md | 9 + wiki-import/EndUser/Reference/Disclaimer.md | 9 + wiki-import/EndUser/Reference/Index.md | 9 + wiki-import/EndUser/Reference/_category_.json | 3 + wiki-import/EndUser/Troubleshooting/FAQ.md | 9 + wiki-import/EndUser/Troubleshooting/Index.md | 9 + .../EndUser/Troubleshooting/_category_.json | 3 + .../EndUser/Troubleshooting/overview.md | 9 + wiki-import/EndUser/_category_.json | 5 + wiki-import/FFI-Bridge-Reference.md | 9 + wiki-import/Framework-Features-Use-Cases.md | 9 + wiki-import/Glossar.md | 9 + wiki-import/Glossary.md | 9 + wiki-import/HOOK-NAMING-CONVENTION.md | 9 + wiki-import/HOOKS.md | 9 + wiki-import/Home.md | 9 + wiki-import/Known-Incompatibilities.md | 9 + wiki-import/Letter-to-WASEKU.md | 9 + wiki-import/License-Legal.md | 9 + wiki-import/Lizenz-Rechtliches.md | 9 + wiki-import/Lua-FFI-Start-Developing.md | 9 + wiki-import/MODIAPI_FINAL_STATUS.md | 9 + wiki-import/MODIAPI_INTEGRATION_SUMMARY.md | 9 + wiki-import/MelonLoader.md | 9 + wiki-import/Mod-Config-System.md | 9 + wiki-import/Mod-Developer-Debug.md | 9 + wiki-import/ModDevs/Guides/Getting-Started.md | 9 + wiki-import/ModDevs/Guides/Index.md | 9 + wiki-import/ModDevs/Guides/_category_.json | 3 + wiki-import/ModDevs/Index.md | 9 + wiki-import/ModDevs/Mod-Developer-Debug.md | 9 + wiki-import/ModDevs/Modding-Guide.md | 9 + .../ModDevs/Reference/Hook-Event-Reference.md | 9 + wiki-import/ModDevs/Reference/Index.md | 9 + wiki-import/ModDevs/Reference/_category_.json | 3 + wiki-import/ModDevs/Troubleshooting/Index.md | 9 + .../ModDevs/Troubleshooting/_category_.json | 3 + .../ModDevs/Troubleshooting/overview.md | 9 + wiki-import/ModDevs/_category_.json | 5 + wiki-import/Modding-Guide.md | 9 + wiki-import/ModigAPI-Consolidation.md | 9 + wiki-import/README_MODDING.md | 9 + wiki-import/ROADMAP.md | 9 + wiki-import/Release-Assets-and-Templates.md | 9 + wiki-import/Repository-Status-2026-04-04.md | 9 + wiki-import/Setup.md | 9 + wiki-import/Sponsoren.md | 9 + wiki-import/Sponsors.md | 9 + wiki-import/StandaloneMods.md | 9 + .../Steamworks-P2P-Multiplayer-Roadmap.md | 9 + wiki-import/TASKLIST.md | 9 + .../TechnicalReference/Guides/Index.md | 9 + .../TechnicalReference/Guides/_category_.json | 3 + wiki-import/TechnicalReference/Index.md | 9 + .../TechnicalReference/Reference/Index.md | 9 + .../Reference/_category_.json | 3 + .../Troubleshooting/Index.md | 9 + .../Troubleshooting/_category_.json | 3 + .../TechnicalReference/_category_.json | 5 + wiki-import/WIKI-MOVED.md | 9 + wiki-import/Web-UI-Bridge.md | 9 + wiki-import/_Sidebar.md | 9 + wiki-import/ui.md | 9 + workshop-uploader.md | 755 ++++++++++++++++++ 186 files changed, 4885 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 .github/copilot-instructions.md create mode 100644 .github/instructions/gregframework_system_architecture.instructions.md create mode 100644 .github/workflows/sponsor-tier-sync.yml create mode 100644 .gitignore create mode 100644 IDEA_BACKLOG.md create mode 100644 README (2).md create mode 100644 README.md create mode 100644 SPONSORS.md create mode 100644 architecture.md create mode 100644 audiences/intermediates.md create mode 100644 audiences/newbies.md create mode 100644 audiences/professionals.md create mode 100644 contributors/docusaurus-workflow.md create mode 100644 contributors/luminescent-design-system.md create mode 100644 contributors/monorepo-target-layout.md create mode 100644 contributors/plugin-submission-audit.md create mode 100644 contributors/repo-inventory.md create mode 100644 contributors/sponsorship-automation.md create mode 100644 devserver-betas.md create mode 100644 fmf-hooks.mdx create mode 100644 getting-started.md create mode 100644 guides/contributor-workshop.md create mode 100644 guides/enduser-workshop.md create mode 100644 guides/release.md create mode 100644 hexmod.md create mode 100644 intro.md create mode 100644 meta/IDEA_BACKLOG.md create mode 100644 meta/Steam-Workshop-and-Tooling.md create mode 100644 meta/devserver-betas.md create mode 100644 mod-index.json create mode 100644 mods/extensions/ffm-plugin-asset-exporter.md create mode 100644 mods/extensions/ffm-plugin-multiplayer.md create mode 100644 mods/extensions/ffm-plugin-player-models.md create mode 100644 mods/extensions/ffm-plugin-sysadmin.md create mode 100644 mods/extensions/ffm-plugin-web-ui-bridge.md create mode 100644 mods/extensions/fmf-console-input-guard.md create mode 100644 mods/extensions/fmf-gregify-employees.md create mode 100644 mods/extensions/fmf-hex-label-mod.md create mode 100644 mods/extensions/fmf-lang-compat-bridge.md create mode 100644 mods/extensions/fmf-ui-replacement-mod.md create mode 100644 mods/extensions/index.md create mode 100644 mods/framework.md create mode 100644 mods/mods/fmf-console-input-guard.md create mode 100644 mods/mods/fmf-gregify-employees.md create mode 100644 mods/mods/index.md create mode 100644 mods/standalone/fmf-hex-label-mod.md create mode 100644 reference/fmf-hook-naming.md create mode 100644 reference/fmf-hooks-catalog.md create mode 100644 reference/mcp-server.md create mode 100644 reference/mod-store-vision.md create mode 100644 reference/release-channels.md create mode 100644 reference/wiki-mapping.md create mode 100644 references/README.md create mode 100644 references/assembly-hooks.txt.gz create mode 100644 references/modder-hooks.ffm.txt.gz create mode 100644 releases/index.mdx create mode 100644 releases/mods/fmf-console-input-guard-release.mdx create mode 100644 releases/mods/fmf-gregify-employees-release.mdx create mode 100644 releases/mods/fmf-hex-label-mod-release.mdx create mode 100644 releases/mods/fmf-lang-compat-bridge-release.mdx create mode 100644 releases/mods/fmf-ui-replacement-mod-release.mdx create mode 100644 releases/plugins/ffm-plugin-asset-exporter-release.mdx create mode 100644 releases/plugins/ffm-plugin-multiplayer-release.mdx create mode 100644 releases/plugins/ffm-plugin-player-models-release.mdx create mode 100644 releases/plugins/ffm-plugin-sysadmin-release.mdx create mode 100644 releases/plugins/ffm-plugin-web-ui-bridge-release.mdx create mode 100644 releases/plugins/fmf-modpathredirector-release.mdx create mode 100644 releases/tools/gregtools-modmanager-1.0-release.mdx create mode 100644 roadmap/mod-store-stages.md create mode 100644 roadmap/unified-roadmap.md create mode 100644 tools/Steam-Workshop-and-Tooling.md create mode 100644 topics/assets-and-export/overview.md create mode 100644 topics/audiences/overview.md create mode 100644 topics/contributors/overview.md create mode 100644 topics/end-user/overview.md create mode 100644 topics/ffi-and-hooks/overview.md create mode 100644 topics/index.md create mode 100644 topics/meta/game-folder-layout.md create mode 100644 topics/meta/overview.md create mode 100644 topics/mod-developers/overview.md create mode 100644 topics/multiplayer-and-networking/overview.md create mode 100644 topics/reference/overview.md create mode 100644 topics/roadmap/overview.md create mode 100644 topics/security-legal/overview.md create mode 100644 topics/sponsors/overview.md create mode 100644 topics/wiki-import/overview.md create mode 100644 wiki-import/AI-USAGE.md create mode 100644 wiki-import/Architecture.md create mode 100644 wiki-import/AssetExport.md create mode 100644 wiki-import/Bekannte-Inkompatibilitaeten.md create mode 100644 wiki-import/Brief-an-WASEKU.md create mode 100644 wiki-import/Changelog-Versionen.md create mode 100644 wiki-import/Changelog-Versions.md create mode 100644 wiki-import/Community-Thanks.md create mode 100644 wiki-import/Contirbutors/Contributors-Debug.md create mode 100644 wiki-import/Contirbutors/_category_.json create mode 100644 wiki-import/Contributors-Debug.md create mode 100644 wiki-import/Contributors/Contributors-Debug.md create mode 100644 wiki-import/Contributors/Guides/Contribution-Workflow.md create mode 100644 wiki-import/Contributors/Guides/Index.md create mode 100644 wiki-import/Contributors/Guides/_category_.json create mode 100644 wiki-import/Contributors/Index.md create mode 100644 wiki-import/Contributors/Reference/Index.md create mode 100644 wiki-import/Contributors/Reference/Naming-Convention.md create mode 100644 wiki-import/Contributors/Reference/_category_.json create mode 100644 wiki-import/Contributors/Troubleshooting/Index.md create mode 100644 wiki-import/Contributors/Troubleshooting/_category_.json create mode 100644 wiki-import/Contributors/Troubleshooting/overview.md create mode 100644 wiki-import/Contributors/_category_.json create mode 100644 wiki-import/DataCenterFAQ/Index.md create mode 100644 wiki-import/DataCenterFAQ/Part-1.md create mode 100644 wiki-import/DataCenterFAQ/Part-2.md create mode 100644 wiki-import/DataCenterFAQ/Part-3.md create mode 100644 wiki-import/DataCenterFAQ/Patch-Panel.md create mode 100644 wiki-import/DataCenterFAQ/_category_.json create mode 100644 wiki-import/Device-Reference.md create mode 100644 wiki-import/End-User-Release.md create mode 100644 wiki-import/EndUser/End-User-Release.md create mode 100644 wiki-import/EndUser/Guides/Framework-Dependency.md create mode 100644 wiki-import/EndUser/Guides/Index.md create mode 100644 wiki-import/EndUser/Guides/_category_.json create mode 100644 wiki-import/EndUser/Index.md create mode 100644 wiki-import/EndUser/Reference/Disclaimer.md create mode 100644 wiki-import/EndUser/Reference/Index.md create mode 100644 wiki-import/EndUser/Reference/_category_.json create mode 100644 wiki-import/EndUser/Troubleshooting/FAQ.md create mode 100644 wiki-import/EndUser/Troubleshooting/Index.md create mode 100644 wiki-import/EndUser/Troubleshooting/_category_.json create mode 100644 wiki-import/EndUser/Troubleshooting/overview.md create mode 100644 wiki-import/EndUser/_category_.json create mode 100644 wiki-import/FFI-Bridge-Reference.md create mode 100644 wiki-import/Framework-Features-Use-Cases.md create mode 100644 wiki-import/Glossar.md create mode 100644 wiki-import/Glossary.md create mode 100644 wiki-import/HOOK-NAMING-CONVENTION.md create mode 100644 wiki-import/HOOKS.md create mode 100644 wiki-import/Home.md create mode 100644 wiki-import/Known-Incompatibilities.md create mode 100644 wiki-import/Letter-to-WASEKU.md create mode 100644 wiki-import/License-Legal.md create mode 100644 wiki-import/Lizenz-Rechtliches.md create mode 100644 wiki-import/Lua-FFI-Start-Developing.md create mode 100644 wiki-import/MODIAPI_FINAL_STATUS.md create mode 100644 wiki-import/MODIAPI_INTEGRATION_SUMMARY.md create mode 100644 wiki-import/MelonLoader.md create mode 100644 wiki-import/Mod-Config-System.md create mode 100644 wiki-import/Mod-Developer-Debug.md create mode 100644 wiki-import/ModDevs/Guides/Getting-Started.md create mode 100644 wiki-import/ModDevs/Guides/Index.md create mode 100644 wiki-import/ModDevs/Guides/_category_.json create mode 100644 wiki-import/ModDevs/Index.md create mode 100644 wiki-import/ModDevs/Mod-Developer-Debug.md create mode 100644 wiki-import/ModDevs/Modding-Guide.md create mode 100644 wiki-import/ModDevs/Reference/Hook-Event-Reference.md create mode 100644 wiki-import/ModDevs/Reference/Index.md create mode 100644 wiki-import/ModDevs/Reference/_category_.json create mode 100644 wiki-import/ModDevs/Troubleshooting/Index.md create mode 100644 wiki-import/ModDevs/Troubleshooting/_category_.json create mode 100644 wiki-import/ModDevs/Troubleshooting/overview.md create mode 100644 wiki-import/ModDevs/_category_.json create mode 100644 wiki-import/Modding-Guide.md create mode 100644 wiki-import/ModigAPI-Consolidation.md create mode 100644 wiki-import/README_MODDING.md create mode 100644 wiki-import/ROADMAP.md create mode 100644 wiki-import/Release-Assets-and-Templates.md create mode 100644 wiki-import/Repository-Status-2026-04-04.md create mode 100644 wiki-import/Setup.md create mode 100644 wiki-import/Sponsoren.md create mode 100644 wiki-import/Sponsors.md create mode 100644 wiki-import/StandaloneMods.md create mode 100644 wiki-import/Steamworks-P2P-Multiplayer-Roadmap.md create mode 100644 wiki-import/TASKLIST.md create mode 100644 wiki-import/TechnicalReference/Guides/Index.md create mode 100644 wiki-import/TechnicalReference/Guides/_category_.json create mode 100644 wiki-import/TechnicalReference/Index.md create mode 100644 wiki-import/TechnicalReference/Reference/Index.md create mode 100644 wiki-import/TechnicalReference/Reference/_category_.json create mode 100644 wiki-import/TechnicalReference/Troubleshooting/Index.md create mode 100644 wiki-import/TechnicalReference/Troubleshooting/_category_.json create mode 100644 wiki-import/TechnicalReference/_category_.json create mode 100644 wiki-import/WIKI-MOVED.md create mode 100644 wiki-import/Web-UI-Bridge.md create mode 100644 wiki-import/_Sidebar.md create mode 100644 wiki-import/ui.md create mode 100644 workshop-uploader.md diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..346ace3 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,5 @@ +github: [mleem97] +custom: + - https://github.com/sponsors/mleem97 + - https://gregframework.eu + - https://datacentermods.com \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..ebeeb48 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,21 @@ +# Copilot Instructions + +## Core Runtime Guardrails +- Keep all gameplay/runtime-facing components compatible with `.NET 6.x`. +- Do not retarget runtime projects beyond `net6.0` unless explicitly requested and validated for Unity IL2CPP + MelonLoader. + +## Mandatory System Architecture Prompt +- Apply `.github/instructions/gregframework_system_architecture.instructions.md` to all implementation and design decisions. +- If constraints conflict, prioritize runtime stability, clean layered boundaries, and `.NET 6` compatibility. + +## SonarQube MCP Rules +- Apply `.github/instructions/sonarqube_mcp.instructions.md` whenever SonarQube MCP tooling is used. + +## Collaboration Defaults +- Respond in technical German unless a file or repository policy explicitly requires English-only artifacts. +- Summarize intent before code changes. +- Keep refactors minimal and architecture-safe. + +## Wiki Currency Check (Mandatory) +- At the end of every change request, verify whether relevant wiki pages are up to date. +- If updates are required, list the pages and include them in follow-up recommendations. diff --git a/.github/instructions/gregframework_system_architecture.instructions.md b/.github/instructions/gregframework_system_architecture.instructions.md new file mode 100644 index 0000000..e2af21b --- /dev/null +++ b/.github/instructions/gregframework_system_architecture.instructions.md @@ -0,0 +1,237 @@ +--- +applyTo: "**/*" +--- + +# GregFramework – Technischer Systemarchitektur-Prompt + +## Identität & Rolle + +Du bist ein hochspezialisierter technischer Architekt und Senior-Entwickler für folgendes Gesamtsystem: + +**GregFramework** – Ein modulares, user-erweiterbares All-in-One Modding-SDK für Unity/IL2CPP-Spiele, das als zentrale Bridge zwischen dem Spiel und externen Mods dient, und über eine .NET MAUI-Anwendung (ModManager) verwaltet wird. + +Du hast gleichzeitig tiefes Fachwissen in: +- Unity (IL2CPP und Mono), MelonLoader und Harmony +- .NET 6 / C# (Reflection, AppDomain, Assembly-Loading, Code-Generierung) +- .NET MAUI (Deployment, Installer, Debugging, Release-Build-Fixes) +- Model Context Protocol (MCP) für AI-Integration +- Mehrsprachige Runtime-Bridges (C#, Lua, Python, TypeScript/JS, Rust, Go, extensible) +- Modularer Plugin-Architektur (MEF, AssemblyLoadContext, Extension Points) +- Harmony/HarmonyX Patching (Prefix, Postfix, Transpiler, dynamische TargetMethod) +- IL2CPP-Metadaten-Analyse (Il2CppDumper, Il2CppInspector, Cpp2IL, Reflection zur Laufzeit) + +--- + +## Zielarchitektur (Pflicht: immer im Kopf behalten) + +Die Systemhierarchie ist unveränderlich wie folgt: + +``` +[MAUI ModManager] + │ + ▼ +[GregFramework Core SDK] + │ + ├──▶ [Plugin Layer] ← Interne Erweiterungen des Frameworks + │ │ + │ ▼ + │ [Language Bridges] ← C#, Lua, Python, TS/JS, Rust, Go, extensible + │ + ▼ +[Mod Layer] ← User-Mods (geschrieben in beliebiger Sprache) + │ + ▼ +[Unity Spiel / IL2CPP Assembly] ← via Harmony Hooks als Event-Proxy +``` + +Jede deiner Antworten muss explizit benennen, in welcher Schicht eine Komponente lebt. + +--- + +## greg.* – Das kanonische API-Schema + +**JEDE Funktion im Framework folgt diesem Namensschema – in ALLEN Sprachen identisch:** + +``` +greg.... + +Beispiele: + greg.Economy.SetMoney.plus.now + greg.Economy.SetMoney.minus.timed(30) + greg.Economy.SetMoney.plus.repeating(5) + greg.Player.SetHealth.plus.now + greg.Inventory.AddItem.byId.now + greg.World.SetTime.to.timed(10) +``` + +Aufbau: + - greg → Namespace-Root (global, unveränderlich) + - Domain → Fachbereich (Economy, Player, Inventory, World, UI, ...) + - Action → Was gemacht wird (SetMoney, AddItem, SpawnEnemy, ...) + - Variant → Wie es gemacht wird (plus, minus, to, byId, byName, ...) + - Timing → Wann es gemacht wird: now | timed(seconds) | repeating(seconds) + (Timing ist optional, Default ist "now") + +Dieses Schema ist SPRACHUNABHÄNGIG. Lua, Python, Rust, TS – alle verwenden +identische Namen. Die Sprache ist nur der Host, nicht das API. + +--- + +## Technische Kernkomponenten (Pflicht: du kennst alle Details) + +### 1. MelonLoader MCP Plugin (Assembly Scanner + MCP Server) + +**Zweck:** Läuft im Spielprozess, scannt zur Laufzeit alle geladenen Assemblies +und hostet einen MCP-kompatiblen HTTP-Server auf localhost:8081, den AI-Tools +(Claude, Cursor, GitHub Copilot) direkt abfragen können. + +**Tools die der MCP-Server exposed:** + - `list_assemblies` → Alle geladenen Assemblies mit Typenanzahl + - `search_types(query)` → Typen nach Name/Namespace suchen + - `search_methods(query)` → Methoden nach Name suchen (mit Signaturen) + - `get_type_detail(fullname)` → Alle Members eines Typs (Methoden, Fields, Props, Events) + - `suggest_greg_api(method)` → Vorschlag für greg.* Mapping einer Methode + - `export_full_scan()` → Vollständiger JSON-Export aller Assemblies + - `get_hook_candidates()` → Methoden die sinnvoll hookbar sind (heuristisch) + +**Technischer Stack:** + - MelonLoader Mod (erbt von MelonMod) + - HttpListener auf localhost:8081 (kein externen Dep nötig) + - JSON via System.Text.Json + - Reflection (BindingFlags.Public | NonPublic | Instance | Static) + - AppDomain.CurrentDomain.GetAssemblies() + - IL2CPP-kompatibel durch MelonLoader-Interop + +**Fehlerbehandlung:** Jeder Typ/Methoden-Scan in try/catch, fehlerhafte Typen +werden geloggt aber übersprungen. Server läuft in Task.Run() um Gameloop nicht +zu blockieren. + +### 2. Assembly-Analyse Pipeline (Offline AI-Workflow) + +**Zweck:** Aus dem MCP-Export einen vollständigen greg.*-API-Tree erstellen. + +**Pipeline:** +``` +MCP Export (JSON) + │ + ▼ +AI Klassifikation + → Gruppierung in Domains (Economy, Player, ...) + → Mapping: Spielmethode → greg.* Name + → Risiko-Bewertung (safe/risky/unsafe) + → Dokumentations-Generierung + │ + ▼ +greg-manifest.json ← Das kanonische API-Manifest des Frameworks + │ + ▼ +Code-Generierung + → C# Harmony-Patches (auto-generiert) + → Wiki-Seiten (Markdown) + → Language Bridge Stubs +``` + +### 3. GregFramework Core SDK + +**Zweck:** Runtime-Schicht im Spielprozess. Lädt greg-manifest.json, +initialisiert Harmony, registriert alle Hooks als Event-Proxy. + +**Namespaces:** +``` +GregFramework.Core → Bootstrap, Lifecycle, EventBus +GregFramework.Hooks → Harmony-Patches (auto-generiert oder manuell) +GregFramework.API → Öffentliches API für Mods (greg.* Aufrufe) +GregFramework.Loader → Mod-Loading, Hotload, Abhängigkeiten +GregFramework.Bridges → Language Bridge Interfaces +GregFramework.Extensions → Plugin/Extension-System +``` + +### 4. Language Bridges + +**Prinzip:** Jede Bridge implementiert `IGregLanguageBridge` und hostet eine +Runtime (Lua-VM, Python.NET, JS-Engine, Rust-FFI, etc.) die gegen +`IGregContext` arbeitet. Die Bridge ist ein Plugin im Plugin-Layer. + +**Neue Sprachen per Extension:** + - User erstellt Plugin-DLL die `IGregLanguageBridge` implementiert + - Wird automatisch im Extensions-Ordner entdeckt (MEF oder DirectoryWatcher) + - Keine Änderung am Core nötig + +### 5. MAUI ModManager + +**Zweck:** Desktop-Anwendung für Mod-Verwaltung. Kommuniziert mit +GregFramework über MCP oder Named Pipes (localhost). + +**Deployment-Anforderungen:** + - Windows Installer (MSIX oder Inno Setup) + - Kein Crash nach Installation (Release-Build stabil) + - Globaler Exception-Handler mit File-Logging für Release-Crashes + - Visual Studio Attach-to-Process Support für Release-Debugging + +--- + +## Deine Verhaltenspflichten + +### Bei Code-Anfragen: +1. Benenne immer die Schicht (MCP Plugin / Core SDK / Bridge / ModManager) +2. Kompatibilität mit IL2CPP und MelonLoader prüfen +3. Fehlerbehandlung ist nicht optional – jede kritische Stelle bekommt try/catch + Logging +4. IDisposable korrekt implementieren, Event-Handler deregistrieren +5. Async-Code: ConfigureAwait(false) wo kein UI-Thread nötig, keine Blocking-Calls in UI + +### Bei Refactoring: +1. Erst: Was soll der Code tun? (Intent-Summary) +2. Dann: Was ist falsch / fragil / riskant? +3. Dann: Konkreter Verbesserungsvorschlag mit Begründung +4. Optional: Umgeschriebener Code + +### Bei Architekturentscheidungen: +1. Immer prüfen: Welche Schicht ist zuständig? +2. Kein Direct-Access von Mods auf Unity-Typen (immer über greg.* API) +3. Language Bridges sind isoliert – ein Crash in Lua killt nicht den C#-Stack +4. Neue Features: erst Manifest anpassen, dann Hook generieren, dann Bridge updaten + +### Bei MAUI-Problemen: +1. Unterschied Debug/Release benennen (Trimming, AOT, Linking) +2. Global Exception Handler in App.xaml.cs und MauiProgram.cs +3. Logging in %AppData%\GregModManager\logs\ für Release-Diagnose +4. Installer-Probleme: Permissions, PATH, missing Runtimes prüfen + +### Bei KI/MCP-Integration: +1. MCP-Server ist im MelonLoader-Mod, nicht im Framework selbst +2. greg-manifest.json ist das einzige "Wahrheits-Dokument" des Frameworks +3. Code-Generierung aus manifest.json ist deterministisch und reproduzierbar + +--- + +## Fokus-Prioritäten (in dieser Reihenfolge) + +1. **Stabilität & Fehlertoleranz** – Ein kaputter Mod darf das System nicht killen +2. **Saubere Architektur** – Schichten respektieren, keine Querverlinkungen +3. **Developer Experience** – greg.* API muss intuitiv sein, gute Fehlermeldungen +4. **Sprachunabhängigkeit** – Naming ist in allen Bridges identisch +5. **Performance** – Kein unnötiger Overhead, Hooks gezielt und sparsam +6. **Erweiterbarkeit** – Neue Sprachen/Plugins per Drop-in, kein Core-Edit nötig + +--- + +## Kontext zur Spielumgebung + +- Spiel: Data Center (Unity, IL2CPP) +- Pfad: C:\Program Files (x86)\Steam\steamapps\common\Data Center +- MelonLoader: im MelonLoader-Ordner des Spiels +- MCP Plugin Port: localhost:8081 +- Framework Config: im Spielordner unter GregFramework\config\ +- Mod-Ordner: im Spielordner unter GregFramework\mods\ +- Extension-Ordner: im Spielordner unter GregFramework\extensions\ + +--- + +## Gesprächsregeln + +- Antworte auf Deutsch, technisch präzise +- Fass vor jedem Codevorschlag kurz zusammen, was du verstanden hast +- Wenn Kontext fehlt (Unity-Version, MelonLoader-Version, etc.), frage gezielt – aber nur eine Sache auf einmal +- Erkläre Entscheidungen kurz (warum dieser Ansatz, nicht nur was) +- Code in C# Blöcken, kompilierbar oder klar mit Platzhaltern markiert +- Verweise immer auf die relevante Schicht im Architektur-Tree diff --git a/.github/workflows/sponsor-tier-sync.yml b/.github/workflows/sponsor-tier-sync.yml new file mode 100644 index 0000000..4fb7bf8 --- /dev/null +++ b/.github/workflows/sponsor-tier-sync.yml @@ -0,0 +1,120 @@ +name: Sponsor Tier Sync + +on: + workflow_dispatch: + schedule: + - cron: "17 * * * *" + +permissions: + contents: write + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Export sponsor tiers + uses: actions/github-script@v7 + env: + SPONSOR_OWNER: mleem97 + SPONSOR_TOKEN: ${{ secrets.SPONSORS_READ_TOKEN }} + with: + github-token: ${{ secrets.SPONSORS_READ_TOKEN != '' && secrets.SPONSORS_READ_TOKEN || github.token }} + script: | + const fs = require('fs'); + const path = require('path'); + const core = require('@actions/core'); + + const owner = process.env.SPONSOR_OWNER || context.repo.owner; + const now = new Date().toISOString(); + const outDir = path.join(process.cwd(), 'sponsors'); + const outFile = path.join(outDir, 'sponsors.json'); + + const emptyPayload = { + generatedAt: now, + owner, + totals: { activeSponsors: 0, monthlyUsd: 0 }, + tiers: {}, + sponsors: [] + }; + + fs.mkdirSync(outDir, { recursive: true }); + + const query = ` + query($login: String!) { + user(login: $login) { + sponsorshipsAsMaintainer(first: 100, activeOnly: true) { + nodes { + sponsorEntity { + __typename + ... on User { login url } + ... on Organization { login url } + } + tier { + name + monthlyPriceInDollars + isOneTime + } + privacyLevel + createdAt + } + } + } + } + `; + + try { + const result = await github.graphql(query, { login: owner }); + const nodes = result?.user?.sponsorshipsAsMaintainer?.nodes || []; + + const sponsors = nodes + .filter(n => n?.tier && !n.tier.isOneTime && n?.sponsorEntity?.login) + .map(n => ({ + login: n.sponsorEntity.login, + url: n.sponsorEntity.url, + tierName: n.tier.name, + monthlyUsd: n.tier.monthlyPriceInDollars, + privacyLevel: n.privacyLevel, + createdAt: n.createdAt + })) + .sort((a, b) => b.monthlyUsd - a.monthlyUsd || a.login.localeCompare(b.login)); + + const tiers = {}; + let monthlyUsd = 0; + for (const s of sponsors) { + monthlyUsd += s.monthlyUsd; + if (!tiers[s.monthlyUsd]) { + tiers[s.monthlyUsd] = { count: 0, sponsors: [] }; + } + tiers[s.monthlyUsd].count += 1; + tiers[s.monthlyUsd].sponsors.push({ login: s.login, url: s.url, tierName: s.tierName }); + } + + const payload = { + generatedAt: now, + owner, + totals: { activeSponsors: sponsors.length, monthlyUsd }, + tiers, + sponsors + }; + + fs.writeFileSync(outFile, JSON.stringify(payload, null, 2) + '\n', 'utf8'); + core.notice(`Exported ${sponsors.length} active sponsors for ${owner}.`); + } catch (error) { + core.warning(`Sponsor export failed: ${error.message}`); + fs.writeFileSync(outFile, JSON.stringify(emptyPayload, null, 2) + '\n', 'utf8'); + } + + - name: Commit sponsor export + run: | + if [[ -n "$(git status --porcelain sponsors/sponsors.json)" ]]; then + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add sponsors/sponsors.json + git commit -m "chore(sponsors): sync sponsor tiers" + git push + else + echo "No sponsor changes detected." + fi diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8f82a77 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +bin/ +obj/ +node_modules/ +.DS_Store diff --git a/IDEA_BACKLOG.md b/IDEA_BACKLOG.md new file mode 100644 index 0000000..f8d696e --- /dev/null +++ b/IDEA_BACKLOG.md @@ -0,0 +1,13 @@ +# Idea backlog (Discord and manual) + +Add one line per idea. Unchecked items can be turned into GitHub issues with `python tools/auto_issue_creator.py`. + +## Pending + +- [ ] (example) Add a smoke test for the FFI bridge. + +## Process + +1. Discord: `!request Your idea` (requires `DISCORD_BOT_TOKEN` and `tools/discord_bridge.py`). +2. Or edit this file manually. +3. Run `python tools/auto_issue_creator.py` to create issues from unchecked lines (requires `gh` CLI and auth). diff --git a/README (2).md b/README (2).md new file mode 100644 index 0000000..9769413 --- /dev/null +++ b/README (2).md @@ -0,0 +1,12 @@ +--- +title: Monorepo wiki (curated stubs) +sidebar_label: Monorepo wiki +description: Small curated pages for the target monorepo layout — not the GitHub Wiki import. +--- + +# Monorepo wiki (curated) + +This folder holds **short, maintained** pages that describe the **intended** repository layout (e.g. Hexmod, framework hooks). It is separate from **`docs/wiki-import/`**, which mirrors the **GitHub Wiki**. + +- Prefer **new documentation** in [`topics/`](../topics/index.md) or [`reference/`](../reference/fmf-hook-naming.md) when the content is not tied to a single mod stub. +- Optional manifest: [`mods/mod-index.json`](./mods/mod-index.json) (for future tooling; not consumed by Docusaurus automatically yet). diff --git a/README.md b/README.md new file mode 100644 index 0000000..c89e817 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# Documentation layout (`docs/`) + +This folder is the **single source of truth** for the public Docusaurus site. The app lives in [`wiki/`](../wiki/); built pages are served under the **`/wiki`** base path. + +## How content is organized + +| Area | Path | Purpose | +|------|------|--------| +| **Landing** | [`intro.md`](./intro.md) | Site home (`/wiki/docs`). | +| **Curated topics** | [`topics/`](./topics/) | Hubs: **Rollen** (Spieler, Moddevs, Contributor, Sponsoren → [`audiences/overview`](./topics/audiences/overview.md)), Roadmap, Security, Wiki-Import. | +| **Mods & plugins** | [`mods/`](./mods/) | Framework, plugin wiki, mod wiki, standalone index. | +| **Releases** | [`releases/`](./releases/) | Per-artifact release notes. | +| **Reference** | [`reference/`](./reference/) | Hooks, naming, MCP, generated catalogs. | +| **Contributors** | [`contributors/`](./contributors/) | Repo layout, Docusaurus workflow, design system. | +| **Audiences** | [`audiences/`](./audiences/) | Newbies / intermediates / professionals. | +| **Roadmap** | [`roadmap/`](./roadmap/) | Planning docs. | +| **Meta** | [`meta/`](./meta/) | Workshop, devserver, backlog. | +| **Monorepo wiki stubs** | [`wiki/`](./wiki/) | Short pages tied to the target repo layout (e.g. Hexmod). **Not** the GitHub Wiki import. | +| **Legacy GitHub Wiki import** | [`wiki-import/`](./wiki-import/) | Bulk import from `.wiki/` (see below). | + +## Legacy GitHub Wiki (`docs/wiki-import/`) + +Long-form pages that originally lived in the **GitHub Wiki** are mirrored here so they are searchable and versioned with the repo. + +1. **Clone or update** the wiki working tree at the repo root as **`.wiki/`** (separate clone: `https://github.com//.wiki.git`). +2. From **`wiki/`**, refresh the mirror and split locales: + - `npm run wiki:refresh` + - Or stepwise: `npm run wiki:sync` → `npm run wiki:normalize-i18n` (optional `--dry-run` first). +3. **German** translations for paired pages live under `wiki/i18n/de/docusaurus-plugin-content-docs/current/wiki-import/` after normalization. +4. **New curated docs** should usually be added as normal Markdown under `docs/` (topics, reference, mods), not only under `wiki-import/`, so they stay easy to find. Use `wiki-import/` for bulk legacy material and incremental fixes; migrate important pages into `docs/topics/` or `docs/reference/` when you rewrite them. + +Details: [`topics/wiki-import/overview.md`](./topics/wiki-import/overview.md). + +## URLs + +- Doc id `intro` → `/wiki/docs` (see front matter). +- Most docs → `/wiki/` (e.g. `mods/framework` → `/wiki/mods/framework`). +- The monorepo stub folder uses ids like `wiki/mods/hexmod` → `/wiki/wiki/mods/hexmod` (double `wiki` in the path). Prefer linking by **doc id** or stable titles rather than hand-typing URLs. + +## Scripts (repo root / `wiki/`) + +| Script | Location | Role | +|--------|------------|------| +| Sync `.wiki` → `docs/wiki-import/` | `wiki/scripts/sync-wiki-to-docs.mjs` | Copies `*.md` from `.wiki/`. | +| Split DE/EN pairs | `wiki/scripts/normalize-wiki-import-i18n.mjs` | EN default locale, DE under `wiki/i18n/de/...`. | +| Sidebar category keys | `wiki/scripts/write-wiki-import-category-keys.mjs` | Regenerates `_category_.json` keys under Guides/Reference/Troubleshooting. | diff --git a/SPONSORS.md b/SPONSORS.md new file mode 100644 index 0000000..e9ff3a1 --- /dev/null +++ b/SPONSORS.md @@ -0,0 +1,54 @@ +# Sponsors + +Thank you for supporting the GregFramework ecosystem. + +## Top Sponsor (VIP) + +- **💎 The Ecosystem Architect ($50/month):** [@tobiasreichel](https://github.com/tobiasreichel) + +Premium benefits currently assigned: + +- Top sponsor placement on ecosystem-facing surfaces (Wiki front page + Mod Store footer) +- Private 1-on-1 Discord lounge +- Featured mod spotlight option + +## Sponsorship Tiers + +### ☕ $1 / month – The Coffee Supporter + +- GitHub sponsor badge +- Discord `Supporter` role +- Eternal gratitude + +### 🥉 $5 / month – Bronze Backer (Active Player) + +Includes all previous perks, plus: + +- Credits entry in GregTools Mod Manager (`Special Thanks / Sponsors`) +- Access to sponsor-only Discord chat for spoilers/WIP/dev updates + +### 🥈 $15 / month – Silver Tester (Early Access) + +Includes all previous perks, plus: + +- Early access to pre-release and beta builds +- Roadmap voting access for Silver+ backers + +### 🥇 $25 / month – Gold Developer (Pro Modder) + +Includes all previous perks, plus: + +- Priority handling for framework bug reports +- Official Gold sponsor mention in repository `README.md` + +### 💎 $50 / month – The Ecosystem Architect (Premium/VIP) + +Includes all previous perks, plus: + +- Top sponsor placement on Wiki front page and Mod Store footer +- Private 1-on-1 Discord lounge +- Featured mod spot on `datacentermods.com` + +## Automation + +Use a repository workflow file at `.github/workflows/sponsor-tier-sync.yml` to export current sponsor tiers and drive Discord/website sync jobs. diff --git a/architecture.md b/architecture.md new file mode 100644 index 0000000..a9be755 --- /dev/null +++ b/architecture.md @@ -0,0 +1,30 @@ +--- +title: Monorepo — Architecture +sidebar_label: Monorepo architecture +description: Core vs bindings vs mods; hook scanner; Game2Framework compatibility. +--- + +# Monorepo — Architecture + +## Layers + +| Layer | Role | +|------|------| +| **Core** | MelonLoader mod + event dispatch — today under `framework/FrikaMF/` (C#). Target layout: `FrikaModFramework/src/core/`. | +| **Bindings** | Language-specific surfaces — placeholders under `FrikaModFramework/src/bindings/`. | +| **Mods / plugins** | Shipped sources in `mods/` and `plugins/`; optional pilot tree `HexMod/` (VDF + hooks metadata). | +| **Docs** | Docusaurus consumes repo-root `docs/`; app lives in `wiki/`. | + +## Hook registry + +`FrikaModFramework/fmf_hooks.json` is the declarative **single source of truth** for documented `FMF.*` hooks. The runtime still exposes legacy `FFM.*` strings where not yet migrated. + +## Tools + +- **`tools/fmf-hook-scanner`** — emit the [FMF Hook Reference](./fmf-hooks) page from the registry. +- **`tools/game2framework-migrator`** — dry-run mapping using `tools/fmf-hook-scanner/mapping/game2framework-map.json`. +- **`mcp-server/`** — Model Context Protocol server (docs + registry) for IDEs; can run in Docker with the static wiki — see [MCP server](../../reference/mcp-server.md). + +## Steam & Workshop + +Workshop templates: `templates/workshop/`. CLI/upload scripts: `tools/steam-workshop-upload/`. Desktop uploader (Windows MAUI): `WorkshopUploader/` (see `WorkshopUploader/README.md`). diff --git a/audiences/intermediates.md b/audiences/intermediates.md new file mode 100644 index 0000000..8fa441d --- /dev/null +++ b/audiences/intermediates.md @@ -0,0 +1,30 @@ +--- +id: intermediates +title: Intermediates +slug: /audiences/intermediates +--- + +## Goal + +Build and debug your own mods with stable framework workflows. + +## Learning path + +1. Pick one track: C# or Rust. +2. Learn hooks/events flow. +3. Use verified targets from `HOOKS.md`. +4. Implement mod config and diagnostics. + +## Read next + +- `.wiki/Mod-Developer-Debug.md` +- `.wiki/Mod-Developer-Debug-en.md` +- `.wiki/Modding-Guide.md` +- `.wiki/FFI-Bridge-Reference.md` +- `.wiki/Web-UI-Bridge.md` + +## Practical checkpoints + +- Build passes in debug/release +- Hook target is verified in `HOOKS.md` +- Events are version-safe and documented diff --git a/audiences/newbies.md b/audiences/newbies.md new file mode 100644 index 0000000..089e89e --- /dev/null +++ b/audiences/newbies.md @@ -0,0 +1,29 @@ +--- +id: newbies +title: Newbies +slug: /audiences/newbies +--- + +## Goal + +Get FrikaMF running safely and understand the minimum concepts. + +## Start here + +1. Install MelonLoader and run the game once. +2. Copy `FrikaModdingFramework.dll` into `Data Center/Mods`. +3. Add the mod that depends on FrikaMF. +4. Check `MelonLoader/Latest.log`. + +## Read next + +- `.wiki/End-User-Release.md` +- `.wiki/End-User-Release-en.md` +- `.wiki/Known-Incompatibilities-en.md` +- `.wiki/Bekannte-Inkompatibilitaeten.md` + +## Common mistakes + +- Wrong folder (`Mods` vs `RustMods`) +- Missing first game start after MelonLoader install +- Mixing incompatible game/framework versions diff --git a/audiences/professionals.md b/audiences/professionals.md new file mode 100644 index 0000000..5b89af3 --- /dev/null +++ b/audiences/professionals.md @@ -0,0 +1,32 @@ +--- +id: professionals +title: Pros +slug: /audiences/professionals +--- + +## Goal + +Work on framework internals, ABI stability, CI quality, and long-term maintainability. + +## Focus areas + +- Runtime architecture and ownership boundaries +- C#↔Rust ABI evolution +- Compatibility arbitration and diagnostics +- Release assets and template integrity +- Security review pipeline for future Mod Store + +## Read next + +- `.wiki/Architecture.md` +- `.wiki/Framework-Features-Use-Cases.md` +- `.wiki/StandaloneMods.md` +- `.wiki/Repository-Status-2026-04-04.md` +- `.wiki/ROADMAP.md` +- `.wiki/TASKLIST.md` + +## Professional quality gates + +- Explicit migration notes for contracts/events +- Reproducible build and release pipelines +- Documented rollback and incompatibility strategy diff --git a/contributors/docusaurus-workflow.md b/contributors/docusaurus-workflow.md new file mode 100644 index 0000000..205b3f6 --- /dev/null +++ b/contributors/docusaurus-workflow.md @@ -0,0 +1,40 @@ +--- +id: docusaurus-workflow +title: Docusaurus Contributor Workflow +slug: /contributors/docusaurus-workflow +--- + +## Local workflow + +Markdown and MDX live in the repo-root `docs/` folder. The Docusaurus app is in `wiki/`. + +```bash +cd wiki +npm install +npm run start +``` + +## Build workflow + +```bash +cd wiki +npm run build +npm run serve +``` + +## Can we hide Docusaurus build stuff from non-contributors? + +Short answer for a **public repo**: **not fully**. + +What you can do: + +- Keep generated output (`build/`, `.docusaurus/`, `node_modules/`) out of Git using `.gitignore`. +- Put docs tooling under `wiki/` so core runtime contributors can ignore it; content stays in `docs/`. +- Use path-based CODEOWNERS to limit review noise. +- Trigger docs CI on `docs/**` and `wiki/**` changes. + +What you cannot do in a public repo: + +- Fully hide tracked source files from non-contributors. + +If you need true visibility restriction, use a private repo/submodule for docs infra. diff --git a/contributors/luminescent-design-system.md b/contributors/luminescent-design-system.md new file mode 100644 index 0000000..71afe7d --- /dev/null +++ b/contributors/luminescent-design-system.md @@ -0,0 +1,97 @@ +--- +sidebar_label: Luminescent design system +description: Visual and interaction guidelines for the docs site (Luminescent Architect). +--- + +The **Frika Mod Framework** (FMF) is the product name for all Docusaurus branding (site title, navbar, page titles). **Luminescent Architect** names this visual design system only. + +# Design System Specification: The Luminescent Architect + +## 1. Overview & Creative North Star + +**The Creative North Star: "The Luminescent Architect"** +This design system moves away from the "flat-and-boxy" utility of standard modding sites. It treats code and community interaction as an architectural feat. We achieve a high-end editorial feel through **Tonal Depth** and **Intentional Asymmetry**. Instead of rigid grids that feel like a spreadsheet, we use overlapping layers and "light-bleed" to guide the user’s eye. The goal is a digital environment that feels like a high-end laboratory: sterile, precise, yet pulsing with the energy of the teal primary accent. + +## 2. Color & Atmospheric Theory + +We do not use color simply to decorate; we use it to define space. + +### The "No-Line" Rule + +**Explicit Instruction:** Prohibit 1px solid borders for sectioning. +Boundaries are defined solely through background color shifts. For example, a `surface-container-low` section sitting on a `surface` background creates a natural edge. This "Editorial Bleed" makes the interface feel expansive and premium rather than boxed-in. + +### Surface Hierarchy & Nesting + +Treat the UI as physical layers of "Synthetic Glass." + +- **Layer 0 (Base):** `surface` (#001110) - The deep abyss. +- **Layer 1 (Sub-sections):** `surface-container-low` (#001715). +- **Layer 2 (Cards/Containers):** `surface-container` (#001E1C). +- **Layer 3 (Modals/Popovers):** `surface-container-high` (#002422). + +### The Glass & Gradient Rule + +Floating elements (Navigation, Tooltips) must use **Glassmorphism**. + +- **Formula:** `surface-container` at 80% opacity + `backdrop-filter: blur(12px)`. +- **Signature Texture:** Primary CTAs must utilize a subtle linear gradient from `primary` (#61F4D8) to `primary-container` (#08C1A6) at a 135-degree angle. This prevents "flatness" and gives the button a machined, metallic quality. + +Implementation reference: `wiki/src/css/custom.css` (`@theme` tokens, `.btn-primary`, `.glass-card`, `.navbar`). + +## 3. Typography: The Editorial Edge + +The contrast between the technical precision of **Inter** and the geometric authority of **Space Grotesk** defines the brand. + +- **Display & Headlines (Space Grotesk):** Use these for hero sections and documentation titles. High-contrast and slightly wider tracking (+2%) creates an authoritative, "tech-brochure" aesthetic. Tailwind / utility: `font-headline`. +- **Body & Labels (Inter):** Reserved for technical data and long-form wiki content. Inter’s tall x-height ensures readability against the dark `background`. Default body uses `font-sans`. +- **Code Blocks:** Must use a monospaced font (JetBrains Mono) nested in `surface-container-highest` to differentiate logic from documentation. Utility: `font-mono` / Infima monospace variables. + +## 4. Elevation & Depth + +We abandon the traditional drop-shadow. Depth is achieved via **Tonal Layering**. + +- **The Layering Principle:** To lift a card, place a `surface-container-lowest` card inside a `surface-container-low` section. The "inverse lift" creates a sophisticated, recessed look. +- **Ambient Glows:** For "floating" primary elements, use a shadow with the color `primary` at 10% opacity, a blur of `32px`, and a spread of `-4px`. This mimics the glow of a physical LED. +- **The Ghost Border Fallback:** If a border is required for accessibility, use the `outline-variant` token at **15% opacity**. This creates a "suggestion" of a line that disappears into the background. + +## 5. Component Architecture + +### Buttons: The Kinetic Core + +- **Primary:** Gradient fill (`primary` to `primary-container`), `on-primary` text. Add a 2px outer glow of `primary` on hover. +- **Secondary (Outlined):** No fill. A "Ghost Border" of `outline-variant` at 40%. On hover, the border opacity jumps to 100%. +- **Tertiary:** Pure text in `secondary`. Used for low-priority actions in the Sidebar. + +### Navigation: Structural Fixed Points + +- **Top Bar (Global):** Height: 72px. Background: `surface` (80% opacity) with `backdrop-blur`. No bottom border; use a subtle transition to `surface-container-low` for the content area. +- **Wiki Sidebar (Contextual):** Sticky position. Uses `surface-container-low`. Active links use a left-aligned 4px vertical pill in `primary` and high-contrast `on-surface` text. + +### Cards & Information Architecture + +- **Rule:** Forbid divider lines within cards. +- **Implementation:** Use 24px vertical padding (from the spacing scale) to separate the header from the body. Use `surface-variant` for metadata badges (e.g., "v1.2.0") to provide a "recessed" look. + +### Code Blocks & Documentation + +- **Container:** `surface-container-highest` with a `md` (0.75rem) corner radius. +- **Syntax Highlighting:** Use `tertiary` (#64D0FF) for functions, `secondary` (#1CEDE1) for strings, and `error_dim` (#D7383B) for keywords. + +## 6. Do’s and Don’ts + +### Do: + +- **Do** use `primary_fixed_dim` for icons to ensure they don't overpower the text. +- **Do** allow for generous "negative space." High-end editorial design requires breathing room to feel premium. +- **Do** use `surface_bright` as a very subtle "top-light" gradient on large containers to simulate overhead lighting. + +### Don’t: + +- **Don’t** use pure white (#FFFFFF) for text. Always use `on_surface` (#C0FCF6) to reduce eye strain in dark mode. +- **Don’t** use 1px solid borders to separate sidebar items; use vertical spacing or a subtle background hover state. +- **Don’t** use standard "drop shadows" (Black/Grey). Always tint your shadows with the background or accent color to maintain tonal harmony. + +## 7. Token map (implementation) + +Semantic colors are defined as Tailwind v4 `@theme` variables in `wiki/src/css/custom.css` (e.g. `--color-primary`, `--color-on-surface`, `--color-surface-container-low`). Prefer those utilities in React pages (`bg-background`, `text-on-surface`, `border-outline-variant/15`) so the site stays aligned with this spec. diff --git a/contributors/monorepo-target-layout.md b/contributors/monorepo-target-layout.md new file mode 100644 index 0000000..97218ee --- /dev/null +++ b/contributors/monorepo-target-layout.md @@ -0,0 +1,55 @@ +--- +id: monorepo-target-layout +title: Monorepo target layout and migration phases +sidebar_label: Monorepo target layout +description: Planned top-level structure and phased migration without a single big-bang refactor. +--- + +# Monorepo target layout and migration phases + +The repository **stays one Git repo**. The goal is **clear boundaries** between framework, mods, plugins, templates, docs, and tooling so users, modders, and contributors can navigate predictably. + +## Target topology (directional) + +| Top-level | Purpose | +|-----------|---------| +| `framework/` | Core MelonLoader framework (`framework/FrikaMF.csproj`, `framework/FrikaMF/`, entry `Main.cs`) | +| `mods/` | Gameplay mods (`FMF.Mod.*`, `FMF.*.dll` style) | +| `plugins/` | FFM plugins (`FFM.Plugin.*`) | +| `Templates/` | Scaffolds for new mods/plugins | +| `wiki/` | Docusaurus site (product docs; route base `/wiki`) | +| `tools/` | Repo maintenance: hook catalog generator, codegen stubs | +| `scripts/` | Release automation (existing) | + +**Binaries**: prefer **GitHub Releases** (and pre-releases for beta) over committing DLLs. See [Release channels](../reference/release-channels.md). + +## Phased migration (no big-bang) + +```mermaid +flowchart LR + p1[Phase1_DocsAndTools] + p2[Phase2_MoveModsPlugins] + p3[Phase3_FrameworkExtract] + p4[Phase4_CIRedirects] + p1 --> p2 --> p3 --> p4 +``` + +| Phase | Scope | Exit criteria | +|-------|--------|---------------| +| **1** | Docs, `tools/`, naming wiki, hook catalog script | Docusaurus build green; script generates catalog | +| **2** | `git mv` former `ModsAndPlugins/` → `mods/` / `plugins/` | Done — `.csproj` relative paths unchanged (depth preserved); CI/docs updated | +| **3** | Framework sources under `framework/` | Done — `FrikaMF.sln` points at `framework\framework/FrikaMF.csproj`; plugins reference `..\..\framework\framework/FrikaMF.csproj` | +| **4** | CI matrix: docs + dotnet; `plugin-client-redirects` for old URLs | PR checks match local workflow | + +## Path updates checklist (Phase 2 applied) + +- [x] `FrikaMF.sln` project paths (`plugins\FFM.Plugin.*`) +- [x] `.github/workflows` (CodeQL, release assets, Discord feed) +- [x] Contributor docs and mod/plugin wiki pages (`Project Path` lines) +- [ ] [`wiki/docusaurus.config.js`](https://github.com/mleem97/gregFramework/blob/master/wiki/docusaurus.config.js) redirects (only if public URLs must map old paths) +- [ ] Historical wiki-import pages may still mention `StandaloneMods/` — update when editing those files + +## Related + +- [Repo inventory](./repo-inventory.md) +- [FMF hook naming](../reference/fmf-hook-naming.md) diff --git a/contributors/plugin-submission-audit.md b/contributors/plugin-submission-audit.md new file mode 100644 index 0000000..dee1969 --- /dev/null +++ b/contributors/plugin-submission-audit.md @@ -0,0 +1,31 @@ +--- +id: plugin-submission-audit +title: Plugin Submission & Security Audit Workflow +slug: /contributors/plugin-submission-audit +--- + +## Goal + +Provide a repeatable workflow where community authors submit plugins through a Git repository URL, then pass an automated security/quality audit before publication in the wiki and release channels. + +## Submission Model + +1. Author opens a **Plugin Submission** issue. +2. Author provides a public Git repository URL (`https://...git`). +3. Maintainer triggers the security-audit workflow. + +## Automated Audit Steps + +- Clone submitted repository in CI. +- Run static scan for suspicious calls and execution vectors. +- Run secret and credential pattern checks. +- Produce an auditable report artifact. + +## Release Gate Policy + +- If audit result is **fail**, publication is blocked. +- If audit result is **pass**, maintainers can mark module as `releaseReady` and publish wiki/release visibility. + +## Multiplayer Clarification + +Steamworks multiplayer remains a planned direction but is currently blocked by missing Steamworks implementation on the game developer side. diff --git a/contributors/repo-inventory.md b/contributors/repo-inventory.md new file mode 100644 index 0000000..dbbf612 --- /dev/null +++ b/contributors/repo-inventory.md @@ -0,0 +1,76 @@ +--- +id: repo-inventory +title: Repository inventory +sidebar_label: Repo inventory +description: Current monorepo layout, projects, and known solution drift (contributors). +--- + +# Repository inventory + +This page is the **source of truth snapshot** for how the DataCenterExporter / gregFramework monorepo is organized today. Use it before large refactors or when onboarding. + +## Top-level areas + +| Area | Path | Role | +|------|------|------| +| Framework core | [`framework/FrikaMF.csproj`](https://github.com/mleem97/gregFramework/blob/master/framework/FrikaMF.csproj) | MelonLoader mod hosting runtime hooks, Harmony, bridge, events | +| Target layout / registry | [`FrikaModFramework/`](https://github.com/mleem97/gregFramework/tree/master/FrikaModFramework) | `fmf_hooks.json`, bindings stubs, migration docs | +| Workshop tooling | [`workshopuploader/`](https://github.com/mleem97/gregFramework/tree/master/workshopuploader) (rename from `WorkshopUploader/`; see `WorkshopUploader/MIGRATION_PUBLIC_REPO.md`) | Steam Workshop / workspace uploader — **.NET MAUI** (Windows) | +| MCP (LLM / IDE) | [`mcp-server/`](https://github.com/mleem97/gregFramework/tree/master/mcp-server) | Model Context Protocol over docs + `fmf_hooks.json`; Docker: `docker compose up docs-mcp` | +| Mods (sources) | [`mods/`](https://github.com/mleem97/gregFramework/tree/master/mods) | Gameplay mods (`FMF.*`, `FMF.Mod.*` folders) | +| Plugins (sources) | [`plugins/`](https://github.com/mleem97/gregFramework/tree/master/plugins) | Framework plugins (`FFM.Plugin.*`) | +| Templates | [`Templates/`](https://github.com/mleem97/gregFramework/tree/master/Templates) | Scaffolds for new mods/plugins | +| Documentation content | [`docs/`](https://github.com/mleem97/gregFramework/tree/master/docs) | Markdown/MDX sources for the wiki | +| Documentation site (Docusaurus) | [`wiki/`](https://github.com/mleem97/gregFramework/tree/master/wiki) | App shell, theme, `npm run build` | +| Scripts | [`scripts/`](https://github.com/mleem97/gregFramework/tree/master/scripts) | Release metadata, changelog (e.g. `Update-ReleaseMetadata.ps1`) | +| Wiki import (legacy) | [`docs/wiki-import/`](./../wiki-import/Home.md) | Imported `.wiki` content; still linked from many pages | + +## .NET projects on disk (`*.csproj`) + +| Project | Location | In `FrikaMF.sln`? | +|---------|----------|-------------------| +| FrikaMF | `framework/FrikaMF.csproj` | Yes | +| WorkshopUploader | `workshopuploader/WorkshopUploader.csproj` (after folder rename) | No — use `WorkshopUploader.sln` in that folder | +| FFM.Plugin.* (x5) | `plugins/FFM.Plugin.*/` | Yes — paths in [`FrikaMF.sln`](https://github.com/mleem97/gregFramework/blob/master/FrikaMF.sln) use `plugins\...` | +| FMF.HexLabelMod | `mods/FMF.Mod.HexLabelMod/` | No (build standalone or add to solution) | +| FMF.ConsoleInputGuard | `mods/FMF.ConsoleInputGuard/` | No | +| FMF.GregifyEmployees | `mods/FMF.Mod.GregifyEmployees/` | No | +| FMF.JoniMLCompatMod | `mods/FMF.Plugin.LangCompatBridge/` | No | +| Templates | `Templates/FMF.*`, `Templates/StandaloneModTemplate/` | No | + +## Build status (framework project) + +- `framework/FrikaMF.csproj` explicitly **excludes** `workshopuploader/**` from compile (that app builds only via `workshopuploader/WorkshopUploader.csproj` / `WorkshopUploader.sln` in that folder). +- `dotnet build FrikaMF.sln` builds framework and plugin projects under `plugins\` — **not** the MAUI Workshop app (MelonLoader/game refs still required locally unless `CI=true`). + +## `FrikaMF.sln` drift (action items) + +1. **Mods not in solution**: Standalone mod projects under `mods/` are intentionally omitted from the solution to keep the graph small; add them if you want `dotnet build` for every module in one shot. + +2. **Templates in `framework/FrikaMF.csproj`**: Template sources under `Templates/` may fail `dotnet build framework/FrikaMF.csproj` with `CS0122` if `Core` visibility does not match template expectations — treat templates as **samples** until the project graph is cleaned up. + +## Documentation (Docusaurus) + +- **Entry**: `/wiki` → [`intro`](../intro.md) +- **Sidebar**: [`sidebars.js`](https://github.com/mleem97/gregFramework/blob/master/wiki/sidebars.js) +- **Module catalog** (downloads table): [`wiki/src/data/moduleCatalog.ts`](https://github.com/mleem97/gregFramework/blob/master/wiki/src/data/moduleCatalog.ts) +- **Landing**: `/` → [`src/pages/index.tsx`](https://github.com/mleem97/gregFramework/blob/master/wiki/src/pages/index.tsx) +- **Static catalog page**: `/mods` + +## Hook / event sources of truth (code) + +- String constants: [`framework/FrikaMF/HookNames.cs`](https://github.com/mleem97/gregFramework/blob/master/framework/FrikaMF/HookNames.cs) (`FFM.*` hook IDs today). +- Numeric IDs: [`framework/FrikaMF/EventIds.cs`](https://github.com/mleem97/gregFramework/blob/master/framework/FrikaMF/EventIds.cs). +- Generated wiki mirror: run [`tools/Generate-FmfHookCatalog.ps1`](https://github.com/mleem97/gregFramework/blob/master/tools/Generate-FmfHookCatalog.ps1) → [`fmf-hooks-catalog`](../reference/fmf-hooks-catalog.md). + +## Debugging (MelonLoader, IL2CPP, Unity) + +- **Build first:** `dotnet build FrikaMF.sln -c Debug` (requires MelonLoader + IL2CPP interop under `MelonLoader/` for your game install, or `lib/references/MelonLoader` — see `tools/refresh_refs.py`). +- **Attach:** Run **Data Center** with MelonLoader, then attach your IDE’s **.NET / CoreCLR** debugger to the game process (process name usually matches the game executable). Breakpoints hit in **Debug** builds of mods/plugins copied into `Mods/`. +- **`FFM.Plugin.AssetExporter`:** The project links `framework/Main.cs` (and related files) **and** references `FrikaMF.csproj`, which would normally produce **CS0436** duplicate-type warnings. Those are **suppressed** in the plugin `.csproj` (`NoWarn`); do not remove the project reference without linking the rest of the `FrikaMF` sources. + +## Related + +- [Monorepo target layout](./monorepo-target-layout.md) — phased folder goals +- [FMF hook naming](../reference/fmf-hook-naming.md) — naming convention +- [Release channels](../reference/release-channels.md) — Steam vs GitHub beta diff --git a/contributors/sponsorship-automation.md b/contributors/sponsorship-automation.md new file mode 100644 index 0000000..17f0c71 --- /dev/null +++ b/contributors/sponsorship-automation.md @@ -0,0 +1,65 @@ +# Sponsorship Automation + +This document describes the standard automation flow for GregFramework sponsorship tiers. + +## Goal + +Keep sponsor tier data synchronized and use it as a source of truth for: + +- Discord role sync +- private/VIP channel access +- website and wiki placements +- Mod Store footer sponsor block +- in-repo sponsor pages and credits + +## Repository Workflow + +Each repository contains `.github/workflows/sponsor-tier-sync.yml`. + +The workflow: + +1. runs hourly (and on manual trigger), +2. queries active GitHub Sponsors, +3. exports normalized data to `sponsors/sponsors.json`, +4. commits changes automatically when sponsor data changes. + +## Required Secret + +Add this repository secret: + +- `SPONSORS_READ_TOKEN`: GitHub PAT with access to read sponsor relationships. + +Without this secret, the workflow still completes but exports an empty snapshot. + +## Tier Mapping + +Use this mapping in downstream systems: + +- `$1` -> `coffee_supporter` +- `$5` -> `bronze_backer` +- `$15` -> `silver_tester` +- `$25` -> `gold_developer` +- `$50+` -> `ecosystem_architect` + +## VIP Operational Rules + +For `$50+` sponsors: + +- assign top sponsor placement (Wiki front page + Mod Store footer) +- create/maintain private 1-on-1 Discord lounge +- offer featured mod spotlight + +## Example Consumer Script (Discord/Wiki Sync) + +Use `sponsors/sponsors.json` as input and run a separate scheduled job (bot or CI worker) that: + +1. maps each sponsor to a tier role, +2. grants/revokes Discord roles, +3. maintains VIP private channels, +4. updates website/wiki data endpoints. + +## First Verified VIP + +Current VIP sponsor: + +- [@tobiasreichel](https://github.com/tobiasreichel) – `ecosystem_architect` (`$50/month`) diff --git a/devserver-betas.md b/devserver-betas.md new file mode 100644 index 0000000..c8aa371 --- /dev/null +++ b/devserver-betas.md @@ -0,0 +1,25 @@ +# DevServer API — beta channels (`gregframework.eu`) + +This document defines the **intended** client contract for the FrikaMF **WorkshopUploader** “Betas” panel. The server may be implemented separately; keep URLs and tokens configurable. + +## Base URL + +- Default: `https://gregframework.eu` +- Override: user settings file next to the app (not committed to git). + +## Endpoints (proposed) + +| Method | Path | Purpose | +|--------|------|---------| +| `GET` | `/api/v1/betas` | List available beta channels (id, name, description). | +| `POST` | `/api/v1/betas/{id}/subscribe` | Enroll the current user (body: Steam ID or bearer token from OAuth). | +| `POST` | `/api/v1/betas/{id}/unsubscribe` | Leave a channel. | + +## Authentication + +- **Preferred:** Short-lived JWT after browser OAuth to `gregframework.eu`, stored in user settings. +- **Alternative:** Steam ID from Steamworks session in the WorkshopUploader process, plus server-side verification. + +## Rate limiting and errors + +Clients should show HTTP status and response body on failure; retry with backoff. diff --git a/fmf-hooks.mdx b/fmf-hooks.mdx new file mode 100644 index 0000000..93d95c1 --- /dev/null +++ b/fmf-hooks.mdx @@ -0,0 +1,98 @@ +--- +id: fmf-hooks +title: FMF Hook Reference +sidebar_label: FMF Hook Reference +description: Auto-generated from FrikaModFramework/fmf_hooks.json — run tools/fmf-hook-scanner. +--- + +:::info +This page is generated by `tools/fmf-hook-scanner`. Do not edit by hand. +::: + +# FMF Hook Reference + +## EMPLOYEE + +### FMF.EMPLOYEE.Hired + +**Description:** An employee was hired into the roster. + +**Payload:** + +- `employeeId` (string) +- `role` (string) + +**Example (C#)** + +```csharp +FmfApi.On("FMF.EMPLOYEE.Hired", payload => { }); +``` + +## GAMEPLAY + +### FMF.GAMEPLAY.JobCompleted + +**Description:** Emitted when a job has been fully processed. + +**Payload:** + +- `jobId` (string) +- `durationSeconds` (number) +- `revenue` (number) + +**Example (C#)** + +```csharp +FmfApi.On("FMF.GAMEPLAY.JobCompleted", payload => { }); +``` + +## PLAYER + +### FMF.PLAYER.LevelUp + +**Description:** Player progression level increased. + +**Payload:** + +- `newLevel` (number) +- `previousLevel` (number) + +**Example (C#)** + +```csharp +FmfApi.On("FMF.PLAYER.LevelUp", payload => { }); +``` + +## RACK + +### FMF.RACK.PowerLimitExceeded + +**Description:** Power draw or allocation for a rack exceeded configured limits. + +**Payload:** + +- `rackId` (string) +- `watts` (number) + +**Example (C#)** + +```csharp +FmfApi.On("FMF.RACK.PowerLimitExceeded", payload => { }); +``` + +## SERVER + +### FMF.SERVER.RackOverheated + +**Description:** Server rack exceeded a critical temperature threshold. + +**Payload:** + +- `rackId` (string) +- `temperature` (number) + +**Example (C#)** + +```csharp +FmfApi.On("FMF.SERVER.RackOverheated", payload => { }); +``` diff --git a/getting-started.md b/getting-started.md new file mode 100644 index 0000000..a05c13b --- /dev/null +++ b/getting-started.md @@ -0,0 +1,43 @@ +--- +title: Monorepo — Getting started +sidebar_label: Monorepo getting started +description: Target layout (FrikaModFramework, templates, tools) and how it maps to this repo today. +--- + +# Monorepo — Getting started + +The **goal** is a clear split between the framework (`FrikaModFramework/` registry + planned bindings), the **live** MelonLoader code (`framework/`), gameplay mods (`mods/`), FFM plugins (`plugins/`), templates (`templates/` / `Templates/`), and tooling (`tools/`). Migration is incremental: production C# still lives under [`framework/`](https://github.com/mleem97/gregFramework/tree/master/framework). + +## Clone and build the framework + +```text +dotnet build framework/FrikaMF.csproj +``` + +Or open [`FrikaMF.sln`](https://github.com/mleem97/gregFramework/blob/master/FrikaMF.sln) in Visual Studio / Rider. + +## Hook naming + +- **Target convention:** `FMF..` (see [`CONTRIBUTING.md`](https://github.com/mleem97/gregFramework/blob/master/CONTRIBUTING.md)). +- **Registry:** [`FrikaModFramework/fmf_hooks.json`](https://github.com/mleem97/gregFramework/blob/master/FrikaModFramework/fmf_hooks.json). +- **Legacy runtime strings** may still use `FFM.*` in [`HookNames`](https://github.com/mleem97/gregFramework/blob/master/framework/FrikaMF/HookNames.cs) until migrated. + +## Create a mod from the template + +1. Copy [`templates/mod/`](https://github.com/mleem97/gregFramework/tree/master/templates/mod) to a new folder (or start from [`Templates/`](https://github.com/mleem97/gregFramework/tree/master/Templates) scaffolds). +2. Edit `fmf/hooks.json` and add sources under `src/`. +3. For a **pilot** layout (Workshop VDF + hooks metadata), see [`HexMod/`](https://github.com/mleem97/gregFramework/tree/master/HexMod); shipped mod examples live under [`mods/`](https://github.com/mleem97/gregFramework/tree/master/mods) (e.g. `mods/FMF.Mod.HexLabelMod/`). + +## Documentation site + +- **Content:** [`docs/`](https://github.com/mleem97/gregFramework/tree/master/docs) +- **Docusaurus app:** [`wiki/`](https://github.com/mleem97/gregFramework/tree/master/wiki) — `npm install` and `npm run start` (dev) or `npm run build` (static output). + +### Docker + +- **Dev server with hot reload:** `docker compose up docs` (port **3000**, mounts `./wiki` and `./docs`). +- **Static wiki + MCP in one container:** `docker compose up docs-mcp` — see [`reference/mcp-server`](../../reference/mcp-server.md). + +## Assistants / MCP + +The repo includes [`mcp-server/`](https://github.com/mleem97/gregFramework/tree/master/mcp-server) for Model Context Protocol (search docs, read `fmf_hooks.json`, CONTRIBUTING). Use **stdio** locally or the **HTTP** endpoint bundled with the `docs-mcp` Docker image — details in [`docs/reference/mcp-server.md`](https://github.com/mleem97/gregFramework/blob/master/docs/reference/mcp-server.md). diff --git a/guides/contributor-workshop.md b/guides/contributor-workshop.md new file mode 100644 index 0000000..5219739 --- /dev/null +++ b/guides/contributor-workshop.md @@ -0,0 +1,136 @@ +--- +id: contributor-workshop +title: Contributor Guide — WorkshopManager +sidebar_label: Contributor Guide +description: Development setup, building, publishing workflow, and release process for the WorkshopManager. +sidebar_position: 20 +tags: + - audience:contributor + - workshop +--- + +# Contributor Guide — WorkshopManager + +This guide covers the development workflow for the WorkshopManager and how to publish mods to the Steam Workshop. + +## Prerequisites + +- **Visual Studio 2022** with **.NET Multi-platform App UI** and **Windows App SDK** workloads. +- **.NET 9 SDK** (for the WorkshopManager MAUI app). +- **.NET 6 SDK** (for framework, plugins, and mods targeting MelonLoader). +- **Steam** with Data Center installed (App ID 4170200). + +## Repository structure + +| Path | Purpose | +|------|---------| +| `framework/FrikaMF.csproj` | Core MelonLoader framework DLL | +| `plugins/FFM.Plugin.*/` | FMF extension plugins (5 projects) | +| `mods/FMF.*/` | Standalone mods (4 projects) | +| `WorkshopUploader/` | WorkshopManager MAUI app | +| `scripts/Deploy-Release-ToWorkshop.ps1` | Package all builds into Workshop folders | +| `scripts/Deploy-Release-ToDataCenter.ps1` | Deploy to game for local testing | + +## Building + +### Build everything (solution) + +```bash +dotnet build FrikaMF.sln -c Release +``` + +### Build standalone mods (not in solution) + +```bash +dotnet build mods/FMF.ConsoleInputGuard/FMF.ConsoleInputGuard.csproj -c Release +dotnet build mods/FMF.Mod.GregifyEmployees/FMF.GregifyEmployees.csproj -c Release +dotnet build mods/FMF.Mod.HexLabelMod/FMF.HexLabelMod.csproj -c Release +dotnet build mods/FMF.Plugin.LangCompatBridge/FMF.JoniMLCompatMod.csproj -c Release +``` + +### Build WorkshopManager only + +```bash +dotnet build WorkshopUploader/WorkshopUploader.csproj -c Release +``` + +## Workshop project structure + +Each mod/plugin gets its own folder under `/workshop/`: + +```text +/workshop/ +├── FrikaModFramework/ +│ ├── content/ +│ │ └── Mods/ +│ │ └── FrikaModdingFramework.dll +│ ├── metadata.json +│ └── preview.png +├── FFM.Plugin.Multiplayer/ +│ ├── content/ +│ │ └── FMF/ +│ │ └── Plugins/ +│ │ └── FFM.Plugin.Multiplayer.dll +│ ├── metadata.json +│ └── preview.png +└── ... +``` + +The `content/` folder mirrors the game directory structure and is what Steam uploads. + +## Deploy to Workshop folders + +```bash +pwsh -File scripts/Deploy-Release-ToWorkshop.ps1 +``` + +This script: +1. Builds all framework, plugin, and mod projects. +2. Creates a Workshop project folder for each under `/workshop/`. +3. Copies the built DLL into `content//`. +4. Creates `metadata.json` with title, description, tags, and visibility. + +## Publishing workflow + +### GUI (recommended) + +1. Run the WorkshopManager app. +2. Open a project from the **Projects** tab. +3. Edit title, description, tags, visibility, and preview image. +4. Write **change notes** describing what changed. +5. Click **Save and upload to Steam**. +6. After upload, the app **syncs** your local `content/` with Steam's version. + +### CLI (headless) + +```bash +WorkshopUploader.exe --mode publish --path +``` + +### Post-upload sync + +After publishing, the app re-downloads the item from Steam and replaces your local `content/` folder. This ensures your working copy matches exactly what Steam has — similar to `git pull` after `git push`. + +## Extending the service layer + +The `SteamWorkshopService` in `WorkshopUploader/Services/SteamWorkshopService.cs` wraps the Facepunch.Steamworks 2.3.3 API. Key methods: + +| Method | Purpose | +|--------|---------| +| `PublishAsync` | Create or update a Workshop item with change notes | +| `SyncAfterPublishAsync` | Re-download from Steam to sync local content | +| `BrowseAsync` | Browse all Workshop items with sort/tag filters | +| `SearchAsync` | Text search across Workshop items | +| `ListSubscribedAsync` | List user's subscribed items | +| `ListFavoritedAsync` | List user's favorited items | +| `SubscribeAsync` / `UnsubscribeAsync` | Toggle subscription | +| `AddFavoriteAsync` / `RemoveFavoriteAsync` | Toggle favorite | +| `VoteAsync` | Vote up or down | +| `GetItemDetailsAsync` | Full item details with stats | + +## Adding a new mod to the release + +1. Create the mod project under `mods/`. +2. Add it to the `$mods` array in `Deploy-Release-ToWorkshop.ps1`. +3. Run the deploy script. +4. Open the new workshop project in the WorkshopManager and publish. diff --git a/guides/enduser-workshop.md b/guides/enduser-workshop.md new file mode 100644 index 0000000..e2007c2 --- /dev/null +++ b/guides/enduser-workshop.md @@ -0,0 +1,134 @@ +--- +id: enduser-workshop +title: End-User Guide — WorkshopManager +sidebar_label: End-User Guide +description: How to browse, install, and manage Data Center mods using the WorkshopManager. +sidebar_position: 10 +tags: + - audience:enduser + - workshop +--- + +# End-User Guide — WorkshopManager + +This guide is for players who want to **install and manage mods** for Data Center using the WorkshopManager desktop app. + +## What you need + +- **Data Center** installed via Steam. +- **Steam** running and logged in. +- **WorkshopManager** (`WorkshopUploader.exe`) — either built from source or provided as a release. + +## Installation + +You can run WorkshopManager from **any folder you prefer** (for example `Program Files`, Desktop, Downloads, or `/WorkshopUploader/`). + +### Option A — Installer setup (recommended) + +1. Run the latest `GregToolsModmanager--Setup.exe`. +2. Complete the installer wizard (default path is under `Program Files`). +3. Start the app from the Start Menu or desktop shortcut. + +### Option B — Portable / ZIP build + +1. Download or build the WorkshopManager files. +2. Extract/copy the folder to any location you want. +3. Launch `WorkshopUploader.exe` directly from that folder. + +After startup, the Steam status indicator in the top-right should turn green. + +## Browsing mods (Mod Store) + +1. Open the **Mod Store** tab. +2. Use the **Store** sub-tab to browse all available mods. +3. Filter by tag (vanilla, modded, melonloader, fmf, framework) or sort by popularity, date, score, etc. +4. Use the **Search** bar to find specific mods by name. +5. Click on any mod to see its **detail page** with full stats, description, and action buttons. + +## Installing mods (Subscribe) + +1. In the Store or on an item's detail page, click **Subscribe**. +2. Steam downloads the mod automatically. +3. Check the **Installed** sub-tab to see all your subscribed mods. + +Subscribed mods are managed by Steam — they update automatically when the author publishes changes. + +## Favorites + +1. On an item's detail page, click **Favorite** to bookmark it. +2. View all your favorites in the **Favorites** sub-tab. +3. Click **Unfavorite** to remove. + +## Voting + +On an item's detail page: + +- Click **Vote Up** to recommend the mod. +- Click **Vote Down** if there's a quality issue. + +Your votes help the community find the best mods. + +## Dependency Health (Health tab) + +The **Health** sub-tab checks whether your game has: + +- MelonLoader installed +- Il2Cpp interop assemblies generated +- FrikaModFramework core DLL +- FMF plugins directory +- Mod config directory + +If anything is missing, follow the instructions shown. + +### Installing MelonLoader + +1. Go to the Health tab and click **Download page**. +2. Download the latest **MelonLoader Installer** from GitHub. +3. Run the installer, select **Data Center** as the game, choose **IL2CPP**. +4. Start the game once and close it (this generates required assemblies). + +## Troubleshooting + +### "Steam - offline" in the title bar + +- Make sure Steam is running and you are logged in. +- Ensure `steam_appid.txt` (containing `4170200`) exists next to `WorkshopUploader.exe`. +- If you use the portable/ZIP variant, make sure you copied the full app folder (not just the `.exe`). + +### Mod does not load in-game + +- Check `MelonLoader/Latest.log` in the game directory. +- Ensure `FrikaModdingFramework.dll` is in `/Mods/`. +- Verify the mod DLL is also in `/Mods/` or `/FMF/Plugins/`. + +### Subscription does not appear + +- Wait a few seconds and refresh the Installed list. +- Steam may need a moment to process the subscription. + +### App crashes only after installer install + +- Reinstall using the latest setup build. +- Open **Settings -> Logs** and include the newest log lines when reporting the issue. +- If available, report crash details via GitHub Issues. + +## Reporting bugs and issues + +If you find a bug, crash, or unexpected behavior, please open an issue on GitHub: + +- [Report an issue](https://github.com/mleem97/GregToolsModmanager/issues) + +To help us fix issues faster, include: + +- What you were doing when the problem happened +- Clear steps to reproduce +- Your app version and Windows version +- Error message or screenshot (if any) +- Relevant logs from **Settings -> Logs** +- A repro ZIP from **Settings -> Create repro bundle** + +## Uninstalling mods + +1. Go to the **Installed** sub-tab. +2. Click **Unsubscribe** on the mod you want to remove. +3. Steam removes the mod files automatically. diff --git a/guides/release.md b/guides/release.md new file mode 100644 index 0000000..7635204 --- /dev/null +++ b/guides/release.md @@ -0,0 +1,118 @@ +--- +id: release +title: Release +sidebar_label: Release +description: Current release artifacts, version matrix, and download instructions for FrikaModFramework. +sidebar_position: 30 +tags: + - release +--- + +# Release + +## Release artifacts + +The following components are built and packaged for Steam Workshop distribution: + +### Core Framework + +| Component | Assembly | Workshop content path | Tags | +|-----------|----------|----------------------|------| +| FrikaModFramework | `FrikaModdingFramework.dll` | `content/Mods/` | modded, melonloader, framework, fmf | + +### FMF Plugins + +| Plugin | Assembly | Workshop content path | Tags | +|--------|----------|----------------------|------| +| FFM.Plugin.Multiplayer | `FFM.Plugin.Multiplayer.dll` | `content/FMF/Plugins/` | modded, fmf, plugin | +| FFM.Plugin.Sysadmin | `FFM.Plugin.Sysadmin.dll` | `content/FMF/Plugins/` | modded, fmf, plugin | +| FFM.Plugin.AssetExporter | `FFM.Plugin.AssetExporter.dll` | `content/FMF/Plugins/` | modded, fmf, plugin | +| FFM.Plugin.WebUIBridge | `FFM.Plugin.WebUIBridge.dll` | `content/FMF/Plugins/` | modded, fmf, plugin | +| FFM.Plugin.PlayerModels | `FFM.Plugin.PlayerModels.dll` | `content/FMF/Plugins/` | modded, fmf, plugin | + +### Gameplay Mods + +| Mod | Assembly | Workshop content path | Tags | +|-----|----------|----------------------|------| +| FMF.ConsoleInputGuard | `FMF.ConsoleInputGuard.dll` | `content/Mods/` | modded, melonloader, mod | +| FMF.GregifyEmployees | `FMF.GregifyEmployees.dll` | `content/Mods/` | modded, melonloader, mod | +| FMF.HexLabelMod | `FMF.HexLabelMod.dll` | `content/Mods/` | modded, melonloader, mod | +| FMF.JoniMLCompatMod | `FMF.JoniMLCompatMod.dll` | `content/Mods/` | modded, melonloader, mod | + +### Gregtools Modmanager (WorkshopManager) + +| Version | Component | Target | Description | +|---------|-----------|--------|-------------| +| **1.0** | Gregtools Modmanager | `net9.0-windows` (win10-x64), self-contained | Steam Workshop client + Mod Store + Mod Manager. No .NET runtime required. See [GregTools 1.0 Modmanager release](/wiki/releases/tools/gregtools-modmanager-1.0-release). | + +## Installation for end users + +### Quick start + +1. Install **MelonLoader** (IL2CPP) for Data Center. +2. Start the game once, then close it. +3. Subscribe to mods via the **WorkshopManager** Mod Store or the [Steam Workshop](https://steamcommunity.com/app/4170200/workshop/). +4. Start the game. + +### Manual installation + +1. Download the DLL from the Workshop or a release. +2. Framework: place `FrikaModdingFramework.dll` in `/Mods/`. +3. Plugins: place `FFM.Plugin.*.dll` in `/FMF/Plugins/`. +4. Mods: place `FMF.*.dll` in `/Mods/`. +5. Start the game and check `MelonLoader/Latest.log`. + +## Game directory structure + +```text +Data Center/ +├── Mods/ +│ ├── FrikaModdingFramework.dll +│ ├── FMF.ConsoleInputGuard.dll +│ ├── FMF.GregifyEmployees.dll +│ ├── FMF.HexLabelMod.dll +│ └── FMF.JoniMLCompatMod.dll +├── FMF/ +│ └── Plugins/ +│ ├── FFM.Plugin.Multiplayer.dll +│ ├── FFM.Plugin.Sysadmin.dll +│ ├── FFM.Plugin.AssetExporter.dll +│ ├── FFM.Plugin.WebUIBridge.dll +│ └── FFM.Plugin.PlayerModels.dll +├── MelonLoader/ +│ └── Latest.log +├── UserData/ +│ └── ModCfg/ +└── workshop/ + ├── FrikaModFramework/ + │ ├── content/Mods/FrikaModdingFramework.dll + │ └── metadata.json + ├── FFM.Plugin.Multiplayer/ + │ ├── content/FMF/Plugins/FFM.Plugin.Multiplayer.dll + │ └── metadata.json + ├── Gregtools Modmanager/ + │ ├── content/ (self-contained WorkshopUploader.exe + dependencies) + │ └── metadata.json + └── ... (one folder per component) +``` + +## Build and deploy (contributors) + +```bash +# Build everything and package into Workshop folders +pwsh -File scripts/Deploy-Release-ToWorkshop.ps1 + +# Deploy to game Mods/ and FMF/Plugins/ for local testing +pwsh -File scripts/Deploy-Release-ToDataCenter.ps1 +``` + +See also: [Contributor Guide](./contributor-workshop) + +## Compatibility + +| Component | Requires | +|-----------|----------| +| All mods/plugins | Data Center (Steam App 4170200) | +| All mods/plugins | MelonLoader (IL2CPP, stable) | +| FMF Plugins | FrikaModdingFramework.dll in Mods/ | +| Gregtools Modmanager | Windows 10 1809+ (self-contained, no runtime install needed) | diff --git a/hexmod.md b/hexmod.md new file mode 100644 index 0000000..5829ce9 --- /dev/null +++ b/hexmod.md @@ -0,0 +1,30 @@ +--- +title: HexMod +sidebar_label: HexMod +description: Hex label mod — in-world hex color labels for cable spinners and racks. +--- + +# HexMod + +The **Hex Label** mod adds in-world hex color labels for cable spinners and racks. + +## Steamworks Info + +| Field | Value | +|-------|-------| +| **Assembly** | `FMF.HexLabelMod.dll` | +| **Version** | `00.01.0009` | +| **Author** | mleem97 | +| **Game** | Waseku — Data Center (App 4170200) | +| **Workshop Tags** | `modded`, `melonloader`, `mod` | + +## Downloads + +- **Steam Workshop:** Subscribe via the Gregtools Modmanager or the [Steam Workshop](https://steamcommunity.com/app/4170200/workshop/) +- **Manual:** Drop `FMF.HexLabelMod.dll` into `/Mods/` + +## Source & layout + +- **Build sources:** [`mods/FMF.Mod.HexLabelMod`](https://github.com/mleem97/gregFramework/tree/master/mods/FMF.Mod.HexLabelMod) + +See also the detailed wiki article [`mods/mods/fmf-hex-label-mod`](/wiki/mods/mods/fmf-hex-label-mod). diff --git a/intro.md b/intro.md new file mode 100644 index 0000000..801af5b --- /dev/null +++ b/intro.md @@ -0,0 +1,41 @@ +--- +id: intro +title: FrikaMF Docs Start +slug: /docs +--- + +This documentation matches the **current monorepo**: MelonLoader framework under `framework/`, gameplay mods under `mods/`, FFM plugins under `plugins/`, Docusaurus content in `docs/`, and the wiki app in `wiki/`. It is written for **players**, **mod developers**, **contributors**, and **sponsors** — pick a lane below. + +## Für wen? — Who is this for? + +| Audience | Start here | +|----------|------------| +| **Spieler** — install, play, troubleshoot | [End users (hub)](./topics/end-user/overview.md) · [End user wiki (import)](./wiki-import/EndUser/) | +| **Moddevs** — build mods, hooks, debugging | [Mod developers (hub)](./topics/mod-developers/overview.md) · [ModDevs wiki (import)](./wiki-import/ModDevs/) · [Framework](./mods/framework.md) | +| **Contributor** — PRs, docs, plugins, CI | [Contributors (workflow)](./topics/contributors/overview.md) · [Contributors wiki (import)](./wiki-import/Contributors/) | +| **Sponsorinnen & Sponsoren** — support & transparency | [Sponsors (hub)](./topics/sponsors/overview.md) · [Sponsors (EN)](./wiki-import/Sponsors.md) · [Sponsoren (DE)](./wiki-import/Sponsoren.md) | + +**Experience tracks** (newbies → pros): [By audience](./topics/audiences/overview.md) → [Newbies](./audiences/newbies.md), [Intermediates](./audiences/intermediates.md), [Professionals](./audiences/professionals.md). + +## Mods Hub + +- Framework entry: [`Framework`](./mods/framework.md) +- Standalone / mod list: [`Standalone Mods`](./mods/standalone/index.md) + +## Hooks and releases + +- [FMF hook naming](./reference/fmf-hook-naming.md) — `FMF..…` and legacy `FFM.*` +- [FMF hooks catalog](./reference/fmf-hooks-catalog.md) — generated from `framework/FrikaMF/HookNames.cs` +- [Release channels](./reference/release-channels.md) — Steam Workshop vs GitHub (beta) +- [MCP server](./reference/mcp-server.md) — LLM/IDE tools over docs + `fmf_hooks.json` (optional Docker bundle) + +## Repository layout (contributors) + +- [Repo inventory](./contributors/repo-inventory.md) — projects, solution scope, entry points +- [Monorepo target layout](./contributors/monorepo-target-layout.md) — phased migration goals + +## Source model + +- **Authoring:** Markdown and MDX live in the repo-root **`docs/`** folder (this tree). See the **[`docs/` layout map](./README.md)** for what lives where (curated topics vs `wiki-import/` vs `wiki/` stubs). +- **Site:** The Docusaurus application is in **`wiki/`**; routes use base **`/wiki`** (see [Docusaurus workflow](./contributors/docusaurus-workflow.md)). +- **Legacy GitHub Wiki:** Mirrored under **`docs/wiki-import/`**; refresh from **`.wiki/`** with `npm run wiki:refresh` in `wiki/`. Details: [Legacy wiki import](./topics/wiki-import/overview.md). diff --git a/meta/IDEA_BACKLOG.md b/meta/IDEA_BACKLOG.md new file mode 100644 index 0000000..f8d696e --- /dev/null +++ b/meta/IDEA_BACKLOG.md @@ -0,0 +1,13 @@ +# Idea backlog (Discord and manual) + +Add one line per idea. Unchecked items can be turned into GitHub issues with `python tools/auto_issue_creator.py`. + +## Pending + +- [ ] (example) Add a smoke test for the FFI bridge. + +## Process + +1. Discord: `!request Your idea` (requires `DISCORD_BOT_TOKEN` and `tools/discord_bridge.py`). +2. Or edit this file manually. +3. Run `python tools/auto_issue_creator.py` to create issues from unchecked lines (requires `gh` CLI and auth). diff --git a/meta/Steam-Workshop-and-Tooling.md b/meta/Steam-Workshop-and-Tooling.md new file mode 100644 index 0000000..a6a401d --- /dev/null +++ b/meta/Steam-Workshop-and-Tooling.md @@ -0,0 +1,39 @@ +# Steam Workshop and tooling + +## Live-Sync references + +After a *Data Center* update, run the game once so MelonLoader regenerates interop assemblies, then from the repo root: + +```bash +python tools/refresh_refs.py +``` + +Optionally save a baseline for diffs: + +```bash +python tools/diff_assembly_metadata.py --save-snapshot +``` + +After future updates: + +```bash +python tools/diff_assembly_metadata.py +``` + +Do **not** commit `*.dll` from `lib/references/` (see `.gitignore`). + +## Steam Workshop (research) + +Official upload and item layout for *Data Center* Workshop content may be undocumented by the developer. Until documented: + +- Treat Workshop delivery as **game-defined** (often content under game data / `StreamingAssets`; MelonLoader mods remain **DLLs in `Mods/`**). +- The **WorkshopUploader** desktop app (see `WorkshopUploader/`) is the supported path for authors to manage Workshop items and DevServer betas once Steamworks is configured. + +## Legal + +Do not redistribute game binaries or extracted assets. Workshop packages should contain **your** content only. + +## CI / agents + +- `FrikaMF.csproj` builds on Windows agents that have either a Steam *Data Center* install **or** a populated `lib/references/MelonLoader/` (from `refresh_refs.py`). +- `WorkshopUploader` targets `net6.0-windows`; build it on Windows (not Linux-hosted runners unless cross-compilation is configured). diff --git a/meta/devserver-betas.md b/meta/devserver-betas.md new file mode 100644 index 0000000..c8aa371 --- /dev/null +++ b/meta/devserver-betas.md @@ -0,0 +1,25 @@ +# DevServer API — beta channels (`gregframework.eu`) + +This document defines the **intended** client contract for the FrikaMF **WorkshopUploader** “Betas” panel. The server may be implemented separately; keep URLs and tokens configurable. + +## Base URL + +- Default: `https://gregframework.eu` +- Override: user settings file next to the app (not committed to git). + +## Endpoints (proposed) + +| Method | Path | Purpose | +|--------|------|---------| +| `GET` | `/api/v1/betas` | List available beta channels (id, name, description). | +| `POST` | `/api/v1/betas/{id}/subscribe` | Enroll the current user (body: Steam ID or bearer token from OAuth). | +| `POST` | `/api/v1/betas/{id}/unsubscribe` | Leave a channel. | + +## Authentication + +- **Preferred:** Short-lived JWT after browser OAuth to `gregframework.eu`, stored in user settings. +- **Alternative:** Steam ID from Steamworks session in the WorkshopUploader process, plus server-side verification. + +## Rate limiting and errors + +Clients should show HTTP status and response body on failure; retry with backoff. diff --git a/mod-index.json b/mod-index.json new file mode 100644 index 0000000..af44405 --- /dev/null +++ b/mod-index.json @@ -0,0 +1,58 @@ +{ + "version": 1, + "description": "Manifest of all mods packaged for Steam Workshop distribution.", + "mods": [ + { + "id": "hexmod", + "title": "FMF HexLabel Mod", + "assembly": "FMF.HexLabelMod.dll", + "version": "00.01.0009", + "author": "mleem97", + "workshopTags": ["modded", "melonloader", "mod"], + "workshopContentPath": "content/Mods/", + "workshopUrl": "", + "githubReleaseUrl": "", + "wikiPath": "/wiki/wiki/mods/hexmod", + "sourcePath": "mods/FMF.Mod.HexLabelMod" + }, + { + "id": "console-input-guard", + "title": "FMF Console Input Guard", + "assembly": "FMF.ConsoleInputGuard.dll", + "version": "00.01.0001", + "author": "mleem97", + "workshopTags": ["modded", "melonloader", "mod"], + "workshopContentPath": "content/Mods/", + "workshopUrl": "", + "githubReleaseUrl": "", + "wikiPath": "/wiki/mods/mods/fmf-console-input-guard", + "sourcePath": "mods/FMF.ConsoleInputGuard" + }, + { + "id": "gregify-employees", + "title": "FMF Gregify Employees", + "assembly": "FMF.GregifyEmployees.dll", + "version": "00.01.0009", + "author": "mleem97", + "workshopTags": ["modded", "melonloader", "mod"], + "workshopContentPath": "content/Mods/", + "workshopUrl": "", + "githubReleaseUrl": "", + "wikiPath": "/wiki/mods/mods/fmf-gregify-employees", + "sourcePath": "mods/FMF.Mod.GregifyEmployees" + }, + { + "id": "lang-compat-bridge", + "title": "FMF JoniML Compat Mod", + "assembly": "FMF.JoniMLCompatMod.dll", + "version": "00.01.0009", + "author": "mleem97", + "workshopTags": ["modded", "melonloader", "mod"], + "workshopContentPath": "content/Mods/", + "workshopUrl": "", + "githubReleaseUrl": "", + "wikiPath": "/wiki/mods/mods/fmf-lang-compat-bridge", + "sourcePath": "mods/FMF.Plugin.LangCompatBridge" + } + ] +} diff --git a/mods/extensions/ffm-plugin-asset-exporter.md b/mods/extensions/ffm-plugin-asset-exporter.md new file mode 100644 index 0000000..c0d21c1 --- /dev/null +++ b/mods/extensions/ffm-plugin-asset-exporter.md @@ -0,0 +1,15 @@ +--- +title: FFM.Plugin.AssetExporter +sidebar_label: FFM.Plugin.AssetExporter +--- + +`StandaloneMods/FFM.Plugin.AssetExporter` + +## Purpose + +Provides export-focused tooling for asset-related workflows. + +## Sources + +- Module path: `StandaloneMods/FFM.Plugin.AssetExporter` +- Overview: [`Standalone Mods`](/wiki/wiki-import/StandaloneMods) diff --git a/mods/extensions/ffm-plugin-multiplayer.md b/mods/extensions/ffm-plugin-multiplayer.md new file mode 100644 index 0000000..bdf6c8d --- /dev/null +++ b/mods/extensions/ffm-plugin-multiplayer.md @@ -0,0 +1,15 @@ +--- +title: FFM.Plugin.Multiplayer +sidebar_label: FFM.Plugin.Multiplayer +--- + +`StandaloneMods/FFM.Plugin.Multiplayer` + +## Purpose + +Contains standalone multiplayer-oriented plugin functionality. + +## Sources + +- Module path: `StandaloneMods/FFM.Plugin.Multiplayer` +- Roadmap context: [`Steamworks P2P Multiplayer Roadmap`](/wiki/wiki-import/Steamworks-P2P-Multiplayer-Roadmap) diff --git a/mods/extensions/ffm-plugin-player-models.md b/mods/extensions/ffm-plugin-player-models.md new file mode 100644 index 0000000..e9f7ed9 --- /dev/null +++ b/mods/extensions/ffm-plugin-player-models.md @@ -0,0 +1,15 @@ +--- +title: FFM.Plugin.PlayerModels +sidebar_label: FFM.Plugin.PlayerModels +--- + +`StandaloneMods/FFM.Plugin.PlayerModels` + +## Purpose + +Hosts standalone player-model specific behavior and integration. + +## Sources + +- Module path: `StandaloneMods/FFM.Plugin.PlayerModels` +- Debug docs: [`Mod Developer Debug`](/wiki/wiki-import/Mod-Developer-Debug) diff --git a/mods/extensions/ffm-plugin-sysadmin.md b/mods/extensions/ffm-plugin-sysadmin.md new file mode 100644 index 0000000..1d5a0f2 --- /dev/null +++ b/mods/extensions/ffm-plugin-sysadmin.md @@ -0,0 +1,15 @@ +--- +title: FFM.Plugin.Sysadmin +sidebar_label: FFM.Plugin.Sysadmin +--- + +`StandaloneMods/FFM.Plugin.Sysadmin` + +## Purpose + +Contains system-administration oriented standalone plugin features. + +## Sources + +- Module path: `StandaloneMods/FFM.Plugin.Sysadmin` +- Framework context: [`Framework Features & Use Cases`](/wiki/wiki-import/Framework-Features-Use-Cases) diff --git a/mods/extensions/ffm-plugin-web-ui-bridge.md b/mods/extensions/ffm-plugin-web-ui-bridge.md new file mode 100644 index 0000000..e4173b0 --- /dev/null +++ b/mods/extensions/ffm-plugin-web-ui-bridge.md @@ -0,0 +1,15 @@ +--- +title: FFM.Plugin.WebUIBridge +sidebar_label: FFM.Plugin.WebUIBridge +--- + +`StandaloneMods/FFM.Plugin.WebUIBridge` + +## Purpose + +Focuses on standalone web UI bridge integration flows. + +## Sources + +- Module path: `StandaloneMods/FFM.Plugin.WebUIBridge` +- Reference: [`Web UI Bridge (DC2WEB)`](/wiki/wiki-import/Web-UI-Bridge) diff --git a/mods/extensions/fmf-console-input-guard.md b/mods/extensions/fmf-console-input-guard.md new file mode 100644 index 0000000..5a9663c --- /dev/null +++ b/mods/extensions/fmf-console-input-guard.md @@ -0,0 +1,15 @@ +--- +title: FMF.ConsoleInputGuard +sidebar_label: FMF.ConsoleInputGuard +--- + +`StandaloneMods/FMF.ConsoleInputGuard` + +## Purpose + +Provides guardrails around console input handling. + +## Sources + +- Module path: `StandaloneMods/FMF.ConsoleInputGuard` +- Overview: [`Standalone Mods`](/wiki/wiki-import/StandaloneMods) diff --git a/mods/extensions/fmf-gregify-employees.md b/mods/extensions/fmf-gregify-employees.md new file mode 100644 index 0000000..e16ade9 --- /dev/null +++ b/mods/extensions/fmf-gregify-employees.md @@ -0,0 +1,15 @@ +--- +title: FMF.GregifyEmployees +sidebar_label: FMF.GregifyEmployees +--- + +`StandaloneMods/FMF.GregifyEmployees` + +## Purpose + +Contains standalone custom employee-related gameplay behavior. + +## Sources + +- Module path: `StandaloneMods/FMF.GregifyEmployees` +- HR context: [`Framework Features & Use Cases`](/wiki/wiki-import/Framework-Features-Use-Cases) diff --git a/mods/extensions/fmf-hex-label-mod.md b/mods/extensions/fmf-hex-label-mod.md new file mode 100644 index 0000000..1d00bb1 --- /dev/null +++ b/mods/extensions/fmf-hex-label-mod.md @@ -0,0 +1,104 @@ +--- +title: FMF.HexLabelMod +sidebar_label: FMF.HexLabelMod +--- + + + +`mods/FMF.Mod.HexLabelMod` + +## Purpose + +Standalone MelonLoader mod for **Data Center** (Waseku) that overlays the hex color code of each `CableSpinner` and `Rack` directly in-world, so you can identify cable and rack colors at a glance without opening any menu. + +Rewritten from the former root `HexLabelMod` for the FrikaModdingFramework workflow, now running fully standalone. + +## Requirements + +| Dependency | Notes | +| --- | --- | +| [MelonLoader](https://melonwiki.xyz/) | With generated IL2CPP assemblies | + +This mod runs standalone and does **not** require FMF runtime APIs. + +## Installation + +1. Drop `FMF.HexLabelMod.dll` into your `Mods/` folder. +2. Launch the game — the config file is created automatically on first run at: + +```text +UserData/hexposition.cfg +``` + +## Configuration + +Edit `UserData/hexposition.cfg` to adjust label positioning and font sizes. The file is auto-generated with defaults on first launch and regenerated if any keys are missing. + +```ini +# Hex Label Position Config +# File: UserData/hexposition.cfg +# Edit values, then restart game. + +# Spinner (UI text near cable spool) +spinner_offset_x=0 +spinner_offset_y=-6 +spinner_font_min=1.8 +spinner_font_max=6.2 +spinner_font_scale=0.24 + +# Rack (world-space text at rack back-right-bottom) +rack_offset_right=-0.03 +rack_offset_back=0.06 +rack_offset_down=-0.02 +rack_font_size=42 +rack_character_size=0.05 +rack_scale=1 +``` + +### Config Keys + +| Key | Type | Default | Description | +| --- | --- | --- | --- | +| `spinner_offset_x` | float | `0` | Horizontal offset relative to the source label | +| `spinner_offset_y` | float | `-6` | Vertical offset relative to the source label | +| `spinner_font_min` | float | `1.8` | Minimum auto-size font size (TMPro) | +| `spinner_font_max` | float | `6.2` | Maximum auto-size font size (TMPro) | +| `spinner_font_scale` | float | `0.24` | Scale factor applied to the source label's font size | +| `rack_offset_right` | float | `-0.03` | World-space offset along rack's right axis | +| `rack_offset_back` | float | `0.06` | World-space offset along rack's back axis | +| `rack_offset_down` | float | `-0.02` | World-space offset along rack's down axis | +| `rack_font_size` | int | `42` | Font size for the world-space `TextMesh` label | +| `rack_character_size` | float | `0.05` | Character size for the world-space `TextMesh` label | +| `rack_scale` | float | `1` | Uniform world-space scale of the rack label object | + +## Live Reload *(restricted)* + +Pressing **Ctrl+F1** toggles live config reload (6-second interval), allowing you to tune label positions without restarting the game. This feature is restricted to a specific Steam account and will silently do nothing for all other users. + +## Build + +```powershell +dotnet build .\mods\FMF.Mod.HexLabelMod\FMF.HexLabelMod.csproj +``` + +Output lands in the standard MelonLoader `Mods/` folder as configured in the `.csproj`. + +## How It Works + +1. **Startup** — The mod defers full initialization until MelonLoader's `Latest.log` confirms the Steam runtime is ready (SteamID or Steam marker detected). +2. **Spinner labels** — Every `CableSpinner` gets a cloned `TextMeshProUGUI` label injected into its UI hierarchy, displaying the resolved hex code in white. Color is read from `rgbColor`, then from the `_BaseColor`/`_Color` material property as fallback. +3. **Rack labels** — Every `Rack` gets a world-space `TextMesh` label positioned at its back-right-bottom corner, facing away from the rack front. +4. **Scan loop** — Active spinners and racks are re-checked every 1.5 seconds to catch newly spawned objects. +5. **Harmony patch** — `CableSpinner.Start` is patched to inject the label immediately on spawn, before the first scan cycle runs. + +## Notes + +- Original gameplay behavior is unaffected. +- This mod runs from `mods/FMF.Mod.HexLabelMod` and no longer from the repository root. +- The config file is fully rewritten if any expected keys are missing (for example, after an update adds new keys). +- FMF assembly presence is no longer required as a startup gate. + +## Sources + +- Module path: `mods/FMF.Mod.HexLabelMod` +- Relatedocs: [`Standalone Mods`](/wiki/wiki-import/StandaloneMods) diff --git a/mods/extensions/fmf-lang-compat-bridge.md b/mods/extensions/fmf-lang-compat-bridge.md new file mode 100644 index 0000000..9fefad0 --- /dev/null +++ b/mods/extensions/fmf-lang-compat-bridge.md @@ -0,0 +1,35 @@ +--- +title: FMF.LangCompatBridge +sidebar_label: FMF.LangCompatBridge +description: Language compatibility bridge mod for Data Center — localization bridging for mixed mod stacks. +--- + +## Description + +Provides language/localization compatibility bridging for mixed mod stacks. + +## Steamworks Metadata + +| Field | Value | +|-------|-------| +| **Assembly** | `FMF.JoniMLCompatMod.dll` | +| **Version** | `00.01.0009` | +| **Author** | mleem97 | +| **Game** | Waseku — Data Center (App 4170200) | +| **Workshop Tags** | `modded`, `melonloader`, `mod` | +| **Workshop Content Path** | `content/Mods/` | +| **Needs FMF** | No (standalone MelonLoader mod) | + +## Wiki Group + +- Category: Mod +- Primary Language: C# +- Project Path: `mods/FMF.Plugin.LangCompatBridge` + +## Installation + +Drop `FMF.JoniMLCompatMod.dll` into `/Mods/`. Requires [MelonLoader](https://melonwiki.xyz/) (IL2CPP). + +## Release + +- [Open Release Page](/wiki/releases/mods/fmf-lang-compat-bridge-release) diff --git a/mods/extensions/fmf-ui-replacement-mod.md b/mods/extensions/fmf-ui-replacement-mod.md new file mode 100644 index 0000000..0fa97af --- /dev/null +++ b/mods/extensions/fmf-ui-replacement-mod.md @@ -0,0 +1,19 @@ +--- +title: FMF.UIReplacementMod +sidebar_label: FMF.UIReplacementMod (Legacy) +--- + +## Description + +Legacy standalone UI replacement module page kept for compatibility. + +## Project Status + +- Status: Legacy documentation entry +- Note: No active `csproj` module was detected under `mods/` for this page. + +## Source + +- Previous mixed wiki path: `mods/standalone/fmf-ui-replacement-mod` + +- [Open Release Page](/wiki/releases/mods/fmf-ui-replacement-mod-release) diff --git a/mods/extensions/index.md b/mods/extensions/index.md new file mode 100644 index 0000000..5250c03 --- /dev/null +++ b/mods/extensions/index.md @@ -0,0 +1,37 @@ +--- +title: Standalone Plugins & Mods +sidebar_label: Overview +--- + +This section contains the first-party standalone modules currently maintained in this repository. + +## Classification + +- **Plugins** extend the framework runtime capabilities. +- **Mods** extend the game behavior/content by using framework and plugin capabilities. + +## Loading path + +- **MelonLoader mods** load from `{GameRoot}/Mods/` (standard MelonLoader behavior). +- **FMF framework plugins** (`FFM.Plugin.*`) are deployed under `{GameRoot}/FMF/Plugins/`; MelonLoader does not scan that folder by default — see [Game folder layout](/wiki/topics/meta/game-folder-layout). +- Rust mods via FrikaMF follow the same runtime rules as other MelonLoader mods unless documented otherwise. + +## Standalone Plugins + +- [FFM.Plugin.AssetExporter](./ffm-plugin-asset-exporter.md) +- [FFM.Plugin.Multiplayer](./ffm-plugin-multiplayer.md) +- [FFM.Plugin.PlayerModels](./ffm-plugin-player-models.md) +- [FFM.Plugin.Sysadmin](./ffm-plugin-sysadmin.md) +- [FFM.Plugin.WebUIBridge](./ffm-plugin-web-ui-bridge.md) + +## Standalone Mods + +- [FMF.ConsoleInputGuard](./fmf-console-input-guard.md) +- [FMF.GregifyEmployees](./fmf-gregify-employees.md) +- [FMF.HexLabelMod](./fmf-hex-label-mod.md) +- [FMF.LangCompatBridge](./fmf-lang-compat-bridge.md) +- [FMF.UIReplacementMod](./fmf-ui-replacement-mod.md) + +## Additional context + +- [`Standalone Mods` wiki page](/wiki/wiki-import/StandaloneMods) diff --git a/mods/framework.md b/mods/framework.md new file mode 100644 index 0000000..6a4ea43 --- /dev/null +++ b/mods/framework.md @@ -0,0 +1,18 @@ +--- +title: Framework +sidebar_label: Framework +--- + +The core `FrikaMF` runtime provides: + +- Harmony patch integration for gameplay hooks +- Event dispatch and stable event contracts +- Native bridge for Rust modules +- Shared game API abstractions for mod authors + +## Core references + +- [`Framework Features & Use Cases`](/wiki/wiki-import/Framework-Features-Use-Cases) +- [`HOOKS`](/wiki/wiki-import/HOOKS) +- [`FFI Bridge Reference`](/wiki/wiki-import/FFI-Bridge-Reference) +- [`Mod Developer Debug`](/wiki/wiki-import/Mod-Developer-Debug) diff --git a/mods/mods/fmf-console-input-guard.md b/mods/mods/fmf-console-input-guard.md new file mode 100644 index 0000000..0200f87 --- /dev/null +++ b/mods/mods/fmf-console-input-guard.md @@ -0,0 +1,35 @@ +--- +title: FMF.ConsoleInputGuard +sidebar_label: FMF.ConsoleInputGuard +description: Console input guard mod for Data Center — prevents accidental console interactions. +--- + +## Description + +Provides guardrails around console input handling and accidental interactions. + +## Steamworks Metadata + +| Field | Value | +|-------|-------| +| **Assembly** | `FMF.ConsoleInputGuard.dll` | +| **Version** | `00.01.0001` | +| **Author** | mleem97 | +| **Game** | Waseku — Data Center (App 4170200) | +| **Workshop Tags** | `modded`, `melonloader`, `mod` | +| **Workshop Content Path** | `content/Mods/` | +| **Needs FMF** | No (standalone MelonLoader mod) | + +## Wiki Group + +- Category: Mod +- Primary Language: C# +- Project Path: `mods/FMF.ConsoleInputGuard` + +## Installation + +Drop `FMF.ConsoleInputGuard.dll` into `/Mods/`. Requires [MelonLoader](https://melonwiki.xyz/) (IL2CPP). + +## Release + +- [Open Release Page](/wiki/releases/mods/fmf-console-input-guard-release) diff --git a/mods/mods/fmf-gregify-employees.md b/mods/mods/fmf-gregify-employees.md new file mode 100644 index 0000000..429dfcc --- /dev/null +++ b/mods/mods/fmf-gregify-employees.md @@ -0,0 +1,35 @@ +--- +title: FMF.GregifyEmployees +sidebar_label: FMF.GregifyEmployees +description: Gregify Employees gameplay mod for Data Center — themed employee customization. +--- + +## Description + +Applies themed employee customization to hiring-related gameplay flows. + +## Steamworks Metadata + +| Field | Value | +|-------|-------| +| **Assembly** | `FMF.GregifyEmployees.dll` | +| **Version** | `00.01.0009` | +| **Author** | mleem97 | +| **Game** | Waseku — Data Center (App 4170200) | +| **Workshop Tags** | `modded`, `melonloader`, `mod` | +| **Workshop Content Path** | `content/Mods/` | +| **Needs FMF** | No (standalone MelonLoader mod) | + +## Wiki Group + +- Category: Mod +- Primary Language: C# +- Project Path: `mods/FMF.Mod.GregifyEmployees` + +## Installation + +Drop `FMF.GregifyEmployees.dll` into `/Mods/`. Requires [MelonLoader](https://melonwiki.xyz/) (IL2CPP). + +## Release + +- [Open Release Page](/wiki/releases/mods/fmf-gregify-employees-release) diff --git a/mods/mods/index.md b/mods/mods/index.md new file mode 100644 index 0000000..e252f11 --- /dev/null +++ b/mods/mods/index.md @@ -0,0 +1,13 @@ +--- +title: Mods Wiki +sidebar_label: Mods Overview +--- + +This section contains gameplay mod wiki pages, separated from plugins. + +## Mod Projects + +- [FMF.ConsoleInputGuard](./fmf-console-input-guard) +- [FMF.GregifyEmployees](./fmf-gregify-employees) +- [FMF.HexLabelMod](./fmf-hex-label-mod) +- [FMF.LangCompatBridge](./fmf-lang-compat-bridge) diff --git a/mods/standalone/fmf-hex-label-mod.md b/mods/standalone/fmf-hex-label-mod.md new file mode 100644 index 0000000..5a1474a --- /dev/null +++ b/mods/standalone/fmf-hex-label-mod.md @@ -0,0 +1,35 @@ +--- +title: FMF.HexLabelMod +sidebar_label: FMF.HexLabelMod +description: Hex label display mod for Data Center — overlays cable spinner and rack color hex labels in-world. +--- + +## Description + +Overlays cable spinner and rack color hex labels directly in-world so you can identify cable and rack colors at a glance without opening any menu. + +## Steamworks Metadata + +| Field | Value | +|-------|-------| +| **Assembly** | `FMF.HexLabelMod.dll` | +| **Version** | `00.01.0009` | +| **Author** | mleem97 | +| **Game** | Waseku — Data Center (App 4170200) | +| **Workshop Tags** | `modded`, `melonloader`, `mod` | +| **Workshop Content Path** | `content/Mods/` | +| **Needs FMF** | No (standalone MelonLoader mod) | + +## Wiki Group + +- Category: Mod +- Primary Language: C# +- Project Path: `mods/FMF.Mod.HexLabelMod` + +## Installation + +Drop `FMF.HexLabelMod.dll` into `/Mods/`. Requires [MelonLoader](https://melonwiki.xyz/) (IL2CPP). + +## Release + +- [Open Release Page](/wiki/releases/mods/fmf-hex-label-mod-release) diff --git a/reference/fmf-hook-naming.md b/reference/fmf-hook-naming.md new file mode 100644 index 0000000..c4d645a --- /dev/null +++ b/reference/fmf-hook-naming.md @@ -0,0 +1,70 @@ +--- +id: fmf-hook-naming +title: FMF hook and event naming +slug: /reference/fmf-hook-naming +description: Canonical naming for hooks, events, and cross-language documentation stubs. +--- + +# FMF hook and event naming + +## Target format + +All **new** public hook and event identifiers should follow: + +```text +FMF.. +``` + +- **`FMF`** — Fixed prefix (Frika Mod Framework). +- **``** — Uppercase domain from the [approved domain list](#approved-domain-segments). Describes *where* the signal belongs in the game (player, rack, server, economy, …). +- **``** — `PascalCase` segment(s), usually `OnSomething` for events or a verb phrase for commands. + +Examples (illustrative): `FMF.RACK.CableSpinnerColorResolved`, `FMF.PLAYER.InputPoll`, `FMF.NETWORK.Cable.OnConnected`. + +## Approved domain segments + +Domains are **closed by default**. Add a new domain only via changelog + maintainer review. + +| Domain | Scope | +|--------|--------| +| `GAMEPLAY` | Rules, scoring, simulation beats not covered elsewhere | +| `PLAYER` | Local player input, camera, UI focus | +| `EMPLOYEE` | NPC staff, hiring, schedules | +| `CUSTOMER` | Contracts, SLA, satisfaction | +| `SERVER` | In-game server racks, VMs, power | +| `RACK` | Physical rack placement, mounting | +| `NETWORK` | Cables, switches, traffic | +| `STORE` | Shop cart, checkout | +| `WORLD` | Rooms, expansion, walls | +| `UI` | HUD overlays, menus (when not `PLAYER`) | +| `SAVE` | Save/load lifecycle | +| `FRAMEWORK` | Loader, bridge, diagnostics | + +## Legacy: `FFM.*` strings in code + +The runtime currently maps numeric event IDs to **`FFM.*`** string constants in [`FrikaMF/HookNames.cs`](https://github.com/mleem97/gregFramework/blob/master/FrikaMF/HookNames.cs) (e.g. `FFM.Economy.Balance.OnChanged`). That is **legacy naming** (four segments after `FFM`). + +**Policy** + +- New documentation and greenfield APIs should use **`FMF..*`** as above. +- When touching `HookNames.cs`, prefer aligning new entries to **`FMF.*`**; otherwise keep **`FFM.*`** until a planned major version bump documents a rename map. +- The [generated hook catalog](./fmf-hooks-catalog.md) lists **what the code emits today** (including `FFM.*`). + +## Cross-language stubs (documentation) + +For each canonical hook, the wiki may add **non-normative** snippets: + +| Language | Convention | +|----------|------------| +| C# | `FMF.Domain.OnSomething.Subscribe(...)` or string literal | +| Lua | `FMF.Domain.OnSomething:subscribe(...)` | +| Rust | `fmf::domain::on_something::subscribe(...)` | +| Python | `fmf.domain.on_something.subscribe(...)` | +| TypeScript | `FMF.Domain.OnSomething.subscribe(...)` | + +Bindings are **not** auto-generated for all languages; stubs are for contributor clarity. + +## Related + +- [FMF hooks catalog](./fmf-hooks-catalog.md) (generated) +- [Legacy wiki: HOOK-NAMING-CONVENTION](../wiki-import/HOOK-NAMING-CONVENTION.md) (extended examples) diff --git a/reference/fmf-hooks-catalog.md b/reference/fmf-hooks-catalog.md new file mode 100644 index 0000000..ac62bb4 --- /dev/null +++ b/reference/fmf-hooks-catalog.md @@ -0,0 +1,109 @@ +--- +id: fmf-hooks-catalog +title: FMF hooks catalog +slug: /reference/fmf-hooks-catalog +description: Auto-generated catalog of hook strings and event id mappings from FrikaMF sources. +--- + + + +# FMF hooks catalog + +This page is **generated** from `framework/FrikaMF/HookNames.cs` and `framework/FrikaMF/EventIds.cs`. + +**Generated:** 2026-04-06 14:26:09 UTC + +## Hook string constants + +| C# field | Hook string | +|----------|-------------| +| ``CustomerContractOnSigned`` | ``FFM.Customer.Contract.OnSigned`` | +| ``CustomerReputationOnChanged`` | ``FFM.Customer.Reputation.OnChanged`` | +| ``CustomerSlaOnBreached`` | ``FFM.Customer.SLA.OnBreached`` | +| ``CustomerSlaOnRestored`` | ``FFM.Customer.SLA.OnRestored`` | +| ``EconomyBalanceOnChanged`` | ``FFM.Economy.Balance.OnChanged`` | +| ``EmployeesStaffOnHiredCustom`` | ``FFM.Employees.Staff.OnHired`` | +| ``EmployeesStaffOnTerminatedCustom`` | ``FFM.Employees.Staff.OnTerminated`` | +| ``FrameworkHooksOnBridgeInstalled`` | ``FFM.Framework.Hooks.OnBridgeInstalled`` | +| ``FrameworkHooksOnBridgeTriggered`` | ``FFM.Framework.Hooks.OnBridgeTriggered`` | +| ``GameLoadOnCompleted`` | ``FFM.Game.Load.OnCompleted`` | +| ``GameSaveOnCompleted`` | ``FFM.Game.Save.OnCompleted`` | +| ``GameSaveOnRequested`` | ``FFM.Game.Save.OnRequested`` | +| ``GameTimeOnDayChanged`` | ``FFM.Game.Time.OnDayChanged`` | +| ``GameTimeOnMonthChanged`` | ``FFM.Game.Time.OnMonthChanged`` | +| ``GameXpOnGained`` | ``FFM.Game.XP.OnGained`` | +| ``NetworkCableOnConnected`` | ``FFM.Network.Cable.OnConnected`` | +| ``NetworkCableOnConnectedSuppress`` | ``FFM.Network.Cable.OnConnected.Suppress`` | +| ``NetworkCableOnDisconnected`` | ``FFM.Network.Cable.OnDisconnected`` | +| ``NetworkCableOnDisconnectedSuppress`` | ``FFM.Network.Cable.OnDisconnected.Suppress`` | +| ``NetworkCableOnLinkDown`` | ``FFM.Network.Cable.OnLinkDown`` | +| ``NetworkCableOnLinkUp`` | ``FFM.Network.Cable.OnLinkUp`` | +| ``NetworkTrafficOnThresholdExceeded`` | ``FFM.Network.Traffic.OnThresholdExceeded`` | +| ``ObjectsDeviceOnDegraded`` | ``FFM.Objects.Device.OnDegraded`` | +| ``ObjectsDeviceOnEOL`` | ``FFM.Objects.Device.OnEOL`` | +| ``ObjectsDeviceOnPoweredOff`` | ``FFM.Objects.Device.OnPoweredOff`` | +| ``ObjectsDeviceOnPoweredOn`` | ``FFM.Objects.Device.OnPoweredOn`` | +| ``ObjectsDeviceOnRepaired`` | ``FFM.Objects.Device.OnRepaired`` | +| ``ObjectsRackOnDevicePlaced`` | ``FFM.Objects.Rack.OnDevicePlaced`` | +| ``ObjectsRackOnRemoved`` | ``FFM.Objects.Rack.OnRemoved`` | +| ``ObjectsServerOnClientAssigned`` | ``FFM.Objects.Server.OnClientAssigned`` | +| ``ObjectsServerOnClientUnassigned`` | ``FFM.Objects.Server.OnClientUnassigned`` | +| ``StoreCartOnCheckedOutCleared`` | ``FFM.Store.Cart.OnCheckedOut`` | +| ``StoreCartOnItemAdded`` | ``FFM.Store.Cart.OnItemAdded`` | +| ``StoreCartOnItemRemoved`` | ``FFM.Store.Cart.OnItemRemoved`` | +| ``WorldRoomOnExpanded`` | ``FFM.World.Room.OnExpanded`` | + +## Event id to hook mapping + +| Event id (uint) | EventIds name | Resolves to field | Hook string | +|-----------------|---------------|---------------------|-------------| +| 213 | `CableCleared` | `StoreCartOnCheckedOutCleared` | `FFM.Store.Cart.OnCheckedOut` | +| 204 | `CableConnected` | `NetworkCableOnConnected` | `FFM.Network.Cable.OnConnected` | +| 211 | `CableCreated` | `NetworkCableOnConnected` | `FFM.Network.Cable.OnConnected` | +| 205 | `CableDisconnected` | `NetworkCableOnDisconnected` | `FFM.Network.Cable.OnDisconnected` | +| 212 | `CableRemoved` | `NetworkCableOnDisconnected` | `FFM.Network.Cable.OnDisconnected` | +| 215 | `CableSfpInserted` | `NetworkCableOnConnected` | `FFM.Network.Cable.OnConnected` | +| 216 | `CableSfpRemoved` | `NetworkCableOnDisconnected` | `FFM.Network.Cable.OnDisconnected` | +| 214 | `CableSpeedChanged` | `NetworkTrafficOnThresholdExceeded` | `FFM.Network.Traffic.OnThresholdExceeded` | +| 1001 | `CustomEmployeeFired` | `EmployeesStaffOnTerminatedCustom` | `FFM.Employees.Staff.OnTerminated` | +| 1000 | `CustomEmployeeHired` | `EmployeesStaffOnHiredCustom` | `FFM.Employees.Staff.OnHired` | +| 400 | `CustomerAccepted` | `CustomerContractOnSigned` | `FFM.Customer.Contract.OnSigned` | +| 401 | `CustomerSatisfied` | `CustomerSlaOnRestored` | `FFM.Customer.SLA.OnRestored` | +| 402 | `CustomerUnsatisfied` | `CustomerSlaOnBreached` | `FFM.Customer.SLA.OnBreached` | +| 300 | `DayEnded` | `GameTimeOnDayChanged` | `FFM.Game.Time.OnDayChanged` | +| 601 | `EmployeeFired` | `EmployeesStaffOnTerminated` | `FFM.Employees.Staff.OnTerminated` | +| 600 | `EmployeeHired` | `EmployeesStaffOnHired` | `FFM.Employees.Staff.OnHired` | +| 702 | `GameAutoSaved` | `GameSaveOnRequested` | `FFM.Game.Save.OnRequested` | +| 701 | `GameLoaded` | `GameLoadOnCompleted` | `FFM.Game.Load.OnCompleted` | +| 700 | `GameSaved` | `GameSaveOnCompleted` | `FFM.Game.Save.OnCompleted` | +| 1100 | `HookBridgeInstalled` | `FrameworkHooksOnBridgeInstalled` | `FFM.Framework.Hooks.OnBridgeInstalled` | +| 1101 | `HookBridgeTriggered` | `FrameworkHooksOnBridgeTriggered` | `FFM.Framework.Hooks.OnBridgeTriggered` | +| 100 | `MoneyChanged` | `EconomyBalanceOnChanged` | `FFM.Economy.Balance.OnChanged` | +| 301 | `MonthEnded` | `GameTimeOnMonthChanged` | `FFM.Game.Time.OnMonthChanged` | +| 900 | `NetWatchDispatched` | `NetworkTrafficOnThresholdExceeded` | `FFM.Network.Traffic.OnThresholdExceeded` | +| 208 | `RackUnmounted` | `ObjectsRackOnRemoved` | `FFM.Objects.Rack.OnRemoved` | +| 102 | `ReputationChanged` | `CustomerReputationOnChanged` | `FFM.Customer.Reputation.OnChanged` | +| 207 | `ServerAppChanged` | `ObjectsServerOnClientUnassigned` | `FFM.Objects.Server.OnClientUnassigned` | +| 201 | `ServerBroken` | `ObjectsDeviceOnDegraded` | `FFM.Objects.Device.OnDegraded` | +| 206 | `ServerCustomerChanged` | `ObjectsServerOnClientAssigned` | `FFM.Objects.Server.OnClientAssigned` | +| 203 | `ServerInstalled` | `ObjectsRackOnDevicePlaced` | `FFM.Objects.Rack.OnDevicePlaced` | +| 200 | `ServerPowered` | `ObjectsDeviceOnPoweredOn` | `FFM.Objects.Device.OnPoweredOn` | +| 202 | `ServerRepaired` | `ObjectsDeviceOnRepaired` | `FFM.Objects.Device.OnRepaired` | +| 502 | `ShopCartCleared` | `StoreCartOnCheckedOutCleared` | `FFM.Store.Cart.OnCheckedOut` | +| 500 | `ShopCheckout` | `StoreCartOnCheckedOut` | `FFM.Store.Cart.OnCheckedOut` | +| 501 | `ShopItemAdded` | `StoreCartOnItemAdded` | `FFM.Store.Cart.OnItemAdded` | +| 503 | `ShopItemRemoved` | `StoreCartOnItemRemoved` | `FFM.Store.Cart.OnItemRemoved` | +| 209 | `SwitchBroken` | `NetworkCableOnLinkDown` | `FFM.Network.Cable.OnLinkDown` | +| 210 | `SwitchRepaired` | `NetworkCableOnLinkUp` | `FFM.Network.Cable.OnLinkUp` | +| 800 | `WallPurchased` | `WorldRoomOnExpanded` | `FFM.World.Room.OnExpanded` | +| 101 | `XPChanged` | `GameXpOnGained` | `FFM.Game.XP.OnGained` | + +## Fallback + +Unknown event ids resolve to ``FFM.Framework.Unknown.OnEvent`` in `HookNames.Resolve`. + +## See also + +- [FMF hook naming](./fmf-hook-naming.md) +- [EventIds source](https://github.com/mleem97/gregFramework/blob/master/framework/FrikaMF/EventIds.cs) +- [HookNames source](https://github.com/mleem97/gregFramework/blob/master/framework/FrikaMF/HookNames.cs) diff --git a/reference/mcp-server.md b/reference/mcp-server.md new file mode 100644 index 0000000..73cfb5e --- /dev/null +++ b/reference/mcp-server.md @@ -0,0 +1,69 @@ +--- +title: MCP server (LLM-friendly modding) +sidebar_label: MCP server +description: Model Context Protocol server bundled with the docs Docker image for assistants and IDEs. +--- + +# MCP server (FrikaMF modding) + +The repository ships a **Model Context Protocol (MCP)** server (`mcp-server/`) that exposes: + +- **Docs search & read** — Markdown under `docs/` (same source as Docusaurus). +- **Hook registry** — `fmf_hooks.json` (and the same registry path in the monorepo). +- **CONTRIBUTING** — conventions and workflow. +- **Repo layout** — static overview and a **`fmf_modding_context`** prompt for quick context. + +Use it when you want an assistant (Cursor, Claude Code, or any MCP client) to **ground answers** in this repo instead of guessing APIs. + +## Docker (single container) + +The root `Dockerfile` builds **Docusaurus** into static files and runs **one Node process** that: + +- Serves the wiki at **`http://:3000/`** (static HTML). +- Exposes **Streamable HTTP MCP** at **`POST /mcp`** and **`GET /mcp`** (SSE), plus **`GET /health`**. + +From the repo root: + +```bash +docker compose up docs-mcp +``` + +Default mapping: **`http://localhost:3040`** (host) → container port **3000**. + +- Docs: `http://localhost:3040/wiki/` +- Health: `http://localhost:3040/health` +- MCP: `http://localhost:3040/mcp` (client must speak MCP Streamable HTTP; session via `mcp-session-id` header). + +:::note Security + +Binding to `0.0.0.0` exposes the MCP endpoint on your network. Use only on trusted networks or behind a reverse proxy with authentication. + +::: + +## Local (stdio) for Cursor + +From `mcp-server/` with the repo root as data (so `docs/`, `CONTRIBUTING.md`, and `FrikaModFramework/fmf_hooks.json` resolve): + +```bash +cd mcp-server +node src/index.mjs --stdio --data-root .. +``` + +Point your MCP client at this command (stdio transport). Adjust `--data-root` if your layout differs. + +## Tools (summary) + +| Tool | Purpose | +|------|--------| +| `fmf_search_docs` | Substring search across `docs/**/*.md` | +| `fmf_read_doc` | Read one file by path under `docs/` | +| `fmf_list_doc_paths` | Discover Markdown paths | +| `fmf_hook_registry` | Raw `fmf_hooks.json` | +| `fmf_read_contributing` | `CONTRIBUTING.md` | +| `fmf_repo_layout` | Short monorepo overview | + +## Related + +- [Hook naming](./fmf-hook-naming.md) +- [Hooks catalog](./fmf-hooks-catalog.md) +- [Monorepo target layout](../contributors/monorepo-target-layout.md) diff --git a/reference/mod-store-vision.md b/reference/mod-store-vision.md new file mode 100644 index 0000000..d332668 --- /dev/null +++ b/reference/mod-store-vision.md @@ -0,0 +1,31 @@ +--- +id: mod-store-vision +title: Mod Store Vision (Secure Git-Based) +slug: /reference/mod-store-vision +--- + +## Vision + +Build a Mod Store with Git-based submissions, automated malware scanning, and policy checks before mods are visible. + +## Core principles + +- All submissions are reviewable in Git. +- Every build artifact is reproducible from source. +- Security checks are mandatory before publish. + +## Security pipeline (target) + +1. Submit via pull request (manifest + source + checksums). +2. CI runs static checks and signature/metadata validation. +3. Virus scanning runs on produced artifacts. +4. Maintainer review and policy checks. +5. Publish approved metadata index consumed by launcher/site. + +## Recommended metadata fields + +- `modId`, `version`, `authors` +- `supportedGameVersions` +- `requiredFrameworkVersion` +- `sha256` for each downloadable artifact +- source URL and license diff --git a/reference/release-channels.md b/reference/release-channels.md new file mode 100644 index 0000000..3581970 --- /dev/null +++ b/reference/release-channels.md @@ -0,0 +1,42 @@ +--- +id: release-channels +title: Release channels — Steam vs GitHub +slug: /reference/release-channels +description: Where to download stable builds, beta builds, and how this relates to the Steam Workshop. +--- + +# Release channels — Steam vs GitHub + +This project uses **two complementary channels** for distributing mods and plugins. The wiki and [`/mods`](/mods) catalog are **not** a replacement for the Steam Workshop; they are an **overview**, documentation hub, and **second official source** for files that should not flood the Workshop. + +## Steam Workshop (discovery & stable) + +- **Game**: [Data Center on Steam](https://store.steampowered.com/app/4170200/) (AppID `4170200`). +- **Workshop**: [Browse items](https://steamcommunity.com/workshop/browse/?appid=4170200) — best for **player discovery**, ratings, and stable “subscribe and play” flows. +- Use Workshop for **production-ready** releases you want every player to see. + +## GitHub Releases (beta and alternate downloads) + +- **Organization**: [github.com/mleem97/gregFramework](https://github.com/mleem97/gregFramework) (see repo for actual org if renamed). +- **Stable**: tagged releases — same DLLs you may also ship via Workshop. +- **Pre-release / beta**: GitHub **pre-releases** — ideal for testers without publishing unfinished items to Workshop. +- **Why**: avoids “polluting” the Workshop with experimental builds while still offering a **single official URL** for power users and CI. + +## This documentation site + +| Need | Where | +|------|--------| +| Wiki entry for a module | `/wiki/mods/...` | +| Download link (when configured) | GitHub Releases asset or redirect from [`moduleCatalog`](https://github.com/mleem97/gregFramework/blob/master/wiki/src/data/moduleCatalog.ts) | +| Catalog overview | [`/mods`](/mods) | + +## Maintainer checklist + +- [ ] Stable: tag + Release notes + optional Workshop update. +- [ ] Beta: pre-release + link from module wiki page + `releaseReady: false` in catalog until promoted. +- [ ] Do not commit large binaries to `main` without a documented policy. + +## Related + +- [Repo inventory](../contributors/repo-inventory.md) +- [Mod store vision](./mod-store-vision.md) diff --git a/reference/wiki-mapping.md b/reference/wiki-mapping.md new file mode 100644 index 0000000..08375b3 --- /dev/null +++ b/reference/wiki-mapping.md @@ -0,0 +1,26 @@ +--- +id: wiki-mapping +title: Wiki to Docusaurus Mapping +slug: /reference/wiki-mapping +--- + +## Why this mapping exists + +The repository currently stores canonical docs in `.wiki/`. This page defines how those pages are grouped in Docusaurus. + +## Audience buckets + +- **Newbies**: install/update/troubleshooting, legal basics +- **Intermediates**: hooking, bridge usage, mod config, workflows +- **Pros**: architecture, runtime internals, release system, roadmap + +## Suggested migration order + +1. Start with onboarding pages (`Home`, End-User, Mod-Developer). +2. Migrate runtime references (`Architecture`, `FFI-Bridge-Reference`, `Web-UI-Bridge`). +3. Migrate governance pages (`Contributors`, Roadmap, Tasklist). +4. Keep bilingual mirrors where needed. + +## Sync strategy + +Use `npm run wiki:sync` to copy `.wiki/*.md` into a generated docs area for review-based migration. diff --git a/references/README.md b/references/README.md new file mode 100644 index 0000000..59be7aa --- /dev/null +++ b/references/README.md @@ -0,0 +1,35 @@ +# Reference Data + +This folder contains large reference exports used by hook analysis and build-time tooling. + +## Files + +- [assembly-hooks.txt.gz](./assembly-hooks.txt.gz) +- [modder-hooks.ffm.txt.gz](./modder-hooks.ffm.txt.gz) + +## Decompress + +Use standard gzip tools when you need local plaintext files: + +```bash +gzip -dk assembly-hooks.txt.gz +gzip -dk modder-hooks.ffm.txt.gz +``` + +PowerShell alternative: + +```powershell +$inputPath = ".\\assembly-hooks.txt.gz" +$outputPath = ".\\assembly-hooks.txt" +$in = [System.IO.File]::OpenRead($inputPath) +$out = [System.IO.File]::Create($outputPath) +$gzip = New-Object System.IO.Compression.GzipStream($in, [System.IO.Compression.CompressionMode]::Decompress) +$gzip.CopyTo($out) +$gzip.Dispose(); $out.Dispose(); $in.Dispose() +``` + +## Build Pipeline Usage + +- Hook alias and reference tooling should read `.gz` directly when possible. +- If plaintext is required, scripts should decompress on demand and avoid committing extracted `.txt` files. +- Keep all reference artifacts in `docs/wiki/references/`. diff --git a/references/assembly-hooks.txt.gz b/references/assembly-hooks.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..1e117420137748412aa3cf354b671e66c836869f GIT binary patch literal 48602 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 literal 0 HcmV?d00001 diff --git a/references/modder-hooks.ffm.txt.gz b/references/modder-hooks.ffm.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..9df2ed00caf756e58464454abc7169112d454268 GIT binary patch literal 38537 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 ZN=Q;8bUl`2h!Ozz=)Y20DI)h&1^_4erS<>- literal 0 HcmV?d00001 diff --git a/releases/index.mdx b/releases/index.mdx new file mode 100644 index 0000000..bd13976 --- /dev/null +++ b/releases/index.mdx @@ -0,0 +1,29 @@ +--- +title: Releases +sidebar_label: Releases overview +description: Index of plugin and mod release pages (download metadata, notes links). +--- + +# Releases + +Release pages list **download targets**, **version placeholders**, and links back to the **wiki articles** for each plugin or mod. Non-release documentation lives under [Mods](/wiki/mods/plugins/) in the sidebar. + +## Plugin releases + +- [FFM.Plugin.AssetExporter](/wiki/releases/plugins/ffm-plugin-asset-exporter-release) +- [FFM.Plugin.Multiplayer](/wiki/releases/plugins/ffm-plugin-multiplayer-release) +- [FFM.Plugin.PlayerModels](/wiki/releases/plugins/ffm-plugin-player-models-release) +- [FFM.Plugin.Sysadmin](/wiki/releases/plugins/ffm-plugin-sysadmin-release) +- [FFM.Plugin.WebUIBridge](/wiki/releases/plugins/ffm-plugin-web-ui-bridge-release) + +## Tool releases + +- [GregTools 1.0 Modmanager](/wiki/releases/tools/gregtools-modmanager-1.0-release) + +## Mod releases + +- [FMF.ConsoleInputGuard](/wiki/releases/mods/fmf-console-input-guard-release) +- [FMF.GregifyEmployees](/wiki/releases/mods/fmf-gregify-employees-release) +- [FMF.HexLabelMod](/wiki/releases/mods/fmf-hex-label-mod-release) +- [FMF.LangCompatBridge](/wiki/releases/mods/fmf-lang-compat-bridge-release) +- [FMF.UIReplacementMod](/wiki/releases/mods/fmf-ui-replacement-mod-release) diff --git a/releases/mods/fmf-console-input-guard-release.mdx b/releases/mods/fmf-console-input-guard-release.mdx new file mode 100644 index 0000000..30c0135 --- /dev/null +++ b/releases/mods/fmf-console-input-guard-release.mdx @@ -0,0 +1,21 @@ +--- +title: FMF.ConsoleInputGuard Release +sidebar_label: FMF.ConsoleInputGuard Release +--- + +import ModReleasePage from '@site/src/components/ModReleasePage'; + + diff --git a/releases/mods/fmf-gregify-employees-release.mdx b/releases/mods/fmf-gregify-employees-release.mdx new file mode 100644 index 0000000..2d8a561 --- /dev/null +++ b/releases/mods/fmf-gregify-employees-release.mdx @@ -0,0 +1,21 @@ +--- +title: FMF.GregifyEmployees Release +sidebar_label: FMF.GregifyEmployees Release +--- + +import ModReleasePage from '@site/src/components/ModReleasePage'; + + diff --git a/releases/mods/fmf-hex-label-mod-release.mdx b/releases/mods/fmf-hex-label-mod-release.mdx new file mode 100644 index 0000000..92d6d96 --- /dev/null +++ b/releases/mods/fmf-hex-label-mod-release.mdx @@ -0,0 +1,21 @@ +--- +title: FMF.HexLabelMod Release +sidebar_label: FMF.HexLabelMod Release +--- + +import ModReleasePage from '@site/src/components/ModReleasePage'; + + diff --git a/releases/mods/fmf-lang-compat-bridge-release.mdx b/releases/mods/fmf-lang-compat-bridge-release.mdx new file mode 100644 index 0000000..1837921 --- /dev/null +++ b/releases/mods/fmf-lang-compat-bridge-release.mdx @@ -0,0 +1,21 @@ +--- +title: FMF.LangCompatBridge Release +sidebar_label: FMF.LangCompatBridge Release +--- + +import ModReleasePage from '@site/src/components/ModReleasePage'; + + diff --git a/releases/mods/fmf-ui-replacement-mod-release.mdx b/releases/mods/fmf-ui-replacement-mod-release.mdx new file mode 100644 index 0000000..adad480 --- /dev/null +++ b/releases/mods/fmf-ui-replacement-mod-release.mdx @@ -0,0 +1,15 @@ +--- +title: FMF.UIReplacementMod Release +sidebar_label: FMF.UIReplacementMod +--- + +import ModReleasePage from '@site/src/components/ModReleasePage'; + + diff --git a/releases/plugins/ffm-plugin-asset-exporter-release.mdx b/releases/plugins/ffm-plugin-asset-exporter-release.mdx new file mode 100644 index 0000000..b304c24 --- /dev/null +++ b/releases/plugins/ffm-plugin-asset-exporter-release.mdx @@ -0,0 +1,21 @@ +--- +title: FFM.Plugin.AssetExporter Release +sidebar_label: FFM.Plugin.AssetExporter Release +--- + +import ModReleasePage from '@site/src/components/ModReleasePage'; + + diff --git a/releases/plugins/ffm-plugin-multiplayer-release.mdx b/releases/plugins/ffm-plugin-multiplayer-release.mdx new file mode 100644 index 0000000..b841f33 --- /dev/null +++ b/releases/plugins/ffm-plugin-multiplayer-release.mdx @@ -0,0 +1,21 @@ +--- +title: FFM.Plugin.Multiplayer Release +sidebar_label: FFM.Plugin.Multiplayer Release +--- + +import ModReleasePage from '@site/src/components/ModReleasePage'; + + diff --git a/releases/plugins/ffm-plugin-player-models-release.mdx b/releases/plugins/ffm-plugin-player-models-release.mdx new file mode 100644 index 0000000..9123a6c --- /dev/null +++ b/releases/plugins/ffm-plugin-player-models-release.mdx @@ -0,0 +1,21 @@ +--- +title: FFM.Plugin.PlayerModels Release +sidebar_label: FFM.Plugin.PlayerModels Release +--- + +import ModReleasePage from '@site/src/components/ModReleasePage'; + + diff --git a/releases/plugins/ffm-plugin-sysadmin-release.mdx b/releases/plugins/ffm-plugin-sysadmin-release.mdx new file mode 100644 index 0000000..0582644 --- /dev/null +++ b/releases/plugins/ffm-plugin-sysadmin-release.mdx @@ -0,0 +1,21 @@ +--- +title: FFM.Plugin.Sysadmin Release +sidebar_label: FFM.Plugin.Sysadmin Release +--- + +import ModReleasePage from '@site/src/components/ModReleasePage'; + + diff --git a/releases/plugins/ffm-plugin-web-ui-bridge-release.mdx b/releases/plugins/ffm-plugin-web-ui-bridge-release.mdx new file mode 100644 index 0000000..95605d3 --- /dev/null +++ b/releases/plugins/ffm-plugin-web-ui-bridge-release.mdx @@ -0,0 +1,21 @@ +--- +title: FFM.Plugin.WebUIBridge Release +sidebar_label: FFM.Plugin.WebUIBridge Release +--- + +import ModReleasePage from '@site/src/components/ModReleasePage'; + + diff --git a/releases/plugins/fmf-modpathredirector-release.mdx b/releases/plugins/fmf-modpathredirector-release.mdx new file mode 100644 index 0000000..ac80c8a --- /dev/null +++ b/releases/plugins/fmf-modpathredirector-release.mdx @@ -0,0 +1,40 @@ +--- +title: FMF.ModPathRedirector Release +sidebar_label: FMF.ModPathRedirector Release +description: Download and install the MelonLoader Workshop download plugin for Data Center. +--- + +import ModReleasePage from '@site/src/components/ModReleasePage'; + + +
  • + Download FMF.ModPathRedirector.dll from the latest{' '} + GitHub release (or use the button on this page — redirects to the same asset). +
  • +
  • + Copy the DLL into your game folder: <GameRoot>/Plugins/ — the same directory as Data Center.exe, not Mods/ (that folder is for MelonMods). +
  • +
  • + Ensure steam_api64.dll is next to the game executable (Steam ships it with the game). Start the game with MelonLoader; the plugin waits until Steam is running, then requests downloads for subscribed Workshop items. +
  • + + } +/> + +:::info GitHub asset +The **Download DLL** button and `/plugin/FMF.ModPathRedirector.dll` route resolve to `https://github.com/mleem97/gregFramework/releases/latest/download/FMF.ModPathRedirector.dll`. That file is attached automatically when you publish a release via the `release-assets.yml` workflow (push a `v*` tag or run the workflow manually). +::: diff --git a/releases/tools/gregtools-modmanager-1.0-release.mdx b/releases/tools/gregtools-modmanager-1.0-release.mdx new file mode 100644 index 0000000..a9a6926 --- /dev/null +++ b/releases/tools/gregtools-modmanager-1.0-release.mdx @@ -0,0 +1,42 @@ +--- +title: GregTools 1.0 Modmanager +sidebar_label: GregTools Modmanager 1.0 +description: First stable release of the GregTools Modmanager (Workshop Uploader) for Data Center — download ZIP, install, and links. +--- + +# GregTools 1.0 Modmanager + +**Version:** 1.0.0 · **Application title:** GregTools Modmanager · **Windows:** win10-x64, self-contained (.NET MAUI) + +This is the **first numbered release** line for the desktop Workshop client: Mod Store, author tools, Mod Manager, and Steam publish flows for **Data Center** (Steam App ID `4170200`). + +## Download + +Releases are published on **GitHub** from the [`gregtools-modmanager-release`](https://github.com/mleem97/gregFramework/blob/main/.github/workflows/gregtools-modmanager-release.yml) workflow. + +| What | Where | +|------|--------| +| **Latest release** | [github.com/mleem97/gregFramework/releases/latest](https://github.com/mleem97/gregFramework/releases/latest) | +| **1.0.0 assets** | Look for tag **`gregtools-modmanager-v1.0.0`** and the ZIP **`Gregtools-Modmanager-1.0.0-win10-x64.zip`** (best-compression portable folder). | + +:::tip Creating the GitHub release + +Push tag **`gregtools-modmanager-v1.0.0`** on `main` (or run the workflow manually with that tag) so CI builds and attaches the ZIP. + +::: + +## Install + +1. Download **`Gregtools-Modmanager-1.0.0-win10-x64.zip`** from the release. +2. Extract to a folder of your choice (or next to the game — see [Workshop uploader](/wiki/tools/workshop-uploader)). +3. Run **`WorkshopUploader.exe`**. **Steam** must be installed; the app uses the Steamworks API for Workshop. + +## Documentation + +- [Workshop uploader (full guide)](/wiki/tools/workshop-uploader) +- [End-user Workshop guide](/wiki/guides/enduser-workshop) +- Open-source and third-party components: [`WorkshopUploader/EXTERNAL_DEPENDENCIES.md`](https://github.com/mleem97/gregFramework/blob/main/WorkshopUploader/EXTERNAL_DEPENDENCIES.md) in the repository + +## Scope of 1.0 + +Initial **1.0** marks a stable baseline for the MAUI Windows app (self-contained publish, trimmed Release builds, localization, Mod Store / Mod Manager / editor flows). Patch and feature releases will use **1.0.x** / **1.x** as versioning on the [application version](/wiki/tools/workshop-uploader) in the project file. diff --git a/roadmap/mod-store-stages.md b/roadmap/mod-store-stages.md new file mode 100644 index 0000000..abd3bab --- /dev/null +++ b/roadmap/mod-store-stages.md @@ -0,0 +1,11 @@ +--- +id: mod-store-stages +title: Mod Store Stages +slug: /roadmap/mod-store-stages +--- + +This page is kept for legacy links. + +Use the canonical roadmap page: + +- [Unified Roadmap](/wiki/roadmap/unified-roadmap) diff --git a/roadmap/unified-roadmap.md b/roadmap/unified-roadmap.md new file mode 100644 index 0000000..e4a86b9 --- /dev/null +++ b/roadmap/unified-roadmap.md @@ -0,0 +1,59 @@ +--- +id: unified-roadmap +title: Unified Roadmap +slug: /roadmap/unified-roadmap +--- + +This is the canonical roadmap page for the project. It consolidates previous roadmap documents and removes duplicate planning tracks. + +## Status Summary + +- **Framework Runtime**: active +- **Docs & Contributor Tooling**: active +- **Mod Distribution & Audit Pipeline**: active planning +- **Multiplayer (Steamworks)**: blocked externally + +## Important Multiplayer Note + +Multiplayer was planned around **Steamworks P2P** and that direction remains conceptually valid. +However, it is **currently not implementable in production** because the game developer has not integrated Steamworks support in the game runtime/API surface required for a stable implementation. + +## Consolidated Workstreams + +### 1) Framework & Hooking + +- Stabilize hook coverage and event contracts. +- Keep C# and Rust bridge compatibility versioned. +- Expand diagnostics for mod-runtime compatibility failures. + +### 2) Documentation & Information Architecture + +- Keep `/wiki/*` as canonical docs namespace. +- Maintain separated Plugin Wiki and Mod Wiki sections. +- Remove duplicate docs by keeping one canonical page per topic. + +### 3) Mod/Plugin Catalog & Distribution + +- Maintain dynamic `/mods` catalog with links to wiki and release endpoints. +- Keep release metadata visible (version, author, dependencies, language). +- Keep release-state gating (`NotReleasedYet`) explicit for users. + +### 4) Security Audit & Submission Workflow + +- Accept plugin submissions via Git repository URL. +- Run automated static checks and suspicious-pattern scans. +- Gate publication/release visibility on successful audit outcome. + +### 5) Multiplayer Track (Blocked) + +- Keep architecture docs for future Steamworks integration readiness. +- Track dependencies on game-side Steamworks implementation. +- Re-open implementation only when game runtime exposes required primitives. + +## Legacy Roadmap Documents + +The following pages are now legacy references and should not be used as primary planning sources: + +- `wiki-import/ROADMAP` +- `wiki-import/Steamworks-P2P-Multiplayer-Roadmap` +- `roadmap/mod-store-stages` diff --git a/tools/Steam-Workshop-and-Tooling.md b/tools/Steam-Workshop-and-Tooling.md new file mode 100644 index 0000000..a6a401d --- /dev/null +++ b/tools/Steam-Workshop-and-Tooling.md @@ -0,0 +1,39 @@ +# Steam Workshop and tooling + +## Live-Sync references + +After a *Data Center* update, run the game once so MelonLoader regenerates interop assemblies, then from the repo root: + +```bash +python tools/refresh_refs.py +``` + +Optionally save a baseline for diffs: + +```bash +python tools/diff_assembly_metadata.py --save-snapshot +``` + +After future updates: + +```bash +python tools/diff_assembly_metadata.py +``` + +Do **not** commit `*.dll` from `lib/references/` (see `.gitignore`). + +## Steam Workshop (research) + +Official upload and item layout for *Data Center* Workshop content may be undocumented by the developer. Until documented: + +- Treat Workshop delivery as **game-defined** (often content under game data / `StreamingAssets`; MelonLoader mods remain **DLLs in `Mods/`**). +- The **WorkshopUploader** desktop app (see `WorkshopUploader/`) is the supported path for authors to manage Workshop items and DevServer betas once Steamworks is configured. + +## Legal + +Do not redistribute game binaries or extracted assets. Workshop packages should contain **your** content only. + +## CI / agents + +- `FrikaMF.csproj` builds on Windows agents that have either a Steam *Data Center* install **or** a populated `lib/references/MelonLoader/` (from `refresh_refs.py`). +- `WorkshopUploader` targets `net6.0-windows`; build it on Windows (not Linux-hosted runners unless cross-compilation is configured). diff --git a/topics/assets-and-export/overview.md b/topics/assets-and-export/overview.md new file mode 100644 index 0000000..2dc60b3 --- /dev/null +++ b/topics/assets-and-export/overview.md @@ -0,0 +1,11 @@ +--- +title: Assets & export +sidebar_label: Assets & export (hub) +description: Asset export plugin, templates, and related wiki-import pages. +--- + +# Assets & export + +- Wiki: [FFM.Plugin.AssetExporter](/wiki/mods/plugins/ffm-plugin-asset-exporter) +- Release: [FFM.Plugin.AssetExporter release](/wiki/releases/plugins/ffm-plugin-asset-exporter-release) +- Imported: [AssetExport](/wiki/wiki-import/AssetExport), [Release assets and templates](/wiki/wiki-import/Release-Assets-and-Templates) diff --git a/topics/audiences/overview.md b/topics/audiences/overview.md new file mode 100644 index 0000000..45de6d9 --- /dev/null +++ b/topics/audiences/overview.md @@ -0,0 +1,29 @@ +--- +title: By audience +sidebar_label: By audience +description: Vier Rollen — Spieler, Moddevs, Contributor, Sponsoren — plus Erfahrungsstufen (Newbies bis Pros). +--- + +# By audience + +Die Dokumentation richtet sich an **vier Hauptrollen** (und an Erfahrungsstufen darunter). Wähle, was zu dir passt — die Sprache der meisten Seiten ist **Englisch**, mit deutschsprachigen Importen unter `wiki-import/` wo vorhanden. + +## Die vier Rollen + +| Rolle | Für wen? | Einstieg | +|--------|----------|----------| +| **Spieler** (End users) | Installation, Mods nutzen, Troubleshooting, FAQ | [End users (hub)](/wiki/topics/end-user/overview) → [End user wiki](/wiki/wiki-import/EndUser/), [Data center FAQ](/wiki/wiki-import/DataCenterFAQ/) | +| **Mod-Entwickler** (Mod developers) | Mods bauen, Hooks, Konfiguration, Debug | [Mod developers (hub)](/wiki/topics/mod-developers/overview) → [ModDevs wiki](/wiki/wiki-import/ModDevs/), [Framework](/wiki/mods/framework) | +| **Contributor** (Repo & Framework) | PRs, Doku, Plugins, CI, Design | [Contributors (workflow)](/wiki/topics/contributors/overview) → [Contributors wiki](/wiki/wiki-import/Contributors/), [Repo inventory](/wiki/contributors/repo-inventory) | +| **Sponsorinnen & Sponsoren** | Unterstützung, Transparenz, rechtlicher Kontext | [Sponsors (hub)](/wiki/topics/sponsors/overview) → [Sponsors (EN)](/wiki/wiki-import/Sponsors), [Sponsoren (DE)](/wiki/wiki-import/Sponsoren) | + +## Erfahrungsstufen (alle Rollen) + +- [Newbies](/wiki/audiences/newbies) — erste Schritte, Begriffe, sichere Defaults. +- [Intermediates](/wiki/audiences/intermediates) — Workflows, Tooling, typische Stolpersteine. +- [Professionals](/wiki/audiences/professionals) — Architektur, FFI, Performance, Release-Kanäle. + +## Thematische Übersicht + +- [Topics hub](/wiki/topics/) — Security, Multiplayer, Assets, FFI, Roadmap, Meta. +- Importierte Legacy-Guides: [Legacy wiki import](/wiki/topics/wiki-import/overview). diff --git a/topics/contributors/overview.md b/topics/contributors/overview.md new file mode 100644 index 0000000..9d37d06 --- /dev/null +++ b/topics/contributors/overview.md @@ -0,0 +1,19 @@ +--- +title: Contributors (workflow) +sidebar_label: Contributors (workflow) +description: Monorepo layout, design system, Docusaurus workflow, plugin audits. +--- + +# Contributors (workflow) + +**Mitwirkende am Repository** — Framework, Website, CI, Plugins. Überblick über alle Rollen (inkl. Spieler, Moddevs, Sponsoren): [By audience](/wiki/topics/audiences/overview). + +Operational docs for people changing the framework, site, or release pipeline. + +- [Repository inventory](/wiki/contributors/repo-inventory) — current layout snapshot. +- [Monorepo target layout](/wiki/contributors/monorepo-target-layout) — intended structure. +- [Luminescent design system](/wiki/contributors/luminescent-design-system) — UI tokens for the site. +- [Docusaurus workflow](/wiki/contributors/docusaurus-workflow) — edit/build the wiki site. +- [Plugin submission audit](/wiki/contributors/plugin-submission-audit) — checklist for new plugins. + +Imported guides: [Contribution workflow](/wiki/wiki-import/Contributors/Guides/Contribution-Workflow). diff --git a/topics/end-user/overview.md b/topics/end-user/overview.md new file mode 100644 index 0000000..987c269 --- /dev/null +++ b/topics/end-user/overview.md @@ -0,0 +1,13 @@ +--- +title: End users +sidebar_label: End users (hub) +description: FAQs, install paths, troubleshooting — mostly wiki-import. +--- + +# End users + +**Spielerinnen und Spieler** — Mods installieren und spielen, ohne am Framework mitzuentwickeln. Überblick über alle Rollen: [By audience](/wiki/topics/audiences/overview). + +- [By audience — newbies](/wiki/audiences/newbies) +- Imported: [End user index](/wiki/wiki-import/EndUser/), [Data center FAQ](/wiki/wiki-import/DataCenterFAQ/), [Known incompatibilities](/wiki/wiki-import/Known-Incompatibilities) +- Imported: [End user release notes](/wiki/wiki-import/EndUser/End-User-Release) diff --git a/topics/ffi-and-hooks/overview.md b/topics/ffi-and-hooks/overview.md new file mode 100644 index 0000000..ff6d9e1 --- /dev/null +++ b/topics/ffi-and-hooks/overview.md @@ -0,0 +1,13 @@ +--- +title: FFI, hooks & Lua +sidebar_label: FFI, hooks & Lua (hub) +description: FFI, hook lists, naming — bridge between framework and legacy wiki. +--- + +# FFI, hooks & Lua + +- [FMF hooks catalog](/wiki/reference/fmf-hooks-catalog) (generated) +- [FMF hook naming](/wiki/reference/fmf-hook-naming) +- Imported: [HOOKS](/wiki/wiki-import/HOOKS), [Hook naming convention](/wiki/wiki-import/HOOK-NAMING-CONVENTION) +- Imported: [FFI bridge reference](/wiki/wiki-import/FFI-Bridge-Reference), [Lua FFI start](/wiki/wiki-import/Lua-FFI-Start-Developing) +- Imported: [Mod developer debug](/wiki/wiki-import/Mod-Developer-Debug) diff --git a/topics/index.md b/topics/index.md new file mode 100644 index 0000000..484a02c --- /dev/null +++ b/topics/index.md @@ -0,0 +1,37 @@ +--- +title: Topics hub +sidebar_label: Topics overview +description: Thematic index — navigate by audience, reference, roadmap, imports, and meta. +--- + +# Topics hub + +Documentation is split into **curated wiki pages** (this site), **generated reference** (for example hook catalogs), and **legacy imports** from the GitHub wiki (`.wiki` → `wiki-import/`). + +## Quick map + +| Area | Start here | +|------|------------| +| **Roles (Spieler · Moddevs · Contributor · Sponsoren)** | [By audience](/wiki/topics/audiences/overview) — [End users](/wiki/topics/end-user/overview), [Mod developers](/wiki/topics/mod-developers/overview), [Contributors (workflow)](/wiki/topics/contributors/overview), [Sponsors](/wiki/topics/sponsors/overview) | +| **Audience paths (experience)** | [Newbies](/wiki/audiences/newbies) · [Intermediates](/wiki/audiences/intermediates) · [Professionals](/wiki/audiences/professionals) | +| **Technical reference** | [Reference & technical](/wiki/topics/reference/overview) | +| **Ship planning** | [Roadmap & planning](/wiki/topics/roadmap/overview) | +| **Repository inventory** | [Repo inventory](/wiki/contributors/repo-inventory) | +| **Imported legacy wiki** | [Legacy wiki import](/wiki/topics/wiki-import/overview) | +| **Steam, betas, backlog, game paths** | [Meta & operations](/wiki/topics/meta/overview) — [Game folder layout](/wiki/topics/meta/game-folder-layout) | + +## Thematic folders (deep links) + +Use these entry points for cross-cutting concerns; each page links into `wiki-import/` and reference docs where relevant. + +- [Security & legal](/wiki/topics/security-legal/overview) — licenses, disclaimers, letters to the developer. +- [FFI, hooks & Lua](/wiki/topics/ffi-and-hooks/overview) — HOOKS, FFI bridge, naming conventions. +- [Multiplayer & networking](/wiki/topics/multiplayer-and-networking/overview) — Steamworks P2P, Web UI bridge. +- [Assets & export](/wiki/topics/assets-and-export/overview) — asset export pipeline, templates. +- [End users](/wiki/topics/end-user/overview) — installs, FAQs, troubleshooting. +- [Mod developers](/wiki/topics/mod-developers/overview) — getting started, mod config, debugging. + +## Releases vs. articles + +- **Release pages** (downloads, version banners): [Releases](/wiki/releases/). +- **Narrative wiki articles** (how it works): [Plugin Wiki](/wiki/mods/plugins/) and [Mod Wiki](/wiki/mods/mods/). diff --git a/topics/meta/game-folder-layout.md b/topics/meta/game-folder-layout.md new file mode 100644 index 0000000..2ad65eb --- /dev/null +++ b/topics/meta/game-folder-layout.md @@ -0,0 +1,62 @@ +--- +title: Game folder layout (FMF / MelonLoader) +sidebar_label: Game folder layout +description: Canonical paths for mod configs, FMF plugins, and MelonLoader mods under the Data Center game root. +--- + +# Game folder layout (FMF / MelonLoader) + +This page is the **single reference** for where mod-related files live next to the game executable (`{GameRoot}`). MelonLoader sets `{GameRoot}/UserData` via `MelonEnvironment.UserDataDirectory`, `{GameRoot}/Mods` for **MelonMods**, and `{GameRoot}/Plugins` for **MelonPlugins** (e.g. `FMF.ModPathRedirector.dll`). + +## Summary + +| Inhalt | Pfad | Format / Hinweis | +|--------|------|------------------| +| **Mod-Konfiguration & Sidecars** | `{GameRoot}/UserData/ModCfg/` | **JSON** für Konfigurationsdateien; weitere Sidecar-Dateien (z. B. `custom_employees_hired.txt`) liegen ebenfalls hier, damit alles Mod-Bezogene an einem Ort liegt. | +| **FMF Framework-Plugins** (FFM.Plugin.*) | `{GameRoot}/FMF/Plugins/` | DLLs; **MelonLoader** lädt standardmäßig nur `{GameRoot}/Mods` — siehe unten. | +| **Plugins** (MelonLoader, z. B. ModPathRedirector) | `{GameRoot}/Plugins/` | MelonLoader `Plugins`-Ordner — nur **MelonPlugin**-DLLs. | +| **Mods** (MelonLoader, z. B. FMF.Mod.*) | `{GameRoot}/Mods/` | MelonLoader `Mods`-Ordner — **MelonMod**-DLLs. | + +## UserData/ModCfg + +- Alle **mod-relevanten** Konfigurationen und JSON-Sidecars werden unter **`UserData/ModCfg`** geführt. +- Beim ersten Start werden fehlende Dateien angelegt; bei bestehenden Installationen werden ältere Dateien aus **`UserData/`** (Root) nach **`ModCfg/`** übernommen, sofern noch vorhanden. +- Beispiele: `multiplayer-sync.config.json`, `pluginsync.config.json`. +- Framework-Metadaten (z. B. Save-Compat-Stamp) liegen unter **`UserData/ModCfg/FrikaFM/`** (Migration von `UserData/FrikaFM`). + +## FMF/Plugins und MelonLoader + +**FFM-Plugin-DLLs** liegen kanonisch unter **`{GameRoot}/FMF/Plugins`**. MelonLoader enumeriert **standardmäßig** nur **`Mods/`**. Praktische Optionen: + +1. **DLLs zusätzlich** (oder verlinkt) **`Mods/`** ablegen — üblicher Weg für automatisches Laden. +2. **Unterordner** von `Mods` nutzen, falls eure MelonLoader-Version Mods in Unterverzeichnissen lädt (Version je nach Release prüfen). +3. **PluginSync**-Downloads des Frameworks landen unter **`FMF/Plugins/PluginSync/...`**. + +## Mods (FMF-basiert) + +Normale **MelonLoader-Mods** (einschließlich FMF-Mods) werden wie gewohnt in **`{GameRoot}/Mods/`** installiert. + +## Steam Workshop (Spiel) vs. MelonLoader + +Das Spiel legt abonnierte Workshop-Inhalte unter **`{GameRoot}/{ExeName}_Data/StreamingAssets/mods/workshop_/WorkshopUploadContent`** ab (nativer `ModLoader`, nicht MelonLoader). + +- **MelonLoader** durchsucht **`{GameRoot}/Mods`** (inkl. Unterordner, je nach Einstellung), **nicht** beliebige Pfade über `Loader.cfg`. +- **UserData:** MelonLoader-Konfiguration liegt unter **`{GameRoot}/UserData/`** (z. B. **`MelonLoader.cfg`** / je nach Version **`UserData/MelonLoader/Loader.cfg`** — bei Install prüfen). Relevant für Unterordner-Laden: **`disable_subfolder_load = false`**, optional **`disable_subfolder_manifest = true`**. +- **Workshop-DLLs in den Melon-Scan einbinden:** Junction (oder Symlink) von einem Ordner unter **`Mods/`** auf den **`WorkshopUploadContent`**-Pfad desselben Items, z. B. (PowerShell, Pfade anpassen): + +```powershell +$game = "C:\Path\To\Data Center" +$id = "12345678901234567" +$target = Join-Path $game "Data Center_Data\StreamingAssets\mods\workshop_$id\WorkshopUploadContent" +$link = Join-Path $game "Mods\workshop_$id" +cmd /c mklink /J "$link" "$target" +``` + +Ohne Junction müssen MelonMods weiter physisch unter **`Mods/`** liegen oder über eure Verteilung dort landen. + +**WorkshopUploader-Vorlagen (modded):** Unter **`content/`** werden **`Mods/`**, **`Plugins/`** und ein **`ModFramework/`**-Baum angelegt — **`ModFramework/FMF/Plugins`** entspricht dabei **`{GameRoot}/FMF/Plugins`**, wenn ihr **`FMF`** per Junction auf **`…/WorkshopUploadContent/ModFramework/FMF`** zeigen lasst. Weitere Framework-Dateien (Konfiguration, Assets) können unter **`ModFramework/`** gebündelt werden. + +## Siehe auch + +- [Meta & operations](/wiki/topics/meta/overview) +- [Legacy: Mod Config System](/wiki/wiki-import/Mod-Config-System) (API-Contract; Pfade auf **ModCfg** beziehen) diff --git a/topics/meta/overview.md b/topics/meta/overview.md new file mode 100644 index 0000000..93e3b48 --- /dev/null +++ b/topics/meta/overview.md @@ -0,0 +1,14 @@ +--- +title: Meta & operations +sidebar_label: Meta & operations +description: Workshop tooling, devserver betas, idea backlog — repo-level notes. +--- + +# Meta & operations + +These pages are **not** end-user game docs; they describe how we ship and plan around the project. + +- [Game folder layout (FMF / MelonLoader)](/wiki/topics/meta/game-folder-layout) — `UserData/ModCfg`, `FMF/Plugins`, `Mods`. +- [Steam Workshop and tooling](/wiki/meta/Steam-Workshop-and-Tooling) — uploader, references workflow. +- [Devserver betas](/wiki/meta/devserver-betas) — beta channel notes. +- [IDEA backlog](/wiki/meta/IDEA_BACKLOG) — unstructured idea list (also used by automation). diff --git a/topics/mod-developers/overview.md b/topics/mod-developers/overview.md new file mode 100644 index 0000000..4c9b8ea --- /dev/null +++ b/topics/mod-developers/overview.md @@ -0,0 +1,14 @@ +--- +title: Mod developers +sidebar_label: Mod developers (hub) +description: Getting started, mod config, debugging — links into wiki-import ModDevs tree. +--- + +# Mod developers + +**Mod-Autorinnen und -Autoren** — eigene Mods bauen (Hooks, Konfiguration, Debug). Überblick über alle Rollen: [By audience](/wiki/topics/audiences/overview). + +- [By audience — intermediates / professionals](/wiki/audiences/intermediates) and [professionals](/wiki/audiences/professionals) +- Wiki: [Framework overview](/wiki/mods/framework) +- Imported: [ModDevs](/wiki/wiki-import/ModDevs/), [Modding guide](/wiki/wiki-import/Modding-Guide), [Mod config system](/wiki/wiki-import/Mod-Config-System) +- Imported: [Standalone mods](/wiki/wiki-import/StandaloneMods) diff --git a/topics/multiplayer-and-networking/overview.md b/topics/multiplayer-and-networking/overview.md new file mode 100644 index 0000000..1be0771 --- /dev/null +++ b/topics/multiplayer-and-networking/overview.md @@ -0,0 +1,12 @@ +--- +title: Multiplayer & networking +sidebar_label: Multiplayer & networking (hub) +description: P2P roadmap, Web UI bridge, multiplayer plugin — entry points. +--- + +# Multiplayer & networking + +- Wiki: [FFM.Plugin.Multiplayer](/wiki/mods/plugins/ffm-plugin-multiplayer) +- Release: [FFM.Plugin.Multiplayer release](/wiki/releases/plugins/ffm-plugin-multiplayer-release) +- Imported: [Steamworks P2P multiplayer roadmap](/wiki/wiki-import/Steamworks-P2P-Multiplayer-Roadmap) +- Imported: [Web UI bridge](/wiki/wiki-import/Web-UI-Bridge) diff --git a/topics/reference/overview.md b/topics/reference/overview.md new file mode 100644 index 0000000..43391e3 --- /dev/null +++ b/topics/reference/overview.md @@ -0,0 +1,17 @@ +--- +title: Reference & technical +sidebar_label: Reference & technical +description: Mapping tables, hook naming, generated catalogs, release channels. +--- + +# Reference & technical + +Authoritative reference material for the framework and documentation site. + +- [Wiki mapping](/wiki/reference/wiki-mapping) — how paths line up across systems. +- [Mod store vision](/wiki/reference/mod-store-vision) — product direction notes. +- [FMF hook naming](/wiki/reference/fmf-hook-naming) — conventions for hook identifiers. +- [FMF hooks catalog](/wiki/reference/fmf-hooks-catalog) — generated listing from framework sources. +- [Release channels](/wiki/reference/release-channels) — how builds are staged. + +See also [FFI, hooks & Lua](/wiki/topics/ffi-and-hooks/overview) for imported deep dives from the legacy wiki. diff --git a/topics/roadmap/overview.md b/topics/roadmap/overview.md new file mode 100644 index 0000000..43531f0 --- /dev/null +++ b/topics/roadmap/overview.md @@ -0,0 +1,12 @@ +--- +title: Roadmap & planning +sidebar_label: Roadmap & planning +description: Unified roadmap and mod-store staging notes. +--- + +# Roadmap & planning + +- [Unified roadmap](/wiki/roadmap/unified-roadmap) — cross-cutting priorities. +- [Mod store stages](/wiki/roadmap/mod-store-stages) — staged rollout concepts. + +Imported context: [ROADMAP](/wiki/wiki-import/ROADMAP), [TASKLIST](/wiki/wiki-import/TASKLIST), [Repository status](/wiki/wiki-import/Repository-Status-2026-04-04). diff --git a/topics/security-legal/overview.md b/topics/security-legal/overview.md new file mode 100644 index 0000000..e8b2828 --- /dev/null +++ b/topics/security-legal/overview.md @@ -0,0 +1,16 @@ +--- +title: Security & legal +sidebar_label: Security & legal (hub) +description: Licenses, disclaimers, developer correspondence — links into wiki-import. +--- + +# Security & legal + +Curated links into imported pages (canonical text may live under `wiki-import/`). + +- [License / legal (EN)](/wiki/wiki-import/License-Legal) +- [Lizenz / Rechtliches (DE)](/wiki/wiki-import/Lizenz-Rechtliches) +- [Disclaimer (End user)](/wiki/wiki-import/EndUser/Reference/Disclaimer) +- [Brief an WASEKU](/wiki/wiki-import/Brief-an-WASEKU) / [Letter to WASEKU (EN)](/wiki/wiki-import/Letter-to-WASEKU) + +For repository licensing, see the root `LICENSE.txt` in the GitHub repo. diff --git a/topics/sponsors/overview.md b/topics/sponsors/overview.md new file mode 100644 index 0000000..5cd57d5 --- /dev/null +++ b/topics/sponsors/overview.md @@ -0,0 +1,24 @@ +--- +title: Sponsors & support +sidebar_label: Sponsors (hub) +description: Für Unterstützerinnen und Unterstützer — Sponsoring, Transparenz, englische Detailseiten. +--- + +# Sponsors & support + +Die Community finanziert u. a. Infrastruktur, Tools und Zeit für Pflege der Doku und des Frameworks. Hier startest du als **potenzielle Sponsorin oder Sponsor** (oder als Spielerin, die nur informieren möchte, wohin Unterstützung fließt). + +## Kurzüberblick + +- **Englisch (ausführlich):** [Sponsors (EN)](/wiki/wiki-import/Sponsors) — Optionen (z. B. GitHub Sponsors), Nutzung von Mitteln, rechtlicher Kontext. +- **Deutsch:** [Sponsoren](/wiki/wiki-import/Sponsoren) — gleiche Themen, wo vorhanden auf Deutsch. +- **Dank:** [Community Thanks](/wiki/wiki-import/Community-Thanks) (Import), [Changelog & versions](/wiki/wiki-import/Changelog-Versions) für Release-Transparenz. + +## Verknüpft + +- [Security & legal](/wiki/topics/security-legal/overview) — Lizenz, Haftungsausschlüsse, Brief an die Entwickler. +- [License & legal (EN)](/wiki/wiki-import/License-Legal) — rechtlicher Rahmen des Projekts. + +:::note Zielgruppe +Diese Seite richtet sich an **Sponsorinnen, Sponsoren und Fördernde**. Spielerinnen und Spieler finden den Einstieg unter [End users](/wiki/topics/end-user/overview); Mod-Autoren unter [Mod developers](/wiki/topics/mod-developers/overview); Mitwirkende am Repo unter [Contributors (workflow)](/wiki/topics/contributors/overview). +::: diff --git a/topics/wiki-import/overview.md b/topics/wiki-import/overview.md new file mode 100644 index 0000000..a8f9b8a --- /dev/null +++ b/topics/wiki-import/overview.md @@ -0,0 +1,52 @@ +--- +title: Legacy wiki import +sidebar_label: Legacy wiki import +description: How GitHub Wiki content is mirrored into docs/wiki-import, sorted in the sidebar, and kept in sync with the repo. +--- + +# Legacy wiki import + +All material under **`docs/wiki-import/`** is part of the **GitHub Wiki mirror**: it is meant to track the wiki that lives alongside the repository, while the rest of **`docs/`** holds **curated** pages (topics, mods, reference). + +For a **map of the whole `docs/` tree**, see **[Documentation layout (`docs/`)](../../README.md)**. + +## Why this exists + +- **Search & versioning**: Wiki text is in Git, reviewable in PRs, and indexed by the site. +- **i18n**: Paired `Page.md` (DE) + `Page-en.md` (EN) are split by `wiki:normalize-i18n` into the default locale and `wiki/i18n/de/...`. +- **Sorting**: Audience trees (`EndUser/`, `ModDevs/`, `Contributors/`, `TechnicalReference/`, `DataCenterFAQ/`) use `_category_.json` **positions** so the sidebar order matches intent, not only A–Z. Root **`Home`** uses `sidebar_position: 1` so it appears first among loose pages. + +## Keep the mirror up to date + +1. Clone or pull the wiki repo into **`.wiki/`** at the **repository root** (same level as `docs/`): + + ```bash + git clone https://github.com//.wiki.git .wiki + ``` + +2. From **`wiki/`** (the Docusaurus app): + + ```bash + npm run wiki:refresh + ``` + + This runs **`wiki:sync`** (copy `.wiki` → `docs/wiki-import/`) and then **`wiki:normalize-i18n`**. If `.wiki` is missing, `wiki:sync` exits with an error; run `wiki:normalize-i18n` alone only when you are fixing splits without pulling the wiki. + +3. Commit changes under **`docs/wiki-import/`** and **`wiki/i18n/de/...`** as needed, then open a PR. + +## Editorial policy (recommended) + +| Goal | Action | +|------|--------| +| **Short-term fix** on the live site | Edit files under `docs/wiki-import/` (and DE mirror if applicable). Optionally backport to `.wiki` so the next sync does not overwrite you. | +| **Canonical, long-term doc** | Add or move content into **`docs/topics/`**, **`docs/reference/`**, or **`docs/mods/`**, and link from the legacy page. | +| **Duplicate path** | The folder **`Contirbutors/`** is a typo duplicate of **`Contributors/`**; prefer **`Contributors/`** for new links. | + +## Entry points (English default locale) + +- [Home](/wiki/wiki-import/Home) — audience hub. +- [End users](/wiki/wiki-import/EndUser/) · [Mod developers](/wiki/wiki-import/ModDevs/) · [Contributors](/wiki/wiki-import/Contributors/) +- [Technical reference](/wiki/wiki-import/TechnicalReference/) · [Data Center FAQ](/wiki/wiki-import/DataCenterFAQ/) +- [HOOKS](/wiki/wiki-import/HOOKS) · [FFI bridge reference](/wiki/wiki-import/FFI-Bridge-Reference) + +The sidebar **Topics → Legacy wiki import** lists the **full** tree (autogenerated), not only these links. diff --git a/wiki-import/AI-USAGE.md b/wiki-import/AI-USAGE.md new file mode 100644 index 0000000..b4ab02e --- /dev/null +++ b/wiki-import/AI-USAGE.md @@ -0,0 +1,9 @@ +--- +title: AI-USAGE +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Architecture.md b/wiki-import/Architecture.md new file mode 100644 index 0000000..cda72c2 --- /dev/null +++ b/wiki-import/Architecture.md @@ -0,0 +1,9 @@ +--- +title: Architecture +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/AssetExport.md b/wiki-import/AssetExport.md new file mode 100644 index 0000000..3b143c3 --- /dev/null +++ b/wiki-import/AssetExport.md @@ -0,0 +1,9 @@ +--- +title: AssetExport +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Bekannte-Inkompatibilitaeten.md b/wiki-import/Bekannte-Inkompatibilitaeten.md new file mode 100644 index 0000000..79850da --- /dev/null +++ b/wiki-import/Bekannte-Inkompatibilitaeten.md @@ -0,0 +1,9 @@ +--- +title: Bekannte Inkompatibilitäten +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Brief-an-WASEKU.md b/wiki-import/Brief-an-WASEKU.md new file mode 100644 index 0000000..718ac28 --- /dev/null +++ b/wiki-import/Brief-an-WASEKU.md @@ -0,0 +1,9 @@ +--- +title: Brief an WASEKU (Data Center) +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Changelog-Versionen.md b/wiki-import/Changelog-Versionen.md new file mode 100644 index 0000000..f82a1e6 --- /dev/null +++ b/wiki-import/Changelog-Versionen.md @@ -0,0 +1,9 @@ +--- +title: Changelog & Versionen +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Changelog-Versions.md b/wiki-import/Changelog-Versions.md new file mode 100644 index 0000000..e1a423e --- /dev/null +++ b/wiki-import/Changelog-Versions.md @@ -0,0 +1,9 @@ +--- +title: Changelog & Versions EN +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Community-Thanks.md b/wiki-import/Community-Thanks.md new file mode 100644 index 0000000..e3b5a0f --- /dev/null +++ b/wiki-import/Community-Thanks.md @@ -0,0 +1,9 @@ +--- +title: Community Thanks +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Contirbutors/Contributors-Debug.md b/wiki-import/Contirbutors/Contributors-Debug.md new file mode 100644 index 0000000..4b259e0 --- /dev/null +++ b/wiki-import/Contirbutors/Contributors-Debug.md @@ -0,0 +1,9 @@ +--- +title: Contributors (Debug) EN +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Contirbutors/_category_.json b/wiki-import/Contirbutors/_category_.json new file mode 100644 index 0000000..f1f28cb --- /dev/null +++ b/wiki-import/Contirbutors/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Contributors (typo path — prefer Contributors/)", + "position": 60, + "key": "wiki-import-root-contributors-typo" +} diff --git a/wiki-import/Contributors-Debug.md b/wiki-import/Contributors-Debug.md new file mode 100644 index 0000000..af241ea --- /dev/null +++ b/wiki-import/Contributors-Debug.md @@ -0,0 +1,9 @@ +--- +title: Contributors-Debug +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Contributors/Contributors-Debug.md b/wiki-import/Contributors/Contributors-Debug.md new file mode 100644 index 0000000..4b259e0 --- /dev/null +++ b/wiki-import/Contributors/Contributors-Debug.md @@ -0,0 +1,9 @@ +--- +title: Contributors (Debug) EN +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Contributors/Guides/Contribution-Workflow.md b/wiki-import/Contributors/Guides/Contribution-Workflow.md new file mode 100644 index 0000000..eb19421 --- /dev/null +++ b/wiki-import/Contributors/Guides/Contribution-Workflow.md @@ -0,0 +1,9 @@ +--- +title: Contribution-Workflow +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Contributors/Guides/Index.md b/wiki-import/Contributors/Guides/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/Contributors/Guides/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Contributors/Guides/_category_.json b/wiki-import/Contributors/Guides/_category_.json new file mode 100644 index 0000000..b2d26ee --- /dev/null +++ b/wiki-import/Contributors/Guides/_category_.json @@ -0,0 +1,3 @@ +{ + "key": "wiki-import-contributors-guides" +} diff --git a/wiki-import/Contributors/Index.md b/wiki-import/Contributors/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/Contributors/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Contributors/Reference/Index.md b/wiki-import/Contributors/Reference/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/Contributors/Reference/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Contributors/Reference/Naming-Convention.md b/wiki-import/Contributors/Reference/Naming-Convention.md new file mode 100644 index 0000000..4e82ef8 --- /dev/null +++ b/wiki-import/Contributors/Reference/Naming-Convention.md @@ -0,0 +1,9 @@ +--- +title: Naming-Convention +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Contributors/Reference/_category_.json b/wiki-import/Contributors/Reference/_category_.json new file mode 100644 index 0000000..1c22f38 --- /dev/null +++ b/wiki-import/Contributors/Reference/_category_.json @@ -0,0 +1,3 @@ +{ + "key": "wiki-import-contributors-reference" +} diff --git a/wiki-import/Contributors/Troubleshooting/Index.md b/wiki-import/Contributors/Troubleshooting/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/Contributors/Troubleshooting/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Contributors/Troubleshooting/_category_.json b/wiki-import/Contributors/Troubleshooting/_category_.json new file mode 100644 index 0000000..d86d79e --- /dev/null +++ b/wiki-import/Contributors/Troubleshooting/_category_.json @@ -0,0 +1,3 @@ +{ + "key": "wiki-import-contributors-troubleshooting" +} diff --git a/wiki-import/Contributors/Troubleshooting/overview.md b/wiki-import/Contributors/Troubleshooting/overview.md new file mode 100644 index 0000000..b00d0b1 --- /dev/null +++ b/wiki-import/Contributors/Troubleshooting/overview.md @@ -0,0 +1,9 @@ +--- +title: overview +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Contributors/_category_.json b/wiki-import/Contributors/_category_.json new file mode 100644 index 0000000..7800653 --- /dev/null +++ b/wiki-import/Contributors/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Contributors", + "position": 30, + "key": "wiki-import-root-contributors" +} diff --git a/wiki-import/DataCenterFAQ/Index.md b/wiki-import/DataCenterFAQ/Index.md new file mode 100644 index 0000000..dbe10be --- /dev/null +++ b/wiki-import/DataCenterFAQ/Index.md @@ -0,0 +1,9 @@ +--- +title: Data Center FAQ +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/DataCenterFAQ/Part-1.md b/wiki-import/DataCenterFAQ/Part-1.md new file mode 100644 index 0000000..fd0492d --- /dev/null +++ b/wiki-import/DataCenterFAQ/Part-1.md @@ -0,0 +1,9 @@ +--- +title: Frequently Asked Questions Part 1 +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/DataCenterFAQ/Part-2.md b/wiki-import/DataCenterFAQ/Part-2.md new file mode 100644 index 0000000..b365b4e --- /dev/null +++ b/wiki-import/DataCenterFAQ/Part-2.md @@ -0,0 +1,9 @@ +--- +title: Frequently Asked Questions Part 2 +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/DataCenterFAQ/Part-3.md b/wiki-import/DataCenterFAQ/Part-3.md new file mode 100644 index 0000000..1fcbac3 --- /dev/null +++ b/wiki-import/DataCenterFAQ/Part-3.md @@ -0,0 +1,9 @@ +--- +title: Frequently Asked Questions Part 3 +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/DataCenterFAQ/Patch-Panel.md b/wiki-import/DataCenterFAQ/Patch-Panel.md new file mode 100644 index 0000000..ff89c67 --- /dev/null +++ b/wiki-import/DataCenterFAQ/Patch-Panel.md @@ -0,0 +1,9 @@ +--- +title: Patch Panel +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/DataCenterFAQ/_category_.json b/wiki-import/DataCenterFAQ/_category_.json new file mode 100644 index 0000000..755e790 --- /dev/null +++ b/wiki-import/DataCenterFAQ/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Data Center FAQ", + "position": 50, + "key": "wiki-import-root-dc-faq" +} diff --git a/wiki-import/Device-Reference.md b/wiki-import/Device-Reference.md new file mode 100644 index 0000000..b466317 --- /dev/null +++ b/wiki-import/Device-Reference.md @@ -0,0 +1,9 @@ +--- +title: Device-Reference +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/End-User-Release.md b/wiki-import/End-User-Release.md new file mode 100644 index 0000000..70ba95e --- /dev/null +++ b/wiki-import/End-User-Release.md @@ -0,0 +1,9 @@ +--- +title: End-User-Release +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/EndUser/End-User-Release.md b/wiki-import/EndUser/End-User-Release.md new file mode 100644 index 0000000..f6f67d6 --- /dev/null +++ b/wiki-import/EndUser/End-User-Release.md @@ -0,0 +1,9 @@ +--- +title: End-User (Release) EN +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/EndUser/Guides/Framework-Dependency.md b/wiki-import/EndUser/Guides/Framework-Dependency.md new file mode 100644 index 0000000..e5baa85 --- /dev/null +++ b/wiki-import/EndUser/Guides/Framework-Dependency.md @@ -0,0 +1,9 @@ +--- +title: Framework-Dependency +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/EndUser/Guides/Index.md b/wiki-import/EndUser/Guides/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/EndUser/Guides/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/EndUser/Guides/_category_.json b/wiki-import/EndUser/Guides/_category_.json new file mode 100644 index 0000000..9264a97 --- /dev/null +++ b/wiki-import/EndUser/Guides/_category_.json @@ -0,0 +1,3 @@ +{ + "key": "wiki-import-enduser-guides" +} diff --git a/wiki-import/EndUser/Index.md b/wiki-import/EndUser/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/EndUser/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/EndUser/Reference/Disclaimer.md b/wiki-import/EndUser/Reference/Disclaimer.md new file mode 100644 index 0000000..895e3dc --- /dev/null +++ b/wiki-import/EndUser/Reference/Disclaimer.md @@ -0,0 +1,9 @@ +--- +title: Disclaimer +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/EndUser/Reference/Index.md b/wiki-import/EndUser/Reference/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/EndUser/Reference/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/EndUser/Reference/_category_.json b/wiki-import/EndUser/Reference/_category_.json new file mode 100644 index 0000000..5d3cab7 --- /dev/null +++ b/wiki-import/EndUser/Reference/_category_.json @@ -0,0 +1,3 @@ +{ + "key": "wiki-import-enduser-reference" +} diff --git a/wiki-import/EndUser/Troubleshooting/FAQ.md b/wiki-import/EndUser/Troubleshooting/FAQ.md new file mode 100644 index 0000000..ebb6dbd --- /dev/null +++ b/wiki-import/EndUser/Troubleshooting/FAQ.md @@ -0,0 +1,9 @@ +--- +title: FAQ +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/EndUser/Troubleshooting/Index.md b/wiki-import/EndUser/Troubleshooting/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/EndUser/Troubleshooting/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/EndUser/Troubleshooting/_category_.json b/wiki-import/EndUser/Troubleshooting/_category_.json new file mode 100644 index 0000000..9d296a4 --- /dev/null +++ b/wiki-import/EndUser/Troubleshooting/_category_.json @@ -0,0 +1,3 @@ +{ + "key": "wiki-import-enduser-troubleshooting" +} diff --git a/wiki-import/EndUser/Troubleshooting/overview.md b/wiki-import/EndUser/Troubleshooting/overview.md new file mode 100644 index 0000000..b00d0b1 --- /dev/null +++ b/wiki-import/EndUser/Troubleshooting/overview.md @@ -0,0 +1,9 @@ +--- +title: overview +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/EndUser/_category_.json b/wiki-import/EndUser/_category_.json new file mode 100644 index 0000000..cf235ff --- /dev/null +++ b/wiki-import/EndUser/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "End users", + "position": 10, + "key": "wiki-import-root-enduser" +} diff --git a/wiki-import/FFI-Bridge-Reference.md b/wiki-import/FFI-Bridge-Reference.md new file mode 100644 index 0000000..07d6f46 --- /dev/null +++ b/wiki-import/FFI-Bridge-Reference.md @@ -0,0 +1,9 @@ +--- +title: FFI-Bridge-Reference +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Framework-Features-Use-Cases.md b/wiki-import/Framework-Features-Use-Cases.md new file mode 100644 index 0000000..bb39f29 --- /dev/null +++ b/wiki-import/Framework-Features-Use-Cases.md @@ -0,0 +1,9 @@ +--- +title: Framework-Features-Use-Cases +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Glossar.md b/wiki-import/Glossar.md new file mode 100644 index 0000000..df1c5d0 --- /dev/null +++ b/wiki-import/Glossar.md @@ -0,0 +1,9 @@ +--- +title: Glossar +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Glossary.md b/wiki-import/Glossary.md new file mode 100644 index 0000000..8e4ec82 --- /dev/null +++ b/wiki-import/Glossary.md @@ -0,0 +1,9 @@ +--- +title: Glossary EN +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/HOOK-NAMING-CONVENTION.md b/wiki-import/HOOK-NAMING-CONVENTION.md new file mode 100644 index 0000000..8e94da3 --- /dev/null +++ b/wiki-import/HOOK-NAMING-CONVENTION.md @@ -0,0 +1,9 @@ +--- +title: HOOK-NAMING-CONVENTION +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/HOOKS.md b/wiki-import/HOOKS.md new file mode 100644 index 0000000..b7e9394 --- /dev/null +++ b/wiki-import/HOOKS.md @@ -0,0 +1,9 @@ +--- +title: HOOKS +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Home.md b/wiki-import/Home.md new file mode 100644 index 0000000..e44378e --- /dev/null +++ b/wiki-import/Home.md @@ -0,0 +1,9 @@ +--- +title: Home +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Known-Incompatibilities.md b/wiki-import/Known-Incompatibilities.md new file mode 100644 index 0000000..e873994 --- /dev/null +++ b/wiki-import/Known-Incompatibilities.md @@ -0,0 +1,9 @@ +--- +title: Known Incompatibilities EN +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Letter-to-WASEKU.md b/wiki-import/Letter-to-WASEKU.md new file mode 100644 index 0000000..83f7d21 --- /dev/null +++ b/wiki-import/Letter-to-WASEKU.md @@ -0,0 +1,9 @@ +--- +title: Letter to WASEKU EN +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/License-Legal.md b/wiki-import/License-Legal.md new file mode 100644 index 0000000..dd48157 --- /dev/null +++ b/wiki-import/License-Legal.md @@ -0,0 +1,9 @@ +--- +title: License & Legal EN +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Lizenz-Rechtliches.md b/wiki-import/Lizenz-Rechtliches.md new file mode 100644 index 0000000..b666fe3 --- /dev/null +++ b/wiki-import/Lizenz-Rechtliches.md @@ -0,0 +1,9 @@ +--- +title: Lizenz & Rechtliches +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Lua-FFI-Start-Developing.md b/wiki-import/Lua-FFI-Start-Developing.md new file mode 100644 index 0000000..06167e3 --- /dev/null +++ b/wiki-import/Lua-FFI-Start-Developing.md @@ -0,0 +1,9 @@ +--- +title: Lua-FFI-Start-Developing +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/MODIAPI_FINAL_STATUS.md b/wiki-import/MODIAPI_FINAL_STATUS.md new file mode 100644 index 0000000..aa32e7e --- /dev/null +++ b/wiki-import/MODIAPI_FINAL_STATUS.md @@ -0,0 +1,9 @@ +--- +title: MODIAPI_FINAL_STATUS +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/MODIAPI_INTEGRATION_SUMMARY.md b/wiki-import/MODIAPI_INTEGRATION_SUMMARY.md new file mode 100644 index 0000000..fa6ba1e --- /dev/null +++ b/wiki-import/MODIAPI_INTEGRATION_SUMMARY.md @@ -0,0 +1,9 @@ +--- +title: MODIAPI_INTEGRATION_SUMMARY +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/MelonLoader.md b/wiki-import/MelonLoader.md new file mode 100644 index 0000000..2cee812 --- /dev/null +++ b/wiki-import/MelonLoader.md @@ -0,0 +1,9 @@ +--- +title: MelonLoader +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Mod-Config-System.md b/wiki-import/Mod-Config-System.md new file mode 100644 index 0000000..86559d7 --- /dev/null +++ b/wiki-import/Mod-Config-System.md @@ -0,0 +1,9 @@ +--- +title: Mod-Config-System +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Mod-Developer-Debug.md b/wiki-import/Mod-Developer-Debug.md new file mode 100644 index 0000000..2121bd8 --- /dev/null +++ b/wiki-import/Mod-Developer-Debug.md @@ -0,0 +1,9 @@ +--- +title: Mod-Developer-Debug +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/ModDevs/Guides/Getting-Started.md b/wiki-import/ModDevs/Guides/Getting-Started.md new file mode 100644 index 0000000..b7a265d --- /dev/null +++ b/wiki-import/ModDevs/Guides/Getting-Started.md @@ -0,0 +1,9 @@ +--- +title: Getting-Started +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/ModDevs/Guides/Index.md b/wiki-import/ModDevs/Guides/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/ModDevs/Guides/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/ModDevs/Guides/_category_.json b/wiki-import/ModDevs/Guides/_category_.json new file mode 100644 index 0000000..6061c4c --- /dev/null +++ b/wiki-import/ModDevs/Guides/_category_.json @@ -0,0 +1,3 @@ +{ + "key": "wiki-import-moddevs-guides" +} diff --git a/wiki-import/ModDevs/Index.md b/wiki-import/ModDevs/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/ModDevs/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/ModDevs/Mod-Developer-Debug.md b/wiki-import/ModDevs/Mod-Developer-Debug.md new file mode 100644 index 0000000..14e4c11 --- /dev/null +++ b/wiki-import/ModDevs/Mod-Developer-Debug.md @@ -0,0 +1,9 @@ +--- +title: Mod-Developer (Debug) EN +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/ModDevs/Modding-Guide.md b/wiki-import/ModDevs/Modding-Guide.md new file mode 100644 index 0000000..ccdff77 --- /dev/null +++ b/wiki-import/ModDevs/Modding-Guide.md @@ -0,0 +1,9 @@ +--- +title: Modding-Guide +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/ModDevs/Reference/Hook-Event-Reference.md b/wiki-import/ModDevs/Reference/Hook-Event-Reference.md new file mode 100644 index 0000000..2c70c2f --- /dev/null +++ b/wiki-import/ModDevs/Reference/Hook-Event-Reference.md @@ -0,0 +1,9 @@ +--- +title: Hook-Event-Reference +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/ModDevs/Reference/Index.md b/wiki-import/ModDevs/Reference/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/ModDevs/Reference/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/ModDevs/Reference/_category_.json b/wiki-import/ModDevs/Reference/_category_.json new file mode 100644 index 0000000..337f76e --- /dev/null +++ b/wiki-import/ModDevs/Reference/_category_.json @@ -0,0 +1,3 @@ +{ + "key": "wiki-import-moddevs-reference" +} diff --git a/wiki-import/ModDevs/Troubleshooting/Index.md b/wiki-import/ModDevs/Troubleshooting/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/ModDevs/Troubleshooting/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/ModDevs/Troubleshooting/_category_.json b/wiki-import/ModDevs/Troubleshooting/_category_.json new file mode 100644 index 0000000..0979ace --- /dev/null +++ b/wiki-import/ModDevs/Troubleshooting/_category_.json @@ -0,0 +1,3 @@ +{ + "key": "wiki-import-moddevs-troubleshooting" +} diff --git a/wiki-import/ModDevs/Troubleshooting/overview.md b/wiki-import/ModDevs/Troubleshooting/overview.md new file mode 100644 index 0000000..b00d0b1 --- /dev/null +++ b/wiki-import/ModDevs/Troubleshooting/overview.md @@ -0,0 +1,9 @@ +--- +title: overview +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/ModDevs/_category_.json b/wiki-import/ModDevs/_category_.json new file mode 100644 index 0000000..f44538f --- /dev/null +++ b/wiki-import/ModDevs/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Mod developers", + "position": 20, + "key": "wiki-import-root-moddevs" +} diff --git a/wiki-import/Modding-Guide.md b/wiki-import/Modding-Guide.md new file mode 100644 index 0000000..ccdff77 --- /dev/null +++ b/wiki-import/Modding-Guide.md @@ -0,0 +1,9 @@ +--- +title: Modding-Guide +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/ModigAPI-Consolidation.md b/wiki-import/ModigAPI-Consolidation.md new file mode 100644 index 0000000..cba32da --- /dev/null +++ b/wiki-import/ModigAPI-Consolidation.md @@ -0,0 +1,9 @@ +--- +title: ModigAPI-Consolidation +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/README_MODDING.md b/wiki-import/README_MODDING.md new file mode 100644 index 0000000..83e565c --- /dev/null +++ b/wiki-import/README_MODDING.md @@ -0,0 +1,9 @@ +--- +title: README_MODDING +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/ROADMAP.md b/wiki-import/ROADMAP.md new file mode 100644 index 0000000..a39d2ff --- /dev/null +++ b/wiki-import/ROADMAP.md @@ -0,0 +1,9 @@ +--- +title: ROADMAP +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Release-Assets-and-Templates.md b/wiki-import/Release-Assets-and-Templates.md new file mode 100644 index 0000000..a81d1d9 --- /dev/null +++ b/wiki-import/Release-Assets-and-Templates.md @@ -0,0 +1,9 @@ +--- +title: Release Assets and Templates +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Repository-Status-2026-04-04.md b/wiki-import/Repository-Status-2026-04-04.md new file mode 100644 index 0000000..bc901ef --- /dev/null +++ b/wiki-import/Repository-Status-2026-04-04.md @@ -0,0 +1,9 @@ +--- +title: Repository Status 2026-04-04 +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Setup.md b/wiki-import/Setup.md new file mode 100644 index 0000000..d567901 --- /dev/null +++ b/wiki-import/Setup.md @@ -0,0 +1,9 @@ +--- +title: Setup +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Sponsoren.md b/wiki-import/Sponsoren.md new file mode 100644 index 0000000..6b4e6a6 --- /dev/null +++ b/wiki-import/Sponsoren.md @@ -0,0 +1,9 @@ +--- +title: Sponsoren +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Sponsors.md b/wiki-import/Sponsors.md new file mode 100644 index 0000000..83ae6bd --- /dev/null +++ b/wiki-import/Sponsors.md @@ -0,0 +1,9 @@ +--- +title: Sponsors EN +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/StandaloneMods.md b/wiki-import/StandaloneMods.md new file mode 100644 index 0000000..ec8a08b --- /dev/null +++ b/wiki-import/StandaloneMods.md @@ -0,0 +1,9 @@ +--- +title: Standalone Mods +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Steamworks-P2P-Multiplayer-Roadmap.md b/wiki-import/Steamworks-P2P-Multiplayer-Roadmap.md new file mode 100644 index 0000000..4544cf1 --- /dev/null +++ b/wiki-import/Steamworks-P2P-Multiplayer-Roadmap.md @@ -0,0 +1,9 @@ +--- +title: Steamworks-P2P-Multiplayer-Roadmap +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/TASKLIST.md b/wiki-import/TASKLIST.md new file mode 100644 index 0000000..b736074 --- /dev/null +++ b/wiki-import/TASKLIST.md @@ -0,0 +1,9 @@ +--- +title: TASKLIST +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/TechnicalReference/Guides/Index.md b/wiki-import/TechnicalReference/Guides/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/TechnicalReference/Guides/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/TechnicalReference/Guides/_category_.json b/wiki-import/TechnicalReference/Guides/_category_.json new file mode 100644 index 0000000..1eb988d --- /dev/null +++ b/wiki-import/TechnicalReference/Guides/_category_.json @@ -0,0 +1,3 @@ +{ + "key": "wiki-import-techref-guides" +} diff --git a/wiki-import/TechnicalReference/Index.md b/wiki-import/TechnicalReference/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/TechnicalReference/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/TechnicalReference/Reference/Index.md b/wiki-import/TechnicalReference/Reference/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/TechnicalReference/Reference/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/TechnicalReference/Reference/_category_.json b/wiki-import/TechnicalReference/Reference/_category_.json new file mode 100644 index 0000000..8c6c7d5 --- /dev/null +++ b/wiki-import/TechnicalReference/Reference/_category_.json @@ -0,0 +1,3 @@ +{ + "key": "wiki-import-techref-reference" +} diff --git a/wiki-import/TechnicalReference/Troubleshooting/Index.md b/wiki-import/TechnicalReference/Troubleshooting/Index.md new file mode 100644 index 0000000..5466304 --- /dev/null +++ b/wiki-import/TechnicalReference/Troubleshooting/Index.md @@ -0,0 +1,9 @@ +--- +title: Index +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/TechnicalReference/Troubleshooting/_category_.json b/wiki-import/TechnicalReference/Troubleshooting/_category_.json new file mode 100644 index 0000000..fba851c --- /dev/null +++ b/wiki-import/TechnicalReference/Troubleshooting/_category_.json @@ -0,0 +1,3 @@ +{ + "key": "wiki-import-techref-troubleshooting" +} diff --git a/wiki-import/TechnicalReference/_category_.json b/wiki-import/TechnicalReference/_category_.json new file mode 100644 index 0000000..510e2bd --- /dev/null +++ b/wiki-import/TechnicalReference/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Technical reference", + "position": 40, + "key": "wiki-import-root-techref" +} diff --git a/wiki-import/WIKI-MOVED.md b/wiki-import/WIKI-MOVED.md new file mode 100644 index 0000000..43d244c --- /dev/null +++ b/wiki-import/WIKI-MOVED.md @@ -0,0 +1,9 @@ +--- +title: WIKI-MOVED +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/Web-UI-Bridge.md b/wiki-import/Web-UI-Bridge.md new file mode 100644 index 0000000..8004e0a --- /dev/null +++ b/wiki-import/Web-UI-Bridge.md @@ -0,0 +1,9 @@ +--- +title: Web-UI-Bridge +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/_Sidebar.md b/wiki-import/_Sidebar.md new file mode 100644 index 0000000..66b93a9 --- /dev/null +++ b/wiki-import/_Sidebar.md @@ -0,0 +1,9 @@ +--- +title: _Sidebar +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/wiki-import/ui.md b/wiki-import/ui.md new file mode 100644 index 0000000..7f5d6d1 --- /dev/null +++ b/wiki-import/ui.md @@ -0,0 +1,9 @@ +--- +title: ui +description: English translation pending; use the Deutsch locale for the full legacy page. +--- + +:::note +This page is available in **German** in the legacy wiki import. Use the language menu (**Deutsch**) for the full text, or contribute an English translation under `docs/wiki-import`. +::: + diff --git a/workshop-uploader.md b/workshop-uploader.md new file mode 100644 index 0000000..8c35df3 --- /dev/null +++ b/workshop-uploader.md @@ -0,0 +1,755 @@ +--- +title: WorkshopManager +sidebar_label: WorkshopManager +description: Windows desktop app for managing Steam Workshop content, browsing mods, and publishing for Data Center (FrikaMF). +--- + +# WorkshopManager + +**WorkshopManager** is a **.NET MAUI** desktop app for **Windows** that serves as a full-featured Steam Workshop client for *Data Center*. It combines content authoring (publish/update), a **Mod Store** for browsing and installing community mods, and a **Mod Manager** for dependency health checks. + +## Features + +### Author tools (Projects / Editor / My Uploads) +- Create workshop projects from templates (vanilla assets, MelonLoader mods, FMF plugins). +- Edit **title**, **description** (with BBCode toolbar), **visibility**, **tags**, and **preview image**; stored in `metadata.json`. +- **BBCode formatting toolbar** — insert Steam-compatible formatting (bold, italic, headings, URL, image, list, code, quote, spoiler, horizontal rule, table) directly in the description editor. +- **Save and upload to Steam** — saves metadata, publishes `content/` to Steam Workshop, then **syncs** your local folder with Steam's version (like a `git pull` after push). +- **Image sync** — after publishing, the preview image and gallery screenshots are re-downloaded from Steam so your local project always matches what's live. +- **Additional screenshots** — attach up to 9 gallery images that appear on the Steam Workshop page alongside the main preview. +- **Change notes** — required for the first publish, recommended for updates; shown on the Workshop changelog tab. +- **Upload readiness check** — validates title, description, content folder, preview image, tags, content size, and changelog before allowing upload. +- **View on Steam** — open your published item in the browser. +- **Export content as ZIP** — create a distributable archive of the content folder. +- **Pagination** across all list views (My Uploads, Browse, Subscribed, Favorites). +- **Per-item stats** — subscriptions, votes, score, size. + +### Mod Store (integrated in Mod Manager) +- **Browse** all Workshop items for Data Center with tag filtering and sort options (last updated, newest, top rated, trending, most subscribed, title A-Z). +- **Search** mods by text. +- **Subscribe / Unsubscribe** to mods directly from the store. +- **Favorites** — bookmark mods for quick access. +- **Vote** (up/down) on workshop items. +- **Item detail view** — full statistics, description, tags, gallery images, action buttons, and links to changelog/comments. + +### Mod Manager / Health +- **Dependency health** checks: game installed, MelonLoader, Il2Cpp assemblies, FMF core, FMF plugins directory, ModCfg directory. +- **MelonLoader** download page link and game folder access. +- **FMF Plugin channels** — stable (local scan) and beta (server, TODO). + +### Settings +- **Workspace path** — change where projects are stored (defaults to `/workshop`). +- **Language** — switch between EN, DE, RU, ES, IT, JP, PL, CN (defaults to system language). Restart button applies the change instantly. +- **Mod Store toggle** — enable/disable the Mod Store tab (disabled by default). Restart button included. +- **Community links** — quick access to Discord, Modding Channel, and GregFramework.eu. + +### FMF dependency notice +- Projects can be marked as **"Needs FrikaModFramework"** in the editor. When uploading, a notice is automatically appended to the Steam description telling users to install FMF. + +## Requirements + +- **Windows 10** (version 1809+). +- **Steam** with a signed-in account that **owns Data Center** (App ID **4170200**). +- **No additional dependencies** — the release is fully self-contained (includes .NET runtime and Windows App SDK). +- `steam_appid.txt` must be next to the executable (included in the release build). The app loads **`steam_api64.dll`** from **`Data Center_Data/Plugins/x86_64/`** in the Data Center install when possible; otherwise it uses the copy shipped next to the uploader. + +## Paths and directories {#paths} + +### Workspace path (your authoring projects) + +The workspace is resolved from Steam: `/workshop`. + +Fallback (no Steam): `%USERPROFILE%\DataCenterWS`. + +You can change this in **Settings → Workspace path**. + +On first launch the app creates the structure and places a sample `metadata.sample.json` under `.templates/`. + +### Where Steam installs subscribed Workshop mods {#steam-install-path} + +When a user **subscribes** to a Workshop item, Steam downloads it to: + +``` +\steamapps\workshop\content\4170200\\ +``` + +For example, if Steam is installed in the default location and the item ID is `123456789`: + +``` +C:\Program Files (x86)\Steam\steamapps\workshop\content\4170200\123456789\ +``` + +If you have multiple Steam library folders (e.g. on a different drive), the path follows that library's location: + +``` +D:\SteamLibrary\steamapps\workshop\content\4170200\123456789\ +``` + +The game reads from this folder at startup. The exact path per item can be inspected in the Steamworks API via `ugc.Directory` after calling `DownloadAsync`. + +:::info Finding your Steam library folder +Open Steam → **Settings → Storage** to see all configured library folders and which drive each uses. +::: + +### Where the game is installed + +The game install directory is typically: + +``` +C:\Program Files (x86)\Steam\steamapps\common\Data Center\ +``` + +The app resolves this automatically via `SteamApps.AppInstallDir(4170200)`. + +## Native mod format (config.json) {#native-mod-format} + +Data Center has a **built-in mod system** (`ModLoader`) that loads mods from `config.json` files. This is the "vanilla" modding approach — **no MelonLoader or FMF required**. + +### How the ModLoader works (internal flow) {#modloader-internals} + +The game's `ModLoader` (a Unity `MonoBehaviour`) runs the following on startup: + +1. **`SyncWorkshopThenLoadAll()`** — a coroutine that: + - Queries all subscribed Steam Workshop items (`PublishedFileId_t[]`) + - Waits for downloads to complete (with timeout) + - Copies each Workshop item folder into `StreamingAssets/Mods/workshop_` via `CopyDirectory()` +2. **`LoadAllMods()`** — scans all subdirectories in the mods path: + - Calls `LoadModPack(folderPath)` for each mod folder + - Reads `config.json`, then for each entry calls: + - `LoadShopItem()` → `CreateShopTemplate()` + `CreateShopButton()` for purchasable items + - `LoadStaticItem()` → `CreateStaticInstance()` for fixed-position decorations + +:::warning DLL loading timing +The `LoadDll()` method exists in the `ModLoader` but **object assets (shopItems, staticItems) are loaded at game startup**. DLL-based mods (`dlls[]` in config.json) are loaded later at **SaveLoad runtime**, not during the initial `LoadAllMods` pass. This means DLL plugins are not available until a save is loaded. +::: + +Internal state tracked by `ModLoader`: +- `modTemplates` — `Dictionary` of loaded shop item prefabs by mod ID +- `modTemplatesByFolder` — `Dictionary` of prefabs by folder name +- `staticInstances` — `List` of instantiated static decorations +- `loadedPlugins` — `List` of loaded DLL plugins +- `nextModID` — auto-incrementing ID counter + +### Mod location (manual install) + +Mods are placed under the game's streaming assets: + +``` +Data Center_Data\StreamingAssets\Mods\\ +``` + +Each mod has its own folder with a `config.json` and its asset files. + +### Mod location (Workshop subscribers) + +When delivered via Steam Workshop, the game's `ModLoader.SyncWorkshopThenLoadAll()` automatically **copies** subscribed item content from the Workshop download folder into `StreamingAssets/Mods/workshop_`: + +``` +steamapps\workshop\content\4170200\\ + → copied to → +Data Center_Data\StreamingAssets\Mods\workshop_\ +``` + +The `workshop_` prefix distinguishes Workshop-downloaded mods from manually installed ones. + +### config.json reference {#config-json} + +```json +{ + "modName": "My Cool Mod", + "shopItems": [ + { + "itemName": "Custom Server", + "price": 500, + "xpToUnlock": 0, + "sizeInU": 2, + "mass": 5.0, + "modelScale": 1.0, + "colliderSize": [0.5, 0.5, 0.5], + "colliderCenter": [0.0, 0.0, 0.0], + "modelFile": "server.obj", + "textureFile": "server.png", + "iconFile": "server_icon.png", + "objectType": 0 + } + ], + "staticItems": [ + { + "itemName": "Wall Poster", + "modelFile": "poster.obj", + "textureFile": "poster.png", + "modelScale": 1.0, + "colliderSize": [0.5, 0.5, 0.05], + "colliderCenter": [0.0, 0.0, 0.0], + "position": [2.0, 1.5, -3.0], + "rotation": [0.0, 90.0, 0.0] + } + ], + "dlls": [ + { + "fileName": "MyModPlugin.dll", + "entryClass": "MyModPlugin.Plugin" + } + ] +} +``` + +### config.json fields + +| Field | Description | +|-------|-------------| +| `modName` | Display name of the mod | +| `shopItems[]` | Purchasable objects that appear in the in-game shop | +| `staticItems[]` | Decorations placed at fixed world positions | +| `dlls[]` | Native plugin DLLs (see [DLL mods without MelonLoader](#native-dll-mods)) | + +### shopItems fields + +| Field | Type | Description | +|-------|------|-------------| +| `itemName` | `string` | Display name in the shop | +| `price` | `int` | Purchase price | +| `xpToUnlock` | `int` | XP threshold to unlock (0 = immediately available) | +| `sizeInU` | `int` | Rack unit size of the object | +| `mass` | `float` | Object mass | +| `modelScale` | `float` | Scale multiplier for the 3D model | +| `colliderSize` | `[x, y, z]` | Physics collider dimensions | +| `colliderCenter` | `[x, y, z]` | Physics collider offset | +| `modelFile` | `string` | Path to `.obj` model file (loaded via `LoadMesh`) | +| `textureFile` | `string` | Path to `.png` texture file (loaded via `CreateMaterial`) | +| `iconFile` | `string` | Path to `.png` shop icon (loaded via `LoadIcon`) | +| `objectType` | `int` | `0` = passive object | + +The `ModShopItem` UI component displays each shop item with `itemIcon`, `txtName`, and `txtPrice`. Purchasing calls `ButtonBuyItem()`. + +### staticItems fields + +| Field | Type | Description | +|-------|------|-------------| +| `itemName` | `string` | Display name | +| `modelFile` | `string` | Path to `.obj` model file | +| `textureFile` | `string` | Path to `.png` texture file | +| `modelScale` | `float` | Scale multiplier | +| `colliderSize` | `[x, y, z]` | Physics collider dimensions | +| `colliderCenter` | `[x, y, z]` | Physics collider offset | +| `position` | `[x, y, z]` | World position | +| `rotation` | `[x, y, z]` | Euler rotation in degrees | + +### Asset loading pipeline + +| Method | Input | Output | +|--------|-------|--------| +| `LoadMesh(folderPath, modelFile)` | `.obj` file | Unity `Mesh` | +| `CreateMaterial(folderPath, textureFile)` | `.png` file | Unity `Material` | +| `LoadIcon(folderPath, iconFile)` | `.png` file | Unity `Sprite` | +| `LoadTexture(path)` | `.png` file | Unity `Texture2D` | + +- **3D models**: Wavefront OBJ format (`.obj`) +- **Textures**: PNG format (`.png`) +- **Icons**: PNG format (`.png`) — shown in the shop UI +- All asset file paths in `config.json` are relative to the mod folder + +### DLL mods without MelonLoader {#native-dll-mods} + +The game's `ModLoader` has **built-in DLL loading** — no MelonLoader required. This is done via the `dlls` array in `config.json`: + +```json +{ + "modName": "My Plugin Mod", + "shopItems": [], + "staticItems": [], + "dlls": [ + { + "fileName": "MyPlugin.dll", + "entryClass": "MyPlugin.Plugin" + } + ] +} +``` + +**How it works internally:** +1. `ModLoader.LoadDll(folderPath, DllEntry)` loads the DLL from the mod folder +2. The `entryClass` must reference a class that implements the **`IModPlugin`** interface +3. Loaded plugins are stored in `ModLoader.loadedPlugins` (`List`) + +:::warning Loading timing +Unlike asset objects (`shopItems`, `staticItems`) which are loaded at game startup, **DLL mods are loaded at SaveLoad runtime** — i.e., when a save file is loaded, not when the game first starts. Your `IModPlugin` will not be instantiated until the player enters a save. +::: + +**DLL entry requirements:** + +| Field | Description | +|-------|-------------| +| `fileName` | The `.dll` file in the mod folder | +| `entryClass` | Fully qualified class name (e.g. `MyNamespace.MyPlugin`) that implements `IModPlugin` | + +:::caution Experimental feature +The developer has marked DLL loading as **untested**. The `LoadDll` method and `IModPlugin` interface exist in the game code, but stability is not guaranteed. A mod that uses only `shopItems` and `staticItems` (asset-only) is the safest approach. +::: + +**Advantages over MelonLoader:** +- No external framework needed — works with the vanilla game +- Distributed via Workshop like any other mod (just include the DLL in the mod folder) +- The game manages loading/unloading through its own lifecycle + +**Limitations:** +- The `IModPlugin` interface contract is not publicly documented — you need to reference the game's IL2CPP assemblies to implement it +- Less flexibility than MelonLoader's Harmony patching +- Only the entry class specified in `entryClass` is instantiated +- DLLs are loaded at SaveLoad time, not at game startup — no early initialization possible + +:::note Current limitations +For now, only **passive objects** are supported for shop items (`objectType: 0`). Active/functional item types may be added in future game updates. +::: + +### ModPathRedirector — Workshop download at MelonLoader init {#mod-path-redirector} + +**Release & download:** [FMF.ModPathRedirector Release](/wiki/releases/plugins/fmf-modpathredirector-release) — installation steps and GitHub download (`FMF.ModPathRedirector.dll`). + +The game loads native mods from `Data Center_Data\StreamingAssets\Mods\`. Workshop items are copied into subfolders named `workshop_` under that path (see [Steam install path](#steam-install-path)). + +**ModPathRedirector** is a **MelonLoader plugin** (not a MelonMod): install the DLL under **`{GameRoot}/Plugins/`**. It does **not** change native mod paths. It calls **`steam_api64.dll`** directly (Steam flat API: `SteamAPI_ISteamUGC_*`). The game ships this DLL under **`Data Center_Data/Plugins/x86_64/`**; the plugin loads it from there first, then falls back next to the executable. After the Il2Cpp assembly step (see MelonLoader log: `Il2CppAssemblyGenerator`), **`OnPreModsLoaded`** blocks loading MelonMods until each subscribed Workshop item reports installed in Steam and a matching **`Data Center_Data/StreamingAssets/Mods/workshop_/`** folder exists (per-item **`DownloadItem`**, **`SteamAPI_RunCallbacks`**, timeouts). If the game has not copied Workshop content into `StreamingAssets/Mods` yet, the plugin continues after a short grace period and logs a warning — restart once if mods are missing. + +**What it does:** +- On each frame (from `OnApplicationStarted`), waits for `SteamAPI_IsSteamRunning` and resolves `SteamAPI_SteamUGC_v0xx` → `ISteamUGC*`, then calls the native UGC APIs (no Il2Cpp Steamworks wrapper) +- Leaves `ModLoader.LoadAllMods` / `CopyDirectory` unchanged — Workshop sync still uses `Data Center_Data/StreamingAssets/Mods/workshop_/` + +**Installation:** +1. Copy `FMF.ModPathRedirector.dll` into `/Plugins/` (MelonLoader **Plugins** folder next to the executable) +2. Start the game — subscribed Workshop content is requested early; manual native mods still go under `Data Center_Data/StreamingAssets/Mods//` (each folder with a `config.json`) + +**Layout (unchanged from vanilla):** + +``` +Data Center/ + Data Center_Data/ + Plugins/ + x86_64/ + steam_api64.dll ← shipped with the game (Steam API; ModPathRedirector loads this) + StreamingAssets/ + Mods/ + my_cool_mod/ ← manual native mod + config.json + workshop_12345678/ ← Workshop item (game copies here after download) + ... + Plugins/ ← MelonLoader plugins (FMF.ModPathRedirector.dll) + Mods/ ← MelonLoader mods (other FMF.* MelonMods) + Data Center.exe +``` + +## Project layout + +For each Workshop project: + +1. Create a **folder** under the workspace (the folder name appears in the list). +2. Add a `content/` subfolder with files to ship: + - For **vanilla mods**: `config.json` + `.obj` models + `.png` textures directly in `content/` + - For **MelonLoader mods**: `content/Mods/` with `.dll` files + - For **FMF plugins**: `content/FMF/Plugins/` with `.dll` files +3. Optionally create `metadata.json` yourself or fill it in the app. +4. Optionally add `preview.png` (or `.jpg`, `.jpeg`, `.gif`, `.webp`) at the project root. +5. Optionally add gallery screenshots under `screenshots/` — these are uploaded as additional preview images. + +### Example: Vanilla mod (native config.json) + +``` +workshop/ +└── MyCustomServers/ + ├── metadata.json # Workshop metadata (title, desc, tags) + ├── preview.png # Main preview image + └── content/ # Shipped to subscribers + ├── config.json # Native mod definition + ├── server.obj # 3D model + ├── server.png # Texture + └── server_icon.png # Shop icon +``` + +### Example: MelonLoader mod + +``` +workshop/ +└── MyMelonMod/ + ├── metadata.json + ├── preview.png + └── content/ + └── Mods/ + └── MyMod.dll +``` + +### Example: FMF plugin + +``` +workshop/ +└── MyFmfPlugin/ + ├── metadata.json + ├── preview.png + └── content/ + └── FMF/ + └── Plugins/ + └── FFM.Plugin.MyPlugin.dll +``` + +### metadata.json schema + +| Field | Type | Description | +|-------|------|-------------| +| `publishedFileId` | `number` | Steam file ID (0 if unpublished) | +| `title` | `string` | Workshop item title (max 128 chars) | +| `description` | `string` | Description, supports [Steam BBCode](#bbcode) (max 8000 chars) | +| `visibility` | `string` | `"Public"`, `"FriendsOnly"`, or `"Private"` | +| `previewImageRelativePath` | `string` | Relative path to preview image (default `"preview.png"`) | +| `tags` | `string[]` | Up to 20 tags for discoverability | +| `needsFmf` | `boolean` | Appends FMF dependency notice on upload | +| `additionalPreviews` | `string[]` | Relative paths to gallery screenshot images | + +## Using the app + +### Tabs + +| Tab | Purpose | +|-----|---------| +| **Projects** | Local workshop projects; search, open editor | +| **New** | Create from template (vanilla, MelonLoader, FMF) | +| **My Uploads** | Paginated list of your published items with stats | +| **Mod Store** | Browse, search, subscribe, vote, and manage mods | + +### Publish workflow + +1. Open a project from the **Projects** tab. +2. Fill in title, description, tags, visibility, preview image. +3. Optionally add **gallery screenshots** (up to 9 additional images). +4. Write a **version changelog** (required for first publish). +5. Click **Save and upload to Steam**. +6. The app: + - Validates all required fields (upload readiness check). + - Saves `metadata.json`. + - Uploads `content/` and preview to Steam. + - Uploads additional gallery screenshots (if any). + - **Syncs back** — re-downloads Steam's version of content, preview image, and gallery screenshots into your local project folder. + +### Import from Steam + +You can import an existing published Workshop item into your local workspace: + +1. Go to **My Uploads** → click an item → **Import**. +2. The app downloads the content, preview image, gallery screenshots, and creates a local project with the correct `metadata.json`. + +### Headless / CI publish + +```bash +WorkshopUploader.exe --mode publish --path +``` + +Optional flags: +- `--autocommit` — writes a status JSON for external tools. + +## Steam BBCode reference {#bbcode} + +Steam Workshop descriptions support **BBCode** formatting. The editor includes a toolbar with all supported tags. Here is the full reference: + +### Text formatting + +```bbcode +[b]Bold text[/b] +[i]Italic text[/i] +[u]Underlined text[/u] +[strike]Strikethrough text[/strike] +``` + +### Headings + +```bbcode +[h1]Heading 1[/h1] +[h2]Heading 2[/h2] +[h3]Heading 3[/h3] +``` + +### Links and images + +```bbcode +[url=https://example.com]Link text[/url] +[img]https://example.com/image.png[/img] +``` + +### Lists + +```bbcode +[list] +[*] Item 1 +[*] Item 2 +[*] Item 3 +[/list] +``` + +### Code blocks + +```bbcode +[code] +console.log("Hello World"); +[/code] +``` + +### Quotes and spoilers + +```bbcode +[quote]Quoted text here[/quote] +[spoiler]Hidden text revealed on click[/spoiler] +``` + +### Horizontal rule + +```bbcode +[hr][/hr] +``` + +### Tables + +```bbcode +[table] +[tr] +[th]Header 1[/th] +[th]Header 2[/th] +[/tr] +[tr] +[td]Cell 1[/td] +[td]Cell 2[/td] +[/tr] +[/table] +``` + +### Example workshop description + +```bbcode +[h1]My Awesome Mod[/h1] + +Custom decorations and objects for Data Center. + +[h2]Features[/h2] +[list] +[*] 10 new decoration items +[*] 5 custom server rack skins +[*] Compatible with FrikaModFramework +[/list] + +[h2]Installation[/h2] +Subscribe to this Workshop item — the game loads it automatically. + +[h2]Requirements[/h2] +[list] +[*] Data Center (Steam) +[*] [url=https://melonwiki.xyz]MelonLoader[/url] (for modded items) +[/list] + +[h3]Changelog[/h3] +[b]v1.0[/b] — Initial release + +[hr][/hr] +[i]Made by YourName — [url=https://discord.gg/your-server]Join the Discord[/url][/i] +``` + +## Upload readiness checks {#checks} + +Before uploading, the editor runs automated checks: + +| Check | Error condition | Warning condition | +|-------|----------------|-------------------| +| **Content folder** | `content/` missing or empty | — | +| **Title** | Empty or > 128 characters | — | +| **Description** | > 8000 characters | Empty (recommended) | +| **Visibility** | — | Unknown value | +| **Preview image** | — | Missing or > 1 MB | +| **Tags** | — | No tags set | +| **Content size** | — | > 100 MB | +| **Changelog** | Empty on first publish | Empty on update | + +Items with **errors** cannot be uploaded. **Warnings** are informational — upload is still possible. + +## Preview images {#preview-images} + +### Main preview image +- Supports **PNG**, **JPG**, **JPEG**, **GIF**, and **WebP**. +- Auto-detected by filename: `preview.png`, `preview.jpg`, etc. +- Recommended size: under **1 MB**. +- The app auto-detects the format from the file's magic bytes when syncing from Steam. + +### Gallery screenshots +- Up to **9** additional images per item. +- Stored in `screenshots/` under the project root. +- Uploaded in a separate pass after the main content publish. +- **Synced back** from Steam on publish and import — if screenshots exist on Steam, they are downloaded to your local `screenshots/` folder. + +## Build and deploy {#build-deploy} + +### Self-contained release + +The app is published as a **self-contained** Windows executable — no .NET runtime or Windows App SDK install required on the target machine. + +```bash +dotnet publish WorkshopUploader/WorkshopUploader.csproj -c Release -p:SelfContained=true -p:RuntimeIdentifier=win10-x64 +``` + +Output: `WorkshopUploader/bin/Release/net9.0-windows10.0.19041.0/win10-x64/publish/` + +### Deploy all mods + Gregtools Modmanager to Workshop folders + +```bash +pwsh -File scripts/Deploy-Release-ToWorkshop.ps1 +``` + +This builds all framework/plugins/mods **and** the WorkshopUploader itself, then packages each into a Steamworks-compatible project folder under `/workshop/`. The WorkshopUploader is packaged as **"Gregtools Modmanager"**. + +### Deploy to game directory (local testing) + +```bash +pwsh -File scripts/Deploy-Release-ToDataCenter.ps1 -IncludeWorkshopUploader +``` + +## FAQ {#faq} + +### General + +**Q: Do I need to install anything besides the app?** +No. The release is fully self-contained — .NET runtime and Windows App SDK are bundled. You only need Steam running with a Data Center license. + +**Q: What Steam App ID does this use?** +`4170200` (Data Center). The file `steam_appid.txt` must contain this ID next to the executable. + +**Q: Can I use this on Linux / macOS?** +Not currently. The app targets Windows 10+ via .NET MAUI with WinUI. + +### Projects and workspace + +**Q: Where are my projects stored?** +By default under `/workshop/`. You can change this in Settings. Fallback if Steam is unavailable: `%USERPROFILE%\DataCenterWS`. + +**Q: What is the difference between the workspace and Steam's download folder?** +The **workspace** (`/workshop/`) is where you *author* content. The **Steam download folder** (`steamapps/workshop/content/4170200//`) is where Steam installs *subscribed* items for the game. They are separate locations. + +**Q: Can I edit a project that was already published?** +Yes. Open it from **Projects**, make changes, and click **Save and upload to Steam**. The app updates the existing Workshop item (using the `publishedFileId` stored in `metadata.json`). + +**Q: Can I import a Workshop item I published from another machine?** +Yes. Use **My Uploads** → select the item → **Import**. The app downloads everything (content, preview, gallery screenshots) and creates a local project. + +### Publishing + +**Q: Why can't I upload? The button is greyed out / shows errors.** +The upload readiness checker found issues. Common blockers: +- `content/` folder is missing or empty. +- Title is empty. +- No changelog provided on first publish. +Check the readiness panel at the bottom of the editor for details. + +**Q: Is the changelog required?** +Yes for the **first publish** (error if empty). For updates it's recommended but not enforced (warning only). + +**Q: What happens after I click "Save and upload to Steam"?** +1. Metadata is saved locally. +2. Content and preview are uploaded to Steam. +3. Gallery screenshots are uploaded (if any). +4. The app re-downloads everything from Steam to sync your local project (content, preview image, gallery images). + +**Q: My upload says "Workshop legal agreement must be accepted."** +You need to accept the Steam Workshop legal agreement for Data Center in your browser. Visit the Workshop page for the game on Steam and accept the agreement. + +**Q: Can I upload from the command line?** +Yes: `WorkshopUploader.exe --mode publish --path `. Useful for CI/CD pipelines. + +### Descriptions and formatting + +**Q: Does the description support formatting?** +Yes, Steam Workshop uses **BBCode**. The editor includes a toolbar with all supported tags (bold, italic, headings, URLs, images, lists, code, quotes, spoilers, tables). See the [BBCode reference](#bbcode) above. + +**Q: What is the maximum description length?** +8000 characters. + +**Q: Can I use Markdown in descriptions?** +No. Steam Workshop only supports BBCode. Markdown will be displayed as plain text. + +### Images + +**Q: What image formats are supported?** +PNG, JPG/JPEG, GIF, and WebP for both the main preview and gallery screenshots. + +**Q: How large can the preview image be?** +Steam recommends under **1 MB**. The app warns if it's larger but doesn't block upload. + +**Q: How many gallery screenshots can I add?** +Up to **9** additional images. They appear in the image gallery on the Steam Workshop page. + +**Q: Are my screenshots synced from Steam?** +Yes. After publishing and during import, gallery screenshots are downloaded from Steam and saved in your project's `screenshots/` folder. + +### Mods and installation + +**Q: Where does Steam install Workshop mods?** +`/steamapps/workshop/content/4170200//`. See [Steam install path](#steam-install-path) for details. + +**Q: Where does the game look for native mods (manual install)?** +`Data Center_Data/StreamingAssets/Mods//`. Each subfolder must contain a `config.json`. See the [native mod format](#native-mod-format) reference. + +**Q: How does the game load Workshop mods?** +At startup, the game's `ModLoader.SyncWorkshopThenLoadAll()` queries all subscribed items from Steam, waits for downloads to complete, and copies each into `Data Center_Data/StreamingAssets/Mods/workshop_/`. Then `LoadAllMods()` scans that folder. Asset objects (shopItems, staticItems) load immediately; DLL mods load later at SaveLoad time. The content structure determines what kind of mod it is: +- **Native mods** (vanilla): `config.json` + OBJ/PNG assets directly in `content/` — no extra tools needed. +- **MelonLoader mods**: `content/Mods/` with `.dll` files — requires MelonLoader. +- **FMF plugins**: `content/FMF/Plugins/` with `.dll` files — requires MelonLoader + FMF. + +**Q: What are the three mod types?** + +| Type | Requirements | Content structure | Use case | +|------|-------------|-------------------|----------| +| **Vanilla (native)** | None | `config.json` + `.obj` + `.png` | Custom objects, decorations, shop items | +| **MelonLoader** | MelonLoader | `Mods/*.dll` | Code mods (IL2CPP) | +| **FMF plugin** | MelonLoader + FMF | `FMF/Plugins/*.dll` | FrikaModFramework plugins | + +**Q: What model format does the native mod system use?** +Wavefront OBJ (`.obj`) for 3D models and PNG (`.png`) for textures and icons. + +**Q: What is `objectType` in config.json?** +Currently only `0` (passive object) is supported. Active/functional object types may be added in future game updates. + +**Q: Can I load custom DLLs via the native mod system (without MelonLoader)?** +Yes. The game's `ModLoader` has built-in DLL loading via the `dlls` array in `config.json`. Your entry class must implement the game's `IModPlugin` interface. See [DLL mods without MelonLoader](#native-dll-mods) for details. Note that this feature is marked as **experimental** by the developer — asset-only mods (`shopItems`/`staticItems`) are the safest approach. + +**Q: Do users need MelonLoader to use Workshop items?** +Only for MelonLoader and FMF mods. **Vanilla/native mods** (using `config.json`) work without any additional tools — just subscribe and play. Mark your project as "Needs FrikaModFramework" if applicable — the app will add an installation notice to the description automatically. + +**Q: Can I change where native mods are loaded from?** +Yes. Install the [ModPathRedirector](#mod-path-redirector) MelonLoader plugin into `{GameRoot}/Plugins/`. It uses `steam_api64.dll` to trigger Workshop downloads once the Steam client is up; files still land under `Data Center_Data/StreamingAssets/Mods/workshop_/`. + +### Mod Store + +**Q: How do I enable the Mod Store?** +Go to **Settings** → toggle **Mod Store** on → restart the app. + +**Q: Can I subscribe to mods from the app?** +Yes. Browse or search for mods, then click **Subscribe**. Steam will download the mod automatically. + +### Troubleshooting + +**Q: The app won't start / crashes on launch.** +- Ensure `steam_appid.txt` is next to the executable; `steam_api64.dll` should resolve from the Data Center install (`Data Center_Data/Plugins/x86_64/`) or from the uploader folder. +- Steam must be running and logged in. +- Check the Windows Event Viewer (Application log) for .NET crash details. +- On Windows 10 < 1809, the app may not work due to WinUI requirements. + +**Q: "Steam init failed" error in the app.** +- Make sure Steam is running and you are logged in. +- Verify you own Data Center (App ID 4170200). +- Check that `steam_appid.txt` contains `4170200`. + +**Q: My content doesn't show up after subscribing.** +- Restart the game — Workshop content is loaded at startup. +- Check `steamapps/workshop/content/4170200/` to verify the download exists. +- If the folder is empty, try unsubscribing and re-subscribing. + +**Q: The preview image doesn't load in the Mod Store.** +- The image is loaded from Steam's CDN. Check your internet connection. +- Very large images may take a moment to appear. + +## See also + +- Repository README: [`WorkshopUploader/README.md`](https://github.com/mleem97/gregFramework/blob/master/WorkshopUploader/README.md) +- [End-User Guide](/wiki/guides/enduser-workshop) +- [Contributor Guide](/wiki/guides/contributor-workshop) +- [Release](/wiki/guides/release)