@erik9994857/cag 1.0.3 → 2.0.2

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.
@@ -1,853 +1,929 @@
1
- var path = require("path");
2
- var fs = require("fs");
3
- var childProcess = require("child_process");
4
-
5
- function ExeBuilder(engine) {
6
- this.engine = engine;
7
- this.info = engine.getInfo();
8
- this.projectRoot = engine.projectRoot;
9
- this.outputDir = path.join(this.projectRoot, "dist");
10
- this.tempDir = path.join(this.outputDir, ".cag-build");
11
- this.projectName = this.info.ID.split(".")[1] || this.info.ID.split(".")[0] || "cag-app";
12
- }
13
-
14
- ExeBuilder.prototype.build = function (options) {
15
- options = options || {};
16
- var platform = options.platform || this.detectPlatform();
17
- var arch = options.arch || "x64";
18
- var ePlatform = this.mapPlatform(platform);
19
-
20
- this.ensureDirectories();
21
- this.generateElectronApp();
22
- this.installElectron();
23
- this.runPackager(ePlatform, arch);
24
-
25
- var folderName = this.projectName + "-" + ePlatform + "-" + arch;
26
- var folder = path.join(this.outputDir, folderName);
27
- var exeName = this.projectName + (platform === "win" ? ".exe" : "");
28
- var exePath = path.join(folder, exeName);
29
-
30
- this.cleanup();
31
-
32
- return {
33
- outputPath: exePath,
34
- outputDir: folder,
35
- outputName: exeName,
36
- platform: platform,
37
- arch: arch,
38
- size: this.getFileSize(exePath),
39
- success: true
40
- };
41
- };
42
-
43
- ExeBuilder.prototype.buildAll = function (options) {
44
- options = options || {};
45
- var results = [];
46
- var targets = [
47
- { platform: "win", ePlatform: "win32", arch: "x64", ext: ".exe" },
48
- { platform: "macos", ePlatform: "darwin", arch: "x64", ext: "" },
49
- { platform: "linux", ePlatform: "linux", arch: "x64", ext: "" }
50
- ];
51
-
52
- this.ensureDirectories();
53
- this.generateElectronApp();
54
- this.installElectron();
55
-
56
- for (var i = 0; i < targets.length; i++) {
57
- var t = targets[i];
58
- try {
59
- this.runPackager(t.ePlatform, t.arch);
60
- var folderName = this.projectName + "-" + t.ePlatform + "-" + t.arch;
61
- var folder = path.join(this.outputDir, folderName);
62
- var exeName = this.projectName + t.ext;
63
- var exePath = path.join(folder, exeName);
64
- results.push({
65
- outputPath: exePath,
66
- outputDir: folder,
67
- outputName: exeName,
68
- platform: t.platform,
69
- arch: t.arch,
70
- size: this.getFileSize(exePath),
71
- success: true
72
- });
73
- } catch (e) {
74
- results.push({
75
- outputName: this.projectName + t.ext,
76
- platform: t.platform,
77
- arch: t.arch,
78
- success: false,
79
- error: e.message
80
- });
81
- }
82
- }
83
-
84
- this.cleanup();
85
- return results;
86
- };
87
-
88
- ExeBuilder.prototype.ensureDirectories = function () {
89
- if (!fs.existsSync(this.outputDir)) fs.mkdirSync(this.outputDir, { recursive: true });
90
- if (fs.existsSync(this.tempDir)) this.removeDirectory(this.tempDir);
91
- fs.mkdirSync(this.tempDir, { recursive: true });
92
- };
93
-
94
- ExeBuilder.prototype.generateElectronApp = function () {
95
- fs.writeFileSync(path.join(this.tempDir, "main.js"), this.getMainProcessSource());
96
- fs.writeFileSync(path.join(this.tempDir, "index.html"), this.getRendererSource());
97
- var pkg = {
98
- name: this.projectName,
99
- version: this.info.version || "1.0.0",
100
- main: "main.js",
101
- dependencies: {}
102
- };
103
- fs.writeFileSync(path.join(this.tempDir, "package.json"), JSON.stringify(pkg, null, 2));
104
- };
105
-
106
- ExeBuilder.prototype.installElectron = function () {
107
- childProcess.execSync("npm install electron@latest --save --no-audit --no-fund", {
108
- cwd: this.tempDir,
109
- stdio: "inherit"
110
- });
111
- };
112
-
113
- ExeBuilder.prototype.getMainProcessSource = function () {
114
- var lines = [];
115
- lines.push("var electron = require('electron');");
116
- lines.push("var app = electron.app;");
117
- lines.push("var BrowserWindow = electron.BrowserWindow;");
118
- lines.push("var globalShortcut = electron.globalShortcut;");
119
- lines.push("var path = require('path');");
120
- lines.push("");
121
- lines.push("var win = null;");
122
- lines.push("");
123
- lines.push("app.whenReady().then(function () {");
124
- lines.push(" win = new BrowserWindow({");
125
- lines.push(" width: 1280,");
126
- lines.push(" height: 720,");
127
- lines.push(" minWidth: 800,");
128
- lines.push(" minHeight: 600,");
129
- lines.push(" title: " + JSON.stringify(this.info.ID + " - CaG Engine") + ",");
130
- lines.push(" fullscreenable: true,");
131
- lines.push(" webPreferences: {");
132
- lines.push(" nodeIntegration: false,");
133
- lines.push(" contextIsolation: true");
134
- lines.push(" },");
135
- lines.push(" backgroundColor: '#1a1a2e',");
136
- lines.push(" autoHideMenuBar: true,");
137
- lines.push(" show: false");
138
- lines.push(" });");
139
- lines.push(" win.setMenuBarVisibility(false);");
140
- lines.push(" win.loadFile('index.html');");
141
- lines.push(" win.once('ready-to-show', function () { win.show(); });");
142
- lines.push(" win.on('closed', function () { win = null; });");
143
- lines.push(" win.webContents.on('before-input-event', function (event, input) {");
144
- lines.push(" if (input.key === 'F11') {");
145
- lines.push(" win.setFullScreen(!win.isFullScreen());");
146
- lines.push(" }");
147
- lines.push(" });");
148
- lines.push("});");
149
- lines.push("");
150
- lines.push("app.on('window-all-closed', function () { app.quit(); });");
151
- return lines.join("\n");
152
- };
153
-
154
- ExeBuilder.prototype.getRendererSource = function () {
155
- var blocks = this.generateWorldBlocks();
156
- var info = this.info;
157
-
158
- var css = [
159
- "* { margin: 0; padding: 0; box-sizing: border-box; }",
160
- "body { background: #1a1a2e; overflow: hidden; font-family: monospace; }",
161
- "#loading { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #1a1a2e; display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 999; color: #fff; transition: opacity 0.5s; }",
162
- "#loading.fade-out { opacity: 0; pointer-events: none; }",
163
- "#loading h1 { font-size: 64px; color: #4ecca3; margin-bottom: 4px; letter-spacing: 6px; text-shadow: 0 0 30px rgba(78,204,163,0.3); }",
164
- "#loading h2 { font-size: 14px; color: #666; margin-bottom: 30px; font-weight: normal; letter-spacing: 2px; }",
165
- "#load-status { font-size: 12px; color: #555; margin-bottom: 12px; min-height: 18px; transition: color 0.3s; }",
166
- "#bar-outer { width: 300px; height: 3px; background: #2a2a3e; border-radius: 2px; overflow: hidden; }",
167
- "#bar-inner { width: 0%; height: 100%; background: linear-gradient(90deg, #4ecca3, #45b7d1); border-radius: 2px; transition: width 0.4s ease-out; }",
168
- "#loading .version-tag { font-size: 11px; color: #444; margin-top: 20px; }",
169
- "#click-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.65); display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 998; cursor: pointer; transition: opacity 0.3s; }",
170
- "#click-overlay:hover { background: rgba(0,0,0,0.55); }",
171
- "#click-overlay .play-icon { width: 80px; height: 80px; border: 3px solid #4ecca3; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-bottom: 16px; transition: transform 0.2s, border-color 0.2s; }",
172
- "#click-overlay:hover .play-icon { transform: scale(1.1); border-color: #45b7d1; }",
173
- "#click-overlay .play-triangle { width: 0; height: 0; border-style: solid; border-width: 15px 0 15px 28px; border-color: transparent transparent transparent #4ecca3; margin-left: 6px; }",
174
- "#click-overlay span { color: #888; font-size: 16px; letter-spacing: 3px; }",
175
- "canvas { display: block; position: fixed; top: 0; left: 0; }",
176
- "#hud { position: fixed; top: 10px; left: 10px; z-index: 100; pointer-events: none; }",
177
- "#hud-box { background: rgba(0,0,0,0.7); padding: 10px 14px; border-left: 3px solid #4ecca3; backdrop-filter: blur(4px); border-radius: 0 4px 4px 0; }",
178
- "#hud-box div { color: #bbb; font-size: 11px; line-height: 17px; }",
179
- "#hud-box .hud-title { color: #4ecca3; font-size: 13px; font-weight: bold; margin-bottom: 3px; letter-spacing: 1px; }",
180
- "#hud-box .hud-sep { border-top: 1px solid #333; margin: 4px 0; }",
181
- "#crosshair { position: fixed; top: 50%; left: 50%; width: 24px; height: 24px; margin: -12px 0 0 -12px; z-index: 100; pointer-events: none; }",
182
- "#crosshair::before { content: ''; position: absolute; top: 11px; left: 3px; width: 18px; height: 2px; background: rgba(255,255,255,0.6); border-radius: 1px; }",
183
- "#crosshair::after { content: ''; position: absolute; top: 3px; left: 11px; width: 2px; height: 18px; background: rgba(255,255,255,0.6); border-radius: 1px; }",
184
- "#controls-hint { position: fixed; bottom: 10px; right: 10px; z-index: 100; pointer-events: none; background: rgba(0,0,0,0.55); padding: 8px 14px; color: #666; font-size: 10px; border-radius: 4px; line-height: 16px; backdrop-filter: blur(4px); }",
185
- "#controls-hint b { color: #888; }"
186
- ].join("\n");
187
-
188
- var vs = [
189
- "attribute vec3 aPos;",
190
- "attribute vec3 aColor;",
191
- "attribute vec3 aNormal;",
192
- "uniform mat4 uProj;",
193
- "uniform mat4 uView;",
194
- "varying vec3 vColor;",
195
- "varying vec3 vNormal;",
196
- "varying vec3 vWorldPos;",
197
- "void main() {",
198
- " vColor = aColor;",
199
- " vNormal = aNormal;",
200
- " vWorldPos = aPos;",
201
- " gl_Position = uProj * uView * vec4(aPos, 1.0);",
202
- "}"
203
- ].join("\\n");
204
-
205
- var fs_shader = [
206
- "precision mediump float;",
207
- "varying vec3 vColor;",
208
- "varying vec3 vNormal;",
209
- "varying vec3 vWorldPos;",
210
- "uniform vec3 uSunDir;",
211
- "uniform vec3 uSunColor;",
212
- "uniform vec3 uAmbient;",
213
- "uniform vec3 uFogColor;",
214
- "uniform float uFogNear;",
215
- "uniform float uFogFar;",
216
- "uniform vec3 uCamPos;",
217
- "void main() {",
218
- " vec3 norm = normalize(vNormal);",
219
- " float diff = max(dot(norm, normalize(uSunDir)), 0.0);",
220
- " vec3 lit = vColor * (uAmbient + uSunColor * diff);",
221
- " float dist = length(vWorldPos - uCamPos);",
222
- " float fog = clamp((dist - uFogNear) / (uFogFar - uFogNear), 0.0, 1.0);",
223
- " vec3 final = mix(lit, uFogColor, fog);",
224
- " gl_FragColor = vec4(final, 1.0);",
225
- "}"
226
- ].join("\\n");
227
-
228
- var js = [];
229
- js.push("var BLOCKS = " + JSON.stringify(blocks) + ";");
230
- js.push("var INFO = " + JSON.stringify(info) + ";");
231
- js.push("");
232
- js.push("var VS_SRC = \"" + vs + "\";");
233
- js.push("var FS_SRC = \"" + fs_shader + "\";");
234
- js.push("");
235
- js.push("var cag = {");
236
- js.push(" gl: null, canvas: null, program: null,");
237
- js.push(" camX: 16, camY: 12, camZ: 16,");
238
- js.push(" pitch: -0.5, yaw: 0.8,");
239
- js.push(" keys: {}, locked: false,");
240
- js.push(" velY: 0, onGround: false, gravity: 0.015,");
241
- js.push(" fps: 0, frames: 0, lastFps: 0,");
242
- js.push(" vbo: null, vertCount: 0,");
243
- js.push(" blockMap: {},");
244
- js.push(" uProj: null, uView: null, uSunDir: null, uSunColor: null,");
245
- js.push(" uAmbient: null, uFogColor: null, uFogNear: null, uFogFar: null, uCamPos: null,");
246
- js.push("");
247
- js.push(" init: function () {");
248
- js.push(" this.canvas = document.getElementById('cag-canvas');");
249
- js.push(" this.gl = this.canvas.getContext('webgl', { antialias: true });");
250
- js.push(" if (!this.gl) { alert('WebGL not supported'); return; }");
251
- js.push(" this.resize();");
252
- js.push(" this.buildBlockMap();");
253
- js.push(" this.compileShaders();");
254
- js.push(" this.buildMesh();");
255
- js.push(" this.bindInput();");
256
- js.push(" var loadEl = document.getElementById('loading');");
257
- js.push(" loadEl.classList.add('fade-out');");
258
- js.push(" setTimeout(function () {");
259
- js.push(" loadEl.style.display = 'none';");
260
- js.push(" document.getElementById('click-overlay').style.display = 'flex';");
261
- js.push(" }, 500);");
262
- js.push(" this.lastFps = performance.now();");
263
- js.push(" this.loop();");
264
- js.push(" },");
265
- js.push("");
266
- js.push(" buildBlockMap: function () {");
267
- js.push(" for (var i = 0; i < BLOCKS.length; i++) {");
268
- js.push(" var b = BLOCKS[i];");
269
- js.push(" this.blockMap[b.x + ',' + b.y + ',' + b.z] = b;");
270
- js.push(" }");
271
- js.push(" },");
272
- js.push("");
273
- js.push(" hasBlock: function (x, y, z) {");
274
- js.push(" return this.blockMap[x + ',' + y + ',' + z] !== undefined;");
275
- js.push(" },");
276
- js.push("");
277
- js.push(" compileShaders: function () {");
278
- js.push(" var gl = this.gl;");
279
- js.push(" var vs = gl.createShader(gl.VERTEX_SHADER);");
280
- js.push(" gl.shaderSource(vs, VS_SRC); gl.compileShader(vs);");
281
- js.push(" var fs = gl.createShader(gl.FRAGMENT_SHADER);");
282
- js.push(" gl.shaderSource(fs, FS_SRC); gl.compileShader(fs);");
283
- js.push(" this.program = gl.createProgram();");
284
- js.push(" gl.attachShader(this.program, vs);");
285
- js.push(" gl.attachShader(this.program, fs);");
286
- js.push(" gl.linkProgram(this.program);");
287
- js.push(" gl.useProgram(this.program);");
288
- js.push(" this.uProj = gl.getUniformLocation(this.program, 'uProj');");
289
- js.push(" this.uView = gl.getUniformLocation(this.program, 'uView');");
290
- js.push(" this.uSunDir = gl.getUniformLocation(this.program, 'uSunDir');");
291
- js.push(" this.uSunColor = gl.getUniformLocation(this.program, 'uSunColor');");
292
- js.push(" this.uAmbient = gl.getUniformLocation(this.program, 'uAmbient');");
293
- js.push(" this.uFogColor = gl.getUniformLocation(this.program, 'uFogColor');");
294
- js.push(" this.uFogNear = gl.getUniformLocation(this.program, 'uFogNear');");
295
- js.push(" this.uFogFar = gl.getUniformLocation(this.program, 'uFogFar');");
296
- js.push(" this.uCamPos = gl.getUniformLocation(this.program, 'uCamPos');");
297
- js.push(" gl.uniform3f(this.uSunDir, 0.4, 0.8, 0.3);");
298
- js.push(" gl.uniform3f(this.uSunColor, 1.0, 0.95, 0.85);");
299
- js.push(" gl.uniform3f(this.uAmbient, 0.35, 0.38, 0.5);");
300
- js.push(" gl.uniform3f(this.uFogColor, 0.53, 0.81, 0.92);");
301
- js.push(" gl.uniform1f(this.uFogNear, 30.0);");
302
- js.push(" gl.uniform1f(this.uFogFar, 70.0);");
303
- js.push(" },");
304
- js.push("");
305
- js.push(" getBlockColor: function (b, face) {");
306
- js.push(" var r, g, bl;");
307
- js.push(" if (b.t === 'grass') {");
308
- js.push(" if (face === 'top') { r = 0.34; g = 0.68; bl = 0.24; }");
309
- js.push(" else if (face === 'bottom') { r = 0.55; g = 0.38; bl = 0.18; }");
310
- js.push(" else { r = 0.42; g = 0.50; bl = 0.22; }");
311
- js.push(" } else if (b.t === 'dirt') {");
312
- js.push(" r = 0.55; g = 0.38; bl = 0.18;");
313
- js.push(" } else if (b.t === 'stone') {");
314
- js.push(" if (face === 'top') { r = 0.58; g = 0.58; bl = 0.58; }");
315
- js.push(" else { r = 0.50; g = 0.50; bl = 0.50; }");
316
- js.push(" } else if (b.t === 'sand') {");
317
- js.push(" if (face === 'top') { r = 0.86; g = 0.82; bl = 0.58; }");
318
- js.push(" else { r = 0.80; g = 0.76; bl = 0.52; }");
319
- js.push(" } else if (b.t === 'water') {");
320
- js.push(" if (face === 'top') { r = 0.15; g = 0.45; bl = 0.75; }");
321
- js.push(" else { r = 0.10; g = 0.35; bl = 0.65; }");
322
- js.push(" } else if (b.t === 'snow') {");
323
- js.push(" if (face === 'top') { r = 0.95; g = 0.96; bl = 0.98; }");
324
- js.push(" else { r = 0.88; g = 0.90; bl = 0.92; }");
325
- js.push(" } else if (b.t === 'wood' || b.t === 'log') {");
326
- js.push(" if (face === 'top' || face === 'bottom') { r = 0.62; g = 0.52; bl = 0.30; }");
327
- js.push(" else { r = 0.45; g = 0.32; bl = 0.18; }");
328
- js.push(" } else if (b.t === 'leaf' || b.t === 'leaves') {");
329
- js.push(" r = 0.20; g = 0.55; bl = 0.15;");
330
- js.push(" } else if (b.t === 'planks') {");
331
- js.push(" r = 0.72; g = 0.58; bl = 0.35;");
332
- js.push(" } else if (b.t === 'cobblestone' || b.t === 'cobble') {");
333
- js.push(" if (face === 'top') { r = 0.52; g = 0.52; bl = 0.52; }");
334
- js.push(" else { r = 0.44; g = 0.44; bl = 0.44; }");
335
- js.push(" } else if (b.t === 'gravel') {");
336
- js.push(" r = 0.56; g = 0.54; bl = 0.50;");
337
- js.push(" } else if (b.t === 'clay') {");
338
- js.push(" r = 0.62; g = 0.60; bl = 0.58;");
339
- js.push(" } else if (b.t === 'iron' || b.t === 'ore') {");
340
- js.push(" r = 0.65; g = 0.60; bl = 0.55;");
341
- js.push(" } else if (b.t === 'gold') {");
342
- js.push(" r = 0.90; g = 0.78; bl = 0.20;");
343
- js.push(" } else if (b.t === 'diamond') {");
344
- js.push(" r = 0.30; g = 0.85; bl = 0.88;");
345
- js.push(" } else if (b.t === 'lava') {");
346
- js.push(" r = 0.95; g = 0.35; bl = 0.05;");
347
- js.push(" } else if (b.t === 'brick') {");
348
- js.push(" r = 0.65; g = 0.32; bl = 0.25;");
349
- js.push(" } else {");
350
- js.push(" var hash = 0;");
351
- js.push(" for (var ci = 0; ci < b.t.length; ci++) hash = ((hash << 5) - hash) + b.t.charCodeAt(ci);");
352
- js.push(" r = 0.3 + (((hash & 0xFF) / 255) * 0.5);");
353
- js.push(" g = 0.3 + ((((hash >> 8) & 0xFF) / 255) * 0.5);");
354
- js.push(" bl = 0.3 + ((((hash >> 16) & 0xFF) / 255) * 0.5);");
355
- js.push(" }");
356
- js.push(" var v = 0.85 + (b.y / 12.0) * 0.15;");
357
- js.push(" return [r * v, g * v, bl * v];");
358
- js.push(" },");
359
- js.push("");
360
- js.push(" buildMesh: function () {");
361
- js.push(" var verts = [];");
362
- js.push(" var self = this;");
363
- js.push(" function quad(x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4, nx,ny,nz, c) {");
364
- js.push(" verts.push(x1,y1,z1, c[0],c[1],c[2], nx,ny,nz);");
365
- js.push(" verts.push(x2,y2,z2, c[0],c[1],c[2], nx,ny,nz);");
366
- js.push(" verts.push(x3,y3,z3, c[0],c[1],c[2], nx,ny,nz);");
367
- js.push(" verts.push(x1,y1,z1, c[0],c[1],c[2], nx,ny,nz);");
368
- js.push(" verts.push(x3,y3,z3, c[0],c[1],c[2], nx,ny,nz);");
369
- js.push(" verts.push(x4,y4,z4, c[0],c[1],c[2], nx,ny,nz);");
370
- js.push(" }");
371
- js.push(" for (var i = 0; i < BLOCKS.length; i++) {");
372
- js.push(" var b = BLOCKS[i];");
373
- js.push(" var x = b.x, y = b.y, z = b.z;");
374
- js.push(" if (!self.hasBlock(x, y+1, z)) {");
375
- js.push(" var ct = self.getBlockColor(b, 'top');");
376
- js.push(" quad(x,y+1,z, x+1,y+1,z, x+1,y+1,z+1, x,y+1,z+1, 0,1,0, ct);");
377
- js.push(" }");
378
- js.push(" if (!self.hasBlock(x, y-1, z)) {");
379
- js.push(" var cb = self.getBlockColor(b, 'bottom');");
380
- js.push(" quad(x,y,z+1, x+1,y,z+1, x+1,y,z, x,y,z, 0,-1,0, cb);");
381
- js.push(" }");
382
- js.push(" if (!self.hasBlock(x, y, z-1)) {");
383
- js.push(" var cn = self.getBlockColor(b, 'side');");
384
- js.push(" quad(x,y,z, x+1,y,z, x+1,y+1,z, x,y+1,z, 0,0,-1, cn);");
385
- js.push(" }");
386
- js.push(" if (!self.hasBlock(x, y, z+1)) {");
387
- js.push(" var cs = self.getBlockColor(b, 'side');");
388
- js.push(" quad(x+1,y,z+1, x,y,z+1, x,y+1,z+1, x+1,y+1,z+1, 0,0,1, cs);");
389
- js.push(" }");
390
- js.push(" if (!self.hasBlock(x-1, y, z)) {");
391
- js.push(" var cw = self.getBlockColor(b, 'side');");
392
- js.push(" quad(x,y,z+1, x,y,z, x,y+1,z, x,y+1,z+1, -1,0,0, cw);");
393
- js.push(" }");
394
- js.push(" if (!self.hasBlock(x+1, y, z)) {");
395
- js.push(" var ce = self.getBlockColor(b, 'side');");
396
- js.push(" quad(x+1,y,z, x+1,y,z+1, x+1,y+1,z+1, x+1,y+1,z, 1,0,0, ce);");
397
- js.push(" }");
398
- js.push(" }");
399
- js.push(" this.vertCount = verts.length / 9;");
400
- js.push(" var gl = this.gl;");
401
- js.push(" this.vbo = gl.createBuffer();");
402
- js.push(" gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);");
403
- js.push(" gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);");
404
- js.push(" var aPos = gl.getAttribLocation(this.program, 'aPos');");
405
- js.push(" var aColor = gl.getAttribLocation(this.program, 'aColor');");
406
- js.push(" var aNormal = gl.getAttribLocation(this.program, 'aNormal');");
407
- js.push(" gl.enableVertexAttribArray(aPos);");
408
- js.push(" gl.enableVertexAttribArray(aColor);");
409
- js.push(" gl.enableVertexAttribArray(aNormal);");
410
- js.push(" gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 36, 0);");
411
- js.push(" gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, 36, 12);");
412
- js.push(" gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 36, 24);");
413
- js.push(" },");
414
- js.push("");
415
- js.push(" resize: function () {");
416
- js.push(" this.canvas.width = window.innerWidth;");
417
- js.push(" this.canvas.height = window.innerHeight;");
418
- js.push(" if (this.gl) this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);");
419
- js.push(" },");
420
- js.push("");
421
- js.push(" bindInput: function () {");
422
- js.push(" var self = this;");
423
- js.push(" document.addEventListener('keydown', function (e) { self.keys[e.code] = true; });");
424
- js.push(" document.addEventListener('keyup', function (e) { self.keys[e.code] = false; });");
425
- js.push(" window.addEventListener('resize', function () { self.resize(); });");
426
- js.push(" document.addEventListener('mousemove', function (e) {");
427
- js.push(" if (!self.locked) return;");
428
- js.push(" self.yaw += e.movementX * 0.003;");
429
- js.push(" self.pitch -= e.movementY * 0.003;");
430
- js.push(" if (self.pitch > 1.5) self.pitch = 1.5;");
431
- js.push(" if (self.pitch < -1.5) self.pitch = -1.5;");
432
- js.push(" });");
433
- js.push(" document.addEventListener('pointerlockchange', function () {");
434
- js.push(" self.locked = document.pointerLockElement === self.canvas;");
435
- js.push(" var ov = document.getElementById('click-overlay');");
436
- js.push(" ov.style.display = self.locked ? 'none' : 'flex';");
437
- js.push(" });");
438
- js.push(" document.getElementById('click-overlay').addEventListener('click', function () {");
439
- js.push(" self.canvas.requestPointerLock();");
440
- js.push(" });");
441
- js.push(" },");
442
- js.push("");
443
- js.push(" getGroundY: function (x, z) {");
444
- js.push(" var bx = Math.floor(x);");
445
- js.push(" var bz = Math.floor(z);");
446
- js.push(" for (var y = 30; y >= 0; y--) {");
447
- js.push(" if (this.hasBlock(bx, y, bz)) return y + 1;");
448
- js.push(" }");
449
- js.push(" return 1;");
450
- js.push(" },");
451
- js.push("");
452
- js.push(" update: function () {");
453
- js.push(" if (!this.locked) return;");
454
- js.push(" var spd = 0.08;");
455
- js.push(" if (this.keys['ShiftLeft'] || this.keys['ShiftRight']) spd = 0.16;");
456
- js.push(" var dx = 0, dz = 0;");
457
- js.push(" var sy = Math.sin(this.yaw), cy = Math.cos(this.yaw);");
458
- js.push(" if (this.keys['KeyW'] || this.keys['ArrowUp']) { dx -= sy * spd; dz -= cy * spd; }");
459
- js.push(" if (this.keys['KeyS'] || this.keys['ArrowDown']) { dx += sy * spd; dz += cy * spd; }");
460
- js.push(" if (this.keys['KeyA'] || this.keys['ArrowLeft']) { dx -= cy * spd; dz += sy * spd; }");
461
- js.push(" if (this.keys['KeyD'] || this.keys['ArrowRight']) { dx += cy * spd; dz -= sy * spd; }");
462
- js.push(" var nx = this.camX + dx;");
463
- js.push(" var nz = this.camZ + dz;");
464
- js.push(" var feet = Math.floor(this.camY - 1.6);");
465
- js.push(" var bnx = Math.floor(nx);");
466
- js.push(" var bnz = Math.floor(nz);");
467
- js.push(" if (!this.hasBlock(bnx, feet, Math.floor(this.camZ)) && !this.hasBlock(bnx, feet+1, Math.floor(this.camZ))) {");
468
- js.push(" this.camX = nx;");
469
- js.push(" }");
470
- js.push(" if (!this.hasBlock(Math.floor(this.camX), feet, bnz) && !this.hasBlock(Math.floor(this.camX), feet+1, bnz)) {");
471
- js.push(" this.camZ = nz;");
472
- js.push(" }");
473
- js.push(" this.velY -= this.gravity;");
474
- js.push(" this.camY += this.velY;");
475
- js.push(" var gy = this.getGroundY(this.camX, this.camZ) + 1.6;");
476
- js.push(" if (this.camY <= gy) {");
477
- js.push(" this.camY = gy;");
478
- js.push(" this.velY = 0;");
479
- js.push(" this.onGround = true;");
480
- js.push(" } else {");
481
- js.push(" this.onGround = false;");
482
- js.push(" }");
483
- js.push(" if (this.keys['Space'] && this.onGround) {");
484
- js.push(" this.velY = 0.18;");
485
- js.push(" this.onGround = false;");
486
- js.push(" }");
487
- js.push(" },");
488
- js.push("");
489
- js.push(" perspective: function (fov, aspect, near, far) {");
490
- js.push(" var f = 1.0 / Math.tan(fov / 2);");
491
- js.push(" var nf = 1.0 / (near - far);");
492
- js.push(" return new Float32Array([");
493
- js.push(" f/aspect, 0, 0, 0,");
494
- js.push(" 0, f, 0, 0,");
495
- js.push(" 0, 0, (far+near)*nf, -1,");
496
- js.push(" 0, 0, 2*far*near*nf, 0");
497
- js.push(" ]);");
498
- js.push(" },");
499
- js.push("");
500
- js.push(" lookAt: function () {");
501
- js.push(" var cp = Math.cos(this.pitch), sp = Math.sin(this.pitch);");
502
- js.push(" var cy = Math.cos(this.yaw), sy = Math.sin(this.yaw);");
503
- js.push(" var fx = -sy * cp, fy = sp, fz = -cy * cp;");
504
- js.push(" var len = Math.sqrt(fx*fx+fy*fy+fz*fz);");
505
- js.push(" fx/=len; fy/=len; fz/=len;");
506
- js.push(" var ux = 0, uy = 1, uz = 0;");
507
- js.push(" var rx = fy*uz - fz*uy, ry = fz*ux - fx*uz, rz = fx*uy - fy*ux;");
508
- js.push(" len = Math.sqrt(rx*rx+ry*ry+rz*rz);");
509
- js.push(" rx/=len; ry/=len; rz/=len;");
510
- js.push(" ux = ry*fz - rz*fy; uy = rz*fx - rx*fz; uz = rx*fy - ry*fx;");
511
- js.push(" var tx = -rx*this.camX - ry*this.camY - rz*this.camZ;");
512
- js.push(" var ty = -ux*this.camX - uy*this.camY - uz*this.camZ;");
513
- js.push(" var tz = fx*this.camX + fy*this.camY + fz*this.camZ;");
514
- js.push(" return new Float32Array([");
515
- js.push(" rx, ux, -fx, 0,");
516
- js.push(" ry, uy, -fy, 0,");
517
- js.push(" rz, uz, -fz, 0,");
518
- js.push(" tx, ty, tz, 1");
519
- js.push(" ]);");
520
- js.push(" },");
521
- js.push("");
522
- js.push(" render: function () {");
523
- js.push(" var gl = this.gl;");
524
- js.push(" gl.clearColor(0.53, 0.81, 0.92, 1.0);");
525
- js.push(" gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);");
526
- js.push(" gl.enable(gl.DEPTH_TEST);");
527
- js.push(" gl.enable(gl.CULL_FACE);");
528
- js.push(" gl.cullFace(gl.BACK);");
529
- js.push(" var aspect = this.canvas.width / this.canvas.height;");
530
- js.push(" var proj = this.perspective(1.2, aspect, 0.1, 200.0);");
531
- js.push(" var view = this.lookAt();");
532
- js.push(" gl.uniformMatrix4fv(this.uProj, false, proj);");
533
- js.push(" gl.uniformMatrix4fv(this.uView, false, view);");
534
- js.push(" gl.uniform3f(this.uCamPos, this.camX, this.camY, this.camZ);");
535
- js.push(" gl.drawArrays(gl.TRIANGLES, 0, this.vertCount);");
536
- js.push(" this.updateHUD();");
537
- js.push(" },");
538
- js.push("");
539
- js.push(" getStandingBlock: function () {");
540
- js.push(" var bx = Math.floor(this.camX);");
541
- js.push(" var bz = Math.floor(this.camZ);");
542
- js.push(" var by = Math.floor(this.camY - 1.7);");
543
- js.push(" var b = this.blockMap[bx + ',' + by + ',' + bz];");
544
- js.push(" return b ? b.t : 'air';");
545
- js.push(" },");
546
- js.push("");
547
- js.push(" updateHUD: function () {");
548
- js.push(" var h = document.getElementById('hud-info');");
549
- js.push(" if (!h) return;");
550
- js.push(" var standing = this.getStandingBlock();");
551
- js.push(" var sprinting = this.keys['ShiftLeft'] || this.keys['ShiftRight'];");
552
- js.push(" var tris = Math.floor(this.vertCount / 3);");
553
- js.push(" h.innerHTML = '<div class=\"hud-title\">CaG Engine</div>' +");
554
- js.push(" '<div>' + INFO.ID + ' v' + INFO.version + '</div>' +");
555
- js.push(" '<div class=\"hud-sep\"></div>' +");
556
- js.push(" '<div>FPS: ' + this.fps + ' | Tris: ' + tris.toLocaleString() + '</div>' +");
557
- js.push(" '<div>Blocks: ' + BLOCKS.length.toLocaleString() + '</div>' +");
558
- js.push(" '<div class=\"hud-sep\"></div>' +");
559
- js.push(" '<div>X: ' + this.camX.toFixed(1) + ' Y: ' + this.camY.toFixed(1) + ' Z: ' + this.camZ.toFixed(1) + '</div>' +");
560
- js.push(" '<div>Standing: ' + standing + (sprinting ? ' [SPRINT]' : '') + '</div>';");
561
- js.push(" },");
562
- js.push("");
563
- js.push(" loop: function () {");
564
- js.push(" this.update();");
565
- js.push(" this.render();");
566
- js.push(" this.frames++;");
567
- js.push(" var now = performance.now();");
568
- js.push(" if (now - this.lastFps >= 1000) {");
569
- js.push(" this.fps = this.frames;");
570
- js.push(" this.frames = 0;");
571
- js.push(" this.lastFps = now;");
572
- js.push(" }");
573
- js.push(" requestAnimationFrame(this.loop.bind(this));");
574
- js.push(" }");
575
- js.push("};");
576
- js.push("");
577
- js.push("var bar = document.getElementById('bar-inner');");
578
- js.push("var status = document.getElementById('load-status');");
579
- js.push("bar.style.width = '10%';");
580
- js.push("status.textContent = 'Initializing CaG Engine...';");
581
- js.push("setTimeout(function () {");
582
- js.push(" bar.style.width = '25%';");
583
- js.push(" status.textContent = 'Parsing project data...';");
584
- js.push("}, 300);");
585
- js.push("setTimeout(function () {");
586
- js.push(" bar.style.width = '45%';");
587
- js.push(" status.textContent = 'Generating terrain (' + BLOCKS.length.toLocaleString() + ' blocks)...';");
588
- js.push("}, 600);");
589
- js.push("setTimeout(function () {");
590
- js.push(" bar.style.width = '65%';");
591
- js.push(" status.textContent = 'Building mesh geometry...';");
592
- js.push("}, 900);");
593
- js.push("setTimeout(function () {");
594
- js.push(" bar.style.width = '80%';");
595
- js.push(" status.textContent = 'Compiling CaG shaders...';");
596
- js.push("}, 1200);");
597
- js.push("setTimeout(function () {");
598
- js.push(" bar.style.width = '95%';");
599
- js.push(" status.textContent = 'Preparing renderer...';");
600
- js.push("}, 1500);");
601
- js.push("setTimeout(function () {");
602
- js.push(" bar.style.width = '100%';");
603
- js.push(" status.textContent = 'Ready';");
604
- js.push(" status.style.color = '#4ecca3';");
605
- js.push("}, 1800);");
606
- js.push("setTimeout(function () { cag.init(); }, 2200);");
607
-
608
- var html = "<!DOCTYPE html>\n<html>\n<head>\n";
609
- html += "<meta charset='utf-8'>\n";
610
- html += "<title>" + info.ID + " - CaG Engine</title>\n";
611
- html += "<style>\n" + css + "\n</style>\n";
612
- html += "</head>\n<body>\n";
613
- html += "<div id='loading'>\n";
614
- html += " <h1>CaG</h1>\n";
615
- html += " <h2>" + info.ID + "</h2>\n";
616
- html += " <div id='load-status'>Starting...</div>\n";
617
- html += " <div id='bar-outer'><div id='bar-inner'></div></div>\n";
618
- html += " <div class='version-tag'>v" + (info.version || "1.0.0") + "</div>\n";
619
- html += "</div>\n";
620
- html += "<div id='click-overlay' style='display:none'><div class='play-icon'><div class='play-triangle'></div></div><span>CLICK TO PLAY</span></div>\n";
621
- html += "<div id='crosshair'></div>\n";
622
- html += "<div id='hud'><div id='hud-box'><div id='hud-info'></div></div></div>\n";
623
- html += "<div id='controls-hint'><b>WASD</b> Move &nbsp; <b>Mouse</b> Look &nbsp; <b>Space</b> Jump &nbsp; <b>Shift</b> Sprint &nbsp; <b>F11</b> Fullscreen &nbsp; <b>Esc</b> Release</div>\n";
624
- html += "<canvas id='cag-canvas'></canvas>\n";
625
- html += "<script>\n" + js.join("\n") + "\n</script>\n";
626
- html += "</body>\n</html>";
627
-
628
- return html;
629
- };
630
-
631
- ExeBuilder.prototype.generateWorldBlocks = function () {
632
- var codeFiles = this.engine.getCodeFiles();
633
- var topLayer = "grass";
634
- var fillLayer = "dirt";
635
- var bottomLayer = "stone";
636
- var size = 32;
637
- var seed = 54321;
638
- var waterLevel = 3;
639
- var hasWater = false;
640
-
641
- for (var i = 0; i < codeFiles.length; i++) {
642
- var content = fs.readFileSync(codeFiles[i], "utf-8");
643
- var lines = content.split("\n");
644
- for (var j = 0; j < lines.length; j++) {
645
- var line = lines[j].trim();
646
- if (line.indexOf("top") !== -1 && line.indexOf("layer") !== -1 && line.indexOf("=") !== -1) {
647
- var eqIdx = line.lastIndexOf("=");
648
- var val = line.substring(eqIdx + 1).trim();
649
- if (val) topLayer = val.toLowerCase();
650
- }
651
- if (line.indexOf("fill") !== -1 && line.indexOf("layer") !== -1 && line.indexOf("=") !== -1) {
652
- var fIdx = line.lastIndexOf("=");
653
- var fVal = line.substring(fIdx + 1).trim();
654
- if (fVal) fillLayer = fVal.toLowerCase();
655
- }
656
- if (line.indexOf("bottom") !== -1 && line.indexOf("layer") !== -1 && line.indexOf("=") !== -1) {
657
- var bIdx = line.lastIndexOf("=");
658
- var bVal = line.substring(bIdx + 1).trim();
659
- if (bVal) bottomLayer = bVal.toLowerCase();
660
- }
661
- if (line.indexOf("water") !== -1) {
662
- hasWater = true;
663
- }
664
- if (line.indexOf("size") !== -1 && line.indexOf("=") !== -1) {
665
- var sIdx = line.lastIndexOf("=");
666
- var sVal = parseInt(line.substring(sIdx + 1).trim(), 10);
667
- if (!isNaN(sVal) && sVal >= 8 && sVal <= 128) size = sVal;
668
- }
669
- if (line.indexOf("seed") !== -1 && line.indexOf("=") !== -1) {
670
- var sdIdx = line.lastIndexOf("=");
671
- var sdVal = parseInt(line.substring(sdIdx + 1).trim(), 10);
672
- if (!isNaN(sdVal)) seed = sdVal;
673
- }
674
- }
675
- }
676
-
677
- var blocks = [];
678
-
679
- var noise = function (x, z, s, scale) {
680
- var nx = x / scale;
681
- var nz = z / scale;
682
- var a = Math.sin(nx * 12.9898 + nz * 78.233 + s) * 43758.5453;
683
- return a - Math.floor(a);
684
- };
685
-
686
- var smoothHeight = function (x, z) {
687
- var h1 = noise(x, z, seed, 8.0) * 4;
688
- var h2 = noise(x, z, seed + 100, 16.0) * 6;
689
- var h3 = noise(x, z, seed + 200, 4.0) * 2;
690
- var h4 = noise(x + 0.5, z + 0.5, seed + 300, 12.0) * 3;
691
-
692
- var avg = 0;
693
- var count = 0;
694
- for (var dx = -1; dx <= 1; dx++) {
695
- for (var dz = -1; dz <= 1; dz++) {
696
- var n1 = noise(x + dx, z + dz, seed, 8.0) * 4;
697
- var n2 = noise(x + dx, z + dz, seed + 100, 16.0) * 6;
698
- avg += n1 + n2;
699
- count++;
700
- }
701
- }
702
- avg = avg / count;
703
-
704
- var raw = (h1 + h2 + h3 + h4) * 0.4 + avg * 0.6;
705
- return Math.max(1, Math.min(Math.floor(raw), 12));
706
- };
707
-
708
- for (var x = 0; x < size; x++) {
709
- for (var z = 0; z < size; z++) {
710
- var height = smoothHeight(x, z);
711
- for (var y = 0; y <= height; y++) {
712
- var blockType;
713
- if (y === height) {
714
- blockType = topLayer;
715
- } else if (y === 0) {
716
- blockType = bottomLayer;
717
- } else if (y >= height - 1) {
718
- blockType = topLayer === "grass" ? fillLayer : topLayer;
719
- } else {
720
- blockType = fillLayer;
721
- }
722
- blocks.push({ x: x, y: y, z: z, t: blockType });
723
- }
724
- if (hasWater && height < waterLevel) {
725
- for (var wy = height + 1; wy <= waterLevel; wy++) {
726
- blocks.push({ x: x, y: wy, z: z, t: "water" });
727
- }
728
- }
729
- }
730
- }
731
-
732
- return blocks;
733
- };
734
-
735
- ExeBuilder.prototype.runPackager = function (platform, arch) {
736
- var bin = this.findPackagerBin();
737
- var cmd = bin + " \"" + this.tempDir + "\" " + this.projectName +
738
- " --platform=" + platform +
739
- " --arch=" + arch +
740
- " --out=\"" + this.outputDir + "\"" +
741
- " --overwrite" +
742
- " --no-prune";
743
-
744
- childProcess.execSync(cmd, {
745
- cwd: this.tempDir,
746
- stdio: "inherit",
747
- shell: true
748
- });
749
- };
750
-
751
- ExeBuilder.prototype.findPackagerBin = function () {
752
- var cagRoot = path.join(__dirname, "..", "..");
753
-
754
- var localBin = path.join(cagRoot, "node_modules", ".bin", "electron-packager");
755
- if (fs.existsSync(localBin)) return "\"" + localBin + "\"";
756
- var localCmd = localBin + ".cmd";
757
- if (fs.existsSync(localCmd)) return "\"" + localCmd + "\"";
758
-
759
- var projBin = path.join(this.projectRoot, "node_modules", ".bin", "electron-packager");
760
- if (fs.existsSync(projBin)) return "\"" + projBin + "\"";
761
- var projCmd = projBin + ".cmd";
762
- if (fs.existsSync(projCmd)) return "\"" + projCmd + "\"";
763
-
764
- try {
765
- childProcess.execSync("electron-packager --version", { stdio: "pipe" });
766
- return "electron-packager";
767
- } catch (e) {}
768
-
769
- var npmPrefix = "";
770
- try {
771
- npmPrefix = childProcess.execSync("npm config get prefix", { stdio: "pipe" }).toString().trim();
772
- } catch (e) {}
773
-
774
- if (npmPrefix) {
775
- var isWin = process.platform === "win32";
776
- if (isWin) {
777
- var gCmd = path.join(npmPrefix, "electron-packager.cmd");
778
- if (fs.existsSync(gCmd)) return "\"" + gCmd + "\"";
779
- var gCmd2 = path.join(npmPrefix, "node_modules", ".bin", "electron-packager.cmd");
780
- if (fs.existsSync(gCmd2)) return "\"" + gCmd2 + "\"";
781
- } else {
782
- var gBin = path.join(npmPrefix, "bin", "electron-packager");
783
- if (fs.existsSync(gBin)) return "\"" + gBin + "\"";
784
- }
785
- }
786
-
787
- try {
788
- var whereCmd = process.platform === "win32" ? "where electron-packager 2>nul" : "which electron-packager 2>/dev/null";
789
- var found = childProcess.execSync(whereCmd, { stdio: "pipe" }).toString().trim().split("\n")[0].trim();
790
- if (found && fs.existsSync(found)) return "\"" + found + "\"";
791
- } catch (e) {}
792
-
793
- return "npx electron-packager";
794
- };
795
-
796
- ExeBuilder.prototype.mapPlatform = function (p) {
797
- if (p === "win") return "win32";
798
- if (p === "macos") return "darwin";
799
- return "linux";
800
- };
801
-
802
- ExeBuilder.prototype.cleanup = function () {
803
- this.removeDirectory(this.tempDir);
804
- };
805
-
806
- ExeBuilder.prototype.removeDirectory = function (dir) {
807
- if (!fs.existsSync(dir)) return;
808
- var entries = fs.readdirSync(dir, { withFileTypes: true });
809
- for (var i = 0; i < entries.length; i++) {
810
- var fullPath = path.join(dir, entries[i].name);
811
- if (entries[i].isDirectory()) {
812
- this.removeDirectory(fullPath);
813
- } else {
814
- fs.unlinkSync(fullPath);
815
- }
816
- }
817
- fs.rmdirSync(dir);
818
- };
819
-
820
- ExeBuilder.prototype.getFileSize = function (filePath) {
821
- if (!fs.existsSync(filePath)) return 0;
822
- return fs.statSync(filePath).size;
823
- };
824
-
825
- ExeBuilder.prototype.getDirectorySize = function (dir) {
826
- if (!fs.existsSync(dir)) return 0;
827
- var total = 0;
828
- var entries = fs.readdirSync(dir, { withFileTypes: true });
829
- for (var i = 0; i < entries.length; i++) {
830
- var fullPath = path.join(dir, entries[i].name);
831
- if (entries[i].isDirectory()) {
832
- total += this.getDirectorySize(fullPath);
833
- } else {
834
- total += fs.statSync(fullPath).size;
835
- }
836
- }
837
- return total;
838
- };
839
-
840
- ExeBuilder.prototype.formatSize = function (bytes) {
841
- if (bytes < 1024) return bytes + " B";
842
- if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
843
- return (bytes / (1024 * 1024)).toFixed(1) + " MB";
844
- };
845
-
846
- ExeBuilder.prototype.detectPlatform = function () {
847
- var p = process.platform;
848
- if (p === "win32") return "win";
849
- if (p === "darwin") return "macos";
850
- return "linux";
851
- };
852
-
853
- module.exports = ExeBuilder;
1
+ var path = require("path");
2
+ var fs = require("fs");
3
+ var childProcess = require("child_process");
4
+
5
+ function ExeBuilder(engine) {
6
+ this.engine = engine;
7
+ this.info = engine.getInfo();
8
+ this.projectRoot = engine.projectRoot;
9
+ this.outputDir = path.join(this.projectRoot, "dist");
10
+ this.tempDir = path.join(this.outputDir, ".cag-build");
11
+ this.projectName = this.info.ID.split(".")[1] || this.info.ID.split(".")[0] || "cag-app";
12
+ }
13
+
14
+ ExeBuilder.prototype.build = function (options) {
15
+ options = options || {};
16
+ var platform = options.platform || this.detectPlatform();
17
+ var arch = options.arch || "x64";
18
+
19
+ this.ensureDirectories();
20
+ var html = this.getRendererSource();
21
+ var exeName, exePath;
22
+
23
+ if (platform === "win") {
24
+ var iconPath = path.join(this.tempDir, "icon.ico");
25
+ this.generateIcon(iconPath);
26
+ var csSource = this.generateCSharpSource(html);
27
+ var csPath = path.join(this.tempDir, "CaGLauncher.cs");
28
+ fs.writeFileSync(csPath, csSource);
29
+ exeName = this.projectName + ".exe";
30
+ exePath = path.join(this.outputDir, exeName);
31
+ this.compileCSharp(csPath, exePath, iconPath);
32
+ } else if (platform === "macos") {
33
+ exeName = this.projectName + ".app";
34
+ exePath = path.join(this.outputDir, exeName);
35
+ this.generateMacApp(html, exePath);
36
+ } else {
37
+ exeName = this.projectName;
38
+ exePath = path.join(this.outputDir, exeName);
39
+ this.generateLinuxApp(html, exePath);
40
+ }
41
+
42
+ this.cleanup();
43
+
44
+ return {
45
+ outputPath: exePath,
46
+ outputDir: this.outputDir,
47
+ outputName: exeName,
48
+ platform: platform,
49
+ arch: arch,
50
+ size: this.getFileSize(exePath) || this.getDirectorySize(exePath),
51
+ success: true
52
+ };
53
+ };
54
+
55
+ ExeBuilder.prototype.buildAll = function () {
56
+ var results = [];
57
+ var targets = [
58
+ { platform: "win", arch: "x64", ext: ".exe" },
59
+ { platform: "macos", arch: "x64", ext: ".app" },
60
+ { platform: "linux", arch: "x64", ext: "" }
61
+ ];
62
+
63
+ this.ensureDirectories();
64
+ var html = this.getRendererSource();
65
+
66
+ for (var i = 0; i < targets.length; i++) {
67
+ var t = targets[i];
68
+ try {
69
+ var exeName, exePath;
70
+ if (t.platform === "win") {
71
+ var iconPath = path.join(this.tempDir, "icon.ico");
72
+ this.generateIcon(iconPath);
73
+ var csSource = this.generateCSharpSource(html);
74
+ var csPath = path.join(this.tempDir, "CaGLauncher.cs");
75
+ fs.writeFileSync(csPath, csSource);
76
+ exeName = this.projectName + "-win-" + t.arch + t.ext;
77
+ exePath = path.join(this.outputDir, exeName);
78
+ this.compileCSharp(csPath, exePath, iconPath);
79
+ } else if (t.platform === "macos") {
80
+ exeName = this.projectName + "-macos-" + t.arch + t.ext;
81
+ exePath = path.join(this.outputDir, exeName);
82
+ this.generateMacApp(html, exePath);
83
+ } else {
84
+ exeName = this.projectName + "-linux-" + t.arch;
85
+ exePath = path.join(this.outputDir, exeName);
86
+ this.generateLinuxApp(html, exePath);
87
+ }
88
+ results.push({
89
+ outputPath: exePath,
90
+ outputDir: this.outputDir,
91
+ outputName: exeName,
92
+ platform: t.platform,
93
+ arch: t.arch,
94
+ size: this.getFileSize(exePath) || this.getDirectorySize(exePath),
95
+ success: true
96
+ });
97
+ } catch (e) {
98
+ results.push({
99
+ outputName: this.projectName + t.ext,
100
+ platform: t.platform,
101
+ arch: t.arch,
102
+ success: false,
103
+ error: e.message
104
+ });
105
+ }
106
+ }
107
+
108
+ this.cleanup();
109
+ return results;
110
+ };
111
+
112
+ ExeBuilder.prototype.ensureDirectories = function () {
113
+ if (!fs.existsSync(this.outputDir)) fs.mkdirSync(this.outputDir, { recursive: true });
114
+ if (fs.existsSync(this.tempDir)) this.removeDirectory(this.tempDir);
115
+ fs.mkdirSync(this.tempDir, { recursive: true });
116
+ };
117
+
118
+ ExeBuilder.prototype.generateIcon = function (outputPath) {
119
+ var width = 32;
120
+ var height = 32;
121
+ var header = Buffer.alloc(6);
122
+ header.writeUInt16LE(0, 0);
123
+ header.writeUInt16LE(1, 2);
124
+ header.writeUInt16LE(1, 4);
125
+ var bmpHeader = Buffer.alloc(40);
126
+ bmpHeader.writeUInt32LE(40, 0);
127
+ bmpHeader.writeInt32LE(width, 4);
128
+ bmpHeader.writeInt32LE(height * 2, 8);
129
+ bmpHeader.writeUInt16LE(1, 12);
130
+ bmpHeader.writeUInt16LE(32, 14);
131
+ bmpHeader.writeUInt32LE(0, 16);
132
+ bmpHeader.writeUInt32LE(width * height * 4, 20);
133
+ var pixelData = Buffer.alloc(width * height * 4);
134
+ for (var i = 0; i < width * height; i++) {
135
+ pixelData.writeUInt8(0xFF, i * 4);
136
+ pixelData.writeUInt8(0xFF, i * 4 + 1);
137
+ pixelData.writeUInt8(0xFF, i * 4 + 2);
138
+ pixelData.writeUInt8(0xFF, i * 4 + 3);
139
+ }
140
+ var andMask = Buffer.alloc(width * height / 8, 0x00);
141
+ var imageDataSize = bmpHeader.length + pixelData.length + andMask.length;
142
+ var dirEntry = Buffer.alloc(16);
143
+ dirEntry.writeUInt8(width, 0);
144
+ dirEntry.writeUInt8(height, 1);
145
+ dirEntry.writeUInt8(0, 2);
146
+ dirEntry.writeUInt8(0, 3);
147
+ dirEntry.writeUInt16LE(1, 4);
148
+ dirEntry.writeUInt16LE(32, 6);
149
+ dirEntry.writeUInt32LE(imageDataSize, 8);
150
+ dirEntry.writeUInt32LE(6 + 16, 12);
151
+ var ico = Buffer.concat([header, dirEntry, bmpHeader, pixelData, andMask]);
152
+ fs.writeFileSync(outputPath, ico);
153
+ };
154
+
155
+ ExeBuilder.prototype.generateCSharpSource = function (html) {
156
+ var b64 = Buffer.from(html, 'utf-8').toString('base64');
157
+ var cs = [
158
+ 'using System;',
159
+ 'using System.IO;',
160
+ 'using System.Diagnostics;',
161
+ 'using System.Text;',
162
+ '',
163
+ 'class CaGLauncher {',
164
+ ' static string DATA = "' + b64 + '";',
165
+ '',
166
+ ' [STAThread]',
167
+ ' static void Main() {',
168
+ ' string html = Encoding.UTF8.GetString(Convert.FromBase64String(DATA));',
169
+ ' string tempDir = Path.Combine(Path.GetTempPath(), "cag_" + Guid.NewGuid().ToString("N"));',
170
+ ' Directory.CreateDirectory(tempDir);',
171
+ ' string htmlPath = Path.Combine(tempDir, "game.html");',
172
+ ' File.WriteAllText(htmlPath, html, Encoding.UTF8);',
173
+ ' string profileDir = Path.Combine(tempDir, "profile");',
174
+ ' Directory.CreateDirectory(profileDir);',
175
+ ' string browser = FindBrowser();',
176
+ ' if (browser != null) {',
177
+ ' string fileUrl = "file:///" + htmlPath.Replace("\\\\", "/");',
178
+ ' string profUrl = profileDir.Replace("\\\\", "/");',
179
+ ' ProcessStartInfo psi = new ProcessStartInfo();',
180
+ ' psi.FileName = browser;',
181
+ ' psi.Arguments = "--app=\\"" + fileUrl + "\\" --window-size=1280,720 --user-data-dir=\\"" + profUrl + "\\" --no-first-run --disable-extensions --disable-default-apps";',
182
+ ' psi.UseShellExecute = false;',
183
+ ' Process p = Process.Start(psi);',
184
+ ' p.WaitForExit();',
185
+ ' } else {',
186
+ ' Process.Start(new ProcessStartInfo(htmlPath) { UseShellExecute = true });',
187
+ ' System.Threading.Thread.Sleep(3000);',
188
+ ' }',
189
+ ' try { Directory.Delete(tempDir, true); } catch {}',
190
+ ' }',
191
+ '',
192
+ ' static string FindBrowser() {',
193
+ ' string[] paths = new string[] {',
194
+ ' Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Microsoft", "Edge", "Application", "msedge.exe"),',
195
+ ' Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Microsoft", "Edge", "Application", "msedge.exe"),',
196
+ ' Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Google", "Chrome", "Application", "chrome.exe"),',
197
+ ' Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Google", "Chrome", "Application", "chrome.exe"),',
198
+ ' Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Google", "Chrome", "Application", "chrome.exe")',
199
+ ' };',
200
+ ' foreach (string p in paths) {',
201
+ ' if (File.Exists(p)) return p;',
202
+ ' }',
203
+ ' return null;',
204
+ ' }',
205
+ '}'
206
+ ].join('\n');
207
+ return cs;
208
+ };
209
+
210
+ ExeBuilder.prototype.findCscCompiler = function () {
211
+ var frameworkPaths = [
212
+ 'C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\csc.exe',
213
+ 'C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe'
214
+ ];
215
+ for (var i = 0; i < frameworkPaths.length; i++) {
216
+ if (fs.existsSync(frameworkPaths[i])) return frameworkPaths[i];
217
+ }
218
+ try {
219
+ childProcess.execSync('csc /help', { stdio: 'pipe', timeout: 5000 });
220
+ return 'csc';
221
+ } catch (e) {}
222
+ return null;
223
+ };
224
+
225
+ ExeBuilder.prototype.compileCSharp = function (csPath, outputPath, iconPath) {
226
+ var csc = this.findCscCompiler();
227
+ if (!csc) {
228
+ throw new Error('C# compiler (csc.exe) not found. Ensure .NET Framework is installed.');
229
+ }
230
+ var cmd = '"' + csc + '" /target:winexe /out:"' + outputPath + '"';
231
+ if (iconPath && fs.existsSync(iconPath)) {
232
+ cmd += ' /win32icon:"' + iconPath + '"';
233
+ }
234
+ cmd += ' /optimize "' + csPath + '"';
235
+ childProcess.execSync(cmd, {
236
+ cwd: this.tempDir,
237
+ stdio: 'inherit',
238
+ shell: true
239
+ });
240
+ };
241
+
242
+ ExeBuilder.prototype.getRendererSource = function () {
243
+ var blocks = this.generateWorldBlocks();
244
+ var info = this.info;
245
+
246
+ var css = [
247
+ "* { margin: 0; padding: 0; box-sizing: border-box; }",
248
+ "body { background: #1a1a2e; overflow: hidden; font-family: monospace; }",
249
+ "#loading { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #1a1a2e; display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 999; color: #fff; transition: opacity 0.5s; }",
250
+ "#loading.fade-out { opacity: 0; pointer-events: none; }",
251
+ "#loading h1 { font-size: 64px; color: #4ecca3; margin-bottom: 4px; letter-spacing: 6px; text-shadow: 0 0 30px rgba(78,204,163,0.3); }",
252
+ "#loading h2 { font-size: 14px; color: #666; margin-bottom: 30px; font-weight: normal; letter-spacing: 2px; }",
253
+ "#load-status { font-size: 12px; color: #555; margin-bottom: 12px; min-height: 18px; transition: color 0.3s; }",
254
+ "#bar-outer { width: 300px; height: 3px; background: #2a2a3e; border-radius: 2px; overflow: hidden; }",
255
+ "#bar-inner { width: 0%; height: 100%; background: linear-gradient(90deg, #4ecca3, #45b7d1); border-radius: 2px; transition: width 0.4s ease-out; }",
256
+ "#loading .version-tag { font-size: 11px; color: #444; margin-top: 20px; }",
257
+ "#click-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.65); display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 998; cursor: pointer; transition: opacity 0.3s; }",
258
+ "#click-overlay:hover { background: rgba(0,0,0,0.55); }",
259
+ "#click-overlay .play-icon { width: 80px; height: 80px; border: 3px solid #4ecca3; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-bottom: 16px; transition: transform 0.2s, border-color 0.2s; }",
260
+ "#click-overlay:hover .play-icon { transform: scale(1.1); border-color: #45b7d1; }",
261
+ "#click-overlay .play-triangle { width: 0; height: 0; border-style: solid; border-width: 15px 0 15px 28px; border-color: transparent transparent transparent #4ecca3; margin-left: 6px; }",
262
+ "#click-overlay span { color: #888; font-size: 16px; letter-spacing: 3px; }",
263
+ "canvas { display: block; position: fixed; top: 0; left: 0; }",
264
+ "#hud { position: fixed; top: 10px; left: 10px; z-index: 100; pointer-events: none; }",
265
+ "#hud-box { background: rgba(0,0,0,0.7); padding: 10px 14px; border-left: 3px solid #4ecca3; backdrop-filter: blur(4px); border-radius: 0 4px 4px 0; }",
266
+ "#hud-box div { color: #bbb; font-size: 11px; line-height: 17px; }",
267
+ "#hud-box .hud-title { color: #4ecca3; font-size: 13px; font-weight: bold; margin-bottom: 3px; letter-spacing: 1px; }",
268
+ "#hud-box .hud-sep { border-top: 1px solid #333; margin: 4px 0; }",
269
+ "#crosshair { position: fixed; top: 50%; left: 50%; width: 24px; height: 24px; margin: -12px 0 0 -12px; z-index: 100; pointer-events: none; }",
270
+ "#crosshair::before { content: ''; position: absolute; top: 11px; left: 3px; width: 18px; height: 2px; background: rgba(255,255,255,0.6); border-radius: 1px; }",
271
+ "#crosshair::after { content: ''; position: absolute; top: 3px; left: 11px; width: 2px; height: 18px; background: rgba(255,255,255,0.6); border-radius: 1px; }",
272
+ "#controls-hint { position: fixed; bottom: 10px; right: 10px; z-index: 100; pointer-events: none; background: rgba(0,0,0,0.55); padding: 8px 14px; color: #666; font-size: 10px; border-radius: 4px; line-height: 16px; backdrop-filter: blur(4px); }",
273
+ "#controls-hint b { color: #888; }"
274
+ ].join("\n");
275
+
276
+ var vs = [
277
+ "attribute vec3 aPos;",
278
+ "attribute vec3 aColor;",
279
+ "attribute vec3 aNormal;",
280
+ "uniform mat4 uProj;",
281
+ "uniform mat4 uView;",
282
+ "varying vec3 vColor;",
283
+ "varying vec3 vNormal;",
284
+ "varying vec3 vWorldPos;",
285
+ "void main() {",
286
+ " vColor = aColor;",
287
+ " vNormal = aNormal;",
288
+ " vWorldPos = aPos;",
289
+ " gl_Position = uProj * uView * vec4(aPos, 1.0);",
290
+ "}"
291
+ ].join("\\n");
292
+
293
+ var fs_shader = [
294
+ "precision mediump float;",
295
+ "varying vec3 vColor;",
296
+ "varying vec3 vNormal;",
297
+ "varying vec3 vWorldPos;",
298
+ "uniform vec3 uSunDir;",
299
+ "uniform vec3 uSunColor;",
300
+ "uniform vec3 uAmbient;",
301
+ "uniform vec3 uFogColor;",
302
+ "uniform float uFogNear;",
303
+ "uniform float uFogFar;",
304
+ "uniform vec3 uCamPos;",
305
+ "void main() {",
306
+ " vec3 norm = normalize(vNormal);",
307
+ " float diff = max(dot(norm, normalize(uSunDir)), 0.0);",
308
+ " vec3 lit = vColor * (uAmbient + uSunColor * diff);",
309
+ " float dist = length(vWorldPos - uCamPos);",
310
+ " float fog = clamp((dist - uFogNear) / (uFogFar - uFogNear), 0.0, 1.0);",
311
+ " vec3 final = mix(lit, uFogColor, fog);",
312
+ " gl_FragColor = vec4(final, 1.0);",
313
+ "}"
314
+ ].join("\\n");
315
+
316
+ var js = [];
317
+ js.push("var BLOCKS = " + JSON.stringify(blocks) + ";");
318
+ js.push("var INFO = " + JSON.stringify(info) + ";");
319
+ js.push("");
320
+ js.push("var VS_SRC = \"" + vs + "\";");
321
+ js.push("var FS_SRC = \"" + fs_shader + "\";");
322
+ js.push("");
323
+ js.push("var cag = {");
324
+ js.push(" gl: null, canvas: null, program: null,");
325
+ js.push(" camX: 16, camY: 12, camZ: 16,");
326
+ js.push(" pitch: -0.5, yaw: 0.8,");
327
+ js.push(" keys: {}, locked: false,");
328
+ js.push(" velY: 0, onGround: false, gravity: 0.015,");
329
+ js.push(" fps: 0, frames: 0, lastFps: 0,");
330
+ js.push(" vbo: null, vertCount: 0,");
331
+ js.push(" blockMap: {},");
332
+ js.push(" uProj: null, uView: null, uSunDir: null, uSunColor: null,");
333
+ js.push(" uAmbient: null, uFogColor: null, uFogNear: null, uFogFar: null, uCamPos: null,");
334
+ js.push("");
335
+ js.push(" init: function () {");
336
+ js.push(" this.canvas = document.getElementById('cag-canvas');");
337
+ js.push(" this.gl = this.canvas.getContext('webgl', { antialias: true });");
338
+ js.push(" if (!this.gl) { alert('WebGL not supported'); return; }");
339
+ js.push(" this.resize();");
340
+ js.push(" this.buildBlockMap();");
341
+ js.push(" this.compileShaders();");
342
+ js.push(" this.buildMesh();");
343
+ js.push(" this.bindInput();");
344
+ js.push(" var loadEl = document.getElementById('loading');");
345
+ js.push(" loadEl.classList.add('fade-out');");
346
+ js.push(" setTimeout(function () {");
347
+ js.push(" loadEl.style.display = 'none';");
348
+ js.push(" document.getElementById('click-overlay').style.display = 'flex';");
349
+ js.push(" }, 500);");
350
+ js.push(" this.lastFps = performance.now();");
351
+ js.push(" this.loop();");
352
+ js.push(" },");
353
+ js.push("");
354
+ js.push(" buildBlockMap: function () {");
355
+ js.push(" for (var i = 0; i < BLOCKS.length; i++) {");
356
+ js.push(" var b = BLOCKS[i];");
357
+ js.push(" this.blockMap[b.x + ',' + b.y + ',' + b.z] = b;");
358
+ js.push(" }");
359
+ js.push(" },");
360
+ js.push("");
361
+ js.push(" hasBlock: function (x, y, z) {");
362
+ js.push(" return this.blockMap[x + ',' + y + ',' + z] !== undefined;");
363
+ js.push(" },");
364
+ js.push("");
365
+ js.push(" compileShaders: function () {");
366
+ js.push(" var gl = this.gl;");
367
+ js.push(" var vs = gl.createShader(gl.VERTEX_SHADER);");
368
+ js.push(" gl.shaderSource(vs, VS_SRC); gl.compileShader(vs);");
369
+ js.push(" var fs = gl.createShader(gl.FRAGMENT_SHADER);");
370
+ js.push(" gl.shaderSource(fs, FS_SRC); gl.compileShader(fs);");
371
+ js.push(" this.program = gl.createProgram();");
372
+ js.push(" gl.attachShader(this.program, vs);");
373
+ js.push(" gl.attachShader(this.program, fs);");
374
+ js.push(" gl.linkProgram(this.program);");
375
+ js.push(" gl.useProgram(this.program);");
376
+ js.push(" this.uProj = gl.getUniformLocation(this.program, 'uProj');");
377
+ js.push(" this.uView = gl.getUniformLocation(this.program, 'uView');");
378
+ js.push(" this.uSunDir = gl.getUniformLocation(this.program, 'uSunDir');");
379
+ js.push(" this.uSunColor = gl.getUniformLocation(this.program, 'uSunColor');");
380
+ js.push(" this.uAmbient = gl.getUniformLocation(this.program, 'uAmbient');");
381
+ js.push(" this.uFogColor = gl.getUniformLocation(this.program, 'uFogColor');");
382
+ js.push(" this.uFogNear = gl.getUniformLocation(this.program, 'uFogNear');");
383
+ js.push(" this.uFogFar = gl.getUniformLocation(this.program, 'uFogFar');");
384
+ js.push(" this.uCamPos = gl.getUniformLocation(this.program, 'uCamPos');");
385
+ js.push(" gl.uniform3f(this.uSunDir, 0.4, 0.8, 0.3);");
386
+ js.push(" gl.uniform3f(this.uSunColor, 1.0, 0.95, 0.85);");
387
+ js.push(" gl.uniform3f(this.uAmbient, 0.35, 0.38, 0.5);");
388
+ js.push(" gl.uniform3f(this.uFogColor, 0.53, 0.81, 0.92);");
389
+ js.push(" gl.uniform1f(this.uFogNear, 30.0);");
390
+ js.push(" gl.uniform1f(this.uFogFar, 70.0);");
391
+ js.push(" },");
392
+ js.push("");
393
+ js.push(" getBlockColor: function (b, face) {");
394
+ js.push(" var r, g, bl;");
395
+ js.push(" if (b.t === 'grass') {");
396
+ js.push(" if (face === 'top') { r = 0.34; g = 0.68; bl = 0.24; }");
397
+ js.push(" else if (face === 'bottom') { r = 0.55; g = 0.38; bl = 0.18; }");
398
+ js.push(" else { r = 0.42; g = 0.50; bl = 0.22; }");
399
+ js.push(" } else if (b.t === 'dirt') {");
400
+ js.push(" r = 0.55; g = 0.38; bl = 0.18;");
401
+ js.push(" } else if (b.t === 'stone') {");
402
+ js.push(" if (face === 'top') { r = 0.58; g = 0.58; bl = 0.58; }");
403
+ js.push(" else { r = 0.50; g = 0.50; bl = 0.50; }");
404
+ js.push(" } else if (b.t === 'sand') {");
405
+ js.push(" if (face === 'top') { r = 0.86; g = 0.82; bl = 0.58; }");
406
+ js.push(" else { r = 0.80; g = 0.76; bl = 0.52; }");
407
+ js.push(" } else if (b.t === 'water') {");
408
+ js.push(" if (face === 'top') { r = 0.15; g = 0.45; bl = 0.75; }");
409
+ js.push(" else { r = 0.10; g = 0.35; bl = 0.65; }");
410
+ js.push(" } else if (b.t === 'snow') {");
411
+ js.push(" if (face === 'top') { r = 0.95; g = 0.96; bl = 0.98; }");
412
+ js.push(" else { r = 0.88; g = 0.90; bl = 0.92; }");
413
+ js.push(" } else if (b.t === 'wood' || b.t === 'log') {");
414
+ js.push(" if (face === 'top' || face === 'bottom') { r = 0.62; g = 0.52; bl = 0.30; }");
415
+ js.push(" else { r = 0.45; g = 0.32; bl = 0.18; }");
416
+ js.push(" } else if (b.t === 'leaf' || b.t === 'leaves') {");
417
+ js.push(" r = 0.20; g = 0.55; bl = 0.15;");
418
+ js.push(" } else if (b.t === 'planks') {");
419
+ js.push(" r = 0.72; g = 0.58; bl = 0.35;");
420
+ js.push(" } else if (b.t === 'cobblestone' || b.t === 'cobble') {");
421
+ js.push(" if (face === 'top') { r = 0.52; g = 0.52; bl = 0.52; }");
422
+ js.push(" else { r = 0.44; g = 0.44; bl = 0.44; }");
423
+ js.push(" } else if (b.t === 'gravel') {");
424
+ js.push(" r = 0.56; g = 0.54; bl = 0.50;");
425
+ js.push(" } else if (b.t === 'clay') {");
426
+ js.push(" r = 0.62; g = 0.60; bl = 0.58;");
427
+ js.push(" } else if (b.t === 'iron' || b.t === 'ore') {");
428
+ js.push(" r = 0.65; g = 0.60; bl = 0.55;");
429
+ js.push(" } else if (b.t === 'gold') {");
430
+ js.push(" r = 0.90; g = 0.78; bl = 0.20;");
431
+ js.push(" } else if (b.t === 'diamond') {");
432
+ js.push(" r = 0.30; g = 0.85; bl = 0.88;");
433
+ js.push(" } else if (b.t === 'lava') {");
434
+ js.push(" r = 0.95; g = 0.35; bl = 0.05;");
435
+ js.push(" } else if (b.t === 'brick') {");
436
+ js.push(" r = 0.65; g = 0.32; bl = 0.25;");
437
+ js.push(" } else {");
438
+ js.push(" var hash = 0;");
439
+ js.push(" for (var ci = 0; ci < b.t.length; ci++) hash = ((hash << 5) - hash) + b.t.charCodeAt(ci);");
440
+ js.push(" r = 0.3 + (((hash & 0xFF) / 255) * 0.5);");
441
+ js.push(" g = 0.3 + ((((hash >> 8) & 0xFF) / 255) * 0.5);");
442
+ js.push(" bl = 0.3 + ((((hash >> 16) & 0xFF) / 255) * 0.5);");
443
+ js.push(" }");
444
+ js.push(" var v = 0.85 + (b.y / 12.0) * 0.15;");
445
+ js.push(" return [r * v, g * v, bl * v];");
446
+ js.push(" },");
447
+ js.push("");
448
+ js.push(" buildMesh: function () {");
449
+ js.push(" var verts = [];");
450
+ js.push(" var self = this;");
451
+ js.push(" function quad(x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4, nx,ny,nz, c) {");
452
+ js.push(" verts.push(x1,y1,z1, c[0],c[1],c[2], nx,ny,nz);");
453
+ js.push(" verts.push(x2,y2,z2, c[0],c[1],c[2], nx,ny,nz);");
454
+ js.push(" verts.push(x3,y3,z3, c[0],c[1],c[2], nx,ny,nz);");
455
+ js.push(" verts.push(x1,y1,z1, c[0],c[1],c[2], nx,ny,nz);");
456
+ js.push(" verts.push(x3,y3,z3, c[0],c[1],c[2], nx,ny,nz);");
457
+ js.push(" verts.push(x4,y4,z4, c[0],c[1],c[2], nx,ny,nz);");
458
+ js.push(" }");
459
+ js.push(" for (var i = 0; i < BLOCKS.length; i++) {");
460
+ js.push(" var b = BLOCKS[i];");
461
+ js.push(" var x = b.x, y = b.y, z = b.z;");
462
+ js.push(" if (!self.hasBlock(x, y+1, z)) {");
463
+ js.push(" var ct = self.getBlockColor(b, 'top');");
464
+ js.push(" quad(x,y+1,z, x+1,y+1,z, x+1,y+1,z+1, x,y+1,z+1, 0,1,0, ct);");
465
+ js.push(" }");
466
+ js.push(" if (!self.hasBlock(x, y-1, z)) {");
467
+ js.push(" var cb = self.getBlockColor(b, 'bottom');");
468
+ js.push(" quad(x,y,z+1, x+1,y,z+1, x+1,y,z, x,y,z, 0,-1,0, cb);");
469
+ js.push(" }");
470
+ js.push(" if (!self.hasBlock(x, y, z-1)) {");
471
+ js.push(" var cn = self.getBlockColor(b, 'side');");
472
+ js.push(" quad(x,y,z, x+1,y,z, x+1,y+1,z, x,y+1,z, 0,0,-1, cn);");
473
+ js.push(" }");
474
+ js.push(" if (!self.hasBlock(x, y, z+1)) {");
475
+ js.push(" var cs = self.getBlockColor(b, 'side');");
476
+ js.push(" quad(x+1,y,z+1, x,y,z+1, x,y+1,z+1, x+1,y+1,z+1, 0,0,1, cs);");
477
+ js.push(" }");
478
+ js.push(" if (!self.hasBlock(x-1, y, z)) {");
479
+ js.push(" var cw = self.getBlockColor(b, 'side');");
480
+ js.push(" quad(x,y,z+1, x,y,z, x,y+1,z, x,y+1,z+1, -1,0,0, cw);");
481
+ js.push(" }");
482
+ js.push(" if (!self.hasBlock(x+1, y, z)) {");
483
+ js.push(" var ce = self.getBlockColor(b, 'side');");
484
+ js.push(" quad(x+1,y,z, x+1,y,z+1, x+1,y+1,z+1, x+1,y+1,z, 1,0,0, ce);");
485
+ js.push(" }");
486
+ js.push(" }");
487
+ js.push(" this.vertCount = verts.length / 9;");
488
+ js.push(" var gl = this.gl;");
489
+ js.push(" this.vbo = gl.createBuffer();");
490
+ js.push(" gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);");
491
+ js.push(" gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);");
492
+ js.push(" var aPos = gl.getAttribLocation(this.program, 'aPos');");
493
+ js.push(" var aColor = gl.getAttribLocation(this.program, 'aColor');");
494
+ js.push(" var aNormal = gl.getAttribLocation(this.program, 'aNormal');");
495
+ js.push(" gl.enableVertexAttribArray(aPos);");
496
+ js.push(" gl.enableVertexAttribArray(aColor);");
497
+ js.push(" gl.enableVertexAttribArray(aNormal);");
498
+ js.push(" gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 36, 0);");
499
+ js.push(" gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, 36, 12);");
500
+ js.push(" gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 36, 24);");
501
+ js.push(" },");
502
+ js.push("");
503
+ js.push(" resize: function () {");
504
+ js.push(" this.canvas.width = window.innerWidth;");
505
+ js.push(" this.canvas.height = window.innerHeight;");
506
+ js.push(" if (this.gl) this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);");
507
+ js.push(" },");
508
+ js.push("");
509
+ js.push(" bindInput: function () {");
510
+ js.push(" var self = this;");
511
+ js.push(" document.addEventListener('keydown', function (e) { self.keys[e.code] = true; });");
512
+ js.push(" document.addEventListener('keyup', function (e) { self.keys[e.code] = false; });");
513
+ js.push(" window.addEventListener('resize', function () { self.resize(); });");
514
+ js.push(" document.addEventListener('mousemove', function (e) {");
515
+ js.push(" if (!self.locked) return;");
516
+ js.push(" self.yaw += e.movementX * 0.003;");
517
+ js.push(" self.pitch -= e.movementY * 0.003;");
518
+ js.push(" if (self.pitch > 1.5) self.pitch = 1.5;");
519
+ js.push(" if (self.pitch < -1.5) self.pitch = -1.5;");
520
+ js.push(" });");
521
+ js.push(" document.addEventListener('pointerlockchange', function () {");
522
+ js.push(" self.locked = document.pointerLockElement === self.canvas;");
523
+ js.push(" var ov = document.getElementById('click-overlay');");
524
+ js.push(" ov.style.display = self.locked ? 'none' : 'flex';");
525
+ js.push(" });");
526
+ js.push(" document.getElementById('click-overlay').addEventListener('click', function () {");
527
+ js.push(" self.canvas.requestPointerLock();");
528
+ js.push(" });");
529
+ js.push(" },");
530
+ js.push("");
531
+ js.push(" getGroundY: function (x, z) {");
532
+ js.push(" var bx = Math.floor(x);");
533
+ js.push(" var bz = Math.floor(z);");
534
+ js.push(" for (var y = 30; y >= 0; y--) {");
535
+ js.push(" if (this.hasBlock(bx, y, bz)) return y + 1;");
536
+ js.push(" }");
537
+ js.push(" return 1;");
538
+ js.push(" },");
539
+ js.push("");
540
+ js.push(" update: function () {");
541
+ js.push(" if (!this.locked) return;");
542
+ js.push(" var spd = 0.08;");
543
+ js.push(" if (this.keys['ShiftLeft'] || this.keys['ShiftRight']) spd = 0.16;");
544
+ js.push(" var dx = 0, dz = 0;");
545
+ js.push(" var sy = Math.sin(this.yaw), cy = Math.cos(this.yaw);");
546
+ js.push(" if (this.keys['KeyW'] || this.keys['ArrowUp']) { dx -= sy * spd; dz -= cy * spd; }");
547
+ js.push(" if (this.keys['KeyS'] || this.keys['ArrowDown']) { dx += sy * spd; dz += cy * spd; }");
548
+ js.push(" if (this.keys['KeyA'] || this.keys['ArrowLeft']) { dx -= cy * spd; dz += sy * spd; }");
549
+ js.push(" if (this.keys['KeyD'] || this.keys['ArrowRight']) { dx += cy * spd; dz -= sy * spd; }");
550
+ js.push(" var nx = this.camX + dx;");
551
+ js.push(" var nz = this.camZ + dz;");
552
+ js.push(" var feet = Math.floor(this.camY - 1.6);");
553
+ js.push(" var bnx = Math.floor(nx);");
554
+ js.push(" var bnz = Math.floor(nz);");
555
+ js.push(" if (!this.hasBlock(bnx, feet, Math.floor(this.camZ)) && !this.hasBlock(bnx, feet+1, Math.floor(this.camZ))) {");
556
+ js.push(" this.camX = nx;");
557
+ js.push(" }");
558
+ js.push(" if (!this.hasBlock(Math.floor(this.camX), feet, bnz) && !this.hasBlock(Math.floor(this.camX), feet+1, bnz)) {");
559
+ js.push(" this.camZ = nz;");
560
+ js.push(" }");
561
+ js.push(" this.velY -= this.gravity;");
562
+ js.push(" this.camY += this.velY;");
563
+ js.push(" var gy = this.getGroundY(this.camX, this.camZ) + 1.6;");
564
+ js.push(" if (this.camY <= gy) {");
565
+ js.push(" this.camY = gy;");
566
+ js.push(" this.velY = 0;");
567
+ js.push(" this.onGround = true;");
568
+ js.push(" } else {");
569
+ js.push(" this.onGround = false;");
570
+ js.push(" }");
571
+ js.push(" if (this.keys['Space'] && this.onGround) {");
572
+ js.push(" this.velY = 0.18;");
573
+ js.push(" this.onGround = false;");
574
+ js.push(" }");
575
+ js.push(" },");
576
+ js.push("");
577
+ js.push(" perspective: function (fov, aspect, near, far) {");
578
+ js.push(" var f = 1.0 / Math.tan(fov / 2);");
579
+ js.push(" var nf = 1.0 / (near - far);");
580
+ js.push(" return new Float32Array([");
581
+ js.push(" f/aspect, 0, 0, 0,");
582
+ js.push(" 0, f, 0, 0,");
583
+ js.push(" 0, 0, (far+near)*nf, -1,");
584
+ js.push(" 0, 0, 2*far*near*nf, 0");
585
+ js.push(" ]);");
586
+ js.push(" },");
587
+ js.push("");
588
+ js.push(" lookAt: function () {");
589
+ js.push(" var cp = Math.cos(this.pitch), sp = Math.sin(this.pitch);");
590
+ js.push(" var cy = Math.cos(this.yaw), sy = Math.sin(this.yaw);");
591
+ js.push(" var fx = -sy * cp, fy = sp, fz = -cy * cp;");
592
+ js.push(" var len = Math.sqrt(fx*fx+fy*fy+fz*fz);");
593
+ js.push(" fx/=len; fy/=len; fz/=len;");
594
+ js.push(" var ux = 0, uy = 1, uz = 0;");
595
+ js.push(" var rx = fy*uz - fz*uy, ry = fz*ux - fx*uz, rz = fx*uy - fy*ux;");
596
+ js.push(" len = Math.sqrt(rx*rx+ry*ry+rz*rz);");
597
+ js.push(" rx/=len; ry/=len; rz/=len;");
598
+ js.push(" ux = ry*fz - rz*fy; uy = rz*fx - rx*fz; uz = rx*fy - ry*fx;");
599
+ js.push(" var tx = -rx*this.camX - ry*this.camY - rz*this.camZ;");
600
+ js.push(" var ty = -ux*this.camX - uy*this.camY - uz*this.camZ;");
601
+ js.push(" var tz = fx*this.camX + fy*this.camY + fz*this.camZ;");
602
+ js.push(" return new Float32Array([");
603
+ js.push(" rx, ux, -fx, 0,");
604
+ js.push(" ry, uy, -fy, 0,");
605
+ js.push(" rz, uz, -fz, 0,");
606
+ js.push(" tx, ty, tz, 1");
607
+ js.push(" ]);");
608
+ js.push(" },");
609
+ js.push("");
610
+ js.push(" render: function () {");
611
+ js.push(" var gl = this.gl;");
612
+ js.push(" gl.clearColor(0.53, 0.81, 0.92, 1.0);");
613
+ js.push(" gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);");
614
+ js.push(" gl.enable(gl.DEPTH_TEST);");
615
+ js.push(" gl.enable(gl.CULL_FACE);");
616
+ js.push(" gl.cullFace(gl.BACK);");
617
+ js.push(" var aspect = this.canvas.width / this.canvas.height;");
618
+ js.push(" var proj = this.perspective(1.2, aspect, 0.1, 200.0);");
619
+ js.push(" var view = this.lookAt();");
620
+ js.push(" gl.uniformMatrix4fv(this.uProj, false, proj);");
621
+ js.push(" gl.uniformMatrix4fv(this.uView, false, view);");
622
+ js.push(" gl.uniform3f(this.uCamPos, this.camX, this.camY, this.camZ);");
623
+ js.push(" gl.drawArrays(gl.TRIANGLES, 0, this.vertCount);");
624
+ js.push(" this.updateHUD();");
625
+ js.push(" },");
626
+ js.push("");
627
+ js.push(" getStandingBlock: function () {");
628
+ js.push(" var bx = Math.floor(this.camX);");
629
+ js.push(" var bz = Math.floor(this.camZ);");
630
+ js.push(" var by = Math.floor(this.camY - 1.7);");
631
+ js.push(" var b = this.blockMap[bx + ',' + by + ',' + bz];");
632
+ js.push(" return b ? b.t : 'air';");
633
+ js.push(" },");
634
+ js.push("");
635
+ js.push(" updateHUD: function () {");
636
+ js.push(" var h = document.getElementById('hud-info');");
637
+ js.push(" if (!h) return;");
638
+ js.push(" var standing = this.getStandingBlock();");
639
+ js.push(" var sprinting = this.keys['ShiftLeft'] || this.keys['ShiftRight'];");
640
+ js.push(" var tris = Math.floor(this.vertCount / 3);");
641
+ js.push(" h.innerHTML = '<div class=\"hud-title\">CaG Engine</div>' +");
642
+ js.push(" '<div>' + INFO.ID + ' v' + INFO.version + '</div>' +");
643
+ js.push(" '<div class=\"hud-sep\"></div>' +");
644
+ js.push(" '<div>FPS: ' + this.fps + ' | Tris: ' + tris.toLocaleString() + '</div>' +");
645
+ js.push(" '<div>Blocks: ' + BLOCKS.length.toLocaleString() + '</div>' +");
646
+ js.push(" '<div class=\"hud-sep\"></div>' +");
647
+ js.push(" '<div>X: ' + this.camX.toFixed(1) + ' Y: ' + this.camY.toFixed(1) + ' Z: ' + this.camZ.toFixed(1) + '</div>' +");
648
+ js.push(" '<div>Standing: ' + standing + (sprinting ? ' [SPRINT]' : '') + '</div>';");
649
+ js.push(" },");
650
+ js.push("");
651
+ js.push(" loop: function () {");
652
+ js.push(" this.update();");
653
+ js.push(" this.render();");
654
+ js.push(" this.frames++;");
655
+ js.push(" var now = performance.now();");
656
+ js.push(" if (now - this.lastFps >= 1000) {");
657
+ js.push(" this.fps = this.frames;");
658
+ js.push(" this.frames = 0;");
659
+ js.push(" this.lastFps = now;");
660
+ js.push(" }");
661
+ js.push(" requestAnimationFrame(this.loop.bind(this));");
662
+ js.push(" }");
663
+ js.push("};");
664
+ js.push("");
665
+ js.push("var bar = document.getElementById('bar-inner');");
666
+ js.push("var status = document.getElementById('load-status');");
667
+ js.push("bar.style.width = '10%';");
668
+ js.push("status.textContent = 'Initializing CaG Engine...';");
669
+ js.push("setTimeout(function () {");
670
+ js.push(" bar.style.width = '25%';");
671
+ js.push(" status.textContent = 'Parsing project data...';");
672
+ js.push("}, 300);");
673
+ js.push("setTimeout(function () {");
674
+ js.push(" bar.style.width = '45%';");
675
+ js.push(" status.textContent = 'Generating terrain (' + BLOCKS.length.toLocaleString() + ' blocks)...';");
676
+ js.push("}, 600);");
677
+ js.push("setTimeout(function () {");
678
+ js.push(" bar.style.width = '65%';");
679
+ js.push(" status.textContent = 'Building mesh geometry...';");
680
+ js.push("}, 900);");
681
+ js.push("setTimeout(function () {");
682
+ js.push(" bar.style.width = '80%';");
683
+ js.push(" status.textContent = 'Compiling CaG shaders...';");
684
+ js.push("}, 1200);");
685
+ js.push("setTimeout(function () {");
686
+ js.push(" bar.style.width = '95%';");
687
+ js.push(" status.textContent = 'Preparing renderer...';");
688
+ js.push("}, 1500);");
689
+ js.push("setTimeout(function () {");
690
+ js.push(" bar.style.width = '100%';");
691
+ js.push(" status.textContent = 'Ready';");
692
+ js.push(" status.style.color = '#4ecca3';");
693
+ js.push("}, 1800);");
694
+ js.push("setTimeout(function () { cag.init(); }, 2200);");
695
+
696
+ var html = "<!DOCTYPE html>\n<html>\n<head>\n";
697
+ html += "<meta charset='utf-8'>\n";
698
+ html += "<title>" + info.ID + " - CaG Engine</title>\n";
699
+ html += "<style>\n" + css + "\n</style>\n";
700
+ html += "</head>\n<body>\n";
701
+ html += "<div id='loading'>\n";
702
+ html += " <h1>CaG</h1>\n";
703
+ html += " <h2>" + info.ID + "</h2>\n";
704
+ html += " <div id='load-status'>Starting...</div>\n";
705
+ html += " <div id='bar-outer'><div id='bar-inner'></div></div>\n";
706
+ html += " <div class='version-tag'>v" + (info.version || "1.0.0") + "</div>\n";
707
+ html += "</div>\n";
708
+ html += "<div id='click-overlay' style='display:none'><div class='play-icon'><div class='play-triangle'></div></div><span>CLICK TO PLAY</span></div>\n";
709
+ html += "<div id='crosshair'></div>\n";
710
+ html += "<div id='hud'><div id='hud-box'><div id='hud-info'></div></div></div>\n";
711
+ html += "<div id='controls-hint'><b>WASD</b> Move &nbsp; <b>Mouse</b> Look &nbsp; <b>Space</b> Jump &nbsp; <b>Shift</b> Sprint &nbsp; <b>F11</b> Fullscreen &nbsp; <b>Esc</b> Release</div>\n";
712
+ html += "<canvas id='cag-canvas'></canvas>\n";
713
+ html += "<script>\n" + js.join("\n") + "\n</script>\n";
714
+ html += "</body>\n</html>";
715
+
716
+ return html;
717
+ };
718
+
719
+ ExeBuilder.prototype.generateWorldBlocks = function () {
720
+ var codeFiles = this.engine.getCodeFiles();
721
+ var topLayer = "grass";
722
+ var fillLayer = "dirt";
723
+ var bottomLayer = "stone";
724
+ var size = 32;
725
+ var seed = 54321;
726
+ var waterLevel = 3;
727
+ var hasWater = false;
728
+
729
+ for (var i = 0; i < codeFiles.length; i++) {
730
+ var content = fs.readFileSync(codeFiles[i], "utf-8");
731
+ var lines = content.split("\n");
732
+ for (var j = 0; j < lines.length; j++) {
733
+ var line = lines[j].trim();
734
+ if (line.indexOf("top") !== -1 && line.indexOf("layer") !== -1 && line.indexOf("=") !== -1) {
735
+ var eqIdx = line.lastIndexOf("=");
736
+ var val = line.substring(eqIdx + 1).trim();
737
+ if (val) topLayer = val.toLowerCase();
738
+ }
739
+ if (line.indexOf("fill") !== -1 && line.indexOf("layer") !== -1 && line.indexOf("=") !== -1) {
740
+ var fIdx = line.lastIndexOf("=");
741
+ var fVal = line.substring(fIdx + 1).trim();
742
+ if (fVal) fillLayer = fVal.toLowerCase();
743
+ }
744
+ if (line.indexOf("bottom") !== -1 && line.indexOf("layer") !== -1 && line.indexOf("=") !== -1) {
745
+ var bIdx = line.lastIndexOf("=");
746
+ var bVal = line.substring(bIdx + 1).trim();
747
+ if (bVal) bottomLayer = bVal.toLowerCase();
748
+ }
749
+ if (line.indexOf("water") !== -1) {
750
+ hasWater = true;
751
+ }
752
+ if (line.indexOf("size") !== -1 && line.indexOf("=") !== -1) {
753
+ var sIdx = line.lastIndexOf("=");
754
+ var sVal = parseInt(line.substring(sIdx + 1).trim(), 10);
755
+ if (!isNaN(sVal) && sVal >= 8 && sVal <= 128) size = sVal;
756
+ }
757
+ if (line.indexOf("seed") !== -1 && line.indexOf("=") !== -1) {
758
+ var sdIdx = line.lastIndexOf("=");
759
+ var sdVal = parseInt(line.substring(sdIdx + 1).trim(), 10);
760
+ if (!isNaN(sdVal)) seed = sdVal;
761
+ }
762
+ }
763
+ }
764
+
765
+ var blocks = [];
766
+
767
+ var noise = function (x, z, s, scale) {
768
+ var nx = x / scale;
769
+ var nz = z / scale;
770
+ var a = Math.sin(nx * 12.9898 + nz * 78.233 + s) * 43758.5453;
771
+ return a - Math.floor(a);
772
+ };
773
+
774
+ var smoothHeight = function (x, z) {
775
+ var h1 = noise(x, z, seed, 8.0) * 4;
776
+ var h2 = noise(x, z, seed + 100, 16.0) * 6;
777
+ var h3 = noise(x, z, seed + 200, 4.0) * 2;
778
+ var h4 = noise(x + 0.5, z + 0.5, seed + 300, 12.0) * 3;
779
+
780
+ var avg = 0;
781
+ var count = 0;
782
+ for (var dx = -1; dx <= 1; dx++) {
783
+ for (var dz = -1; dz <= 1; dz++) {
784
+ var n1 = noise(x + dx, z + dz, seed, 8.0) * 4;
785
+ var n2 = noise(x + dx, z + dz, seed + 100, 16.0) * 6;
786
+ avg += n1 + n2;
787
+ count++;
788
+ }
789
+ }
790
+ avg = avg / count;
791
+
792
+ var raw = (h1 + h2 + h3 + h4) * 0.4 + avg * 0.6;
793
+ return Math.max(1, Math.min(Math.floor(raw), 12));
794
+ };
795
+
796
+ for (var x = 0; x < size; x++) {
797
+ for (var z = 0; z < size; z++) {
798
+ var height = smoothHeight(x, z);
799
+ for (var y = 0; y <= height; y++) {
800
+ var blockType;
801
+ if (y === height) {
802
+ blockType = topLayer;
803
+ } else if (y === 0) {
804
+ blockType = bottomLayer;
805
+ } else if (y >= height - 1) {
806
+ blockType = topLayer === "grass" ? fillLayer : topLayer;
807
+ } else {
808
+ blockType = fillLayer;
809
+ }
810
+ blocks.push({ x: x, y: y, z: z, t: blockType });
811
+ }
812
+ if (hasWater && height < waterLevel) {
813
+ for (var wy = height + 1; wy <= waterLevel; wy++) {
814
+ blocks.push({ x: x, y: wy, z: z, t: "water" });
815
+ }
816
+ }
817
+ }
818
+ }
819
+
820
+ return blocks;
821
+ };
822
+
823
+ ExeBuilder.prototype.generateMacApp = function (html, appPath) {
824
+ var contentsDir = path.join(appPath, 'Contents');
825
+ var macosDir = path.join(contentsDir, 'MacOS');
826
+ var resourcesDir = path.join(contentsDir, 'Resources');
827
+ fs.mkdirSync(macosDir, { recursive: true });
828
+ fs.mkdirSync(resourcesDir, { recursive: true });
829
+ fs.writeFileSync(path.join(resourcesDir, 'game.html'), html);
830
+ var launcher = '#!/bin/bash\n';
831
+ launcher += 'DIR="$(cd "$(dirname "$0")" && pwd)"\n';
832
+ launcher += 'HTML="$DIR/../Resources/game.html"\n';
833
+ launcher += 'if [ -d "/Applications/Google Chrome.app" ]; then\n';
834
+ launcher += ' open -a "Google Chrome" --args --app="file://$HTML" --window-size=1280,720 --disable-extensions --no-first-run\n';
835
+ launcher += 'elif [ -d "/Applications/Microsoft Edge.app" ]; then\n';
836
+ launcher += ' open -a "Microsoft Edge" --args --app="file://$HTML" --window-size=1280,720 --disable-extensions --no-first-run\n';
837
+ launcher += 'else\n';
838
+ launcher += ' open "$HTML"\n';
839
+ launcher += 'fi\n';
840
+ var launcherPath = path.join(macosDir, this.projectName);
841
+ fs.writeFileSync(launcherPath, launcher);
842
+ try { fs.chmodSync(launcherPath, 0o755); } catch (e) {}
843
+ var plist = '<?xml version="1.0" encoding="UTF-8"?>\n';
844
+ plist += '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n';
845
+ plist += '<plist version="1.0">\n<dict>\n';
846
+ plist += ' <key>CFBundleExecutable</key>\n <string>' + this.projectName + '</string>\n';
847
+ plist += ' <key>CFBundleName</key>\n <string>' + this.info.ID + '</string>\n';
848
+ plist += ' <key>CFBundleIdentifier</key>\n <string>' + this.info.ID + '</string>\n';
849
+ plist += ' <key>CFBundleVersion</key>\n <string>' + (this.info.version || '1.0.0') + '</string>\n';
850
+ plist += ' <key>CFBundlePackageType</key>\n <string>APPL</string>\n';
851
+ plist += '</dict>\n</plist>\n';
852
+ fs.writeFileSync(path.join(contentsDir, 'Info.plist'), plist);
853
+ };
854
+
855
+ ExeBuilder.prototype.generateLinuxApp = function (html, outputPath) {
856
+ var b64 = Buffer.from(html, 'utf-8').toString('base64');
857
+ var script = '#!/bin/bash\n';
858
+ script += 'TEMP_DIR=$(mktemp -d /tmp/cag_XXXXXX)\n';
859
+ script += 'HTML_PATH="$TEMP_DIR/game.html"\n';
860
+ script += 'echo "' + b64 + '" | base64 -d > "$HTML_PATH"\n';
861
+ script += 'cleanup() { rm -rf "$TEMP_DIR"; }\n';
862
+ script += 'trap cleanup EXIT\n';
863
+ script += 'if command -v google-chrome &>/dev/null; then\n';
864
+ script += ' google-chrome --app="file://$HTML_PATH" --window-size=1280,720 --disable-extensions --no-first-run 2>/dev/null\n';
865
+ script += 'elif command -v chromium-browser &>/dev/null; then\n';
866
+ script += ' chromium-browser --app="file://$HTML_PATH" --window-size=1280,720 --disable-extensions --no-first-run 2>/dev/null\n';
867
+ script += 'elif command -v chromium &>/dev/null; then\n';
868
+ script += ' chromium --app="file://$HTML_PATH" --window-size=1280,720 --disable-extensions --no-first-run 2>/dev/null\n';
869
+ script += 'elif command -v microsoft-edge &>/dev/null; then\n';
870
+ script += ' microsoft-edge --app="file://$HTML_PATH" --window-size=1280,720 --disable-extensions --no-first-run 2>/dev/null\n';
871
+ script += 'else\n';
872
+ script += ' xdg-open "$HTML_PATH" 2>/dev/null || open "$HTML_PATH"\n';
873
+ script += 'fi\n';
874
+ fs.writeFileSync(outputPath, script);
875
+ try { fs.chmodSync(outputPath, 0o755); } catch (e) {}
876
+ };
877
+
878
+ ExeBuilder.prototype.cleanup = function () {
879
+ this.removeDirectory(this.tempDir);
880
+ };
881
+
882
+ ExeBuilder.prototype.removeDirectory = function (dir) {
883
+ if (!fs.existsSync(dir)) return;
884
+ var entries = fs.readdirSync(dir, { withFileTypes: true });
885
+ for (var i = 0; i < entries.length; i++) {
886
+ var fullPath = path.join(dir, entries[i].name);
887
+ if (entries[i].isDirectory()) {
888
+ this.removeDirectory(fullPath);
889
+ } else {
890
+ fs.unlinkSync(fullPath);
891
+ }
892
+ }
893
+ fs.rmdirSync(dir);
894
+ };
895
+
896
+ ExeBuilder.prototype.getFileSize = function (filePath) {
897
+ if (!fs.existsSync(filePath)) return 0;
898
+ return fs.statSync(filePath).size;
899
+ };
900
+
901
+ ExeBuilder.prototype.getDirectorySize = function (dir) {
902
+ if (!fs.existsSync(dir)) return 0;
903
+ var total = 0;
904
+ var entries = fs.readdirSync(dir, { withFileTypes: true });
905
+ for (var i = 0; i < entries.length; i++) {
906
+ var fullPath = path.join(dir, entries[i].name);
907
+ if (entries[i].isDirectory()) {
908
+ total += this.getDirectorySize(fullPath);
909
+ } else {
910
+ total += fs.statSync(fullPath).size;
911
+ }
912
+ }
913
+ return total;
914
+ };
915
+
916
+ ExeBuilder.prototype.formatSize = function (bytes) {
917
+ if (bytes < 1024) return bytes + " B";
918
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
919
+ return (bytes / (1024 * 1024)).toFixed(1) + " MB";
920
+ };
921
+
922
+ ExeBuilder.prototype.detectPlatform = function () {
923
+ var p = process.platform;
924
+ if (p === "win32") return "win";
925
+ if (p === "darwin") return "macos";
926
+ return "linux";
927
+ };
928
+
929
+ module.exports = ExeBuilder;