@erik9994857/cag 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cag ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+
3
+ var CagCLI = require("../src/cli/cag-cli");
4
+
5
+ var cli = new CagCLI(process.argv.slice(2));
6
+ var exitCode = cli.run();
7
+
8
+ if (typeof exitCode === "number") {
9
+ process.exitCode = exitCode;
10
+ }
@@ -0,0 +1,4 @@
1
+ ID=PROJECTNAME.project.gg
2
+ version=1.0.0
3
+ codefolder=code
4
+ resourcesfolder=resources
@@ -0,0 +1,3 @@
1
+ Depency Model.API
2
+ Depency WorldGen.API
3
+ Depency Pull.API
@@ -0,0 +1,5 @@
1
+ Import Model.API
2
+ Import WorldGen.API
3
+
4
+ {function use Model.API UseModel Grass
5
+ {function use WorldGen.API generate world top layer = Grass
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@erik9994857/cag",
3
+ "version": "1.0.0",
4
+ "description": "CaG — A code library and custom language for building 3D worlds with .cagc files, .bbmodel support, and auto UV mapping",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "cag": "./bin/cag"
8
+ },
9
+ "files": [
10
+ "src/",
11
+ "bin/",
12
+ "defaults/"
13
+ ],
14
+ "scripts": {
15
+ "test": "node test/basic.test.js",
16
+ "start": "node src/index.js"
17
+ },
18
+ "keywords": [
19
+ "cag",
20
+ "game-engine",
21
+ "3d",
22
+ "bbmodel",
23
+ "blockbench",
24
+ "voxel",
25
+ "worldgen",
26
+ "custom-language"
27
+ ],
28
+ "author": "Erik9994857",
29
+ "license": "MIT",
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ }
33
+ }
@@ -0,0 +1,144 @@
1
+ const ModelAPI = require("./model-api");
2
+ const WorldGenAPI = require("./worldgen-api");
3
+ const PullAPI = require("./pull-api");
4
+
5
+ class APIRegistry {
6
+ constructor() {
7
+ this.apis = {};
8
+ this.initialized = false;
9
+ this.registrationOrder = [];
10
+ }
11
+
12
+ initialize(engine) {
13
+ var resourceMap = engine.getResourceMap();
14
+ var info = engine.getInfo();
15
+ var path = require("path");
16
+
17
+ var modelAPI = new ModelAPI(resourceMap);
18
+ this.register("Model.API", modelAPI);
19
+
20
+ var worldGenAPI = new WorldGenAPI(modelAPI);
21
+ this.register("WorldGen.API", worldGenAPI);
22
+
23
+ var resourcesPath = path.join(engine.projectRoot, "src", info.resourcesfolder);
24
+ var pullAPI = new PullAPI(resourceMap, resourcesPath);
25
+ this.register("Pull.API", pullAPI);
26
+
27
+ this.initialized = true;
28
+ return this;
29
+ }
30
+
31
+ register(name, apiInstance) {
32
+ if (this.apis[name]) {
33
+ throw new Error("API already registered: " + name);
34
+ }
35
+
36
+ this.apis[name] = {
37
+ name: name,
38
+ instance: apiInstance,
39
+ callCount: 0,
40
+ lastCalled: null
41
+ };
42
+
43
+ this.registrationOrder.push(name);
44
+ return this;
45
+ }
46
+
47
+ get(name) {
48
+ if (!this.apis[name]) {
49
+ throw new Error("API not found: " + name);
50
+ }
51
+
52
+ return this.apis[name].instance;
53
+ }
54
+
55
+ execute(apiName, action, params) {
56
+ if (!this.apis[apiName]) {
57
+ throw new Error("API not registered: " + apiName);
58
+ }
59
+
60
+ var entry = this.apis[apiName];
61
+ entry.callCount++;
62
+ entry.lastCalled = new Date().toISOString();
63
+
64
+ return entry.instance.execute(action, params);
65
+ }
66
+
67
+ has(name) {
68
+ return this.apis[name] !== undefined;
69
+ }
70
+
71
+ list() {
72
+ var result = [];
73
+ for (var i = 0; i < this.registrationOrder.length; i++) {
74
+ var name = this.registrationOrder[i];
75
+ var entry = this.apis[name];
76
+ result.push({
77
+ name: name,
78
+ callCount: entry.callCount,
79
+ lastCalled: entry.lastCalled
80
+ });
81
+ }
82
+ return result;
83
+ }
84
+
85
+ getStats() {
86
+ var totalCalls = 0;
87
+ var apiCount = this.registrationOrder.length;
88
+
89
+ for (var i = 0; i < this.registrationOrder.length; i++) {
90
+ totalCalls += this.apis[this.registrationOrder[i]].callCount;
91
+ }
92
+
93
+ return {
94
+ apiCount: apiCount,
95
+ totalCalls: totalCalls,
96
+ initialized: this.initialized,
97
+ apis: this.list()
98
+ };
99
+ }
100
+
101
+ reset() {
102
+ for (var i = 0; i < this.registrationOrder.length; i++) {
103
+ var name = this.registrationOrder[i];
104
+ this.apis[name].callCount = 0;
105
+ this.apis[name].lastCalled = null;
106
+ }
107
+ }
108
+
109
+ unregister(name) {
110
+ if (!this.apis[name]) {
111
+ return false;
112
+ }
113
+
114
+ delete this.apis[name];
115
+
116
+ var idx = this.registrationOrder.indexOf(name);
117
+ if (idx !== -1) {
118
+ this.registrationOrder.splice(idx, 1);
119
+ }
120
+
121
+ return true;
122
+ }
123
+
124
+ validateDependencies(dependencies) {
125
+ var missing = [];
126
+ var found = [];
127
+
128
+ for (var i = 0; i < dependencies.length; i++) {
129
+ if (this.has(dependencies[i])) {
130
+ found.push(dependencies[i]);
131
+ } else {
132
+ missing.push(dependencies[i]);
133
+ }
134
+ }
135
+
136
+ return {
137
+ valid: missing.length === 0,
138
+ found: found,
139
+ missing: missing
140
+ };
141
+ }
142
+ }
143
+
144
+ module.exports = APIRegistry;
@@ -0,0 +1,274 @@
1
+ const path = require("path");
2
+ const fs = require("fs");
3
+
4
+ class ModelAPI {
5
+ constructor(resourceMap, defaultModelsPath) {
6
+ this.resourceMap = resourceMap || {};
7
+ this.loadedModels = {};
8
+ this.defaultModelsPath = defaultModelsPath || path.join(__dirname, "..", "models", "defaults");
9
+ }
10
+
11
+ execute(action, params) {
12
+ switch (action) {
13
+ case "UseModel":
14
+ return this.useModel(params);
15
+ case "RemoveModel":
16
+ return this.removeModel(params);
17
+ case "ListModels":
18
+ return this.listModels();
19
+ default:
20
+ throw new Error("Unknown Model.API action: " + action);
21
+ }
22
+ }
23
+
24
+ useModel(params) {
25
+ if (!params || params.length === 0) {
26
+ throw new Error("UseModel requires a model name parameter");
27
+ }
28
+
29
+ const modelName = params[0];
30
+
31
+ if (this.loadedModels[modelName]) {
32
+ return this.loadedModels[modelName];
33
+ }
34
+
35
+ let resource = this.resourceMap[modelName];
36
+
37
+ if (!resource) {
38
+ resource = this.loadDefaultModel(modelName);
39
+ }
40
+
41
+ if (!resource) {
42
+ throw new Error("Model not found: " + modelName);
43
+ }
44
+
45
+ const model = this.loadModel(modelName, resource);
46
+ this.loadedModels[modelName] = model;
47
+ return model;
48
+ }
49
+
50
+ loadDefaultModel(modelName) {
51
+ const modelPath = path.join(this.defaultModelsPath, modelName.toLowerCase() + ".bbmodel");
52
+ const texturePath = path.join(this.defaultModelsPath, modelName.toLowerCase() + ".png");
53
+
54
+ if (fs.existsSync(modelPath)) {
55
+ return {
56
+ model: modelPath,
57
+ texture: fs.existsSync(texturePath) ? texturePath : null,
58
+ paired: fs.existsSync(texturePath),
59
+ isDefault: true
60
+ };
61
+ }
62
+
63
+ return null;
64
+ }
65
+
66
+ loadModel(name, resource) {
67
+ const result = {
68
+ name: name,
69
+ geometry: null,
70
+ texture: null,
71
+ uvMappings: null,
72
+ valid: false
73
+ };
74
+
75
+ if (resource.model) {
76
+ const modelData = this.parseBBModel(resource.model);
77
+ result.geometry = modelData.geometry;
78
+ result.uvMappings = modelData.uvMappings;
79
+ }
80
+
81
+ if (resource.texture) {
82
+ result.texture = this.loadTexture(resource.texture);
83
+ }
84
+
85
+ if (result.geometry && result.texture && result.uvMappings) {
86
+ result.uvMappings = this.validateAndFixUVMappings(result.uvMappings, result.texture);
87
+ result.valid = true;
88
+ } else if (result.geometry) {
89
+ result.valid = true;
90
+ }
91
+
92
+ return result;
93
+ }
94
+
95
+ parseBBModel(filePath) {
96
+ const raw = fs.readFileSync(filePath, "utf-8");
97
+ let data;
98
+
99
+ try {
100
+ data = JSON.parse(raw);
101
+ } catch (e) {
102
+ throw new Error("Invalid .bbmodel file (not valid JSON): " + filePath);
103
+ }
104
+
105
+ const geometry = {
106
+ elements: [],
107
+ bones: [],
108
+ resolution: data.resolution || { width: 16, height: 16 }
109
+ };
110
+
111
+ const uvMappings = [];
112
+
113
+ if (data.elements && Array.isArray(data.elements)) {
114
+ for (const element of data.elements) {
115
+ const geomElement = {
116
+ name: element.name || "unnamed",
117
+ from: element.from || [0, 0, 0],
118
+ to: element.to || [1, 1, 1],
119
+ rotation: element.rotation || [0, 0, 0],
120
+ origin: element.origin || [0, 0, 0],
121
+ faces: {}
122
+ };
123
+
124
+ if (element.faces) {
125
+ const faceNames = ["north", "south", "east", "west", "up", "down"];
126
+ for (const faceName of faceNames) {
127
+ if (element.faces[faceName]) {
128
+ const face = element.faces[faceName];
129
+ geomElement.faces[faceName] = {
130
+ uv: face.uv || [0, 0, 16, 16],
131
+ texture: face.texture !== undefined ? face.texture : 0
132
+ };
133
+ uvMappings.push({
134
+ element: geomElement.name,
135
+ face: faceName,
136
+ uv: face.uv || [0, 0, 16, 16],
137
+ textureIndex: face.texture !== undefined ? face.texture : 0
138
+ });
139
+ }
140
+ }
141
+ }
142
+
143
+ geometry.elements.push(geomElement);
144
+ }
145
+ }
146
+
147
+ if (data.outliner && Array.isArray(data.outliner)) {
148
+ geometry.bones = this.parseOutliner(data.outliner);
149
+ }
150
+
151
+ return { geometry, uvMappings };
152
+ }
153
+
154
+ parseOutliner(outliner) {
155
+ const bones = [];
156
+
157
+ for (const item of outliner) {
158
+ if (typeof item === "object" && item !== null && !Array.isArray(item)) {
159
+ const bone = {
160
+ name: item.name || "unnamed",
161
+ origin: item.origin || [0, 0, 0],
162
+ rotation: item.rotation || [0, 0, 0],
163
+ children: []
164
+ };
165
+
166
+ if (item.children && Array.isArray(item.children)) {
167
+ bone.children = this.parseOutliner(item.children);
168
+ }
169
+
170
+ bones.push(bone);
171
+ }
172
+ }
173
+
174
+ return bones;
175
+ }
176
+
177
+ loadTexture(filePath) {
178
+ if (!fs.existsSync(filePath)) {
179
+ return null;
180
+ }
181
+
182
+ const ext = path.extname(filePath).toLowerCase();
183
+ const buffer = fs.readFileSync(filePath);
184
+ let width = 0;
185
+ let height = 0;
186
+
187
+ if (ext === ".png") {
188
+ if (buffer.length >= 24) {
189
+ width = buffer.readUInt32BE(16);
190
+ height = buffer.readUInt32BE(20);
191
+ }
192
+ }
193
+
194
+ return {
195
+ path: filePath,
196
+ width: width,
197
+ height: height,
198
+ format: ext.substring(1),
199
+ size: buffer.length
200
+ };
201
+ }
202
+
203
+ validateAndFixUVMappings(uvMappings, texture) {
204
+ if (!texture || texture.width === 0 || texture.height === 0) {
205
+ return uvMappings;
206
+ }
207
+
208
+ const fixed = [];
209
+
210
+ for (const mapping of uvMappings) {
211
+ const uv = mapping.uv;
212
+ let needsFix = false;
213
+
214
+ if (uv[0] < 0 || uv[1] < 0 || uv[2] < 0 || uv[3] < 0) {
215
+ needsFix = true;
216
+ }
217
+
218
+ if (uv[2] > texture.width || uv[3] > texture.height) {
219
+ needsFix = true;
220
+ }
221
+
222
+ if (needsFix) {
223
+ const fixedMapping = Object.assign({}, mapping);
224
+ fixedMapping.uv = [
225
+ Math.max(0, Math.min(uv[0], texture.width)),
226
+ Math.max(0, Math.min(uv[1], texture.height)),
227
+ Math.max(0, Math.min(uv[2], texture.width)),
228
+ Math.max(0, Math.min(uv[3], texture.height))
229
+ ];
230
+ fixedMapping.wasFixed = true;
231
+ fixed.push(fixedMapping);
232
+ } else {
233
+ fixed.push(mapping);
234
+ }
235
+ }
236
+
237
+ return fixed;
238
+ }
239
+
240
+ removeModel(params) {
241
+ if (!params || params.length === 0) {
242
+ throw new Error("RemoveModel requires a model name parameter");
243
+ }
244
+
245
+ const modelName = params[0];
246
+
247
+ if (!this.loadedModels[modelName]) {
248
+ throw new Error("Model not loaded: " + modelName);
249
+ }
250
+
251
+ delete this.loadedModels[modelName];
252
+ return { removed: modelName };
253
+ }
254
+
255
+ listModels() {
256
+ return Object.keys(this.loadedModels).map(function (name) {
257
+ return {
258
+ name: name,
259
+ valid: this.loadedModels[name].valid,
260
+ hasTexture: this.loadedModels[name].texture !== null
261
+ };
262
+ }.bind(this));
263
+ }
264
+
265
+ getLoadedModel(name) {
266
+ return this.loadedModels[name] || null;
267
+ }
268
+
269
+ isModelLoaded(name) {
270
+ return this.loadedModels[name] !== undefined;
271
+ }
272
+ }
273
+
274
+ module.exports = ModelAPI;