@zigrivers/scaffold 3.4.1 → 3.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -0
- package/content/knowledge/game/game-accessibility.md +328 -0
- package/content/knowledge/game/game-ai-patterns.md +567 -0
- package/content/knowledge/game/game-asset-pipeline.md +363 -0
- package/content/knowledge/game/game-audio-design.md +344 -0
- package/content/knowledge/game/game-binary-vcs-strategy.md +396 -0
- package/content/knowledge/game/game-design-document.md +269 -0
- package/content/knowledge/game/game-domain-patterns.md +299 -0
- package/content/knowledge/game/game-economy-design.md +355 -0
- package/content/knowledge/game/game-engine-selection.md +242 -0
- package/content/knowledge/game/game-input-systems.md +379 -0
- package/content/knowledge/game/game-level-content-design.md +483 -0
- package/content/knowledge/game/game-liveops-analytics.md +280 -0
- package/content/knowledge/game/game-localization.md +323 -0
- package/content/knowledge/game/game-milestone-definitions.md +337 -0
- package/content/knowledge/game/game-modding-ugc.md +390 -0
- package/content/knowledge/game/game-narrative-design.md +404 -0
- package/content/knowledge/game/game-networking.md +393 -0
- package/content/knowledge/game/game-performance-budgeting.md +389 -0
- package/content/knowledge/game/game-platform-certification.md +417 -0
- package/content/knowledge/game/game-project-structure.md +360 -0
- package/content/knowledge/game/game-save-systems.md +452 -0
- package/content/knowledge/game/game-testing-strategy.md +470 -0
- package/content/knowledge/game/game-ui-patterns.md +477 -0
- package/content/knowledge/game/game-vr-ar-design.md +313 -0
- package/content/knowledge/review/review-art-bible.md +305 -0
- package/content/knowledge/review/review-game-design.md +303 -0
- package/content/knowledge/review/review-game-economy.md +272 -0
- package/content/knowledge/review/review-game-ui.md +293 -0
- package/content/knowledge/review/review-netcode.md +280 -0
- package/content/knowledge/review/review-platform-cert.md +341 -0
- package/content/methodology/custom-defaults.yml +25 -0
- package/content/methodology/deep.yml +25 -0
- package/content/methodology/game-overlay.yml +145 -0
- package/content/methodology/mvp.yml +25 -0
- package/content/pipeline/architecture/ai-behavior-design.md +87 -0
- package/content/pipeline/architecture/netcode-spec.md +86 -0
- package/content/pipeline/architecture/review-netcode.md +78 -0
- package/content/pipeline/foundation/performance-budgets.md +91 -0
- package/content/pipeline/modeling/narrative-bible.md +84 -0
- package/content/pipeline/pre/game-design-document.md +90 -0
- package/content/pipeline/pre/review-gdd.md +74 -0
- package/content/pipeline/quality/analytics-telemetry.md +98 -0
- package/content/pipeline/quality/live-ops-plan.md +99 -0
- package/content/pipeline/quality/platform-cert-prep.md +129 -0
- package/content/pipeline/quality/playtest-plan.md +84 -0
- package/content/pipeline/specification/art-bible.md +87 -0
- package/content/pipeline/specification/audio-design.md +97 -0
- package/content/pipeline/specification/content-structure-design.md +142 -0
- package/content/pipeline/specification/economy-design.md +105 -0
- package/content/pipeline/specification/game-accessibility.md +82 -0
- package/content/pipeline/specification/game-ui-spec.md +97 -0
- package/content/pipeline/specification/input-controls-spec.md +81 -0
- package/content/pipeline/specification/localization-plan.md +113 -0
- package/content/pipeline/specification/modding-ugc-spec.md +116 -0
- package/content/pipeline/specification/online-services-spec.md +104 -0
- package/content/pipeline/specification/review-economy.md +87 -0
- package/content/pipeline/specification/review-game-ui.md +73 -0
- package/content/pipeline/specification/save-system-spec.md +116 -0
- package/dist/cli/commands/adopt.d.ts.map +1 -1
- package/dist/cli/commands/adopt.js +25 -0
- package/dist/cli/commands/adopt.js.map +1 -1
- package/dist/cli/commands/adopt.test.js +28 -1
- package/dist/cli/commands/adopt.test.js.map +1 -1
- package/dist/cli/commands/build.test.js +3 -0
- package/dist/cli/commands/build.test.js.map +1 -1
- package/dist/cli/commands/init.d.ts +1 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +6 -0
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/init.test.js +12 -1
- package/dist/cli/commands/init.test.js.map +1 -1
- package/dist/cli/commands/knowledge.test.js +8 -0
- package/dist/cli/commands/knowledge.test.js.map +1 -1
- package/dist/cli/commands/next.d.ts.map +1 -1
- package/dist/cli/commands/next.js +19 -5
- package/dist/cli/commands/next.js.map +1 -1
- package/dist/cli/commands/next.test.js +56 -0
- package/dist/cli/commands/next.test.js.map +1 -1
- package/dist/cli/commands/rework.d.ts.map +1 -1
- package/dist/cli/commands/rework.js +11 -2
- package/dist/cli/commands/rework.js.map +1 -1
- package/dist/cli/commands/rework.test.js +5 -0
- package/dist/cli/commands/rework.test.js.map +1 -1
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +54 -4
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/run.test.js +384 -0
- package/dist/cli/commands/run.test.js.map +1 -1
- package/dist/cli/commands/skip.test.js +3 -0
- package/dist/cli/commands/skip.test.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +16 -3
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/status.test.js +55 -0
- package/dist/cli/commands/status.test.js.map +1 -1
- package/dist/cli/output/auto.d.ts +3 -0
- package/dist/cli/output/auto.d.ts.map +1 -1
- package/dist/cli/output/auto.js +9 -0
- package/dist/cli/output/auto.js.map +1 -1
- package/dist/cli/output/context.d.ts +6 -0
- package/dist/cli/output/context.d.ts.map +1 -1
- package/dist/cli/output/context.js.map +1 -1
- package/dist/cli/output/context.test.js +87 -0
- package/dist/cli/output/context.test.js.map +1 -1
- package/dist/cli/output/error-display.test.js +3 -0
- package/dist/cli/output/error-display.test.js.map +1 -1
- package/dist/cli/output/interactive.d.ts +3 -0
- package/dist/cli/output/interactive.d.ts.map +1 -1
- package/dist/cli/output/interactive.js +76 -0
- package/dist/cli/output/interactive.js.map +1 -1
- package/dist/cli/output/json.d.ts +3 -0
- package/dist/cli/output/json.d.ts.map +1 -1
- package/dist/cli/output/json.js +9 -0
- package/dist/cli/output/json.js.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +3 -2
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +641 -15
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +26 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/config/schema.test.js +192 -1
- package/dist/config/schema.test.js.map +1 -1
- package/dist/core/assembly/overlay-loader.d.ts +24 -0
- package/dist/core/assembly/overlay-loader.d.ts.map +1 -0
- package/dist/core/assembly/overlay-loader.js +190 -0
- package/dist/core/assembly/overlay-loader.js.map +1 -0
- package/dist/core/assembly/overlay-loader.test.d.ts +2 -0
- package/dist/core/assembly/overlay-loader.test.d.ts.map +1 -0
- package/dist/core/assembly/overlay-loader.test.js +106 -0
- package/dist/core/assembly/overlay-loader.test.js.map +1 -0
- package/dist/core/assembly/overlay-resolver.d.ts +15 -0
- package/dist/core/assembly/overlay-resolver.d.ts.map +1 -0
- package/dist/core/assembly/overlay-resolver.js +58 -0
- package/dist/core/assembly/overlay-resolver.js.map +1 -0
- package/dist/core/assembly/overlay-resolver.test.d.ts +2 -0
- package/dist/core/assembly/overlay-resolver.test.d.ts.map +1 -0
- package/dist/core/assembly/overlay-resolver.test.js +246 -0
- package/dist/core/assembly/overlay-resolver.test.js.map +1 -0
- package/dist/core/assembly/overlay-state-resolver.d.ts +26 -0
- package/dist/core/assembly/overlay-state-resolver.d.ts.map +1 -0
- package/dist/core/assembly/overlay-state-resolver.js +63 -0
- package/dist/core/assembly/overlay-state-resolver.js.map +1 -0
- package/dist/core/assembly/overlay-state-resolver.test.d.ts +2 -0
- package/dist/core/assembly/overlay-state-resolver.test.d.ts.map +1 -0
- package/dist/core/assembly/overlay-state-resolver.test.js +256 -0
- package/dist/core/assembly/overlay-state-resolver.test.js.map +1 -0
- package/dist/core/assembly/preset-loader.d.ts +1 -0
- package/dist/core/assembly/preset-loader.d.ts.map +1 -1
- package/dist/core/assembly/preset-loader.js +2 -0
- package/dist/core/assembly/preset-loader.js.map +1 -1
- package/dist/core/dependency/eligibility.test.js +3 -0
- package/dist/core/dependency/eligibility.test.js.map +1 -1
- package/dist/e2e/game-pipeline.test.d.ts +10 -0
- package/dist/e2e/game-pipeline.test.d.ts.map +1 -0
- package/dist/e2e/game-pipeline.test.js +298 -0
- package/dist/e2e/game-pipeline.test.js.map +1 -0
- package/dist/e2e/init.test.js +3 -0
- package/dist/e2e/init.test.js.map +1 -1
- package/dist/project/adopt.d.ts +3 -1
- package/dist/project/adopt.d.ts.map +1 -1
- package/dist/project/adopt.js +29 -1
- package/dist/project/adopt.js.map +1 -1
- package/dist/project/adopt.test.js +51 -1
- package/dist/project/adopt.test.js.map +1 -1
- package/dist/types/config.d.ts +50 -4
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.test.d.ts +2 -0
- package/dist/types/config.test.d.ts.map +1 -0
- package/dist/types/config.test.js +97 -0
- package/dist/types/config.test.js.map +1 -0
- package/dist/utils/eligible.d.ts +3 -2
- package/dist/utils/eligible.d.ts.map +1 -1
- package/dist/utils/eligible.js +18 -4
- package/dist/utils/eligible.js.map +1 -1
- package/dist/utils/errors.d.ts +4 -0
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +31 -0
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/errors.test.js +4 -1
- package/dist/utils/errors.test.js.map +1 -1
- package/dist/wizard/questions.d.ts +4 -0
- package/dist/wizard/questions.d.ts.map +1 -1
- package/dist/wizard/questions.js +59 -1
- package/dist/wizard/questions.js.map +1 -1
- package/dist/wizard/questions.test.js +178 -4
- package/dist/wizard/questions.test.js.map +1 -1
- package/dist/wizard/wizard.d.ts +1 -0
- package/dist/wizard/wizard.d.ts.map +1 -1
- package/dist/wizard/wizard.js +4 -1
- package/dist/wizard/wizard.js.map +1 -1
- package/dist/wizard/wizard.test.js +102 -4
- package/dist/wizard/wizard.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: game-modding-ugc
|
|
3
|
+
description: Mod API design, packaging formats, sandboxing, versioning, content moderation, distribution platforms, and file-based loading
|
|
4
|
+
topics: [game-dev, modding, ugc, sandbox, steam-workshop, mod-api]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Modding and user-generated content (UGC) transform a game from a finished product into a platform. The most enduring games in history — Minecraft, Skyrim, Counter-Strike (itself a mod), DOTA (a Warcraft III mod), Garry's Mod — owe their longevity to modding communities that produce content at a scale no studio can match. But modding support is not free: it requires deliberate API design, security sandboxing, compatibility management across game updates, content moderation, and distribution infrastructure. A poorly designed mod system creates more problems than it solves — crashes blamed on the base game, security exploits, copyright-infringing content, and player fragmentation. The goal is to expose enough of the game's systems to enable creativity while maintaining stability, security, and a coherent player experience.
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
### Mod API Surface Design
|
|
12
|
+
|
|
13
|
+
The mod API defines what modders can change. The fundamental tension is between power (more API surface enables richer mods) and stability (every exposed API becomes a compatibility contract).
|
|
14
|
+
|
|
15
|
+
**API tiering approach:**
|
|
16
|
+
- **Tier 1 — Data mods**: Replace or add data files (textures, models, audio, level layouts, item stats). No code execution. Safest and simplest. Enables cosmetic mods, balance tweaks, new maps/levels.
|
|
17
|
+
- **Tier 2 — Script mods**: Execute sandboxed scripts (Lua, JavaScript, or a custom DSL) that call game APIs. Enables new game mechanics, UI modifications, custom AI behaviors. Requires a security sandbox.
|
|
18
|
+
- **Tier 3 — Plugin mods**: Load native code (DLL, SO) that hooks into the engine. Maximum power — can modify anything. Maximum risk — crashes, exploits, and anti-cheat conflicts. Appropriate only for PC single-player or private servers.
|
|
19
|
+
|
|
20
|
+
Most games should support Tier 1 and Tier 2. Tier 3 is appropriate for sandbox games (Minecraft Java, Skyrim, Factorio) where the community expects deep modification.
|
|
21
|
+
|
|
22
|
+
**API design principles:**
|
|
23
|
+
- Expose game systems through well-defined interfaces, not internal data structures
|
|
24
|
+
- Version the API explicitly (v1, v2) — mods declare which API version they target
|
|
25
|
+
- Provide read access broadly (game state, entity properties, world data) and write access narrowly (only through validated setter functions)
|
|
26
|
+
- Event-driven architecture: mods subscribe to game events (on_entity_spawn, on_level_load, on_player_damage) rather than polling or patching functions
|
|
27
|
+
- Rate-limit expensive operations (file I/O, network calls, entity spawning) to prevent mods from degrading performance
|
|
28
|
+
|
|
29
|
+
### Packaging Formats
|
|
30
|
+
|
|
31
|
+
Mods need a standard packaging format that includes metadata, assets, scripts, and dependency declarations.
|
|
32
|
+
|
|
33
|
+
**Standard mod package structure:**
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
my-awesome-mod/
|
|
37
|
+
├── mod.json # Manifest: ID, version, dependencies, API version
|
|
38
|
+
├── README.md # Description for mod browsers
|
|
39
|
+
├── thumbnail.png # 512x512 preview image for mod listing
|
|
40
|
+
├── assets/
|
|
41
|
+
│ ├── textures/ # Replacement or new textures
|
|
42
|
+
│ ├── models/ # Replacement or new 3D models
|
|
43
|
+
│ ├── audio/ # Replacement or new audio files
|
|
44
|
+
│ └── levels/ # New level/map data
|
|
45
|
+
├── scripts/
|
|
46
|
+
│ ├── main.lua # Entry point script
|
|
47
|
+
│ └── utils.lua # Helper modules
|
|
48
|
+
├── localization/
|
|
49
|
+
│ ├── en.json # English strings
|
|
50
|
+
│ └── de.json # German strings
|
|
51
|
+
└── config/
|
|
52
|
+
└── defaults.json # Default settings (overridable by player)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The mod manifest (`mod.json`) is the critical file:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"id": "com.modauthor.awesome-mod",
|
|
60
|
+
"name": "Awesome Gameplay Overhaul",
|
|
61
|
+
"version": "2.1.0",
|
|
62
|
+
"api_version": "1.4",
|
|
63
|
+
"game_version_min": "3.0.0",
|
|
64
|
+
"game_version_max": "3.99.99",
|
|
65
|
+
"authors": ["ModAuthor"],
|
|
66
|
+
"description": "Overhauls combat mechanics with new dodge and parry systems.",
|
|
67
|
+
"dependencies": [
|
|
68
|
+
{ "id": "com.modauthor.core-lib", "version": ">=1.0.0" }
|
|
69
|
+
],
|
|
70
|
+
"conflicts": [
|
|
71
|
+
{ "id": "com.other.combat-rewrite", "reason": "Both modify the combat system" }
|
|
72
|
+
],
|
|
73
|
+
"permissions": ["file_read", "game_state_write", "ui_overlay"],
|
|
74
|
+
"entry_point": "scripts/main.lua",
|
|
75
|
+
"load_order": 100,
|
|
76
|
+
"tags": ["gameplay", "combat", "overhaul"]
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Distribute mods as `.zip` archives (renamed to `.modpak`, `.pak`, or a game-specific extension). The mod loader extracts to a known directory and validates the manifest before loading.
|
|
81
|
+
|
|
82
|
+
### Sandboxing and Security
|
|
83
|
+
|
|
84
|
+
Mods that execute code (Tier 2 and above) are a security risk. A malicious mod could read files, exfiltrate data, mine cryptocurrency, or corrupt save files. Sandboxing limits what mod code can do.
|
|
85
|
+
|
|
86
|
+
## Deep Guidance
|
|
87
|
+
|
|
88
|
+
### Sandboxing Architecture
|
|
89
|
+
|
|
90
|
+
The sandbox must enforce two boundaries: what system resources mod code can access, and how much computational resource it can consume.
|
|
91
|
+
|
|
92
|
+
**Lua sandbox implementation:**
|
|
93
|
+
|
|
94
|
+
Lua is the most popular embedded scripting language for game mods (used by World of Warcraft, Factorio, Garry's Mod, Roblox) because it is fast, lightweight, and easy to sandbox.
|
|
95
|
+
|
|
96
|
+
```lua
|
|
97
|
+
-- sandbox.lua: Create a restricted Lua environment for mod execution
|
|
98
|
+
local function create_sandbox(mod_id, api)
|
|
99
|
+
-- Whitelist of allowed standard library functions
|
|
100
|
+
local safe_globals = {
|
|
101
|
+
-- Math
|
|
102
|
+
math = {
|
|
103
|
+
abs = math.abs, ceil = math.ceil, floor = math.floor,
|
|
104
|
+
max = math.max, min = math.min, random = math.random,
|
|
105
|
+
sqrt = math.sqrt, sin = math.sin, cos = math.cos,
|
|
106
|
+
pi = math.pi, huge = math.huge,
|
|
107
|
+
},
|
|
108
|
+
-- String manipulation
|
|
109
|
+
string = {
|
|
110
|
+
byte = string.byte, char = string.char, find = string.find,
|
|
111
|
+
format = string.format, gmatch = string.gmatch, gsub = string.gsub,
|
|
112
|
+
len = string.len, lower = string.lower, upper = string.upper,
|
|
113
|
+
match = string.match, rep = string.rep, reverse = string.reverse,
|
|
114
|
+
sub = string.sub,
|
|
115
|
+
},
|
|
116
|
+
-- Table operations
|
|
117
|
+
table = {
|
|
118
|
+
concat = table.concat, insert = table.insert,
|
|
119
|
+
remove = table.remove, sort = table.sort, unpack = table.unpack,
|
|
120
|
+
},
|
|
121
|
+
-- Safe builtins
|
|
122
|
+
pairs = pairs, ipairs = ipairs, next = next,
|
|
123
|
+
type = type, tostring = tostring, tonumber = tonumber,
|
|
124
|
+
select = select, error = error, pcall = pcall, xpcall = xpcall,
|
|
125
|
+
setmetatable = setmetatable, getmetatable = getmetatable,
|
|
126
|
+
|
|
127
|
+
-- Game API (injected per mod based on declared permissions)
|
|
128
|
+
game = api,
|
|
129
|
+
|
|
130
|
+
-- Mod-scoped print (routes to mod log, not stdout)
|
|
131
|
+
print = function(...)
|
|
132
|
+
api.log(mod_id, "INFO", table.concat({...}, "\t"))
|
|
133
|
+
end,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
-- EXPLICITLY BLOCKED (not present in safe_globals):
|
|
137
|
+
-- os (file system, process execution)
|
|
138
|
+
-- io (file read/write)
|
|
139
|
+
-- debug (can escape sandbox via debug.getinfo, debug.sethook)
|
|
140
|
+
-- loadfile, dofile (arbitrary file execution)
|
|
141
|
+
-- rawget, rawset (bypass metatables)
|
|
142
|
+
-- collectgarbage (can cause pauses)
|
|
143
|
+
-- require (replaced with mod-scoped import below)
|
|
144
|
+
|
|
145
|
+
-- Mod-scoped require: can only load files within the mod's directory
|
|
146
|
+
safe_globals.require = function(module_name)
|
|
147
|
+
local allowed_path = api.resolve_mod_path(mod_id, module_name)
|
|
148
|
+
if not allowed_path then
|
|
149
|
+
error("Module not found or access denied: " .. module_name)
|
|
150
|
+
end
|
|
151
|
+
return api.load_module(allowed_path, safe_globals)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
return safe_globals
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
-- Load and execute a mod's entry point within the sandbox
|
|
158
|
+
local function load_mod(mod_id, entry_script, api)
|
|
159
|
+
local sandbox_env = create_sandbox(mod_id, api)
|
|
160
|
+
local chunk, err = loadfile(entry_script, "t", sandbox_env)
|
|
161
|
+
if not chunk then
|
|
162
|
+
api.log(mod_id, "ERROR", "Failed to load: " .. err)
|
|
163
|
+
return false
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
-- Execute with resource limits
|
|
167
|
+
local co = coroutine.create(chunk)
|
|
168
|
+
local ok, result = coroutine.resume(co)
|
|
169
|
+
if not ok then
|
|
170
|
+
api.log(mod_id, "ERROR", "Runtime error: " .. tostring(result))
|
|
171
|
+
return false
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
return true
|
|
175
|
+
end
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Resource limits:**
|
|
179
|
+
- **CPU time**: Yield the mod coroutine after N instructions (Lua debug hooks or instruction counting). Kill mods that exceed their per-frame budget (e.g., 1 ms per mod per frame).
|
|
180
|
+
- **Memory**: Track Lua memory allocation per mod. Set a ceiling (e.g., 64 MB). Kill mods that exceed it.
|
|
181
|
+
- **Entity spawning**: Rate-limit entity creation (e.g., 100 entities per second). A mod that spawns 10,000 entities per frame will crash the game.
|
|
182
|
+
- **File I/O**: Restrict to the mod's own data directory. No reading game save files, player profiles, or system files. No network access from Tier 2 mods.
|
|
183
|
+
|
|
184
|
+
### Compatibility and Versioning
|
|
185
|
+
|
|
186
|
+
The hardest problem in mod support is maintaining compatibility when the game updates. Every game update risks breaking every installed mod.
|
|
187
|
+
|
|
188
|
+
**Semantic versioning contract:**
|
|
189
|
+
- **Patch updates (3.1.0 → 3.1.1)**: Bug fixes only. No API changes. All mods should continue working.
|
|
190
|
+
- **Minor updates (3.1.x → 3.2.0)**: New API additions, no removals. Existing mods should continue working. New features require new API version.
|
|
191
|
+
- **Major updates (3.x → 4.0)**: API breaking changes allowed. Mods targeting the old API version may not load. Provide a migration guide.
|
|
192
|
+
|
|
193
|
+
**Compatibility enforcement:**
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
// mod-loader.ts: Version compatibility checking
|
|
197
|
+
interface ModManifest {
|
|
198
|
+
id: string;
|
|
199
|
+
version: string;
|
|
200
|
+
api_version: string;
|
|
201
|
+
game_version_min: string;
|
|
202
|
+
game_version_max: string;
|
|
203
|
+
dependencies: Array<{ id: string; version: string }>;
|
|
204
|
+
conflicts: Array<{ id: string; reason: string }>;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function checkCompatibility(
|
|
208
|
+
mod: ModManifest,
|
|
209
|
+
gameVersion: string,
|
|
210
|
+
currentApiVersion: string,
|
|
211
|
+
loadedMods: Map<string, ModManifest>
|
|
212
|
+
): { compatible: boolean; errors: string[] } {
|
|
213
|
+
const errors: string[] = [];
|
|
214
|
+
|
|
215
|
+
// Check game version range
|
|
216
|
+
if (!semverSatisfies(gameVersion, `>=${mod.game_version_min}`)) {
|
|
217
|
+
errors.push(
|
|
218
|
+
`Requires game version >= ${mod.game_version_min}, current: ${gameVersion}`
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
if (
|
|
222
|
+
mod.game_version_max &&
|
|
223
|
+
!semverSatisfies(gameVersion, `<=${mod.game_version_max}`)
|
|
224
|
+
) {
|
|
225
|
+
errors.push(
|
|
226
|
+
`Requires game version <= ${mod.game_version_max}, current: ${gameVersion}`
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Check API version compatibility (minor version must match or exceed)
|
|
231
|
+
if (!apiVersionCompatible(mod.api_version, currentApiVersion)) {
|
|
232
|
+
errors.push(
|
|
233
|
+
`Requires API version ${mod.api_version}, current: ${currentApiVersion}`
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Check dependencies are loaded and version-compatible
|
|
238
|
+
for (const dep of mod.dependencies) {
|
|
239
|
+
const loaded = loadedMods.get(dep.id);
|
|
240
|
+
if (!loaded) {
|
|
241
|
+
errors.push(`Missing required dependency: ${dep.id} ${dep.version}`);
|
|
242
|
+
} else if (!semverSatisfies(loaded.version, dep.version)) {
|
|
243
|
+
errors.push(
|
|
244
|
+
`Dependency ${dep.id} version ${loaded.version} ` +
|
|
245
|
+
`does not satisfy ${dep.version}`
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Check for conflicts
|
|
251
|
+
for (const conflict of mod.conflicts) {
|
|
252
|
+
if (loadedMods.has(conflict.id)) {
|
|
253
|
+
errors.push(
|
|
254
|
+
`Conflicts with loaded mod ${conflict.id}: ${conflict.reason}`
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return { compatible: errors.length === 0, errors };
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Load order resolution:**
|
|
264
|
+
|
|
265
|
+
Mods load in a deterministic order based on dependencies. Use topological sort:
|
|
266
|
+
|
|
267
|
+
1. Build a dependency graph from all enabled mods
|
|
268
|
+
2. Detect cycles (circular dependencies are an error — refuse to load the cycle)
|
|
269
|
+
3. Topologically sort the graph — dependencies load before dependents
|
|
270
|
+
4. Within the same dependency tier, sort by the `load_order` field (lower numbers load first)
|
|
271
|
+
5. Data mods that replace the same asset use "last loaded wins" — the mod with the highest load order takes priority
|
|
272
|
+
|
|
273
|
+
### Content Moderation Pipeline
|
|
274
|
+
|
|
275
|
+
UGC platforms must moderate content to prevent copyright infringement, hate speech, exploits, and inappropriate material.
|
|
276
|
+
|
|
277
|
+
**Automated moderation (first pass):**
|
|
278
|
+
- **Hash matching**: Compare uploaded textures and audio against known copyrighted material databases (e.g., YouTube Content ID equivalent)
|
|
279
|
+
- **Text scanning**: Scan mod descriptions, in-game text, and script string literals for slurs, hate speech, and banned terms
|
|
280
|
+
- **Image classification**: ML-based NSFW detection on uploaded textures and thumbnails
|
|
281
|
+
- **Malware scanning**: Scan executable content (scripts, native plugins) for known malware signatures and suspicious patterns (file system access, network calls, process spawning)
|
|
282
|
+
- **Size limits**: Enforce maximum mod package size (e.g., 2 GB) to prevent abuse of storage infrastructure
|
|
283
|
+
|
|
284
|
+
**Community moderation (second pass):**
|
|
285
|
+
- Player reporting system: "Report this mod" button with category selection (offensive, broken, copyright, malware)
|
|
286
|
+
- Reputation system: Trusted modders (positive history, verified identity) get expedited review
|
|
287
|
+
- Community upvotes/downvotes surface quality and flag problems
|
|
288
|
+
|
|
289
|
+
**Manual review (final pass):**
|
|
290
|
+
- Moderation team reviews flagged content within 24-48 hours
|
|
291
|
+
- Clear guidelines published: what is allowed, what is not, appeal process
|
|
292
|
+
- Strike system: 1st strike = warning + content removal, 2nd = 30-day upload ban, 3rd = permanent ban
|
|
293
|
+
- DMCA/takedown process: legal compliance for copyright claims with counter-notification support
|
|
294
|
+
|
|
295
|
+
### Distribution Platforms
|
|
296
|
+
|
|
297
|
+
**Steam Workshop:**
|
|
298
|
+
- Largest PC mod distribution platform (~30,000 games with Workshop support)
|
|
299
|
+
- Steamworks API handles upload, download, update, subscription, and dependency resolution
|
|
300
|
+
- Players subscribe to mods; Steam auto-downloads and updates
|
|
301
|
+
- Workshop items support tags, previews, changelogs, and dependency declarations
|
|
302
|
+
- Revenue sharing available through the Curated Workshop (game developer sets the split)
|
|
303
|
+
- Limitation: Steam-only; mods are not available to players on other stores
|
|
304
|
+
|
|
305
|
+
**mod.io:**
|
|
306
|
+
- Cross-platform mod distribution (PC, console, mobile)
|
|
307
|
+
- REST API + SDKs for Unity, Unreal, and custom engines
|
|
308
|
+
- Handles upload, download, moderation, reporting, and authentication
|
|
309
|
+
- Supports monetization (paid mods) with configurable revenue splits
|
|
310
|
+
- Free tier available for indie developers
|
|
311
|
+
- Integration effort: medium (REST API is straightforward; SDK simplifies further)
|
|
312
|
+
- Advantage over Steam Workshop: works on consoles and non-Steam PC stores
|
|
313
|
+
|
|
314
|
+
**Custom in-game browser:**
|
|
315
|
+
- For games that want full control over the mod experience
|
|
316
|
+
- Build a mod repository server, upload API, and in-game browser UI
|
|
317
|
+
- Higher development cost but complete control over curation, moderation, and branding
|
|
318
|
+
- Appropriate for live-service games with a large dedicated team
|
|
319
|
+
|
|
320
|
+
**Local file-based loading (offline games):**
|
|
321
|
+
- For single-player and offline games, support loading mods from a local directory
|
|
322
|
+
- Standard path: `<game_install>/mods/` or `<user_documents>/GameName/mods/`
|
|
323
|
+
- Scan the directory at startup, validate manifests, and present an in-game mod manager
|
|
324
|
+
- No server infrastructure required
|
|
325
|
+
- Players distribute mods via file sharing, Nexus Mods, or other community sites
|
|
326
|
+
|
|
327
|
+
### Mod Manager UI
|
|
328
|
+
|
|
329
|
+
Every moddable game needs an in-game mod manager:
|
|
330
|
+
|
|
331
|
+
```
|
|
332
|
+
┌──────────────────────────────────────────────────────────┐
|
|
333
|
+
│ MOD MANAGER [Close] │
|
|
334
|
+
├──────────────────────────────────────────────────────────┤
|
|
335
|
+
│ │
|
|
336
|
+
│ Installed Mods (7) [Browse Workshop] │
|
|
337
|
+
│ │
|
|
338
|
+
│ ☑ ▲ Core Library v1.2.0 ★★★★☆ [Configure] │
|
|
339
|
+
│ ☑ ▲ Better Textures v3.0.1 ★★★★★ [Configure] │
|
|
340
|
+
│ ☑ Combat Overhaul v2.1.0 ★★★★☆ [Configure] │
|
|
341
|
+
│ ☑ New Weapons Pack v1.5.0 ★★★☆☆ [Configure] │
|
|
342
|
+
│ ☐ Hardcore Mode v1.0.0 ★★★★☆ [Configure] │
|
|
343
|
+
│ ☑ UI Improvements v4.2.0 ★★★★★ [Configure] │
|
|
344
|
+
│ ⚠ ☑ Old Mod v0.9.0 ★★☆☆☆ [Configure] │
|
|
345
|
+
│ ⚠ Outdated: targets API v1.2, current v1.4 │
|
|
346
|
+
│ │
|
|
347
|
+
│ ────────────────────────────────────────────────────── │
|
|
348
|
+
│ Load Order: [Auto-resolve] [Manual override] │
|
|
349
|
+
│ │
|
|
350
|
+
│ 1. Core Library (dependency: required first) │
|
|
351
|
+
│ 2. Better Textures (data mod: load order 50) │
|
|
352
|
+
│ 3. Combat Overhaul (script mod: load order 100) │
|
|
353
|
+
│ 4. New Weapons Pack (depends on: Combat Overhaul) │
|
|
354
|
+
│ 5. UI Improvements (script mod: load order 200) │
|
|
355
|
+
│ │
|
|
356
|
+
│ [Apply Changes] [Revert] [Export Load Order] │
|
|
357
|
+
└──────────────────────────────────────────────────────────┘
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Essential mod manager features:
|
|
361
|
+
- Enable/disable individual mods without uninstalling
|
|
362
|
+
- Drag-and-drop or arrow-button load order adjustment
|
|
363
|
+
- Dependency resolution with clear error messages ("Mod A requires Mod B v2.0+, you have v1.3")
|
|
364
|
+
- Conflict detection with explanation ("Mod A and Mod B both modify the combat system — only one can be active")
|
|
365
|
+
- Compatibility warnings for mods targeting older API versions
|
|
366
|
+
- Per-mod configuration UI (if the mod exposes settings via `config/defaults.json`)
|
|
367
|
+
- "Disable all mods" panic button for troubleshooting crashes
|
|
368
|
+
- Export/import load order profiles for sharing configurations
|
|
369
|
+
|
|
370
|
+
### Save File Compatibility
|
|
371
|
+
|
|
372
|
+
Mods that add entities, items, or game state to save files create a compatibility challenge when mods are added or removed mid-playthrough.
|
|
373
|
+
|
|
374
|
+
**Design rules:**
|
|
375
|
+
- Separate mod data from base game data in the save file. Use a namespace: `mods.com.author.modname.custom_data`
|
|
376
|
+
- When loading a save with missing mod data, warn the player but do not crash. Remove orphaned mod data gracefully.
|
|
377
|
+
- When loading a save with a mod that was not present when the save was created, initialize the mod's state to defaults
|
|
378
|
+
- Never let mod data corrupt base game save data. Validate the save structure before writing.
|
|
379
|
+
- Provide a "clean save" option that strips all mod data and reverts to the base game state
|
|
380
|
+
|
|
381
|
+
### Telemetry and Crash Reporting with Mods
|
|
382
|
+
|
|
383
|
+
Mods are the leading cause of player-reported crashes, but players often blame the base game:
|
|
384
|
+
|
|
385
|
+
- Tag every crash report with the list of active mods and their versions
|
|
386
|
+
- If a crash occurs inside mod code (sandboxed script), report it as a mod crash, not a game crash
|
|
387
|
+
- Aggregate crash data by mod combination to identify problematic mods or mod interactions
|
|
388
|
+
- Expose a "mod-free mode" that launches the game with all mods disabled for comparison testing
|
|
389
|
+
- In the crash report UI, display: "You have 7 mods installed. Try disabling mods to determine if a mod is causing this issue."
|
|
390
|
+
- Provide mod authors with anonymized crash data for their mod so they can fix issues proactively
|