universal-game-modder 0.1.4

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.
Files changed (92) hide show
  1. package/ACCEPTABLE_USE.md +49 -0
  2. package/CHANGELOG.md +119 -0
  3. package/LICENSE +21 -0
  4. package/LICENSE-EULA.md +62 -0
  5. package/README.md +162 -0
  6. package/VERIFICATION.md +132 -0
  7. package/dist/backends/child-pool.js +91 -0
  8. package/dist/backends/mcp-child.js +102 -0
  9. package/dist/backends/mcp-child.js.map +1 -0
  10. package/dist/core/config.d.ts +10 -0
  11. package/dist/core/config.js +48 -0
  12. package/dist/core/config.js.map +1 -0
  13. package/dist/core/engine-detector.d.ts +13 -0
  14. package/dist/core/engine-detector.js +247 -0
  15. package/dist/core/engine-detector.js.map +1 -0
  16. package/dist/core/event-bus.js +18 -0
  17. package/dist/core/file-classifier.js +394 -0
  18. package/dist/core/model-db.js +517 -0
  19. package/dist/core/model-db.js.map +1 -0
  20. package/dist/core/router.d.ts +6 -0
  21. package/dist/core/router.js +143 -0
  22. package/dist/core/router.js.map +1 -0
  23. package/dist/core/session.js +114 -0
  24. package/dist/decoders/audio-export.js +57 -0
  25. package/dist/decoders/mesh-export.d.ts +20 -0
  26. package/dist/decoders/mesh-export.js +174 -0
  27. package/dist/decoders/mesh-export.js.map +1 -0
  28. package/dist/decoders/native/disassembler.js +85 -0
  29. package/dist/decoders/native/disassembler.js.map +1 -0
  30. package/dist/decoders/png-encode.d.ts +17 -0
  31. package/dist/decoders/png-encode.js +33 -0
  32. package/dist/decoders/texture-decode.js +278 -0
  33. package/dist/decoders/texture-decode.js.map +1 -0
  34. package/dist/decoders/unity/audioclip.d.ts +55 -0
  35. package/dist/decoders/unity/audioclip.js +87 -0
  36. package/dist/decoders/unity/audioclip.js.map +1 -0
  37. package/dist/decoders/unity/binary-reader.d.ts +45 -0
  38. package/dist/decoders/unity/binary-reader.js +141 -0
  39. package/dist/decoders/unity/binary-reader.js.map +1 -0
  40. package/dist/decoders/unity/mesh.js +198 -0
  41. package/dist/decoders/unity/mesh.js.map +1 -0
  42. package/dist/decoders/unity/ress-reader.d.ts +33 -0
  43. package/dist/decoders/unity/ress-reader.js +82 -0
  44. package/dist/decoders/unity/ress-reader.js.map +1 -0
  45. package/dist/decoders/unity/serialized-file.d.ts +65 -0
  46. package/dist/decoders/unity/serialized-file.js +144 -0
  47. package/dist/decoders/unity/texture2d.js +185 -0
  48. package/dist/decoders/unity/vertex-data.d.ts +54 -0
  49. package/dist/decoders/unity/vertex-data.js +174 -0
  50. package/dist/decoders/unity/vertex-data.js.map +1 -0
  51. package/dist/index.d.ts +1 -0
  52. package/dist/index.js +23 -0
  53. package/dist/server.d.ts +1 -0
  54. package/dist/server.js +66 -0
  55. package/dist/tools/decode-tools.d.ts +29 -0
  56. package/dist/tools/decode-tools.js +441 -0
  57. package/dist/tools/delegates/jar-editor.d.ts +2 -0
  58. package/dist/tools/delegates/jar-editor.js +276 -0
  59. package/dist/tools/delegates/unity-decompiler.d.ts +2 -0
  60. package/dist/tools/delegates/unity-decompiler.js +291 -0
  61. package/dist/tools/delegates/unreal-assets.d.ts +2 -0
  62. package/dist/tools/delegates/unreal-assets.js +122 -0
  63. package/dist/tools/detection-tools.d.ts +2 -0
  64. package/dist/tools/detection-tools.js +164 -0
  65. package/dist/tools/detection-tools.js.map +1 -0
  66. package/dist/tools/index.d.ts +8 -0
  67. package/dist/tools/index.js +30 -0
  68. package/dist/tools/index.js.map +1 -0
  69. package/dist/tools/native/index.d.ts +2 -0
  70. package/dist/tools/native/index.js +1201 -0
  71. package/dist/tools/native-comprehension-tools.js +298 -0
  72. package/dist/tools/native-comprehension-tools.js.map +1 -0
  73. package/dist/tools/unpack-tools.d.ts +2 -0
  74. package/dist/tools/unpack-tools.js +382 -0
  75. package/dist/tools/workflow-tools.d.ts +2 -0
  76. package/dist/tools/workflow-tools.js +434 -0
  77. package/dist/utils/logger.d.ts +11 -0
  78. package/dist/utils/logger.js +49 -0
  79. package/dist/utils/logger.js.map +1 -0
  80. package/dist/utils/path-utils.d.ts +15 -0
  81. package/dist/utils/path-utils.js +24 -0
  82. package/dist/utils/path-utils.js.map +1 -0
  83. package/dist/utils/steam-finder.d.ts +8 -0
  84. package/dist/utils/steam-finder.js +32 -0
  85. package/dist/web/web-server.d.ts +1 -0
  86. package/dist/web/web-server.js +369 -0
  87. package/licenses/THIRD-PARTY-NOTICES.md +35 -0
  88. package/llms-install.md +34 -0
  89. package/package.json +69 -0
  90. package/server.json +20 -0
  91. package/setup.ps1 +34 -0
  92. package/ugm.config.json +9 -0
