@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.
@@ -0,0 +1,728 @@
1
+ var path = require("path");
2
+ var fs = require("fs");
3
+ var childProcess = require("child_process");
4
+
5
+ function ElectronRunner(engine, apiSettings) {
6
+ this.engine = engine;
7
+ this.info = engine.getInfo();
8
+ this.projectRoot = engine.projectRoot;
9
+ this.tempDir = path.join(this.projectRoot, ".cag-run");
10
+ this.apiSettings = apiSettings || {};
11
+ }
12
+
13
+ ElectronRunner.prototype.run = function () {
14
+ this.ensureDirectories();
15
+ this.generateApp();
16
+ this.installAndLaunch();
17
+ return { success: true };
18
+ };
19
+
20
+ ElectronRunner.prototype.ensureDirectories = function () {
21
+ fs.mkdirSync(this.tempDir, { recursive: true });
22
+ };
23
+
24
+ ElectronRunner.prototype.generateApp = function () {
25
+ var pkgPath = path.join(this.tempDir, "package.json");
26
+ if (!fs.existsSync(pkgPath)) {
27
+ var pkg = {
28
+ name: "cag-runner",
29
+ version: "1.0.0",
30
+ main: "main.js",
31
+ dependencies: {}
32
+ };
33
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
34
+ }
35
+ fs.writeFileSync(path.join(this.tempDir, "main.js"), this.getMainProcessSource());
36
+ fs.writeFileSync(path.join(this.tempDir, "index.html"), this.getRendererSource());
37
+ };
38
+
39
+ ElectronRunner.prototype.installAndLaunch = function () {
40
+ var electronBin = path.join(this.tempDir, "node_modules", ".bin", "electron");
41
+ if (process.platform === "win32") {
42
+ electronBin = electronBin + ".cmd";
43
+ }
44
+
45
+ if (!fs.existsSync(electronBin)) {
46
+ childProcess.execSync("npm install electron --save --no-audit --no-fund", {
47
+ cwd: this.tempDir,
48
+ stdio: "inherit"
49
+ });
50
+ }
51
+
52
+ childProcess.execSync("\"" + electronBin + "\" .", {
53
+ cwd: this.tempDir,
54
+ stdio: "inherit",
55
+ shell: true
56
+ });
57
+ };
58
+
59
+ ElectronRunner.prototype.getMainProcessSource = function () {
60
+ var s = this.apiSettings;
61
+ var windowTitle = s.windowTitle || "Default Game";
62
+ var windowWidth = s.windowWidth || 1280;
63
+ var windowHeight = s.windowHeight || 720;
64
+ var bg = s.background || "#1a1a2e";
65
+
66
+ var lines = [];
67
+ lines.push("var electron = require('electron');");
68
+ lines.push("var app = electron.app;");
69
+ lines.push("var BrowserWindow = electron.BrowserWindow;");
70
+ lines.push("var path = require('path');");
71
+ lines.push("");
72
+ lines.push("var win = null;");
73
+ lines.push("");
74
+ lines.push("app.whenReady().then(function () {");
75
+ lines.push(" win = new BrowserWindow({");
76
+ lines.push(" width: " + windowWidth + ",");
77
+ lines.push(" height: " + windowHeight + ",");
78
+ lines.push(" minWidth: 800,");
79
+ lines.push(" minHeight: 600,");
80
+ lines.push(" title: " + JSON.stringify(windowTitle) + ",");
81
+ lines.push(" fullscreenable: true,");
82
+ lines.push(" webPreferences: { nodeIntegration: false, contextIsolation: true },");
83
+ lines.push(" backgroundColor: " + JSON.stringify(bg) + ",");
84
+ lines.push(" autoHideMenuBar: true,");
85
+ lines.push(" show: false");
86
+ lines.push(" });");
87
+ lines.push(" win.setMenuBarVisibility(false);");
88
+ lines.push(" win.loadFile('index.html');");
89
+ lines.push(" win.once('ready-to-show', function () { win.show(); });");
90
+ lines.push(" win.on('closed', function () { win = null; });");
91
+ lines.push(" win.webContents.on('before-input-event', function (event, input) {");
92
+ lines.push(" if (input.key === 'F11') win.setFullScreen(!win.isFullScreen());");
93
+ lines.push(" });");
94
+ lines.push("});");
95
+ lines.push("app.on('window-all-closed', function () { app.quit(); });");
96
+ return lines.join("\n");
97
+ };
98
+
99
+ ElectronRunner.prototype.getRendererSource = function () {
100
+ var s = this.apiSettings;
101
+ var chunkSize = s.chunkSize || 16;
102
+ var renderDist = s.renderDistance || 1;
103
+ var sky = s.skyColor || { r: 0.53, g: 0.81, b: 0.92 };
104
+ var fogC = s.fogColor || { r: 0.53, g: 0.81, b: 0.92 };
105
+ var fogNear = s.fogNear || 64;
106
+ var fogFar = s.fogFar || 128;
107
+ var sunD = s.sunDirection || { x: 0.5, y: 1.0, z: 0.3 };
108
+ var amb = s.ambientLight || { r: 0.3, g: 0.3, b: 0.4 };
109
+ var fov = s.fov || 70;
110
+ var pSpeed = s.playerSpeed || 0.08;
111
+ var jHeight = s.jumpHeight || 0.18;
112
+ var grav = s.gravity || 0.015;
113
+ var spX = s.spawnX || 8;
114
+ var spY = s.spawnY || 2;
115
+ var spZ = s.spawnZ || 8;
116
+ var windowTitle = s.windowTitle || "Default Game";
117
+ var infoID = this.info.ID;
118
+ var infoVer = this.info.version || "1.0.0";
119
+
120
+ var css = [
121
+ "* { margin: 0; padding: 0; box-sizing: border-box; }",
122
+ "body { background: #1a1a2e; overflow: hidden; font-family: monospace; }",
123
+ "#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; }",
124
+ "#loading.fade-out { opacity: 0; pointer-events: none; }",
125
+ "#loading h1 { font-size: 64px; color: #4ecca3; margin-bottom: 4px; letter-spacing: 6px; text-shadow: 0 0 30px rgba(78,204,163,0.3); }",
126
+ "#loading h2 { font-size: 14px; color: #666; margin-bottom: 30px; font-weight: normal; letter-spacing: 2px; }",
127
+ "#load-status { font-size: 12px; color: #555; margin-bottom: 12px; min-height: 18px; transition: color 0.3s; }",
128
+ "#bar-outer { width: 300px; height: 3px; background: #2a2a3e; border-radius: 2px; overflow: hidden; }",
129
+ "#bar-inner { width: 0%; height: 100%; background: linear-gradient(90deg, #4ecca3, #45b7d1); border-radius: 2px; transition: width 0.4s ease-out; }",
130
+ ".version-tag { font-size: 11px; color: #444; margin-top: 20px; }",
131
+ "#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; }",
132
+ "#click-overlay:hover { background: rgba(0,0,0,0.55); }",
133
+ ".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; }",
134
+ "#click-overlay:hover .play-icon { transform: scale(1.1); border-color: #45b7d1; }",
135
+ ".play-triangle { width: 0; height: 0; border-style: solid; border-width: 15px 0 15px 28px; border-color: transparent transparent transparent #4ecca3; margin-left: 6px; }",
136
+ "#click-overlay span { color: #888; font-size: 16px; letter-spacing: 3px; }",
137
+ "canvas { display: block; position: fixed; top: 0; left: 0; }",
138
+ "#hud { position: fixed; top: 10px; left: 10px; z-index: 100; pointer-events: none; }",
139
+ "#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; }",
140
+ "#hud-box div { color: #bbb; font-size: 11px; line-height: 17px; }",
141
+ ".hud-title { color: #4ecca3; font-size: 13px; font-weight: bold; margin-bottom: 3px; letter-spacing: 1px; }",
142
+ ".hud-sep { border-top: 1px solid #333; margin: 4px 0; }",
143
+ "#crosshair { position: fixed; top: 50%; left: 50%; width: 24px; height: 24px; margin: -12px 0 0 -12px; z-index: 100; pointer-events: none; }",
144
+ "#crosshair::before { content: ''; position: absolute; top: 11px; left: 3px; width: 18px; height: 2px; background: rgba(255,255,255,0.6); border-radius: 1px; }",
145
+ "#crosshair::after { content: ''; position: absolute; top: 3px; left: 11px; width: 2px; height: 18px; background: rgba(255,255,255,0.6); border-radius: 1px; }",
146
+ "#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); }",
147
+ "#controls-hint b { color: #888; }"
148
+ ].join("\n");
149
+
150
+ var vsSrc = [
151
+ "attribute vec3 aPos;",
152
+ "attribute vec2 aUV;",
153
+ "attribute vec3 aNormal;",
154
+ "uniform mat4 uProj;",
155
+ "uniform mat4 uView;",
156
+ "varying vec2 vUV;",
157
+ "varying vec3 vNormal;",
158
+ "varying vec3 vWorldPos;",
159
+ "void main() {",
160
+ " vUV = aUV;",
161
+ " vNormal = aNormal;",
162
+ " vWorldPos = aPos;",
163
+ " gl_Position = uProj * uView * vec4(aPos, 1.0);",
164
+ "}"
165
+ ].join("\\n");
166
+
167
+ var fsSrc = [
168
+ "precision mediump float;",
169
+ "varying vec2 vUV;",
170
+ "varying vec3 vNormal;",
171
+ "varying vec3 vWorldPos;",
172
+ "uniform sampler2D uTexture;",
173
+ "uniform vec3 uSunDir;",
174
+ "uniform vec3 uSunColor;",
175
+ "uniform vec3 uAmbient;",
176
+ "uniform vec3 uFogColor;",
177
+ "uniform float uFogNear;",
178
+ "uniform float uFogFar;",
179
+ "uniform vec3 uCamPos;",
180
+ "void main() {",
181
+ " vec4 texColor = texture2D(uTexture, vUV);",
182
+ " vec3 norm = normalize(vNormal);",
183
+ " float diff = max(dot(norm, normalize(uSunDir)), 0.0);",
184
+ " vec3 lit = texColor.rgb * (uAmbient + uSunColor * diff);",
185
+ " float dist = length(vWorldPos - uCamPos);",
186
+ " float fog = clamp((dist - uFogNear) / (uFogFar - uFogNear), 0.0, 1.0);",
187
+ " vec3 final = mix(lit, uFogColor, fog);",
188
+ " gl_FragColor = vec4(final, texColor.a);",
189
+ "}"
190
+ ].join("\\n");
191
+
192
+ var js = [];
193
+ js.push("var CHUNK_SIZE = " + chunkSize + ";");
194
+ js.push("var RENDER_DISTANCE = " + renderDist + ";");
195
+ js.push("var INFO_ID = " + JSON.stringify(infoID) + ";");
196
+ js.push("var INFO_VERSION = " + JSON.stringify(infoVer) + ";");
197
+ js.push("var VS_SRC = \"" + vsSrc + "\";");
198
+ js.push("var FS_SRC = \"" + fsSrc + "\";");
199
+ js.push("");
200
+
201
+ var modelDataForRenderer = {};
202
+ if (s.models) {
203
+ var sModelNames = Object.keys(s.models);
204
+ for (var mi = 0; mi < sModelNames.length; mi++) {
205
+ var smName = sModelNames[mi];
206
+ var smData = s.models[smName];
207
+ var faceUVMap = {};
208
+ if (smData.uvMappings) {
209
+ for (var ui = 0; ui < smData.uvMappings.length; ui++) {
210
+ var mapping = smData.uvMappings[ui];
211
+ if (!faceUVMap[mapping.face]) {
212
+ faceUVMap[mapping.face] = mapping.uv;
213
+ }
214
+ }
215
+ }
216
+ modelDataForRenderer[smName] = {
217
+ textureDataURL: smData.textureDataURL || null,
218
+ resW: smData.resolution ? smData.resolution.width : 16,
219
+ resH: smData.resolution ? smData.resolution.height : 16,
220
+ faceUV: faceUVMap
221
+ };
222
+ }
223
+ }
224
+ js.push("var MODELS = " + JSON.stringify(modelDataForRenderer) + ";");
225
+ js.push("");
226
+
227
+ js.push("var game = {");
228
+ js.push(" gl: null, canvas: null, program: null,");
229
+ js.push(" camX: " + spX + ", camY: " + spY + ", camZ: " + spZ + ",");
230
+ js.push(" pitch: -0.3, yaw: 0,");
231
+ js.push(" keys: {}, locked: false,");
232
+ js.push(" velY: 0, onGround: false,");
233
+ js.push(" gravity: " + grav + ",");
234
+ js.push(" speed: " + pSpeed + ",");
235
+ js.push(" jumpHeight: " + jHeight + ",");
236
+ js.push(" fps: 0, frames: 0, lastFps: 0,");
237
+ js.push(" chunks: {},");
238
+ js.push(" totalVerts: 0,");
239
+ js.push(" totalBlocks: 0,");
240
+ js.push(" uProj: null, uView: null, uSunDir: null, uSunColor: null,");
241
+ js.push(" uAmbient: null, uFogColor: null, uFogNear: null, uFogFar: null, uCamPos: null,");
242
+ js.push(" started: false");
243
+ js.push("};");
244
+ js.push("");
245
+
246
+ // Chunk generation - flat terrain
247
+ js.push("function generateChunkBlocks(cx, cz) {");
248
+ js.push(" var blocks = [];");
249
+ js.push(" for (var x = 0; x < CHUNK_SIZE; x++) {");
250
+ js.push(" for (var z = 0; z < CHUNK_SIZE; z++) {");
251
+ js.push(" blocks.push({ x: cx * CHUNK_SIZE + x, y: 0, z: cz * CHUNK_SIZE + z, type: 'grass' });");
252
+ js.push(" }");
253
+ js.push(" }");
254
+ js.push(" return blocks;");
255
+ js.push("}");
256
+ js.push("");
257
+
258
+ // Face helper
259
+ js.push("function addFace(pos, uvArr, nrm, v0, v1, v2, v3, fuv, normal) {");
260
+ js.push(" pos.push(v0[0],v0[1],v0[2], v1[0],v1[1],v1[2], v2[0],v2[1],v2[2]);");
261
+ js.push(" pos.push(v0[0],v0[1],v0[2], v2[0],v2[1],v2[2], v3[0],v3[1],v3[2]);");
262
+ js.push(" uvArr.push(fuv[0],fuv[1], fuv[2],fuv[1], fuv[2],fuv[3]);");
263
+ js.push(" uvArr.push(fuv[0],fuv[1], fuv[2],fuv[3], fuv[0],fuv[3]);");
264
+ js.push(" for (var i = 0; i < 6; i++) {");
265
+ js.push(" nrm.push(normal[0], normal[1], normal[2]);");
266
+ js.push(" }");
267
+ js.push("}");
268
+ js.push("");
269
+
270
+ // UV normalization helper
271
+ js.push("function normUV(faceUV, resW, resH) {");
272
+ js.push(" if (!faceUV) return [0, 1, 1, 0];");
273
+ js.push(" return [faceUV[0]/resW, 1.0-faceUV[1]/resH, faceUV[2]/resW, 1.0-faceUV[3]/resH];");
274
+ js.push("}");
275
+ js.push("");
276
+
277
+ // Get UV coordinates for a model by name (case-insensitive)
278
+ js.push("function getModelUVs(name) {");
279
+ js.push(" var ln = name.toLowerCase();");
280
+ js.push(" var keys = Object.keys(MODELS);");
281
+ js.push(" for (var i = 0; i < keys.length; i++) {");
282
+ js.push(" if (keys[i].toLowerCase() === ln) {");
283
+ js.push(" var m = MODELS[keys[i]];");
284
+ js.push(" var f = m.faceUV || {};");
285
+ js.push(" return {");
286
+ js.push(" up: normUV(f.up, m.resW, m.resH),");
287
+ js.push(" down: normUV(f.down, m.resW, m.resH),");
288
+ js.push(" north: normUV(f.north, m.resW, m.resH),");
289
+ js.push(" south: normUV(f.south, m.resW, m.resH),");
290
+ js.push(" east: normUV(f.east, m.resW, m.resH),");
291
+ js.push(" west: normUV(f.west, m.resW, m.resH)");
292
+ js.push(" };");
293
+ js.push(" }");
294
+ js.push(" }");
295
+ js.push(" return { up:[0,1,1,0], down:[0,1,1,0], north:[0,1,1,0], south:[0,1,1,0], east:[0,1,1,0], west:[0,1,1,0] };");
296
+ js.push("}");
297
+ js.push("");
298
+
299
+ // Build mesh for a chunk
300
+ js.push("function buildChunkMesh(blocks, blockMap) {");
301
+ js.push(" var pos = [], uvArr = [], nrm = [];");
302
+ js.push(" var uv = getModelUVs('Grass');");
303
+ js.push(" for (var i = 0; i < blocks.length; i++) {");
304
+ js.push(" var b = blocks[i];");
305
+ js.push(" var x = b.x, y = b.y, z = b.z;");
306
+ js.push(" var k = function(bx,by,bz) { return bx+','+by+','+bz; };");
307
+ js.push(" if (!blockMap[k(x,y+1,z)]) addFace(pos,uvArr,nrm, [x,y+1,z],[x+1,y+1,z],[x+1,y+1,z+1],[x,y+1,z+1], uv.up, [0,1,0]);");
308
+ js.push(" if (!blockMap[k(x,y-1,z)]) addFace(pos,uvArr,nrm, [x,y,z+1],[x+1,y,z+1],[x+1,y,z],[x,y,z], uv.down, [0,-1,0]);");
309
+ js.push(" if (!blockMap[k(x,y,z-1)]) addFace(pos,uvArr,nrm, [x,y,z],[x+1,y,z],[x+1,y+1,z],[x,y+1,z], uv.north, [0,0,-1]);");
310
+ js.push(" if (!blockMap[k(x,y,z+1)]) addFace(pos,uvArr,nrm, [x+1,y,z+1],[x,y,z+1],[x,y+1,z+1],[x+1,y+1,z+1], uv.south, [0,0,1]);");
311
+ js.push(" if (!blockMap[k(x+1,y,z)]) addFace(pos,uvArr,nrm, [x+1,y,z],[x+1,y,z+1],[x+1,y+1,z+1],[x+1,y+1,z], uv.east, [1,0,0]);");
312
+ js.push(" if (!blockMap[k(x-1,y,z)]) addFace(pos,uvArr,nrm, [x,y,z+1],[x,y,z],[x,y+1,z],[x,y+1,z+1], uv.west, [-1,0,0]);");
313
+ js.push(" }");
314
+ js.push(" return { positions: pos, uvs: uvArr, normals: nrm };");
315
+ js.push("}");
316
+ js.push("");
317
+
318
+ // Build block map including neighbor chunks
319
+ js.push("function buildBlockMap(blocks, cx, cz) {");
320
+ js.push(" var map = {};");
321
+ js.push(" for (var i = 0; i < blocks.length; i++) {");
322
+ js.push(" var b = blocks[i];");
323
+ js.push(" map[b.x+','+b.y+','+b.z] = true;");
324
+ js.push(" }");
325
+ js.push(" var dirs = [[cx-1,cz],[cx+1,cz],[cx,cz-1],[cx,cz+1]];");
326
+ js.push(" for (var n = 0; n < dirs.length; n++) {");
327
+ js.push(" var nk = dirs[n][0]+','+dirs[n][1];");
328
+ js.push(" if (game.chunks[nk]) {");
329
+ js.push(" var nb = game.chunks[nk].blocks;");
330
+ js.push(" for (var j = 0; j < nb.length; j++) {");
331
+ js.push(" map[nb[j].x+','+nb[j].y+','+nb[j].z] = true;");
332
+ js.push(" }");
333
+ js.push(" }");
334
+ js.push(" }");
335
+ js.push(" return map;");
336
+ js.push("}");
337
+ js.push("");
338
+
339
+ // Load a chunk
340
+ js.push("function loadChunk(cx, cz) {");
341
+ js.push(" var chunkKey = cx+','+cz;");
342
+ js.push(" var blocks = generateChunkBlocks(cx, cz);");
343
+ js.push(" var blockMap = buildBlockMap(blocks, cx, cz);");
344
+ js.push(" var mesh = buildChunkMesh(blocks, blockMap);");
345
+ js.push(" var vertCount = mesh.positions.length / 3;");
346
+ js.push(" var gl = game.gl;");
347
+ js.push(" var vbo = gl.createBuffer();");
348
+ js.push(" var data = new Float32Array(vertCount * 8);");
349
+ js.push(" for (var v = 0; v < vertCount; v++) {");
350
+ js.push(" data[v*8+0] = mesh.positions[v*3+0];");
351
+ js.push(" data[v*8+1] = mesh.positions[v*3+1];");
352
+ js.push(" data[v*8+2] = mesh.positions[v*3+2];");
353
+ js.push(" data[v*8+3] = mesh.uvs[v*2+0];");
354
+ js.push(" data[v*8+4] = mesh.uvs[v*2+1];");
355
+ js.push(" data[v*8+5] = mesh.normals[v*3+0];");
356
+ js.push(" data[v*8+6] = mesh.normals[v*3+1];");
357
+ js.push(" data[v*8+7] = mesh.normals[v*3+2];");
358
+ js.push(" }");
359
+ js.push(" gl.bindBuffer(gl.ARRAY_BUFFER, vbo);");
360
+ js.push(" gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);");
361
+ js.push(" game.chunks[chunkKey] = { vbo: vbo, vertCount: vertCount, blocks: blocks, cx: cx, cz: cz };");
362
+ js.push(" game.totalVerts += vertCount;");
363
+ js.push(" game.totalBlocks += blocks.length;");
364
+ js.push("}");
365
+ js.push("");
366
+
367
+ // Unload a chunk
368
+ js.push("function unloadChunk(chunkKey) {");
369
+ js.push(" var chunk = game.chunks[chunkKey];");
370
+ js.push(" if (chunk) {");
371
+ js.push(" game.gl.deleteBuffer(chunk.vbo);");
372
+ js.push(" game.totalVerts -= chunk.vertCount;");
373
+ js.push(" game.totalBlocks -= chunk.blocks.length;");
374
+ js.push(" delete game.chunks[chunkKey];");
375
+ js.push(" }");
376
+ js.push("}");
377
+ js.push("");
378
+
379
+ // Update chunks based on player position
380
+ js.push("function updateChunks() {");
381
+ js.push(" var pcx = Math.floor(game.camX / CHUNK_SIZE);");
382
+ js.push(" var pcz = Math.floor(game.camZ / CHUNK_SIZE);");
383
+ js.push(" var needed = {};");
384
+ js.push(" for (var dx = -RENDER_DISTANCE; dx <= RENDER_DISTANCE; dx++) {");
385
+ js.push(" for (var dz = -RENDER_DISTANCE; dz <= RENDER_DISTANCE; dz++) {");
386
+ js.push(" var cx = pcx + dx;");
387
+ js.push(" var cz = pcz + dz;");
388
+ js.push(" var ck = cx+','+cz;");
389
+ js.push(" needed[ck] = true;");
390
+ js.push(" if (!game.chunks[ck]) loadChunk(cx, cz);");
391
+ js.push(" }");
392
+ js.push(" }");
393
+ js.push(" var loaded = Object.keys(game.chunks);");
394
+ js.push(" for (var i = 0; i < loaded.length; i++) {");
395
+ js.push(" if (!needed[loaded[i]]) unloadChunk(loaded[i]);");
396
+ js.push(" }");
397
+ js.push("}");
398
+ js.push("");
399
+
400
+ // Init WebGL
401
+ js.push("game.init = function () {");
402
+ js.push(" this.canvas = document.getElementById('cag-canvas');");
403
+ js.push(" this.gl = this.canvas.getContext('webgl', { antialias: true });");
404
+ js.push(" if (!this.gl) { alert('WebGL not supported'); return; }");
405
+ js.push(" this.resize();");
406
+ js.push(" window.addEventListener('resize', function () { game.resize(); });");
407
+ js.push("");
408
+ js.push(" var gl = this.gl;");
409
+ js.push(" var vs = gl.createShader(gl.VERTEX_SHADER);");
410
+ js.push(" gl.shaderSource(vs, VS_SRC);");
411
+ js.push(" gl.compileShader(vs);");
412
+ js.push(" var fsh = gl.createShader(gl.FRAGMENT_SHADER);");
413
+ js.push(" gl.shaderSource(fsh, FS_SRC);");
414
+ js.push(" gl.compileShader(fsh);");
415
+ js.push("");
416
+ js.push(" this.program = gl.createProgram();");
417
+ js.push(" gl.attachShader(this.program, vs);");
418
+ js.push(" gl.attachShader(this.program, fsh);");
419
+ js.push(" gl.bindAttribLocation(this.program, 0, 'aPos');");
420
+ js.push(" gl.bindAttribLocation(this.program, 1, 'aUV');");
421
+ js.push(" gl.bindAttribLocation(this.program, 2, 'aNormal');");
422
+ js.push(" gl.linkProgram(this.program);");
423
+ js.push(" gl.useProgram(this.program);");
424
+ js.push("");
425
+ js.push(" this.uProj = gl.getUniformLocation(this.program, 'uProj');");
426
+ js.push(" this.uView = gl.getUniformLocation(this.program, 'uView');");
427
+ js.push(" this.uSunDir = gl.getUniformLocation(this.program, 'uSunDir');");
428
+ js.push(" this.uSunColor = gl.getUniformLocation(this.program, 'uSunColor');");
429
+ js.push(" this.uAmbient = gl.getUniformLocation(this.program, 'uAmbient');");
430
+ js.push(" this.uFogColor = gl.getUniformLocation(this.program, 'uFogColor');");
431
+ js.push(" this.uFogNear = gl.getUniformLocation(this.program, 'uFogNear');");
432
+ js.push(" this.uFogFar = gl.getUniformLocation(this.program, 'uFogFar');");
433
+ js.push(" this.uCamPos = gl.getUniformLocation(this.program, 'uCamPos');");
434
+ js.push(" this.uTexture = gl.getUniformLocation(this.program, 'uTexture');");
435
+ js.push("");
436
+ js.push(" gl.uniform3f(this.uSunDir, " + sunD.x + ", " + sunD.y + ", " + sunD.z + ");");
437
+ js.push(" gl.uniform3f(this.uSunColor, 1.0, 0.95, 0.8);");
438
+ js.push(" gl.uniform3f(this.uAmbient, " + amb.r + ", " + amb.g + ", " + amb.b + ");");
439
+ js.push(" gl.uniform3f(this.uFogColor, " + fogC.r + ", " + fogC.g + ", " + fogC.b + ");");
440
+ js.push(" gl.uniform1f(this.uFogNear, " + fogNear + ");");
441
+ js.push(" gl.uniform1f(this.uFogFar, " + fogFar + ");");
442
+ js.push("");
443
+ js.push(" this.blockTexture = gl.createTexture();");
444
+ js.push(" gl.bindTexture(gl.TEXTURE_2D, this.blockTexture);");
445
+ js.push(" gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([128,128,128,255]));");
446
+ js.push(" var texKeys = Object.keys(MODELS);");
447
+ js.push(" if (texKeys.length > 0 && MODELS[texKeys[0]].textureDataURL) {");
448
+ js.push(" var texImg = new Image();");
449
+ js.push(" texImg.onload = function() {");
450
+ js.push(" var g = game.gl;");
451
+ js.push(" g.bindTexture(g.TEXTURE_2D, game.blockTexture);");
452
+ js.push(" g.texImage2D(g.TEXTURE_2D, 0, g.RGBA, g.RGBA, g.UNSIGNED_BYTE, texImg);");
453
+ js.push(" g.texParameteri(g.TEXTURE_2D, g.TEXTURE_MIN_FILTER, g.NEAREST);");
454
+ js.push(" g.texParameteri(g.TEXTURE_2D, g.TEXTURE_MAG_FILTER, g.NEAREST);");
455
+ js.push(" g.texParameteri(g.TEXTURE_2D, g.TEXTURE_WRAP_S, g.CLAMP_TO_EDGE);");
456
+ js.push(" g.texParameteri(g.TEXTURE_2D, g.TEXTURE_WRAP_T, g.CLAMP_TO_EDGE);");
457
+ js.push(" };");
458
+ js.push(" texImg.src = MODELS[texKeys[0]].textureDataURL;");
459
+ js.push(" }");
460
+ js.push("");
461
+ js.push(" gl.enable(gl.DEPTH_TEST);");
462
+ js.push(" gl.enable(gl.CULL_FACE);");
463
+ js.push(" gl.cullFace(gl.BACK);");
464
+ js.push(" gl.clearColor(" + sky.r + ", " + sky.g + ", " + sky.b + ", 1.0);");
465
+ js.push("");
466
+ js.push(" updateChunks();");
467
+ js.push("");
468
+ js.push(" document.addEventListener('keydown', function (e) { game.keys[e.code] = true; });");
469
+ js.push(" document.addEventListener('keyup', function (e) { game.keys[e.code] = false; });");
470
+ js.push(" document.addEventListener('mousemove', function (e) {");
471
+ js.push(" if (!game.locked) return;");
472
+ js.push(" game.yaw += e.movementX * 0.002;");
473
+ js.push(" game.pitch -= e.movementY * 0.002;");
474
+ js.push(" game.pitch = Math.max(-Math.PI/2 + 0.01, Math.min(Math.PI/2 - 0.01, game.pitch));");
475
+ js.push(" });");
476
+ js.push("");
477
+ js.push(" this.canvas.addEventListener('click', function () {");
478
+ js.push(" game.canvas.requestPointerLock();");
479
+ js.push(" });");
480
+ js.push(" document.addEventListener('pointerlockchange', function () {");
481
+ js.push(" game.locked = document.pointerLockElement === game.canvas;");
482
+ js.push(" document.getElementById('crosshair').style.display = game.locked ? 'block' : 'none';");
483
+ js.push(" });");
484
+ js.push("");
485
+ js.push(" setInterval(function () { game.fps = game.frames; game.frames = 0; }, 1000);");
486
+ js.push("");
487
+ js.push(" game.started = true;");
488
+ js.push(" game.loop();");
489
+ js.push("};");
490
+ js.push("");
491
+
492
+ // Resize
493
+ js.push("game.resize = function () {");
494
+ js.push(" this.canvas.width = window.innerWidth;");
495
+ js.push(" this.canvas.height = window.innerHeight;");
496
+ js.push(" this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);");
497
+ js.push("};");
498
+ js.push("");
499
+
500
+ // Update
501
+ js.push("game.update = function () {");
502
+ js.push(" if (!this.locked) return;");
503
+ js.push(" var speed = this.speed;");
504
+ js.push(" var sprint = this.keys['ShiftLeft'] || this.keys['ShiftRight'];");
505
+ js.push(" if (sprint) speed *= 1.8;");
506
+ js.push(" var dx = 0, dz = 0;");
507
+ js.push(" var sinY = Math.sin(this.yaw);");
508
+ js.push(" var cosY = Math.cos(this.yaw);");
509
+ js.push(" if (this.keys['KeyW']) { dx -= sinY * speed; dz -= cosY * speed; }");
510
+ js.push(" if (this.keys['KeyS']) { dx += sinY * speed; dz += cosY * speed; }");
511
+ js.push(" if (this.keys['KeyA']) { dx -= cosY * speed; dz += sinY * speed; }");
512
+ js.push(" if (this.keys['KeyD']) { dx += cosY * speed; dz -= sinY * speed; }");
513
+ js.push(" this.camX += dx;");
514
+ js.push(" this.camZ += dz;");
515
+ js.push(" this.velY -= this.gravity;");
516
+ js.push(" this.camY += this.velY;");
517
+ js.push(" if (this.camY < 1 + 1.6) {");
518
+ js.push(" this.camY = 1 + 1.6;");
519
+ js.push(" this.velY = 0;");
520
+ js.push(" this.onGround = true;");
521
+ js.push(" } else {");
522
+ js.push(" this.onGround = false;");
523
+ js.push(" }");
524
+ js.push(" if (this.keys['Space'] && this.onGround) {");
525
+ js.push(" this.velY = this.jumpHeight;");
526
+ js.push(" this.onGround = false;");
527
+ js.push(" }");
528
+ js.push("};");
529
+ js.push("");
530
+
531
+ // Render
532
+ js.push("game.render = function () {");
533
+ js.push(" var gl = this.gl;");
534
+ js.push(" gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);");
535
+ js.push("");
536
+ js.push(" var fovRad = " + fov + " * Math.PI / 180;");
537
+ js.push(" var aspect = this.canvas.width / this.canvas.height;");
538
+ js.push(" var near = 0.1, far = 300;");
539
+ js.push(" var f = 1.0 / Math.tan(fovRad / 2);");
540
+ js.push(" var proj = new Float32Array([");
541
+ js.push(" f/aspect, 0, 0, 0,");
542
+ js.push(" 0, f, 0, 0,");
543
+ js.push(" 0, 0, (far+near)/(near-far), -1,");
544
+ js.push(" 0, 0, (2*far*near)/(near-far), 0");
545
+ js.push(" ]);");
546
+ js.push(" gl.uniformMatrix4fv(this.uProj, false, proj);");
547
+ js.push("");
548
+ js.push(" var cp = Math.cos(this.pitch), sp = Math.sin(this.pitch);");
549
+ js.push(" var cy = Math.cos(this.yaw), sy = Math.sin(this.yaw);");
550
+ js.push(" var fx = -sy*cp, fy = sp, fz = -cy*cp;");
551
+ js.push(" var rx = fy*0 - fz*1, ry = fz*0 - fx*0, rz = fx*1 - fy*0;");
552
+ js.push(" var rl = Math.sqrt(rx*rx+ry*ry+rz*rz);");
553
+ js.push(" rx /= rl; ry /= rl; rz /= rl;");
554
+ js.push(" var ux = ry*fz-rz*fy, uy = rz*fx-rx*fz, uz = rx*fy-ry*fx;");
555
+ js.push(" var view = new Float32Array([");
556
+ js.push(" rx, ux, -fx, 0,");
557
+ js.push(" ry, uy, -fy, 0,");
558
+ js.push(" rz, uz, -fz, 0,");
559
+ js.push(" -(rx*this.camX+ry*this.camY+rz*this.camZ),");
560
+ js.push(" -(ux*this.camX+uy*this.camY+uz*this.camZ),");
561
+ js.push(" -(-fx*this.camX+-fy*this.camY+-fz*this.camZ),");
562
+ js.push(" 1");
563
+ js.push(" ]);");
564
+ js.push(" gl.uniformMatrix4fv(this.uView, false, view);");
565
+ js.push(" gl.uniform3f(this.uCamPos, this.camX, this.camY, this.camZ);");
566
+ js.push("");
567
+ js.push(" gl.activeTexture(gl.TEXTURE0);");
568
+ js.push(" gl.bindTexture(gl.TEXTURE_2D, this.blockTexture);");
569
+ js.push(" gl.uniform1i(this.uTexture, 0);");
570
+ js.push("");
571
+ js.push(" var stride = 8 * 4;");
572
+ js.push(" var keys = Object.keys(this.chunks);");
573
+ js.push(" for (var i = 0; i < keys.length; i++) {");
574
+ js.push(" var chunk = this.chunks[keys[i]];");
575
+ js.push(" if (chunk.vertCount === 0) continue;");
576
+ js.push(" gl.bindBuffer(gl.ARRAY_BUFFER, chunk.vbo);");
577
+ js.push(" gl.enableVertexAttribArray(0);");
578
+ js.push(" gl.enableVertexAttribArray(1);");
579
+ js.push(" gl.enableVertexAttribArray(2);");
580
+ js.push(" gl.vertexAttribPointer(0, 3, gl.FLOAT, false, stride, 0);");
581
+ js.push(" gl.vertexAttribPointer(1, 2, gl.FLOAT, false, stride, 12);");
582
+ js.push(" gl.vertexAttribPointer(2, 3, gl.FLOAT, false, stride, 20);");
583
+ js.push(" gl.drawArrays(gl.TRIANGLES, 0, chunk.vertCount);");
584
+ js.push(" }");
585
+ js.push("};");
586
+ js.push("");
587
+
588
+ // HUD
589
+ js.push("game.updateHUD = function () {");
590
+ js.push(" var pcx = Math.floor(this.camX / CHUNK_SIZE);");
591
+ js.push(" var pcz = Math.floor(this.camZ / CHUNK_SIZE);");
592
+ js.push(" var loadedCount = Object.keys(this.chunks).length;");
593
+ js.push(" var sprint = this.keys['ShiftLeft'] || this.keys['ShiftRight'];");
594
+ js.push(" var html = '<div class=\"hud-title\">' + INFO_ID + '</div>';");
595
+ js.push(" html += '<div>v' + INFO_VERSION + '</div>';");
596
+ js.push(" html += '<div class=\"hud-sep\"></div>';");
597
+ js.push(" html += '<div>FPS: ' + this.fps + '</div>';");
598
+ js.push(" html += '<div>Triangles: ' + Math.floor(this.totalVerts / 3) + '</div>';");
599
+ js.push(" html += '<div>Blocks: ' + this.totalBlocks + '</div>';");
600
+ js.push(" html += '<div>Chunks: ' + loadedCount + '</div>';");
601
+ js.push(" html += '<div class=\"hud-sep\"></div>';");
602
+ js.push(" html += '<div>X: ' + this.camX.toFixed(1) + '</div>';");
603
+ js.push(" html += '<div>Y: ' + this.camY.toFixed(1) + '</div>';");
604
+ js.push(" html += '<div>Z: ' + this.camZ.toFixed(1) + '</div>';");
605
+ js.push(" html += '<div>Chunk: ' + pcx + ', ' + pcz + '</div>';");
606
+ js.push(" html += '<div class=\"hud-sep\"></div>';");
607
+ js.push(" html += '<div>Sprint: ' + (sprint ? 'ON' : 'OFF') + '</div>';");
608
+ js.push(" document.getElementById('hud-info').innerHTML = html;");
609
+ js.push("};");
610
+ js.push("");
611
+
612
+ // Game loop
613
+ js.push("game.loop = function () {");
614
+ js.push(" requestAnimationFrame(function () { game.loop(); });");
615
+ js.push(" game.frames++;");
616
+ js.push(" game.update();");
617
+ js.push(" updateChunks();");
618
+ js.push(" game.render();");
619
+ js.push(" game.updateHUD();");
620
+ js.push("};");
621
+ js.push("");
622
+
623
+ js.push("game.init();");
624
+
625
+ var html = "<!DOCTYPE html>\n<html>\n<head>\n";
626
+ html += "<meta charset='utf-8'>\n";
627
+ html += "<title>" + windowTitle + " - CaG Engine</title>\n";
628
+ html += "<style>\n" + css + "\n</style>\n";
629
+ html += "</head>\n<body>\n";
630
+ html += "<div id='crosshair' style='display:none'></div>\n";
631
+ html += "<div id='hud'><div id='hud-box'><div id='hud-info'></div></div></div>\n";
632
+ 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";
633
+ html += "<canvas id='cag-canvas'></canvas>\n";
634
+ html += "<script>\n" + js.join("\n") + "\n</script>\n";
635
+ html += "</body>\n</html>";
636
+
637
+ return html;
638
+ };
639
+
640
+ ElectronRunner.prototype.cleanup = function () {
641
+ if (fs.existsSync(this.tempDir)) {
642
+ this.removeDirectory(this.tempDir);
643
+ }
644
+ };
645
+
646
+ ElectronRunner.prototype.removeDirectory = function (dir) {
647
+ if (!fs.existsSync(dir)) return;
648
+ var entries = fs.readdirSync(dir, { withFileTypes: true });
649
+ for (var i = 0; i < entries.length; i++) {
650
+ var fullPath = path.join(dir, entries[i].name);
651
+ if (entries[i].isDirectory()) {
652
+ this.removeDirectory(fullPath);
653
+ } else {
654
+ fs.unlinkSync(fullPath);
655
+ }
656
+ }
657
+ fs.rmdirSync(dir);
658
+ };
659
+
660
+ ElectronRunner.extractSettings = function (executor) {
661
+ var settings = {};
662
+ var registry = executor.getRegistry();
663
+
664
+ if (registry.has("Window.API")) {
665
+ var windowAPI = registry.get("Window.API");
666
+ settings.windowTitle = windowAPI.title;
667
+ settings.windowWidth = windowAPI.width;
668
+ settings.windowHeight = windowAPI.height;
669
+ settings.background = windowAPI.background;
670
+ }
671
+ if (registry.has("Render.API")) {
672
+ var renderAPI = registry.get("Render.API");
673
+ settings.skyColor = renderAPI.skyColor;
674
+ settings.fogColor = renderAPI.fogColor;
675
+ settings.fogNear = renderAPI.fogNear;
676
+ settings.fogFar = renderAPI.fogFar;
677
+ settings.sunDirection = renderAPI.sunDirection;
678
+ settings.ambientLight = renderAPI.ambientLight;
679
+ }
680
+ if (registry.has("Camera.API")) {
681
+ var cameraAPI = registry.get("Camera.API");
682
+ settings.fov = cameraAPI.fov;
683
+ }
684
+ if (registry.has("Player.API")) {
685
+ var playerAPI = registry.get("Player.API");
686
+ settings.playerSpeed = playerAPI.speed;
687
+ settings.jumpHeight = playerAPI.jumpHeight;
688
+ settings.gravity = playerAPI.gravity;
689
+ settings.spawnX = playerAPI.spawnPoint.x;
690
+ settings.spawnY = playerAPI.spawnPoint.y;
691
+ settings.spawnZ = playerAPI.spawnPoint.z;
692
+ }
693
+ if (registry.has("Chunk.API")) {
694
+ var chunkAPI = registry.get("Chunk.API");
695
+ settings.chunkSize = chunkAPI.chunkSize;
696
+ settings.renderDistance = chunkAPI.renderDistance;
697
+ }
698
+
699
+ if (registry.has("Model.API")) {
700
+ var modelAPI = registry.get("Model.API");
701
+ var loadedModels = modelAPI.getLoadedModels();
702
+ settings.models = {};
703
+ if (loadedModels) {
704
+ var modelNames = Object.keys(loadedModels);
705
+ for (var mi = 0; mi < modelNames.length; mi++) {
706
+ var mName = modelNames[mi];
707
+ var model = loadedModels[mName];
708
+ if (model && model.valid) {
709
+ var texW = model.texture ? model.texture.width : 0;
710
+ var texH = model.texture ? model.texture.height : 0;
711
+ if (!texW || !texH) {
712
+ texW = model.geometry ? model.geometry.resolution.width : 16;
713
+ texH = model.geometry ? model.geometry.resolution.height : 16;
714
+ }
715
+ settings.models[mName] = {
716
+ uvMappings: model.uvMappings || [],
717
+ resolution: { width: texW, height: texH },
718
+ textureDataURL: model.texture && model.texture.dataURL ? model.texture.dataURL : null
719
+ };
720
+ }
721
+ }
722
+ }
723
+ }
724
+
725
+ return settings;
726
+ };
727
+
728
+ module.exports = ElectronRunner;