const loaderData = { "utils": { "type": "global", "source": "./lib/utils.js", "loadAs": "utils", "dependsOn": [] }, "three": { "type": "global", "source": "./lib/three.js", "loadAs": "three", "dependsOn": [] }, /*"cannon": { "type": "global", "source": "./lib/cannon-es.js", "loadAs": "cannon", "dependsOn": [] },*/ "fflate": { "type": "global", "source": "./lib/fflate.js", "loadAs": "fflate", "dependsOn": [] }, "olm": { "type": "global", "source": "./lib/olm.js", "loadAs": "olm", "dependsOn": [] }, "matrix": { "type": "global", "source": "./lib/browser-matrix.min.js", "loadAs": "matrix", "dependsOn": ["olm"] }, "peer": { "type": "global", "source": "./lib/peer.js", "loadAs": "peer", "dependsOn": [] }, "gamecore": { "type": "global", "source": "./core.js", "loadAs": "gamecore", "dependsOn": ["utils"] }, "locales": { "type": "global", "source": "./lib/locales.js", "loadAs": "locales", "dependsOn": [] }, "menu": { "type": "global", "source": "./menu.js", "loadAs": "menu", "dependsOn": ["locales"] } }; const throwError = window.throwError = function (err) { alert(err + "\n" + err.stack); throw err; }; var onLoadingFinished = function (modules) { var initPromises = {}; var promiseReturners = {}; initPromises["render"] = new Promise(function (resolve, reject) { promiseReturners["render"] = { resolve, reject }; }); initPromises["input"] = new Promise(function (resolve, reject) { promiseReturners["input"] = { resolve, reject }; }); core.on("render-init", function () { promiseReturners["render"].resolve(); fetch("./data/resources/XVoxel.zip").then(function (response) { if (response.status != 200) return throwError(new Error("ENOENT")); response.arrayBuffer().then(function (arrayBuffer) { core.modules["resources"].ResourcePack.loadFromZIP(arrayBuffer).then(function (pack) { window.resourcePacks.push(pack); }).catch(throwError); }).catch(throwError); }).catch(throwError); }); core.on("input-init", function () { promiseReturners["input"].resolve(); }); Promise.all(Object.values(initPromises)).then(function () { addPlayer(0); }); }; window.onload = function (event) { renderersContainer = document.querySelector("#renderers"); var librariesToLoad = Object.keys(loaderData); var libraryLoaders = []; for (var l = 0; l < librariesToLoad.length; l++) { libraryLoaders.push( loadFromMeta(librariesToLoad[l], loaderData[librariesToLoad[l]]) ); } Promise.all(libraryLoaders).then(function (libraries) { core = window.core = new window.gamecore.GameCore(); fetch("./data/strings/" + window.currentLanguage + ".json").then(function (response) { if (response.status != 200) return throwError(new Error("ENOENT")); response.text().then(function (text) { window.locales.loadFromJSON(window.currentLanguage, JSON.parse(text)); menu.makeMenu(); }).catch(throwError); }).catch(throwError); onLoadingFinished(core.modules); core.init(gamecore.loadData.modules).then(function (gameCore) { for (var moduleName in core.modules) { if (core.modules.hasOwnProperty(moduleName)) { var thisModule = core.modules[moduleName]; if ((typeof thisModule) != "object") continue; if ("menus" in thisModule) { for (var menuName in thisModule.menus) { if (thisModule.menus.hasOwnProperty(menuName)) { if (!(menuName in menu.menuData)) { menu.menuData[menuName] = thisModule.menus[menuName]; } else { var thisMenu = thisModule.menus[menuName]; if (!("elements" in thisMenu)) continue; for (var e = 0; e < thisMenu.elements.length; e++) { menu.menuData[menuName].elements.push(thisMenu.elements[e]); } } menu.menus[menuName] = menu.constructMenu(menuName, menu.menuData[menuName]); } } } } } window.menu.showMenu("main", 0); }).catch(throwError); }).catch(throwError); }; var loadFromMeta = function (name, metadata) { return new Promise((function (resolve, reject) { var dependencies = []; if (metadata.dependsOn) { for (var d = 0; d < metadata.dependsOn.length; d++) { var dependencyName = metadata.dependsOn[d]; var dependency = loaderData[dependencyName]; if (!loadings[dependencyName]) { loadings[dependencyName] = loadFromMeta(dependencyName, dependency); } dependencies.push(loadings[dependencyName]); } } Promise.all(dependencies).then((function (deps) { loadings[name] = load(metadata.source).then(function (mod) { window[metadata.loadAs || name] = mod; resolve(mod); }).catch(throwError); }).bind(name)).catch(throwError); }).bind(name)); }; var render = window.render = function (instanceIndex) { if (instanceWorlds[instanceIndex]) { renderers[instanceIndex].render(worlds[instanceWorlds[instanceIndex]], cameras[instanceIndex]); } }; var createWorld = window.createWorld = function (name, generator = "flat", options, generateInitialChunks = true) { return new Promise(function (resolve, reject) { window.worlds[name] = new core.modules["world"].World({ "generator": generator }); if (!generateInitialChunks) return resolve(window.worlds[name]); var min = [-4, 0, -4]; var max = [4, 8, 4]; var positions = []; for (var x = min[0]; x < max[0]; x++) { for (var z = min[2]; z < max[2]; z++) { for (var y = min[1]; y < max[1]; y++) { positions.push([x, y, z]); } } } var chunkGenerators = []; genNext = function (pos, p) { chunkGenerators.push( worlds[name].generateChunk(pos[p]) .then(function (chunk) { if (pos[p + 1]) return genNext(pos, p + 1); Promise.all(chunkGenerators).then(function (chunks) { resolve(worlds[name]); }).catch(throwError); }) .catch(throwError) ); }; genNext(positions, 0); }); }; var startGame = window.startGame = function (instance, worldName) { instanceWorlds[instance] = worldName; renderers[instance].setAnimationLoop(render.bind(window, instance)); inputs[instance].grab(); var chunkSize = window.worlds[worldName].chunkSize; var positions = []; worlds[worldName].chunks.forEach(function (chunk, c) { positions.push( c.split(":") .map(coord => parseInt(coord)) ); }); if (positions.length > 0) { constructNext = function (pos, p) { worlds[worldName].constructChunk(pos[p]).then(function (chunk) { worlds[worldName].addChunk(chunk); if (pos[p + 1]) return constructNext(pos, p + 1); }).catch(throwError); }; constructNext(positions, 0); } }; window.addEventListener("resize", function (event) { reorganizeRenderers(false); for (var r = 0; r < renderers.length; r++) { var rdr = renderers[r]; var rdrDOM = rdr.domElement; var cam = cameras[r]; rdr.setSize(rdrDOM.clientWidth, rdrDOM.clientHeight); cam.aspect = rdrDOM.clientWidth / rdrDOM.clientHeight; cam.updateProjectionMatrix(); } }); var reorganizeRenderers = function (remakeLayout = true) { var rows = []; var rowAmount = Math.round(Math.sqrt(renderers.length)); if (remakeLayout) { for (var r = renderersContainer.childElementCount - 1; r >= 0; r--) { renderersContainer.children.item(r).remove(); } for (var r = 0; r < rowAmount; r++) { rows[r] = document.createElement("div"); rows[r].style.width = "100%"; rows[r].style.height = "calc(100% / " + rowAmount + ")"; renderersContainer.append(rows[r]); } for (var r = 0; r < renderers.length; r++) { rows[Math.floor((r / renderers.length) * rowAmount)].append(renderers[r].domElement); } } else { for (var r = renderersContainer.childElementCount - 1; r >= 0; r--) { rows[r] = renderersContainer.children.item(r); } } for (var r = 0; r < rowAmount; r++) { var row = rows[r]; for (var rd = 0; rd < row.childElementCount; rd++) { row.children[rd].style.width = "calc(100% / " + row.childElementCount + ")"; row.children[rd].style.height = "100%"; } } }; var addPlayer = function (instance, controller) { renderers[instance] = new core.modules["render"].Renderer({ "preserveDrawingBuffer": true, "autoResize": true }); var instanceDOMElement = renderers[instance].domElement; reorganizeRenderers(); for (var r = 0; r < renderers.length; r++) { var rdr = renderers[r]; var rdrDOM = rdr.domElement; rdr.setSize(rdrDOM.clientWidth, rdrDOM.clientHeight, false); } cameras[instance] = new core.modules["render"].PerspectiveCamera(130, instanceDOMElement.width / instanceDOMElement.height, 0.0005, 8192); if (controller instanceof core.modules["input"].Controller) { inputs[instance] = controller; inputs[instance].domElement = instanceDOMElement; } else { inputs[instance] = new core.modules["input"].Controller("desktop", { "domElement": instanceDOMElement }); } renderers[instance].setAnimationLoop(render.bind(window, instance)); }; var quitGame = window.quitGame = function (instance) { }; var closeGame = window.closeGame = function () { window.close(); }; var openSave = window.openSave = function (archive) { return new Promise(function (resolve, reject) { window.fflate.unzip(new Uint8Array(archive), {}, function (error, data) { if (error) return reject(error); var loadedWorlds = {}; for (var filePath in data) { if (data.hasOwnProperty(filePath)) { var matches = filePath.match(/([^\/]+)\/meta\.json/); if (!matches) continue; if (matches[0] !== filePath) continue; if (!matches[1]) continue; var worldFolderName = matches[1]; let worldMeta; try { worldMeta = JSON.parse(window.fflate.strFromU8(data["" + worldFolderName + "/meta.json"])); } catch (err) { console.error(err); continue; } var worldName = worldMeta.name; loadedWorlds[worldName] = new core.modules["world"].World({ "generator": worldMeta.generator }); var chunkFileNameMatcher = new RegExp(worldFolderName + "\/chunks\/(-?[0-9]+:-?[0-9]+:-?[0-9]+)\.json"); for (var fileName in data) { if (data.hasOwnProperty(fileName)) { var nameMatches = fileName.match(chunkFileNameMatcher); if (!nameMatches) continue; if (nameMatches[0] !== fileName) continue; if (!nameMatches[1]) continue; var chunkFileName = nameMatches[1]; let chunkData; try { chunkData = JSON.parse(window.fflate.strFromU8(data[worldFolderName + "/chunks/" + chunkFileName + ".json"])); } catch (err) { console.error(err); continue; } var thisChunk = new core.modules["world"].Chunk(chunkData.size, loadedWorlds[worldName]); for (var b = 0; b < chunkData.blocks.length; b++) { thisChunk.blocks[b] = chunkData.blocks[b]; } var chunkPosition = chunkFileName.split(":").map(function (c) { return parseInt(c); }); loadedWorlds[worldName].setChunk(thisChunk, chunkPosition); loadedWorlds[worldName].add(thisChunk); } } } } resolve(loadedWorlds); }); }); }; var makeSave = window.makeSave = function () { return new Promise(function (resolve, reject) { var files = {}; for (var world in worlds) { if (worlds.hasOwnProperty(world)) { var thisWorld = worlds[world].toJSON(); var worldMeta = { "name": world, "generator": thisWorld.generator }; files[world + "/meta.json"] = fflate.strToU8(JSON.stringify(worldMeta, null, "\t")); for (var chunk in thisWorld.chunks) { if (thisWorld.chunks.hasOwnProperty(chunk)) { var thisChunk = thisWorld.chunks[chunk]; files[world + "/chunks/" + chunk + ".json"] = fflate.strToU8(JSON.stringify(thisChunk, null, "\t")); } } } } window.fflate.zip(files, function (error, archive) { if (error) return reject(error); resolve(archive); }); }); }; let core; var currentLanguage = window.currentLanguage = "en-US"; var instanceWorlds = window.instanceWorlds = []; var renderers = window.renderers = []; var cameras = window.cameras = []; var inputs = window.inputs = []; var loadings = {}; let resourcePacks = window.resourcePacks = []; let worlds = window.worlds = {}; let renderersContainer;