XVoxel-ASync/modules/network.js

359 lines
9.5 KiB
JavaScript

const networkClock = 1000 / 16;
var allowedIDCharacters = "0123456789ABCDEF";
var tokenCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._-";
var generateAccessToken = function () {
var token = "";
for (var c = 0; c < 64; c++) {
token += tokenCharacters[
Math.floor(
Math.random() * tokenCharacters.length
)
];
}
return token;
};
var initMatrixClient = function (baseURL, mxID, withCrypto = false) {
return new Promise(function (resolve, reject) {
var matrixAccessToken = localStorage.getItem("matrix_access_token");
if (!matrixAccessToken) {
matrixAccessToken = generateAccessToken();
localStorage.setItem("matrix_access_token", matrixAccessToken);
}
var matrixClient = global.matrixcs.createClient({
"baseUrl": baseURL,
"accessToken": matrixAccessToken,
"userId": mxID,
"deviceId": "XVoxel"
});
module.exports.matrixClient = matrixClient;
resolve(matrixClient);
if (withCrypto) {
matrixClient.initCrypto().then(function () {
}).catch(console.error);
}
});
};
var loginToMatrix = function (username, password) {
return new Promise(function (resolve, reject) {
if (!("matrixClient" in module.exports)) return reject(new Error("Matrix client hasn't been initialized yet"));
module.exports.matrixClient.loginWithPassword(username, password).then(function (result) {
var matrixAccessToken = module.exports.matrixClient.getAccessToken();
localStorage.setItem("matrix_access_token", matrixAccessToken);
resolve(result);
}).catch(reject);
});
};
var startMatrixClient = function () {
module.exports.matrixClient.startClient();
module.exports.matrixClient.on("sync", function (state, previousState, response) {
if (state === "PREPARED") {
var buttonReadyInterval = setInterval(function () {
var mxWJBtn = document.querySelector("#matrixworldjoin");
if (!mxWJBtn) return;
mxWJBtn.disabled = false;
clearInterval(buttonReadyInterval);
}, 256);
}
});
};
var createWorldRoom = function (name) {
return new Promise(function (resolve, reject) {
module.exports.matrixClient.createRoom({
"name": name,
"visibility": "private",
"topic": "A Voxel world",
"creation_content": {
"type": "m.world.voxel"
}
}).then(resolve).catch(reject);
});
};
var getWorldRooms = function () {
var rooms = module.exports.matrixClient.getRooms();
if (!rooms) return [];
return rooms.filter(room => room.getType() == "m.world.voxel");
};
var syncWorldFromMatrixRoom = function (room) {
return new Promise(function (resolve, reject) {
module.exports.matrixClient.getStateEvent(room.roomId, "m.world.voxel", "save").then(function (event) {
fetch(module.exports.matrixClient.mxcUrlToHttp(event["save_uri"])).then(function (response) {
response.arrayBuffer().then(function (arrayBuffer) {
global.openSave(arrayBuffer).then(function (loadedWorlds) {
for (var worldName in loadedWorlds) {
if (loadedWorlds.hasOwnProperty(worldName)) {
global.worlds[worldName] = loadedWorlds[worldName];
}
}
resolve(global.worlds[room.name]);
}).catch(reject);
}).catch(reject);
}).catch(reject);
}).catch(reject);
});
};
var syncWorldToMatrixRoom = function (room) {
return new Promise(function(resolve, reject) {
global.makeSave().then(function (archive) {
module.exports.matrixClient.uploadContent(archive, {
"name": room.name + ".zip",
"type": "application/zip"
}).then(function (response) {
module.exports.matrixClient.sendStateEvent(room.roomId, "m.world.voxel", {
"save_uri": response.content_uri
}, "save").then(resolve).catch(reject);
}).catch(reject);
}).catch(reject);
});
};
var connectMatrix = function (room) {
return new Promise(function (resolve, reject) {
var groupCall = module.exports.matrixClient.getGroupCallForRoom(room.roomId);
if (!groupCall) {
return module.exports.matrixClient.createGroupCall(
room.roomId, global.matrixcs.GroupCallType["Voice"],
false, global.matrixcs.GroupCallIntent["Room"], true
).then(function (call) {
call.initWithAudioMuted = true;
call.initWithVideoMuted = true;
call.setMicrophoneMuted(true);
call.enter();
resolve(call);
}).catch(console.error);
}
groupCall.initWithAudioMuted = true;
groupCall.initWithVideoMuted = true;
groupCall.setMicrophoneMuted(true);
groupCall.enter();
resolve(groupCall);
});
};
if (localStorage.getItem("matrix_is_connected")) {
initMatrixClient(
localStorage.getItem("matrix_baseurl"),
localStorage.getItem("matrix_mxid")
).then(function (matrixClient) {
module.exports.matrixClient = matrixClient;
startMatrixClient();
}).catch(function (error) {
console.error(error);
localStorage.removeItem("matrix_is_connected");
});
}
var connectRTC = function (options) {
var randomID = (options.username || "Guest") + "-";
for (var c = 0; c < 16; c++) {
randomID += allowedIDCharacters[
Math.floor(
Math.random() * allowedIDCharacters.length
)
];
}
var peerNetwork = new global.peer.default(randomID, {
"host": options.host,
"port": options.port,
"path": options.path
});
return peerNetwork;
};
var connect = function (url) {
try {
var connectionURL = new URL(url);
} catch (error) {
global.throwError(error);
}
var splitPathName = connectionURL.pathname.split(/[\/]/);
var pathNameParts = splitPathName[2].split("@");
if (pathNameParts.length > 1) {
var pathNameCredentials = pathNameParts.split(":");
var pathNameUsername = pathNameCredentials[0];
if (pathNameCredentials.length > 1) {
var pathNamePassword = pathNameCredentials[1];
}
var pathNameConnectionDataIndex = 1;
} else {
var pathNameConnectionDataIndex = 0;
}
var pathNameConnectionData = pathNameParts[pathNameConnectionDataIndex].split(":");
var pathNameAddress = pathNameConnectionData[0];
if (pathNameConnectionData.length > 1) {
var pathNamePort = pathNameConnectionData[1];
}
var pathNamePath = "/" + splitPathName.slice(3).join("/");
switch (connectionURL.protocol) {
case "rtc:":
return connectRTC({
"host": pathNameAddress,
"port": pathNamePort || 443,
"path": pathNamePath || "/",
"username": pathNameUsername || "Guest",
"password": pathNamePassword || null
});
break;
case "ws:":
break;
case "wss:":
break;
case "http:":
break;
case "https:":
break;
default:
}
};
module.exports = {
"initMatrixClient": initMatrixClient,
"loginToMatrix": loginToMatrix,
"startMatrixClient": startMatrixClient,
"createWorldRoom": createWorldRoom,
"getWorldRooms": getWorldRooms,
"connectMatrix": connectMatrix,
"connect": connect,
"menus": {
"play": {
"elements": [
{
"type": "button",
"label": "Multiplayer",
"action": "open-menu",
"action-args": ["serversel", 2]
}
]
},
"serversel": {
"elements": [
{
"type": "text",
"id": "serveraddressinput",
"label": global.locales.getRawString(global.currentLanguage, "play.multiplayer.serveraddressinput.label"),
"placeholder": global.locales.getRawString(global.currentLanguage, "play.multiplayer.serveraddressinput.placeholder"),
"value": "",
"action": "run",
"runnable": function (event) {
var serverConnectButton = global.document.getElementById("serverconnectbutton");
if (event.path[0].value.length < 1) {
serverConnectButton.disabled = true;
} else {
try {
new URL(event.path[0].value);
serverConnectButton.disabled = false;
} catch (e) {
serverConnectButton.disabled = true;
}
}
}
},
{
"type": "button",
"id": "serverconnectbutton",
"label": global.locales.getRawString(global.currentLanguage, "play.multiplayer.serverconnectbutton.label"),
"disabled": true,
"action": "run",
"runnable": function (event) {
var serverAddressInputBox = global.document.getElementById("serveraddressinput");
connect(serverAddressInputBox.value);
}
},
{
"type": "button",
"id": "matrixworldjoin",
"label": global.locales.getRawString(global.currentLanguage, "play.multiplayer.loadmatrixworldsbutton.label"),
"disabled": true,
"action": "run",
"runnable": function (event) {
for (var e = module.exports.menus["serversel"].elements.length - 1; e >= 0; e--) {
var menuElement = module.exports.menus["serversel"].elements[e];
if (("classes" in menuElement) && menuElement.classes.indexOf("mxworldjoinbtn") > -1) module.exports.menus["serversel"].elements.splice(e, 1);
}
var matrixWorldRooms = getWorldRooms();
for (var r = 0; r < matrixWorldRooms.length; r++) {
var matrixWorldRoom = matrixWorldRooms[r];
var menuEntry = {
"type": "button",
"label": global.locales.getFormattedString(
global.currentLanguage,
"play.singleplayer.openworld.label",
{
"WORLD_NAME": matrixWorldRoom.name
}
),
"action": "run",
"runnable": (function (event) {
syncWorldFromMatrixRoom(this).then((function (world) {
global.worlds[this.name] = world;
global.startGame(0, this.name);
}).bind(this)).catch(global.throwError);
global.menu.hideMenus(0);
}).bind(matrixWorldRoom),
"classes": [
"mxworldjoinbtn"
]
};
module.exports.menus["serversel"].elements.push(menuEntry);
}
global.menu.hideMenus(2);
global.menu.menus["serversel"] = global.menu.constructMenu("serversel", module.exports.menus["serversel"]);
global.menu.showMenu("serversel", 2);
}
}
]
}
}
};