@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.
@@ -0,0 +1,331 @@
1
+ const path = require("path");
2
+ const fs = require("fs");
3
+
4
+ class PullAPI {
5
+ constructor(resourceMap, resourcesPath) {
6
+ this.resourceMap = resourceMap || {};
7
+ this.resourcesPath = resourcesPath || process.cwd();
8
+ this.retrievedModels = {};
9
+ this.defaultModelsPath = path.join(__dirname, "..", "models", "defaults");
10
+ this.supportedFormats = [".bbmodel"];
11
+ this.supportedTextures = [".png", ".jpg", ".jpeg", ".bmp", ".tga"];
12
+ }
13
+
14
+ execute(action, params) {
15
+ switch (action) {
16
+ case "retrieve":
17
+ return this.retrieve(params);
18
+ case "search":
19
+ return this.search(params);
20
+ case "list":
21
+ return this.list();
22
+ default:
23
+ throw new Error("Unknown Pull.API action: " + action);
24
+ }
25
+ }
26
+
27
+ retrieve(params) {
28
+ if (!params || params.length === 0) {
29
+ throw new Error("retrieve requires a model ID parameter");
30
+ }
31
+
32
+ var modelId = params[0];
33
+
34
+ if (this.retrievedModels[modelId]) {
35
+ return this.retrievedModels[modelId];
36
+ }
37
+
38
+ var result = this.findInDefaults(modelId);
39
+
40
+ if (!result) {
41
+ result = this.findInResources(modelId);
42
+ }
43
+
44
+ if (!result) {
45
+ throw new Error("Model not found: " + modelId);
46
+ }
47
+
48
+ result = this.validateModel(result);
49
+
50
+ this.retrievedModels[modelId] = result;
51
+
52
+ return result;
53
+ }
54
+
55
+ findInDefaults(modelId) {
56
+ var lowerName = modelId.toLowerCase();
57
+ var modelPath = path.join(this.defaultModelsPath, lowerName + ".bbmodel");
58
+ var texturePath = path.join(this.defaultModelsPath, lowerName + ".png");
59
+
60
+ if (!fs.existsSync(modelPath)) {
61
+ return null;
62
+ }
63
+
64
+ var result = {
65
+ id: modelId,
66
+ source: "defaults",
67
+ modelPath: modelPath,
68
+ texturePath: null,
69
+ modelData: null,
70
+ textureData: null,
71
+ uvMappings: [],
72
+ valid: false
73
+ };
74
+
75
+ if (fs.existsSync(texturePath)) {
76
+ result.texturePath = texturePath;
77
+ }
78
+
79
+ result.modelData = this.loadBBModel(modelPath);
80
+ result.uvMappings = this.extractUVMappings(result.modelData);
81
+
82
+ if (result.texturePath) {
83
+ result.textureData = this.loadTextureInfo(result.texturePath);
84
+ }
85
+
86
+ return result;
87
+ }
88
+
89
+ findInResources(modelId) {
90
+ if (this.resourceMap[modelId]) {
91
+ var res = this.resourceMap[modelId];
92
+ return {
93
+ id: modelId,
94
+ source: "resources",
95
+ modelPath: res.model,
96
+ texturePath: res.texture,
97
+ modelData: res.model ? this.loadBBModel(res.model) : null,
98
+ textureData: res.texture ? this.loadTextureInfo(res.texture) : null,
99
+ uvMappings: [],
100
+ valid: false
101
+ };
102
+ }
103
+
104
+ return null;
105
+ }
106
+
107
+ loadBBModel(filePath) {
108
+ if (!fs.existsSync(filePath)) {
109
+ return null;
110
+ }
111
+
112
+ var raw = fs.readFileSync(filePath, "utf-8");
113
+ var data;
114
+
115
+ try {
116
+ data = JSON.parse(raw);
117
+ } catch (e) {
118
+ throw new Error("Invalid .bbmodel file: " + filePath);
119
+ }
120
+
121
+ return {
122
+ formatVersion: data.meta ? data.meta.format_version : "unknown",
123
+ modelIdentifier: data.name || path.basename(filePath, ".bbmodel"),
124
+ resolution: data.resolution || { width: 16, height: 16 },
125
+ elements: data.elements || [],
126
+ textures: data.textures || [],
127
+ outliner: data.outliner || []
128
+ };
129
+ }
130
+
131
+ loadTextureInfo(filePath) {
132
+ if (!fs.existsSync(filePath)) {
133
+ return null;
134
+ }
135
+
136
+ var ext = path.extname(filePath).toLowerCase();
137
+ var buffer = fs.readFileSync(filePath);
138
+ var width = 0;
139
+ var height = 0;
140
+
141
+ if (ext === ".png" && buffer.length >= 24) {
142
+ width = buffer.readUInt32BE(16);
143
+ height = buffer.readUInt32BE(20);
144
+ }
145
+
146
+ return {
147
+ path: filePath,
148
+ width: width,
149
+ height: height,
150
+ format: ext.substring(1),
151
+ size: buffer.length
152
+ };
153
+ }
154
+
155
+ extractUVMappings(modelData) {
156
+ if (!modelData || !modelData.elements) {
157
+ return [];
158
+ }
159
+
160
+ var mappings = [];
161
+ var faceNames = ["north", "south", "east", "west", "up", "down"];
162
+
163
+ for (var i = 0; i < modelData.elements.length; i++) {
164
+ var element = modelData.elements[i];
165
+ if (!element.faces) {
166
+ continue;
167
+ }
168
+
169
+ for (var j = 0; j < faceNames.length; j++) {
170
+ var faceName = faceNames[j];
171
+ if (element.faces[faceName]) {
172
+ var face = element.faces[faceName];
173
+ mappings.push({
174
+ element: element.name || "element_" + i,
175
+ face: faceName,
176
+ uv: face.uv || [0, 0, 16, 16],
177
+ textureIndex: face.texture !== undefined ? face.texture : 0,
178
+ supported: true
179
+ });
180
+ }
181
+ }
182
+ }
183
+
184
+ return mappings;
185
+ }
186
+
187
+ validateModel(result) {
188
+ if (!result.modelData) {
189
+ result.valid = false;
190
+ return result;
191
+ }
192
+
193
+ if (result.uvMappings.length === 0 && result.modelData) {
194
+ result.uvMappings = this.extractUVMappings(result.modelData);
195
+ }
196
+
197
+ if (result.textureData && result.textureData.width > 0) {
198
+ result.uvMappings = this.fixUVMappings(result.uvMappings, result.textureData);
199
+ }
200
+
201
+ result.valid = true;
202
+ return result;
203
+ }
204
+
205
+ fixUVMappings(mappings, textureData) {
206
+ var fixed = [];
207
+
208
+ for (var i = 0; i < mappings.length; i++) {
209
+ var mapping = mappings[i];
210
+ var uv = mapping.uv;
211
+ var needsFix = false;
212
+
213
+ if (uv[0] < 0 || uv[1] < 0 || uv[2] < 0 || uv[3] < 0) {
214
+ needsFix = true;
215
+ }
216
+
217
+ if (uv[2] > textureData.width || uv[3] > textureData.height) {
218
+ needsFix = true;
219
+ }
220
+
221
+ if (uv[0] > uv[2] || uv[1] > uv[3]) {
222
+ needsFix = true;
223
+ }
224
+
225
+ if (needsFix) {
226
+ var fixedMapping = {};
227
+ for (var key in mapping) {
228
+ fixedMapping[key] = mapping[key];
229
+ }
230
+ fixedMapping.uv = [
231
+ Math.max(0, Math.min(uv[0], textureData.width)),
232
+ Math.max(0, Math.min(uv[1], textureData.height)),
233
+ Math.max(0, Math.min(uv[2], textureData.width)),
234
+ Math.max(0, Math.min(uv[3], textureData.height))
235
+ ];
236
+ if (fixedMapping.uv[0] > fixedMapping.uv[2]) {
237
+ var temp = fixedMapping.uv[0];
238
+ fixedMapping.uv[0] = fixedMapping.uv[2];
239
+ fixedMapping.uv[2] = temp;
240
+ }
241
+ if (fixedMapping.uv[1] > fixedMapping.uv[3]) {
242
+ var temp2 = fixedMapping.uv[1];
243
+ fixedMapping.uv[1] = fixedMapping.uv[3];
244
+ fixedMapping.uv[3] = temp2;
245
+ }
246
+ fixedMapping.wasFixed = true;
247
+ fixedMapping.supported = true;
248
+ fixed.push(fixedMapping);
249
+ } else {
250
+ mapping.supported = true;
251
+ fixed.push(mapping);
252
+ }
253
+ }
254
+
255
+ return fixed;
256
+ }
257
+
258
+ search(params) {
259
+ if (!params || params.length === 0) {
260
+ throw new Error("search requires a query parameter");
261
+ }
262
+
263
+ var query = params[0].toLowerCase();
264
+ var results = [];
265
+
266
+ var defaultFiles = [];
267
+ if (fs.existsSync(this.defaultModelsPath)) {
268
+ try {
269
+ defaultFiles = fs.readdirSync(this.defaultModelsPath);
270
+ } catch (e) {
271
+ defaultFiles = [];
272
+ }
273
+ }
274
+
275
+ for (var i = 0; i < defaultFiles.length; i++) {
276
+ var file = defaultFiles[i];
277
+ var ext = path.extname(file).toLowerCase();
278
+ if (ext === ".bbmodel") {
279
+ var name = path.basename(file, ext);
280
+ if (name.toLowerCase().indexOf(query) !== -1) {
281
+ results.push({
282
+ id: name,
283
+ source: "defaults",
284
+ type: "model"
285
+ });
286
+ }
287
+ }
288
+ }
289
+
290
+ var resourceNames = Object.keys(this.resourceMap);
291
+ for (var j = 0; j < resourceNames.length; j++) {
292
+ if (resourceNames[j].toLowerCase().indexOf(query) !== -1) {
293
+ results.push({
294
+ id: resourceNames[j],
295
+ source: "resources",
296
+ type: this.resourceMap[resourceNames[j]].model ? "model" : "texture"
297
+ });
298
+ }
299
+ }
300
+
301
+ return results;
302
+ }
303
+
304
+ list() {
305
+ var results = [];
306
+
307
+ var retrievedKeys = Object.keys(this.retrievedModels);
308
+ for (var i = 0; i < retrievedKeys.length; i++) {
309
+ var model = this.retrievedModels[retrievedKeys[i]];
310
+ results.push({
311
+ id: model.id,
312
+ source: model.source,
313
+ valid: model.valid,
314
+ hasTexture: model.texturePath !== null,
315
+ uvCount: model.uvMappings.length
316
+ });
317
+ }
318
+
319
+ return results;
320
+ }
321
+
322
+ isRetrieved(modelId) {
323
+ return this.retrievedModels[modelId] !== undefined;
324
+ }
325
+
326
+ getRetrieved(modelId) {
327
+ return this.retrievedModels[modelId] || null;
328
+ }
329
+ }
330
+
331
+ module.exports = PullAPI;
@@ -0,0 +1,332 @@
1
+ class WorldGenAPI {
2
+ constructor(modelAPI) {
3
+ this.modelAPI = modelAPI;
4
+ this.worlds = {};
5
+ this.activeWorld = null;
6
+ this.defaultWorldSize = { x: 64, y: 32, z: 64 };
7
+ this.defaultSeaLevel = 12;
8
+ }
9
+
10
+ execute(action, params) {
11
+ switch (action) {
12
+ case "generate":
13
+ return this.generate(params);
14
+ case "destroy":
15
+ return this.destroy(params);
16
+ case "modify":
17
+ return this.modify(params);
18
+ default:
19
+ throw new Error("Unknown WorldGen.API action: " + action);
20
+ }
21
+ }
22
+
23
+ generate(params) {
24
+ if (!params || params.length === 0) {
25
+ throw new Error("generate requires parameters");
26
+ }
27
+
28
+ var config = this.parseGenerateParams(params);
29
+
30
+ var worldId = config.worldName || "default";
31
+
32
+ var world = {
33
+ id: worldId,
34
+ size: config.size || this.defaultWorldSize,
35
+ layers: {},
36
+ blocks: [],
37
+ generated: false,
38
+ seed: config.seed || Math.floor(Math.random() * 2147483647),
39
+ createdAt: new Date().toISOString()
40
+ };
41
+
42
+ if (config.topLayer) {
43
+ var topModel = null;
44
+ if (this.modelAPI && this.modelAPI.isModelLoaded(config.topLayer)) {
45
+ topModel = this.modelAPI.getLoadedModel(config.topLayer);
46
+ } else if (this.modelAPI) {
47
+ topModel = this.modelAPI.useModel([config.topLayer]);
48
+ }
49
+
50
+ world.layers.top = {
51
+ model: config.topLayer,
52
+ modelData: topModel,
53
+ yLevel: world.size.y - 1
54
+ };
55
+ }
56
+
57
+ if (config.bottomLayer) {
58
+ var bottomModel = null;
59
+ if (this.modelAPI && this.modelAPI.isModelLoaded(config.bottomLayer)) {
60
+ bottomModel = this.modelAPI.getLoadedModel(config.bottomLayer);
61
+ } else if (this.modelAPI) {
62
+ bottomModel = this.modelAPI.useModel([config.bottomLayer]);
63
+ }
64
+
65
+ world.layers.bottom = {
66
+ model: config.bottomLayer,
67
+ modelData: bottomModel,
68
+ yLevel: 0
69
+ };
70
+ }
71
+
72
+ if (config.fillLayer) {
73
+ var fillModel = null;
74
+ if (this.modelAPI && this.modelAPI.isModelLoaded(config.fillLayer)) {
75
+ fillModel = this.modelAPI.getLoadedModel(config.fillLayer);
76
+ } else if (this.modelAPI) {
77
+ fillModel = this.modelAPI.useModel([config.fillLayer]);
78
+ }
79
+
80
+ world.layers.fill = {
81
+ model: config.fillLayer,
82
+ modelData: fillModel,
83
+ yLevelStart: 1,
84
+ yLevelEnd: world.size.y - 2
85
+ };
86
+ }
87
+
88
+ world.blocks = this.generateBlocks(world);
89
+ world.generated = true;
90
+
91
+ this.worlds[worldId] = world;
92
+ this.activeWorld = worldId;
93
+
94
+ return {
95
+ worldId: worldId,
96
+ size: world.size,
97
+ layers: Object.keys(world.layers),
98
+ blockCount: world.blocks.length,
99
+ seed: world.seed
100
+ };
101
+ }
102
+
103
+ parseGenerateParams(params) {
104
+ var config = {};
105
+ var i = 0;
106
+
107
+ while (i < params.length) {
108
+ var param = params[i];
109
+
110
+ if (param === "world") {
111
+ i++;
112
+ continue;
113
+ }
114
+
115
+ if (param === "top" && i + 1 < params.length && params[i + 1] === "layer") {
116
+ i += 2;
117
+ if (i < params.length && params[i] === "=") {
118
+ i++;
119
+ }
120
+ if (i < params.length) {
121
+ config.topLayer = params[i];
122
+ }
123
+ i++;
124
+ continue;
125
+ }
126
+
127
+ if (param === "bottom" && i + 1 < params.length && params[i + 1] === "layer") {
128
+ i += 2;
129
+ if (i < params.length && params[i] === "=") {
130
+ i++;
131
+ }
132
+ if (i < params.length) {
133
+ config.bottomLayer = params[i];
134
+ }
135
+ i++;
136
+ continue;
137
+ }
138
+
139
+ if (param === "fill" && i + 1 < params.length && params[i + 1] === "layer") {
140
+ i += 2;
141
+ if (i < params.length && params[i] === "=") {
142
+ i++;
143
+ }
144
+ if (i < params.length) {
145
+ config.fillLayer = params[i];
146
+ }
147
+ i++;
148
+ continue;
149
+ }
150
+
151
+ if (param === "size") {
152
+ if (i + 1 < params.length && params[i + 1] === "=") {
153
+ i += 2;
154
+ } else {
155
+ i++;
156
+ }
157
+ if (i < params.length) {
158
+ var sizeVal = parseInt(params[i], 10);
159
+ if (!isNaN(sizeVal)) {
160
+ config.size = { x: sizeVal, y: sizeVal, z: sizeVal };
161
+ }
162
+ }
163
+ i++;
164
+ continue;
165
+ }
166
+
167
+ if (param === "seed") {
168
+ if (i + 1 < params.length && params[i + 1] === "=") {
169
+ i += 2;
170
+ } else {
171
+ i++;
172
+ }
173
+ if (i < params.length) {
174
+ var seedVal = parseInt(params[i], 10);
175
+ if (!isNaN(seedVal)) {
176
+ config.seed = seedVal;
177
+ }
178
+ }
179
+ i++;
180
+ continue;
181
+ }
182
+
183
+ if (param === "name") {
184
+ if (i + 1 < params.length && params[i + 1] === "=") {
185
+ i += 2;
186
+ } else {
187
+ i++;
188
+ }
189
+ if (i < params.length) {
190
+ config.worldName = params[i];
191
+ }
192
+ i++;
193
+ continue;
194
+ }
195
+
196
+ i++;
197
+ }
198
+
199
+ return config;
200
+ }
201
+
202
+ generateBlocks(world) {
203
+ var blocks = [];
204
+ var size = world.size;
205
+
206
+ for (var x = 0; x < size.x; x++) {
207
+ for (var z = 0; z < size.z; z++) {
208
+ var height = this.getHeight(x, z, world.seed, size.y);
209
+
210
+ for (var y = 0; y <= height; y++) {
211
+ var blockType = "air";
212
+
213
+ if (y === height && world.layers.top) {
214
+ blockType = world.layers.top.model;
215
+ } else if (y === 0 && world.layers.bottom) {
216
+ blockType = world.layers.bottom.model;
217
+ } else if (world.layers.fill) {
218
+ blockType = world.layers.fill.model;
219
+ }
220
+
221
+ if (blockType !== "air") {
222
+ blocks.push({
223
+ x: x,
224
+ y: y,
225
+ z: z,
226
+ type: blockType
227
+ });
228
+ }
229
+ }
230
+ }
231
+ }
232
+
233
+ return blocks;
234
+ }
235
+
236
+ getHeight(x, z, seed, maxHeight) {
237
+ var hash = ((x * 374761393 + z * 668265263 + seed) & 0x7fffffff) % 1000;
238
+ var normalized = hash / 1000.0;
239
+ var height = Math.floor(normalized * (maxHeight * 0.4)) + Math.floor(maxHeight * 0.3);
240
+ return Math.min(height, maxHeight - 1);
241
+ }
242
+
243
+ destroy(params) {
244
+ if (!params || params.length === 0) {
245
+ if (this.activeWorld) {
246
+ var id = this.activeWorld;
247
+ delete this.worlds[id];
248
+ this.activeWorld = null;
249
+ return { destroyed: id };
250
+ }
251
+ throw new Error("No active world to destroy");
252
+ }
253
+
254
+ var worldId = params[0];
255
+
256
+ if (!this.worlds[worldId]) {
257
+ throw new Error("World not found: " + worldId);
258
+ }
259
+
260
+ delete this.worlds[worldId];
261
+
262
+ if (this.activeWorld === worldId) {
263
+ this.activeWorld = null;
264
+ }
265
+
266
+ return { destroyed: worldId };
267
+ }
268
+
269
+ modify(params) {
270
+ if (!params || params.length < 2) {
271
+ throw new Error("modify requires at least a world ID and modification type");
272
+ }
273
+
274
+ var worldId = params[0];
275
+ var modType = params[1];
276
+
277
+ if (worldId === "active") {
278
+ worldId = this.activeWorld;
279
+ }
280
+
281
+ if (!worldId || !this.worlds[worldId]) {
282
+ throw new Error("World not found: " + (worldId || "none"));
283
+ }
284
+
285
+ var world = this.worlds[worldId];
286
+
287
+ if (modType === "setblock") {
288
+ if (params.length < 6) {
289
+ throw new Error("setblock requires x y z blockType");
290
+ }
291
+ var bx = parseInt(params[2], 10);
292
+ var by = parseInt(params[3], 10);
293
+ var bz = parseInt(params[4], 10);
294
+ var btype = params[5];
295
+
296
+ world.blocks.push({ x: bx, y: by, z: bz, type: btype });
297
+ return { modified: worldId, action: "setblock", position: [bx, by, bz], type: btype };
298
+ }
299
+
300
+ if (modType === "clear") {
301
+ var count = world.blocks.length;
302
+ world.blocks = [];
303
+ return { modified: worldId, action: "clear", blocksRemoved: count };
304
+ }
305
+
306
+ throw new Error("Unknown modification type: " + modType);
307
+ }
308
+
309
+ getWorld(worldId) {
310
+ return this.worlds[worldId || this.activeWorld] || null;
311
+ }
312
+
313
+ getActiveWorld() {
314
+ if (!this.activeWorld) {
315
+ return null;
316
+ }
317
+ return this.worlds[this.activeWorld];
318
+ }
319
+
320
+ listWorlds() {
321
+ return Object.keys(this.worlds).map(function (id) {
322
+ return {
323
+ id: id,
324
+ size: this.worlds[id].size,
325
+ blockCount: this.worlds[id].blocks.length,
326
+ active: id === this.activeWorld
327
+ };
328
+ }.bind(this));
329
+ }
330
+ }
331
+
332
+ module.exports = WorldGenAPI;