XVoxel-ASync/game.js

489 lines
12 KiB
JavaScript

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;