@@ -0,0 +1,369 @@
1
+ import express from 'express';
2
+ import { createServer } from 'http';
3
+ import { WebSocketServer, WebSocket } from 'ws';
4
+ import { join, dirname } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { existsSync } from 'fs';
7
+ import { session } from '../core/session.js';
8
+ import { eventBus } from '../core/event-bus.js';
9
+ import { childPool } from '../backends/child-pool.js';
10
+ import { logger } from '../utils/logger.js';
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+ let wss;
14
+ function broadcast(event, data) {
15
+ if (!wss)
16
+ return;
17
+ const message = JSON.stringify({ event, data, timestamp: Date.now() });
18
+ wss.clients.forEach(client => {
19
+ if (client.readyState === WebSocket.OPEN) {
20
+ client.send(message);
21
+ }
22
+ });
23
+ }
24
+ export function startWebServer(port) {
25
+ const app = express();
26
+ app.use(express.json());
27
+ // Serve static files (React build)
28
+ const publicDir = join(__dirname, '..', 'web', 'public');
29
+ if (existsSync(publicDir)) {
30
+ app.use(express.static(publicDir));
31
+ }
32
+ // API endpoints
33
+ app.get('/api/session', (_req, res) => {
34
+ res.json(session.getSerializableState());
35
+ });
36
+ app.get('/api/children', (_req, res) => {
37
+ res.json(childPool.getStatus());
38
+ });
39
+ app.get('/api/health', (_req, res) => {
40
+ res.json({
41
+ status: 'ok',
42
+ uptime: process.uptime(),
43
+ memory: process.memoryUsage(),
44
+ session: {
45
+ gameName: session.getState().gameName,
46
+ engine: session.getState().engine,
47
+ },
48
+ });
49
+ });
50
+ // SPA fallback
51
+ app.get('*', (_req, res) => {
52
+ const indexPath = join(publicDir, 'index.html');
53
+ if (existsSync(indexPath)) {
54
+ res.sendFile(indexPath);
55
+ }
56
+ else {
57
+ // Serve inline dashboard if no React build exists
58
+ res.send(getInlineDashboard());
59
+ }
60
+ });
61
+ const server = createServer(app);
62
+ // WebSocket for real-time updates
63
+ wss = new WebSocketServer({ server });
64
+ wss.on('connection', (ws) => {
65
+ // Send current state on connect
66
+ ws.send(JSON.stringify({
67
+ event: 'session:full',
68
+ data: session.getSerializableState(),
69
+ timestamp: Date.now(),
70
+ }));
71
+ ws.send(JSON.stringify({
72
+ event: 'children:status',
73
+ data: childPool.getStatus(),
74
+ timestamp: Date.now(),
75
+ }));
76
+ });
77
+ // Wire up event bus to WebSocket broadcast
78
+ const events = [
79
+ 'tool:start', 'tool:complete', 'tool:error',
80
+ 'session:updated', 'child:status', 'build:output', 'engine:detected',
81
+ ];
82
+ for (const event of events) {
83
+ eventBus.on(event, (data) => broadcast(event, data));
84
+ }
85
+ // A dashboard bind failure (e.g. EADDRINUSE) must never kill the MCP server.
86
+ // listen() errors are emitted async on the 'error' event, so a try/catch
87
+ // around startWebServer() cannot catch them.
88
+ server.on('error', (err) => {
89
+ logger.warn(`UGM Web Dashboard unavailable (port ${port}): ${err.message}`);
90
+ });
91
+ wss.on('error', (err) => {
92
+ logger.warn(`UGM Web Dashboard WebSocket error: ${err.message}`);
93
+ });
94
+ server.listen(port, () => {
95
+ logger.info(`UGM Web Dashboard running at http://localhost:${port}`);
96
+ });
97
+ }
98
+ function getInlineDashboard() {
99
+ return `<!DOCTYPE html>
100
+ <html lang="en">
101
+ <head>
102
+ <meta charset="UTF-8">
103
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
104
+ <title>Universal Game Modder</title>
105
+ <style>
106
+ * { margin: 0; padding: 0; box-sizing: border-box; }
107
+ body { font-family: 'Segoe UI', system-ui, sans-serif; background: #0a0a0f; color: #e0e0e0; }
108
+ .header {
109
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
110
+ padding: 20px 30px; border-bottom: 2px solid #e94560;
111
+ display: flex; align-items: center; gap: 15px;
112
+ }
113
+ .header h1 { font-size: 24px; color: #fff; }
114
+ .header .subtitle { color: #888; font-size: 14px; }
115
+ .engine-badge {
116
+ padding: 4px 12px; border-radius: 12px; font-size: 12px; font-weight: 600;
117
+ text-transform: uppercase; letter-spacing: 1px;
118
+ }
119
+ .engine-unity { background: #1a73e8; color: white; }
120
+ .engine-unreal { background: #9b59b6; color: white; }
121
+ .engine-java { background: #e67e22; color: white; }
122
+ .engine-godot { background: #27ae60; color: white; }
123
+ .engine-native { background: #666; color: white; }
124
+ .engine-none { background: #333; color: #888; }
125
+ .container { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; padding: 20px; max-width: 1600px; margin: 0 auto; }
126
+ .panel { background: #12121a; border: 1px solid #2a2a3a; border-radius: 8px; overflow: hidden; }
127
+ .panel-header { padding: 12px 16px; background: #1a1a2e; border-bottom: 1px solid #2a2a3a; font-weight: 600; font-size: 14px; display: flex; justify-content: space-between; align-items: center; }
128
+ .panel-body { padding: 16px; max-height: 400px; overflow-y: auto; }
129
+ .panel-body::-webkit-scrollbar { width: 6px; }
130
+ .panel-body::-webkit-scrollbar-thumb { background: #333; border-radius: 3px; }
131
+ .info-row { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #1a1a2a; }
132
+ .info-label { color: #888; }
133
+ .info-value { color: #e0e0e0; font-family: 'Cascadia Code', monospace; }
134
+ .log-entry { padding: 6px 8px; border-bottom: 1px solid #1a1a2a; font-size: 13px; font-family: monospace; }
135
+ .log-entry.success { border-left: 3px solid #27ae60; }
136
+ .log-entry.error { border-left: 3px solid #e94560; }
137
+ .log-entry.pending { border-left: 3px solid #f39c12; }
138
+ .log-time { color: #555; font-size: 11px; }
139
+ .log-tool { color: #1a73e8; font-weight: 600; }
140
+ .log-backend { color: #888; font-size: 11px; }
141
+ .log-duration { color: #27ae60; float: right; }
142
+ .child-status { display: flex; align-items: center; gap: 8px; padding: 8px 0; }
143
+ .status-dot { width: 8px; height: 8px; border-radius: 50%; }
144
+ .status-dot.connected { background: #27ae60; box-shadow: 0 0 6px #27ae60; }
145
+ .status-dot.idle { background: #555; }
146
+ .status-dot.disconnected { background: #e94560; }
147
+ .status-dot.error { background: #e94560; box-shadow: 0 0 6px #e94560; }
148
+ .full-width { grid-column: 1 / -1; }
149
+ .stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
150
+ .stat-card { background: #1a1a2e; border-radius: 6px; padding: 16px; text-align: center; }
151
+ .stat-value { font-size: 28px; font-weight: 700; color: #e94560; }
152
+ .stat-label { font-size: 12px; color: #888; margin-top: 4px; }
153
+ .empty-state { color: #555; text-align: center; padding: 40px; font-size: 14px; }
154
+ </style>
155
+ </head>
156
+ <body>
157
+ <div class="header">
158
+ <div>
159
+ <h1>Universal Game Modder</h1>
160
+ <div class="subtitle">MCP Tool Orchestrator</div>
161
+ </div>
162
+ <span id="engineBadge" class="engine-badge engine-none">No Game Loaded</span>
163
+ <div style="margin-left: auto; color: #555; font-size: 12px;">
164
+ <span id="connectionStatus">Connecting...</span>
165
+ </div>
166
+ </div>
167
+
168
+ <div class="container">
169
+ <!-- Stats -->
170
+ <div class="panel full-width">
171
+ <div class="panel-body">
172
+ <div class="stat-grid">
173
+ <div class="stat-card">
174
+ <div class="stat-value" id="statToolCalls">0</div>
175
+ <div class="stat-label">Tool Calls</div>
176
+ </div>
177
+ <div class="stat-card">
178
+ <div class="stat-value" id="statSuccessRate">-</div>
179
+ <div class="stat-label">Success Rate</div>
180
+ </div>
181
+ <div class="stat-card">
182
+ <div class="stat-value" id="statTypesFound">0</div>
183
+ <div class="stat-label">Types Cached</div>
184
+ </div>
185
+ <div class="stat-card">
186
+ <div class="stat-value" id="statUptime">0s</div>
187
+ <div class="stat-label">Uptime</div>
188
+ </div>
189
+ </div>
190
+ </div>
191
+ </div>
192
+
193
+ <!-- Game Info -->
194
+ <div class="panel">
195
+ <div class="panel-header">Game Info</div>
196
+ <div class="panel-body" id="gameInfo">
197
+ <div class="empty-state">No game loaded. Use load_game or mod_this_game to start.</div>
198
+ </div>
199
+ </div>
200
+
201
+ <!-- Child Processes -->
202
+ <div class="panel">
203
+ <div class="panel-header">Backend Processes</div>
204
+ <div class="panel-body" id="childProcesses">
205
+ <div class="child-status" data-name="unity-decompiler"><span class="status-dot disconnected"></span> unity-decompiler <span style="color:#555;margin-left:auto">idle</span></div>
206
+ <div class="child-status" data-name="unreal-assets"><span class="status-dot disconnected"></span> unreal-assets <span style="color:#555;margin-left:auto">idle</span></div>
207
+ <div class="child-status" data-name="jar-editor"><span class="status-dot disconnected"></span> jar-editor <span style="color:#555;margin-left:auto">idle</span></div>
208
+ </div>
209
+ </div>
210
+
211
+ <!-- Tool Log -->
212
+ <div class="panel full-width">
213
+ <div class="panel-header">
214
+ Tool Call Log
215
+ <span id="logCount" style="color:#555;font-size:12px">0 calls</span>
216
+ </div>
217
+ <div class="panel-body" id="toolLog" style="max-height: 500px;">
218
+ <div class="empty-state">Waiting for tool calls...</div>
219
+ </div>
220
+ </div>
221
+ </div>
222
+
223
+ <script>
224
+ const state = { toolCalls: 0, successes: 0, startTime: Date.now() };
225
+
226
+ function connect() {
227
+ const ws = new WebSocket('ws://' + location.host);
228
+
229
+ ws.onopen = () => {
230
+ document.getElementById('connectionStatus').textContent = 'Connected';
231
+ document.getElementById('connectionStatus').style.color = '#27ae60';
232
+ };
233
+
234
+ ws.onclose = () => {
235
+ document.getElementById('connectionStatus').textContent = 'Disconnected';
236
+ document.getElementById('connectionStatus').style.color = '#e94560';
237
+ setTimeout(connect, 2000);
238
+ };
239
+
240
+ ws.onmessage = (event) => {
241
+ const msg = JSON.parse(event.data);
242
+ handleEvent(msg.event, msg.data);
243
+ };
244
+ }
245
+
246
+ function handleEvent(event, data) {
247
+ switch (event) {
248
+ case 'session:full':
249
+ case 'session:updated':
250
+ updateGameInfo(data);
251
+ break;
252
+ case 'tool:start':
253
+ addLogEntry(data, 'pending');
254
+ break;
255
+ case 'tool:complete':
256
+ updateLogEntry(data);
257
+ state.toolCalls++;
258
+ if (data.success) state.successes++;
259
+ updateStats();
260
+ break;
261
+ case 'tool:error':
262
+ addLogEntry(data, 'error');
263
+ state.toolCalls++;
264
+ updateStats();
265
+ break;
266
+ case 'child:status':
267
+ updateChildStatus(data);
268
+ break;
269
+ case 'engine:detected':
270
+ updateEngineBadge(data.engine, data.runtime);
271
+ break;
272
+ case 'children:status':
273
+ updateAllChildren(data);
274
+ break;
275
+ }
276
+ }
277
+
278
+ function updateGameInfo(data) {
279
+ const el = document.getElementById('gameInfo');
280
+ if (!data.gameName && !data.gamePath) return;
281
+ el.innerHTML = [
282
+ row('Game', data.gameName || 'Unknown'),
283
+ row('Path', data.gamePath || '-'),
284
+ row('Engine', data.engine || 'Not detected'),
285
+ row('Runtime', data.runtime || '-'),
286
+ row('Assemblies', (data.loadedAssemblies || []).length),
287
+ row('Framework', data.modFramework || '-'),
288
+ ].join('');
289
+ if (data.engine) updateEngineBadge(data.engine, data.runtime);
290
+ if (data.discoveredTypesCount) document.getElementById('statTypesFound').textContent = data.discoveredTypesCount;
291
+ }
292
+
293
+ function row(label, value) {
294
+ return '<div class="info-row"><span class="info-label">' + label + '</span><span class="info-value">' + value + '</span></div>';
295
+ }
296
+
297
+ function addLogEntry(data, status) {
298
+ const el = document.getElementById('toolLog');
299
+ if (el.querySelector('.empty-state')) el.innerHTML = '';
300
+ const entry = document.createElement('div');
301
+ entry.className = 'log-entry ' + status;
302
+ entry.id = 'log-' + data.tool + '-' + (data.timestamp || Date.now());
303
+ entry.innerHTML = '<span class="log-time">' + new Date().toLocaleTimeString() + '</span> '
304
+ + '<span class="log-tool">' + data.tool + '</span> '
305
+ + '<span class="log-backend">[' + data.backend + ']</span>'
306
+ + (status === 'pending' ? '<span class="log-duration" style="color:#f39c12">running...</span>' : '');
307
+ el.insertBefore(entry, el.firstChild);
308
+ document.getElementById('logCount').textContent = el.children.length + ' calls';
309
+ }
310
+
311
+ function updateLogEntry(data) {
312
+ const entries = document.querySelectorAll('.log-entry.pending');
313
+ for (const entry of entries) {
314
+ if (entry.querySelector('.log-tool')?.textContent === data.tool) {
315
+ entry.className = 'log-entry ' + (data.success ? 'success' : 'error');
316
+ const dur = entry.querySelector('.log-duration');
317
+ if (dur) { dur.textContent = data.duration + 'ms'; dur.style.color = data.success ? '#27ae60' : '#e94560'; }
318
+ return;
319
+ }
320
+ }
321
+ addLogEntry(data, data.success ? 'success' : 'error');
322
+ }
323
+
324
+ function updateChildStatus(data) {
325
+ const container = document.getElementById('childProcesses');
326
+ const dotClass = { spawned: 'connected', running: 'connected', idle: 'idle', exited: 'disconnected', error: 'error' };
327
+ const labelText = { spawned: 'running', running: 'running', idle: 'idle', exited: 'stopped', error: 'error' };
328
+ const labelColor = { spawned: '#27ae60', running: '#27ae60', idle: '#888', exited: '#e94560', error: '#e94560' };
329
+ const s = data.status || 'idle';
330
+ const existing = container.querySelector('[data-name="' + data.name + '"]');
331
+ const html = '<span class="status-dot ' + (dotClass[s] || 'idle') + '"></span> '
332
+ + data.name + ' <span style="color:' + (labelColor[s] || '#888') + ';margin-left:auto">' + (labelText[s] || s) + '</span>';
333
+ if (existing) { existing.innerHTML = html; }
334
+ else { const div = document.createElement('div'); div.className = 'child-status'; div.dataset.name = data.name; div.innerHTML = html; container.appendChild(div); }
335
+ }
336
+
337
+ function updateAllChildren(data) {
338
+ for (const [name, info] of Object.entries(data)) {
339
+ updateChildStatus({ name, status: info.connected ? 'running' : 'idle' });
340
+ }
341
+ }
342
+
343
+ function updateEngineBadge(engine, runtime) {
344
+ const el = document.getElementById('engineBadge');
345
+ const engineClasses = { 'unity-mono': 'unity', 'unity-il2cpp': 'unity', 'unreal': 'unreal', 'java': 'java', 'godot': 'godot' };
346
+ const cls = engineClasses[engine] || 'native';
347
+ el.className = 'engine-badge engine-' + cls;
348
+ el.textContent = (engine || 'Unknown') + (runtime ? ' (' + runtime + ')' : '');
349
+ }
350
+
351
+ function updateStats() {
352
+ document.getElementById('statToolCalls').textContent = state.toolCalls;
353
+ document.getElementById('statSuccessRate').textContent = state.toolCalls > 0
354
+ ? Math.round(state.successes / state.toolCalls * 100) + '%' : '-';
355
+ }
356
+
357
+ setInterval(() => {
358
+ const secs = Math.floor((Date.now() - state.startTime) / 1000);
359
+ const mins = Math.floor(secs / 60);
360
+ const hrs = Math.floor(mins / 60);
361
+ document.getElementById('statUptime').textContent = hrs > 0 ? hrs + 'h' + (mins%60) + 'm' : mins > 0 ? mins + 'm' : secs + 's';
362
+ }, 1000);
363
+
364
+ connect();
365
+ </script>
366
+ </body>
367
+ </html>`;
368
+ }
369
+ //# sourceMappingURL=web-server.js.map
@@ -0,0 +1,35 @@
1
+ # Third-Party Notices
2
+
3
+ Universal Game Modder uses the following third-party components. This file records their licenses and attribution.
4
+
5
+ ## Runtime dependencies (bundled in the free open-core edition)
6
+
7
+ The free edition's native tools run in-process via these npm packages (installed by `npm install`, not vendored in this repo):
8
+
9
+ | Component | Purpose | License |
10
+ |-----------|---------|---------|
11
+ | @modelcontextprotocol/sdk | MCP protocol implementation | MIT |
12
+ | better-sqlite3 | Session state storage | MIT |
13
+ | express | Local web server (Pro dashboard) | MIT |
14
+ | ws | WebSocket transport (Pro dashboard) | MIT |
15
+ | pngjs | PNG handling | MIT |
16
+ | texture2ddecoder-wasm | Texture decoding | MIT |
17
+
18
+ ## Pro-tier delegate backends (NOT bundled in this free edition)
19
+
20
+ The Pro tier delegates to external decompilation/asset engines. Their licenses are recorded here for completeness. **None of these binaries ship in the free open-core edition** — they are separate, user-installed or Pro-bundled components. When bundled in the Pro tier, each engine's own LICENSE and NOTICE file must accompany it.
21
+
22
+ | Engine | Used by | License | Obligation on redistribution |
23
+ |--------|---------|---------|------------------------------|
24
+ | ICSharpCode.Decompiler (ILSpy) | Unity Mono/IL2CPP decompilation | MIT | Include MIT license text |
25
+ | CUE4Parse / CUE4Parse-Conversion | Unreal asset reading | Apache-2.0 | Include license text + NOTICE, document modifications |
26
+ | CFR | Java JAR decompilation | MIT | Include MIT license text |
27
+ | Lib.Harmony | Runtime patching (Unreal test path) | MIT / LGPL-2.1 (dual) | Use the MIT path; include license text |
28
+
29
+ ## Known pending item
30
+
31
+ - **capstone-wasm** (native disassembly binding, from the `disasm-web` monorepo): **license undeclared** at the package level. The underlying Capstone engine is BSD-3, but the wasm wrapper carries no explicit license. Native disassembly is therefore **excluded from any shipped bundle** until this is resolved (confirm the monorepo's LICENSE, swap the binding, or make disassembly an optional user-installed capability). It is not part of the free open-core edition.
32
+
33
+ ---
34
+
35
+ *Every license listed above is permissive (MIT / Apache-2.0). None imposes a copyleft source-disclosure obligation on Universal Game Modder's own code.*
@@ -0,0 +1,34 @@
1
+ # Installing Universal Game Modder (for AI agents such as Cline)
2
+
3
+ Universal Game Modder (UGM) is a local stdio MCP server. It needs Node.js 18+ and nothing else.
4
+
5
+ ## Steps
6
+
7
+ 1. Clone the repository:
8
+ `git clone https://github.com/doritoman90000/universal-game-modder.git`
9
+ 2. Install dependencies from the repository root (`dist/` is prebuilt; there is nothing to compile):
10
+ `npm install`
11
+ 3. Register the server with the MCP client. Use the ABSOLUTE path to `dist/index.js` inside the cloned folder. Example MCP settings entry:
12
+
13
+ ```json
14
+ {
15
+ "mcpServers": {
16
+ "universal-game-modder": {
17
+ "command": "node",
18
+ "args": ["/absolute/path/to/universal-game-modder/dist/index.js"]
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ 4. Restart the MCP client. The server announces itself as `universal-game-modder` and lists 100 tools. 29 of them (engine detection, session, and the native binary toolset) run entirely in-process with no configuration. The remaining tools route to optional external backends and return a clear "not configured" error until a path is set in `ugm.config.json`.
25
+
26
+ ## Verify
27
+
28
+ Ask the agent to call `detect_engine` with `{"game_path": "<a game folder>"}` or `analyze_file_format` with `{"file_path": "<any .exe or .dll>"}`. Both return real data on a fresh install. The full verified call list is in `VERIFICATION.md`; the release gate that produced it is `verify-release.mjs`.
29
+
30
+ ## Notes
31
+
32
+ - No API keys, no accounts, no network calls. Everything runs on the local machine.
33
+ - Optional: a machine-specific `ugm.config.local.json` (git-ignored) holds backend paths; the tracked `ugm.config.json` is placeholders only.
34
+ - Read `ACCEPTABLE_USE.md` and `LICENSE-EULA.md` before use. UGM is for modding, interoperability, research and education on software you legally own.
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "universal-game-modder",
3
+ "version": "0.1.4",
4
+ "mcpName": "io.github.doritoman90000/universal-game-modder",
5
+ "description": "Universal Game Modding Machine - One MCP server to mod any game",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "dist/index.js",
9
+ "bin": {
10
+ "universal-game-modder": "dist/index.js"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "licenses",
15
+ "ugm.config.json",
16
+ "setup.ps1",
17
+ "README.md",
18
+ "CHANGELOG.md",
19
+ "VERIFICATION.md",
20
+ "LICENSE",
21
+ "LICENSE-EULA.md",
22
+ "ACCEPTABLE_USE.md",
23
+ "llms-install.md",
24
+ "server.json"
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/doritoman90000/universal-game-modder.git"
29
+ },
30
+ "homepage": "https://github.com/doritoman90000/universal-game-modder#readme",
31
+ "bugs": {
32
+ "url": "https://github.com/doritoman90000/universal-game-modder/issues"
33
+ },
34
+ "keywords": [
35
+ "mcp",
36
+ "model-context-protocol",
37
+ "game-modding",
38
+ "reverse-engineering",
39
+ "binary-analysis",
40
+ "unity",
41
+ "unreal",
42
+ "godot"
43
+ ],
44
+ "scripts": {
45
+ "build": "tsc",
46
+ "build:ui": "cd src/web/public && npx vite build --outDir ../../../dist/web/public",
47
+ "build:all": "npm run build && npm run build:ui",
48
+ "dev": "tsx watch src/index.ts",
49
+ "start": "node dist/index.js"
50
+ },
51
+ "dependencies": {
52
+ "@modelcontextprotocol/sdk": "^1.12.1",
53
+ "better-sqlite3": "^11.8.0",
54
+ "capstone-wasm": "^1.0.3",
55
+ "express": "^4.21.0",
56
+ "pngjs": "^7.0.0",
57
+ "texture2ddecoder-wasm": "^1.2.2",
58
+ "ws": "^8.18.0"
59
+ },
60
+ "devDependencies": {
61
+ "@types/better-sqlite3": "^7.6.11",
62
+ "@types/express": "^4.17.21",
63
+ "@types/node": "^22.10.0",
64
+ "@types/pngjs": "^6.0.5",
65
+ "@types/ws": "^8.5.13",
66
+ "tsx": "^4.19.0",
67
+ "typescript": "^5.7.0"
68
+ }
69
+ }
package/server.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.doritoman90000/universal-game-modder",
4
+ "description": "Local MCP server: detects a game's engine and gives agents native binary analysis/patching tools.",
5
+ "repository": {
6
+ "url": "https://github.com/doritoman90000/universal-game-modder",
7
+ "source": "github"
8
+ },
9
+ "version": "0.1.4",
10
+ "packages": [
11
+ {
12
+ "registryType": "npm",
13
+ "identifier": "universal-game-modder",
14
+ "version": "0.1.4",
15
+ "transport": {
16
+ "type": "stdio"
17
+ }
18
+ }
19
+ ]
20
+ }
package/setup.ps1 ADDED
@@ -0,0 +1,34 @@
1
+ # Universal Game Modder - setup (Windows PowerShell)
2
+ # One-command install for the free open-core edition (v0.1.0).
3
+
4
+ $ErrorActionPreference = "Stop"
5
+ Set-Location -Path $PSScriptRoot
6
+
7
+ Write-Host "Universal Game Modder - setup" -ForegroundColor Cyan
8
+ Write-Host "-----------------------------------"
9
+
10
+ # 1. Node check
11
+ try {
12
+ $nodeVersion = node --version
13
+ Write-Host "Node.js found: $nodeVersion"
14
+ } catch {
15
+ Write-Host "ERROR: Node.js not found. Install Node.js 18+ from https://nodejs.org and re-run." -ForegroundColor Red
16
+ exit 1
17
+ }
18
+
19
+ # 2. Install dependencies
20
+ Write-Host "Installing dependencies (npm install)..." -ForegroundColor Cyan
21
+ npm install
22
+ if ($LASTEXITCODE -ne 0) { Write-Host "ERROR: npm install failed." -ForegroundColor Red; exit 1 }
23
+
24
+ # 3. Build (dist/ ships prebuilt, but rebuild to be safe if src/ is present)
25
+ if (Test-Path "tsconfig.json") {
26
+ Write-Host "Building (npm run build)..." -ForegroundColor Cyan
27
+ npm run build
28
+ if ($LASTEXITCODE -ne 0) { Write-Host "WARNING: build failed; prebuilt dist/ will be used if present." -ForegroundColor Yellow }
29
+ }
30
+
31
+ Write-Host ""
32
+ Write-Host "Done." -ForegroundColor Green
33
+ Write-Host "Next: register the server with your MCP client. See README.md -> Install." -ForegroundColor Green
34
+ Write-Host "The free edition needs no external paths - ugm.config.json ships with empty placeholders."
@@ -0,0 +1,9 @@
1
+ {
2
+ "unityDecompilerExe": "",
3
+ "unrealAssetExe": "",
4
+ "pythonExe": "python",
5
+ "jarEditorScript": "",
6
+ "webPort": 7777,
7
+ "steamPaths": [],
8
+ "modTemplatesDir": ""
9
+ }