beta 4: using more guacamolean command names
a bit less extended and using stock error and ready instead of connect,<0,1,2> this is semver breaking but we're in prerelease so i'm not touching package.json ... until it's considered at least mostly done
This commit is contained in:
parent
f9bd6c5b69
commit
3770586592
93
client.js
93
client.js
|
@ -11,11 +11,12 @@ __messengerjs__.fade = document.createElement("div");
|
||||||
__messengerjs__.fade.style.cssText = "position: absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.75);color:white;text-align:center;font-family:sans-serif;";
|
__messengerjs__.fade.style.cssText = "position: absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.75);color:white;text-align:center;font-family:sans-serif;";
|
||||||
__messengerjs__.fade.textContent = "connecting to WebSocket server...";
|
__messengerjs__.fade.textContent = "connecting to WebSocket server...";
|
||||||
|
|
||||||
const beforeUnloadHandler = function(ev, reason) {
|
const beforeUnloadHandler = function(ev) {
|
||||||
Channel_OnAppClose();
|
Channel_OnAppClose();
|
||||||
channelSocket.send(new TextEncoder("utf8").encode(EncodeCommandString(["disconnect"])));
|
channelSocket.send(new TextEncoder("utf8").encode(EncodeCommandString(["disconnect"])));
|
||||||
channelSocket.close(1000, ((typeof (reason)) === "string") ? reason : "unloading");
|
channelSocket.close(1000, "unloading");
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @section guacamole **/
|
/** @section guacamole **/
|
||||||
const ParseCommandString = function(instruction) {
|
const ParseCommandString = function(instruction) {
|
||||||
if(typeof instruction !== "string") {
|
if(typeof instruction !== "string") {
|
||||||
|
@ -73,14 +74,18 @@ const EncodeCommandString = function(sections) {
|
||||||
|
|
||||||
/** window.external **/
|
/** window.external **/
|
||||||
window.external.CloseApp = function() {
|
window.external.CloseApp = function() {
|
||||||
beforeUnloadHandler(null, "called CloseApp");
|
Channel_OnAppClose();
|
||||||
|
channelSocket.send(new TextEncoder("utf8").encode(EncodeCommandString(["disconnect"])));
|
||||||
|
channelSocket.close(1000, "we called CloseApp");
|
||||||
}
|
}
|
||||||
// this is important because things expect it
|
// this is important because things expect it
|
||||||
/** window.external.Channel **/
|
/** window.external.Channel **/
|
||||||
window.external.Channel = {"Data": "", "SendData": null, "Initialize": null, "Error": {"Data": ""}}; // "Type" is a defined property
|
window.external.Channel = {"Data": "", "SendData": null, "Initialize": null, "Error": {"Data": "", "Type": 0}}; // "Type" is a defined property
|
||||||
|
// things may want Channel.IM: string and Channel.FileInfo: object {Path: string, Size: number, Progress: number<0-100>, Incoming: bool, Status: num<0-3>}
|
||||||
window.external.Channel.SendData = function(d) {channelSocket.send(d)}; // this is dumb but trying to redirect this gives you TypeError's
|
window.external.Channel.SendData = function(d) {channelSocket.send(d)}; // this is dumb but trying to redirect this gives you TypeError's
|
||||||
window.external.Channel.Initialize = function() {
|
window.external.Channel.Initialize = function() {
|
||||||
// no idea
|
// no idea we're already pretty initalized by the time most things call this
|
||||||
|
Channel_OnTypeChanged(); // make client check if its even connected still
|
||||||
};
|
};
|
||||||
Object.defineProperty(window.external.Channel, "Type", {
|
Object.defineProperty(window.external.Channel, "Type", {
|
||||||
get: function Type() {
|
get: function Type() {
|
||||||
|
@ -92,8 +97,9 @@ Object.defineProperty(window.external.Channel, "Type", {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
/** window.external.Users **/
|
/** window.external.Users **/
|
||||||
// This sadly needs to be already configured before an activity tries using it...
|
// yes i define these as the default users... should be created a bit more dynamically
|
||||||
// We can't just block execution until we've got the session...
|
// problem with that is that i'm yet to find a 1 user activity that calls any of these... ReplaceIM abuse maybe?
|
||||||
|
// avaliablity of these properties depends on the permission flags, especially UserProperties being enabled
|
||||||
window.external.yellows111_Users = [
|
window.external.yellows111_Users = [
|
||||||
{"Name": "Initiator", "Email": "initiator@messenger.js", "GlobalIP": "0.0.0.0", "LocalIP": "0.0.0.0", "PUID": "0"},
|
{"Name": "Initiator", "Email": "initiator@messenger.js", "GlobalIP": "0.0.0.0", "LocalIP": "0.0.0.0", "PUID": "0"},
|
||||||
{"Name": "Target", "Email": "target@messenger.js", "GlobalIP": "0.0.0.0", "LocalIP": "0.0.0.1", "PUID": "1"}
|
{"Name": "Target", "Email": "target@messenger.js", "GlobalIP": "0.0.0.0", "LocalIP": "0.0.0.1", "PUID": "1"}
|
||||||
|
@ -120,6 +126,12 @@ Object.defineProperty(window.external.Users, "me", {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
window.external.Messenger exists but all it does is:
|
||||||
|
* allow you to open the Options dialog (on a certain page: number)
|
||||||
|
* allow you to open the phone dialer (with a predetermined number: string)
|
||||||
|
so why would i even bother?
|
||||||
|
*/
|
||||||
/** websocket stuff **/
|
/** websocket stuff **/
|
||||||
channelSocket.binaryType = "arraybuffer"; // the alternative is blob.arrayBuffer(); IT'S A PROMISE CALL AND PROMISES SUXX
|
channelSocket.binaryType = "arraybuffer"; // the alternative is blob.arrayBuffer(); IT'S A PROMISE CALL AND PROMISES SUXX
|
||||||
channelSocket.onmessage = function(event) {
|
channelSocket.onmessage = function(event) {
|
||||||
|
@ -138,45 +150,43 @@ channelSocket.onmessage = function(event) {
|
||||||
__messengerjs__.myNickname = args[3];
|
__messengerjs__.myNickname = args[3];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "connect": {
|
case "ready": {
|
||||||
switch(args[1]) {
|
console.log("joined channel successfully.");
|
||||||
case "0": {
|
__messengerjs__.fade.textContent = "waiting for opponent to join this channel...";
|
||||||
console.error("channel is full or not avaliable");
|
window.addEventListener("beforeunload", beforeUnloadHandler);
|
||||||
// regenerate channel ID if we get 0'd
|
__messengerjs__.imTheChannelOwner = (args[1] !== "1");
|
||||||
__messengerjs__.sessionID = Math.random().toString(36).split('.')[1].substring(0,8);
|
break;
|
||||||
history.pushState(null, "", `#channel=${__messengerjs__.sessionID}`);
|
}
|
||||||
channelSocket.send(new TextEncoder("utf8").encode(EncodeCommandString(["connect", __messengerjs__.sessionID])));
|
case "error": {
|
||||||
|
window.external.Channel.Error.Data = args[1];
|
||||||
|
window.external.Channel.Error.Type = parseInt(args[2]);
|
||||||
|
switch(args[2]) {
|
||||||
|
case "0": { // if we're getting an error with STATUS_SUCCESSFUL... which sounds stupid, but i'm using it as SERVER_CLOSING)
|
||||||
|
__messengerjs__.fade.textContent = "the channel server was closed. please try again later.";
|
||||||
|
__messengerjs__.fade.style.display = "block";
|
||||||
|
channelSocket.close(1000, "server shutdown acknoledged.");
|
||||||
|
Channel_OnTypeChanged();
|
||||||
|
Channel_OnRemoteAppClosed(); // they're gone
|
||||||
|
Channel_OnAppClose(); // we're gone too
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "1": {
|
case "513": { // SERVER_BUSY = channel was full
|
||||||
console.log("joined channel successfully.");
|
console.warn("channel was full, generating new room");
|
||||||
__messengerjs__.fade.textContent = "waiting for opponent to join this channel...";
|
__messengerjs__.sessionID = Math.random().toString(36).split('.')[1].substring(0,8);
|
||||||
window.addEventListener("beforeunload", beforeUnloadHandler);
|
history.pushState(null, "", `#channel=${__messengerjs__.sessionID}`);
|
||||||
if(args[2] === "1") {
|
channelSocket.send(new TextEncoder("utf8").encode(EncodeCommandString(["connect", __messengerjs__.sessionID])));
|
||||||
__messengerjs__.imTheChannelOwner = false;
|
|
||||||
} else {
|
|
||||||
__messengerjs__.imTheChannelOwner = true;
|
|
||||||
if(window.external.Users.Inviter !== window.external.Users.Me) {
|
|
||||||
// This channel has already been closed. Create a new one.
|
|
||||||
beforeUnloadHandler(null, "channel was empty when expecting host");
|
|
||||||
history.replaceState(null, "", window.location.href.split("?")[0]);
|
|
||||||
window.location.reload(); // it's still jank and probably should just be handled elsewhere
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "2": {
|
default: {
|
||||||
console.log("disconnected gracefully.");
|
__messengerjs__.fade.textContent = "server error:" + args[1] + "code (" + args[2] + ")";
|
||||||
break;
|
__messengerjs__.fade.style.display = "block";
|
||||||
|
channelSocket.close(1000, "server sent non-zero error");
|
||||||
|
Channel_OnTypeChanged();
|
||||||
|
Channel_OnDataError(); // this may just attempt to retry to send the failed data... when we're already closed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "disconnect": {
|
|
||||||
// code will never reach here normally (should announce from server on ^C, though)
|
|
||||||
channelSocket.close(1000, "goodbye");
|
|
||||||
Channel_OnTypeChanged();
|
|
||||||
}
|
|
||||||
case "adduser": {
|
case "adduser": {
|
||||||
__messengerjs__.fade.style.display = "none";
|
__messengerjs__.fade.style.display = "none";
|
||||||
if(__messengerjs__.imTheChannelOwner === false) {
|
if(__messengerjs__.imTheChannelOwner === false) {
|
||||||
|
@ -207,7 +217,8 @@ channelSocket.onmessage = function(event) {
|
||||||
__messengerjs__.fade.style.display = "block";
|
__messengerjs__.fade.style.display = "block";
|
||||||
window.removeEventListener("beforeunload", beforeUnloadHandler);
|
window.removeEventListener("beforeunload", beforeUnloadHandler);
|
||||||
Channel_OnRemoteAppClosed();
|
Channel_OnRemoteAppClosed();
|
||||||
beforeUnloadHandler(null, "other user disconnected"); // disconnect ourselves
|
channelSocket.send(new TextEncoder("utf8").encode(EncodeCommandString(["disconnect"])));
|
||||||
|
channelSocket.close(1000, "opponent left");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "sync": {
|
case "sync": {
|
||||||
|
@ -255,8 +266,8 @@ __messengerjs__.OnLoadInterrupt = function(ev) {
|
||||||
createWSEventsNowThatImReady();
|
createWSEventsNowThatImReady();
|
||||||
__messengerjs__.onloadargs = ev;
|
__messengerjs__.onloadargs = ev;
|
||||||
}
|
}
|
||||||
document.addEventListener("DOMContentLoaded", (event) => {
|
document.addEventListener("DOMContentLoaded", function(event) {
|
||||||
// do document.body.onload too since some things suck
|
// do document.body.onload too since some things suck and like doing that
|
||||||
if(typeof document.body.onload === "function") {
|
if(typeof document.body.onload === "function") {
|
||||||
__messengerjs__.onloadfunction = document.body.onload;
|
__messengerjs__.onloadfunction = document.body.onload;
|
||||||
}
|
}
|
||||||
|
|
32
server.js
32
server.js
|
@ -88,18 +88,18 @@ const MessageParser = function(webSocket, message, isBinary) {
|
||||||
}
|
}
|
||||||
case "connect": {
|
case "connect": {
|
||||||
if(args.length <= 1) {
|
if(args.length <= 1) {
|
||||||
webSocket.send(EncodeCommandString(["connect", 0]), true);
|
webSocket.send(EncodeCommandString(["error", "need more arguments", 768]), true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let getChannelId = args[1];
|
let getChannelId = args[1];
|
||||||
if(server.numSubscribers(`channels/${getChannelId}`) >= 2) {
|
if(server.numSubscribers(`channels/${getChannelId}`) >= 2) {
|
||||||
// this room is full
|
// this room is full
|
||||||
webSocket.send(EncodeCommandString(["connect", 0]), true);
|
webSocket.send(EncodeCommandString(["error", "channel has too many members", 513]), true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(UserAttachedNameStorage.has(webSocket.getUserData().uid) === false) {
|
if(UserAttachedNameStorage.has(webSocket.getUserData().uid) === false) {
|
||||||
// user doesn't have a username
|
// user doesn't have a username
|
||||||
webSocket.send(EncodeCommandString(["connect", 0]), true);
|
webSocket.send(EncodeCommandString(["error", "you need a nickname", 769]), true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(ChannelStorage.has(getChannelId) === false) {
|
if(ChannelStorage.has(getChannelId) === false) {
|
||||||
|
@ -109,16 +109,16 @@ const MessageParser = function(webSocket, message, isBinary) {
|
||||||
};
|
};
|
||||||
if(webSocket.isSubscribed(`channels/${getChannelId}`) === true) {
|
if(webSocket.isSubscribed(`channels/${getChannelId}`) === true) {
|
||||||
// already in this channel
|
// already in this channel
|
||||||
webSocket.send(EncodeCommandString(["connect", 0]), true);
|
webSocket.send(EncodeCommandString(["error", "already in this channel", 517]), true);
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
if(UserAttachedChannelStorage.has(webSocket.getUserData().uid) === true) {
|
if(UserAttachedChannelStorage.has(webSocket.getUserData().uid) === true) {
|
||||||
// already in a channel that isn't the one we're trying to join
|
// already in a channel that isn't the one we're trying to join
|
||||||
webSocket.send(EncodeCommandString(["connect", 0]), true);
|
webSocket.send(EncodeCommandString(["error", "already in another channel", 797]), true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
console.log(`user ${UserAttachedNameStorage.get(webSocket.getUserData().uid)} (${webSocket.getUserData().uid}) joined #${getChannelId}`);
|
console.log(`user ${UserAttachedNameStorage.get(webSocket.getUserData().uid)} (${webSocket.getUserData().uid}) joined #${getChannelId}`);
|
||||||
webSocket.send(EncodeCommandString(["connect", 1, server.numSubscribers(`channels/${getChannelId}`)]), true);
|
webSocket.send(EncodeCommandString(["ready", server.numSubscribers(`channels/${getChannelId}`)]), true);
|
||||||
webSocket.subscribe(`channels/${getChannelId}`);
|
webSocket.subscribe(`channels/${getChannelId}`);
|
||||||
UserAttachedChannelStorage.set(webSocket.getUserData().uid, getChannelId);
|
UserAttachedChannelStorage.set(webSocket.getUserData().uid, getChannelId);
|
||||||
// do we keep adduser?
|
// do we keep adduser?
|
||||||
|
@ -145,13 +145,12 @@ const MessageParser = function(webSocket, message, isBinary) {
|
||||||
ChannelStorage.delete(UserAttachedChannelStorage.get(webSocket.getUserData().uid));
|
ChannelStorage.delete(UserAttachedChannelStorage.get(webSocket.getUserData().uid));
|
||||||
}
|
}
|
||||||
UserAttachedChannelStorage.delete(webSocket.getUserData().uid);
|
UserAttachedChannelStorage.delete(webSocket.getUserData().uid);
|
||||||
|
// user is disconnecting next frame!
|
||||||
}
|
}
|
||||||
webSocket.send(EncodeCommandString(["connect", 2]), true); // graceful disconnect message
|
|
||||||
webSocket.send(EncodeCommandString(["disconnect"]), true);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case "sync": {
|
case "sync": {
|
||||||
// client is alive...
|
// client is alive... TODO check if this is too ahead/behind
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
@ -169,6 +168,7 @@ const MessageParser = function(webSocket, message, isBinary) {
|
||||||
webSocket.publish(`channels/${UserAttachedChannelStorage.get(webSocket.getUserData().uid)}`, message, false);
|
webSocket.publish(`channels/${UserAttachedChannelStorage.get(webSocket.getUserData().uid)}`, message, false);
|
||||||
// ask other user if alive still
|
// ask other user if alive still
|
||||||
webSocket.publish(`channels/${UserAttachedChannelStorage.get(webSocket.getUserData().uid)}`, EncodeCommandString(["sync", (new Date).getTime()]), true);
|
webSocket.publish(`channels/${UserAttachedChannelStorage.get(webSocket.getUserData().uid)}`, EncodeCommandString(["sync", (new Date).getTime()]), true);
|
||||||
|
webSocket.ping("pulse");
|
||||||
}
|
}
|
||||||
//console.log(`${new TextDecoder("utf-8").decode(webSocket.getRemoteAddressAsText())} message: ${msg}`);
|
//console.log(`${new TextDecoder("utf-8").decode(webSocket.getRemoteAddressAsText())} message: ${msg}`);
|
||||||
}
|
}
|
||||||
|
@ -176,6 +176,8 @@ const UserStateRequest = function(webSocket, state, data = null) {
|
||||||
switch(state) {
|
switch(state) {
|
||||||
case "open": {
|
case "open": {
|
||||||
//console.log("%s client hello", new TextDecoder("utf-8").decode(webSocket.getRemoteAddressAsText()));
|
//console.log("%s client hello", new TextDecoder("utf-8").decode(webSocket.getRemoteAddressAsText()));
|
||||||
|
webSocket.subscribe("system");
|
||||||
|
webSocket.ping("welcome");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "close": {
|
case "close": {
|
||||||
|
@ -207,4 +209,14 @@ server.ws("/connect", {
|
||||||
"close": function(ws, code, message) {UserStateRequest(ws, "close", {code, message})},
|
"close": function(ws, code, message) {UserStateRequest(ws, "close", {code, message})},
|
||||||
"drain": function(ws) {console.log(`WS going through back-pressure!: ${ws.getBufferedAmount()}`)}
|
"drain": function(ws) {console.log(`WS going through back-pressure!: ${ws.getBufferedAmount()}`)}
|
||||||
});
|
});
|
||||||
server.listen(config.port, function(token) {console.log(token ? `open on ${config.port}` : `failed to listen to ${config.port}`)});
|
server.listen(config.port, function(token) {console.log(token ? `open on ${config.port}` : `failed to listen to ${config.port}`)});
|
||||||
|
|
||||||
|
var serverIsClosing = false;
|
||||||
|
require("process").on('SIGINT', function() {
|
||||||
|
if(serverIsClosing) {return};
|
||||||
|
server.publish("system", EncodeCommandString(["error", "server is closing", 0]), true);
|
||||||
|
serverIsClosing = true;
|
||||||
|
require("process").on('SIGINT', function(){console.warn("emergency shutdown was engaged, clients may be confused!");require("process").exit()});
|
||||||
|
console.log("shutting down server, giving clients 5 seconds...");
|
||||||
|
setTimeout(function(){console.log("server stopping now");server.close();require("process").exit()}, 5000)
|
||||||
|
});
|
Loading…
Reference in New Issue