Add server files
This commit is contained in:
61
.gitignore
vendored
61
.gitignore
vendored
@ -1,60 +1 @@
|
||||
# This .gitignore file should be placed at the root of your Unity project directory
|
||||
#
|
||||
# Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore
|
||||
#
|
||||
/[Ll]ibrary/
|
||||
/[Tt]emp/
|
||||
/[Oo]bj/
|
||||
/[Bb]uild/
|
||||
/[Bb]uilds/
|
||||
/[Ll]ogs/
|
||||
/[Mm]emoryCaptures/
|
||||
|
||||
# Asset meta data should only be ignored when the corresponding asset is also ignored
|
||||
!/[Aa]ssets/**/*.meta
|
||||
|
||||
# Uncomment this line if you wish to ignore the asset store tools plugin
|
||||
# /[Aa]ssets/AssetStoreTools*
|
||||
|
||||
# Autogenerated Jetbrains Rider plugin
|
||||
[Aa]ssets/Plugins/Editor/JetBrains*
|
||||
|
||||
# Visual Studio cache directory
|
||||
.vs/
|
||||
|
||||
# Gradle cache directory
|
||||
.gradle/
|
||||
|
||||
# Autogenerated VS/MD/Consulo solution and project files
|
||||
ExportedObj/
|
||||
.consulo/
|
||||
*.csproj
|
||||
*.unityproj
|
||||
*.sln
|
||||
*.suo
|
||||
*.tmp
|
||||
*.user
|
||||
*.userprefs
|
||||
*.pidb
|
||||
*.booproj
|
||||
*.svd
|
||||
*.pdb
|
||||
*.mdb
|
||||
*.opendb
|
||||
*.VC.db
|
||||
|
||||
# Unity3D generated meta files
|
||||
*.pidb.meta
|
||||
*.pdb.meta
|
||||
*.mdb.meta
|
||||
|
||||
# Unity3D generated file on crash reports
|
||||
sysinfo.txt
|
||||
|
||||
# Builds
|
||||
*.apk
|
||||
*.unitypackage
|
||||
|
||||
# Crashlytics generated file
|
||||
crashlytics-build.properties
|
||||
|
||||
/node_modules/
|
192
INetwork.js
Normal file
192
INetwork.js
Normal file
@ -0,0 +1,192 @@
|
||||
"use strict";
|
||||
var NetEventType;
|
||||
(function (NetEventType) {
|
||||
NetEventType[NetEventType["Invalid"] = 0] = "Invalid";
|
||||
NetEventType[NetEventType["UnreliableMessageReceived"] = 1] = "UnreliableMessageReceived";
|
||||
NetEventType[NetEventType["ReliableMessageReceived"] = 2] = "ReliableMessageReceived";
|
||||
NetEventType[NetEventType["ServerInitialized"] = 3] = "ServerInitialized";
|
||||
NetEventType[NetEventType["ServerInitFailed"] = 4] = "ServerInitFailed";
|
||||
NetEventType[NetEventType["ServerClosed"] = 5] = "ServerClosed";
|
||||
NetEventType[NetEventType["NewConnection"] = 6] = "NewConnection";
|
||||
NetEventType[NetEventType["ConnectionFailed"] = 7] = "ConnectionFailed";
|
||||
NetEventType[NetEventType["Disconnected"] = 8] = "Disconnected";
|
||||
NetEventType[NetEventType["FatalError"] = 100] = "FatalError";
|
||||
NetEventType[NetEventType["Warning"] = 101] = "Warning";
|
||||
NetEventType[NetEventType["Log"] = 102] = "Log"; //not yet used
|
||||
})(NetEventType || (NetEventType = {}));
|
||||
exports.NetEventType = NetEventType;
|
||||
var NetEventDataType;
|
||||
(function (NetEventDataType) {
|
||||
NetEventDataType[NetEventDataType["Null"] = 0] = "Null";
|
||||
NetEventDataType[NetEventDataType["ByteArray"] = 1] = "ByteArray";
|
||||
NetEventDataType[NetEventDataType["UTF16String"] = 2] = "UTF16String";
|
||||
})(NetEventDataType || (NetEventDataType = {}));
|
||||
var NetworkEvent = (function () {
|
||||
function NetworkEvent(t, conId, data) {
|
||||
this.type = t;
|
||||
this.connectionId = conId;
|
||||
this.data = data;
|
||||
}
|
||||
Object.defineProperty(NetworkEvent.prototype, "RawData", {
|
||||
get: function () {
|
||||
return this.data;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(NetworkEvent.prototype, "MessageData", {
|
||||
get: function () {
|
||||
if (typeof this.data != "string")
|
||||
return this.data;
|
||||
return null;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(NetworkEvent.prototype, "Info", {
|
||||
get: function () {
|
||||
if (typeof this.data == "string")
|
||||
return this.data;
|
||||
return null;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(NetworkEvent.prototype, "Type", {
|
||||
get: function () {
|
||||
return this.type;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(NetworkEvent.prototype, "ConnectionId", {
|
||||
get: function () {
|
||||
return this.connectionId;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
//for debugging only
|
||||
NetworkEvent.prototype.toString = function () {
|
||||
var output = "NetworkEvent[";
|
||||
output += "NetEventType: (";
|
||||
output += NetEventType[this.type];
|
||||
output += "), id: (";
|
||||
output += this.connectionId.id;
|
||||
output += "), Data: (";
|
||||
if (typeof this.data == "string") {
|
||||
output += this.data;
|
||||
}
|
||||
output += ")]";
|
||||
return output;
|
||||
};
|
||||
NetworkEvent.parseFromString = function (str) {
|
||||
var values = JSON.parse(str);
|
||||
var data;
|
||||
if (values.data == null) {
|
||||
data = null;
|
||||
}
|
||||
else if (typeof values.data == "string") {
|
||||
data = values.data;
|
||||
}
|
||||
else if (typeof values.data == "object") {
|
||||
//json represents the array as an object containing each index and the
|
||||
//value as string number ... improve that later
|
||||
var arrayAsObject = values.data;
|
||||
var length = 0;
|
||||
for (var prop in arrayAsObject) {
|
||||
//if (arrayAsObject.hasOwnProperty(prop)) { //shouldnt be needed
|
||||
length++;
|
||||
}
|
||||
var buffer = new Uint8Array(Object.keys(arrayAsObject).length);
|
||||
for (var i = 0; i < buffer.length; i++)
|
||||
buffer[i] = arrayAsObject[i];
|
||||
data = buffer;
|
||||
}
|
||||
else {
|
||||
console.error("data can't be parsed");
|
||||
}
|
||||
var evt = new NetworkEvent(values.type, values.connectionId, data);
|
||||
return evt;
|
||||
};
|
||||
NetworkEvent.toString = function (evt) {
|
||||
return JSON.stringify(evt);
|
||||
};
|
||||
NetworkEvent.fromByteArray = function (arr) {
|
||||
var type = arr[0]; //byte
|
||||
var dataType = arr[1]; //byte
|
||||
var id = new Int16Array(arr.buffer, arr.byteOffset + 2, 1)[0]; //short
|
||||
var data = null;
|
||||
if (dataType == NetEventDataType.ByteArray) {
|
||||
var length_1 = new Uint32Array(arr.buffer, arr.byteOffset + 4, 1)[0]; //uint
|
||||
var byteArray = new Uint8Array(arr.buffer, arr.byteOffset + 8, length_1);
|
||||
data = byteArray;
|
||||
}
|
||||
else if (dataType == NetEventDataType.UTF16String) {
|
||||
var length_2 = new Uint32Array(arr.buffer, arr.byteOffset + 4, 1)[0]; //uint
|
||||
var uint16Arr = new Uint16Array(arr.buffer, arr.byteOffset + 8, length_2);
|
||||
var str = "";
|
||||
for (var i = 0; i < uint16Arr.length; i++) {
|
||||
str += String.fromCharCode(uint16Arr[i]);
|
||||
}
|
||||
data = str;
|
||||
}
|
||||
var conId = new ConnectionId(id);
|
||||
var result = new NetworkEvent(type, conId, data);
|
||||
return result;
|
||||
};
|
||||
NetworkEvent.toByteArray = function (evt) {
|
||||
var dataType;
|
||||
var length = 4; //4 bytes are always needed
|
||||
//getting type and length
|
||||
if (evt.data == null) {
|
||||
dataType = NetEventDataType.Null;
|
||||
}
|
||||
else if (typeof evt.data == "string") {
|
||||
dataType = NetEventDataType.UTF16String;
|
||||
var str = evt.data;
|
||||
length += str.length * 2 + 4;
|
||||
}
|
||||
else {
|
||||
dataType = NetEventDataType.ByteArray;
|
||||
var byteArray = evt.data;
|
||||
length += 4 + byteArray.length;
|
||||
}
|
||||
//creating the byte array
|
||||
var result = new Uint8Array(length);
|
||||
result[0] = evt.type;
|
||||
;
|
||||
result[1] = dataType;
|
||||
var conIdField = new Int16Array(result.buffer, result.byteOffset + 2, 1);
|
||||
conIdField[0] = evt.connectionId.id;
|
||||
if (dataType == NetEventDataType.ByteArray) {
|
||||
var byteArray = evt.data;
|
||||
var lengthField = new Uint32Array(result.buffer, result.byteOffset + 4, 1);
|
||||
lengthField[0] = byteArray.length;
|
||||
for (var i = 0; i < byteArray.length; i++) {
|
||||
result[8 + i] = byteArray[i];
|
||||
}
|
||||
}
|
||||
else if (dataType == NetEventDataType.UTF16String) {
|
||||
var str = evt.data;
|
||||
var lengthField = new Uint32Array(result.buffer, result.byteOffset + 4, 1);
|
||||
lengthField[0] = str.length;
|
||||
var dataField = new Uint16Array(result.buffer, result.byteOffset + 8, str.length);
|
||||
for (var i = 0; i < dataField.length; i++) {
|
||||
dataField[i] = str.charCodeAt(i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return NetworkEvent;
|
||||
}());
|
||||
exports.NetworkEvent = NetworkEvent;
|
||||
var ConnectionId = (function () {
|
||||
function ConnectionId(nid) {
|
||||
this.id = nid;
|
||||
}
|
||||
ConnectionId.INVALID = new ConnectionId(-1);
|
||||
return ConnectionId;
|
||||
}());
|
||||
exports.ConnectionId = ConnectionId;
|
||||
//# sourceMappingURL=INetwork.js.map
|
28
README.md
Normal file
28
README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# AirSignal
|
||||
|
||||
## Installation
|
||||
Install [Nodejs](https://nodejs.org/en/) which comes with [NPM](https://www.npmjs.com/get-npm)
|
||||
After installing run the following commands:
|
||||
|
||||
`npm install`
|
||||
This will install all required packages for it to work.
|
||||
|
||||
`node server.js`
|
||||
This will run the server using config.json
|
||||
|
||||
You can change used ports and other details in the file config.json.
|
||||
|
||||
## SSL
|
||||
Create ssl.cert and ssl.key to allow using secure connections. Add ssl details
|
||||
in `config.json` as per the structure shown in `config_template.json`
|
||||
|
||||
## Contact
|
||||
The original creator of the server Christoph Kutza can be reached here:
|
||||
Send a mail to contact@because-why-not.com or BecauseWhyNotHelp@gmail.com or visit http://because-why-not.com !
|
||||
|
||||
If you're here from [AirPeer](https://www.github.com/adrenal/airpeer) and have questions about it, contact Vatsal Ambastha:
|
||||
|
||||
[@email](ambastha.vatsal@gmail.com)
|
||||
[@github](https://www.github.com/adrenak)
|
||||
[@website](http://www.vatsalambastha.com)
|
||||
[@twitter](https://www.twitter.com/vatsalAmbastha)
|
362
WebsocketNetworkServer.js
Normal file
362
WebsocketNetworkServer.js
Normal file
@ -0,0 +1,362 @@
|
||||
"use strict";
|
||||
var ws = require('ws');
|
||||
var inet = require('./INetwork');
|
||||
var WebsocketNetworkServer = (function () {
|
||||
function WebsocketNetworkServer() {
|
||||
this.mPool = {};
|
||||
}
|
||||
WebsocketNetworkServer.log = function (msg) {
|
||||
console.log("(" + new Date().toISOString() + ")" + msg);
|
||||
};
|
||||
WebsocketNetworkServer.prototype.onConnection = function (socket, appname) {
|
||||
//it would be possible to enforce the client to send a certain introduction first
|
||||
//to determine to which pool we add it -> for now only one pool is supported
|
||||
this.mPool[appname].add(socket);
|
||||
};
|
||||
//
|
||||
WebsocketNetworkServer.prototype.addSocketServer = function (websocketServer, appConfig) {
|
||||
var _this = this;
|
||||
if (this.mPool[appConfig.name] == null) {
|
||||
this.mPool[appConfig.name] = new PeerPool(appConfig);
|
||||
}
|
||||
var name = appConfig.name;
|
||||
websocketServer.on('connection', function (socket) { _this.onConnection(socket, name); });
|
||||
};
|
||||
return WebsocketNetworkServer;
|
||||
}());
|
||||
exports.WebsocketNetworkServer = WebsocketNetworkServer;
|
||||
;
|
||||
//Pool of client connects that are allowed to communicate to each other
|
||||
var PeerPool = (function () {
|
||||
function PeerPool(config) {
|
||||
this.mConnections = new Array();
|
||||
this.mServers = {};
|
||||
this.mAddressSharing = false;
|
||||
this.maxAddressLength = 256;
|
||||
this.mAppConfig = config;
|
||||
if (this.mAppConfig.address_sharing) {
|
||||
this.mAddressSharing = this.mAppConfig.address_sharing;
|
||||
}
|
||||
}
|
||||
PeerPool.prototype.hasAddressSharing = function () {
|
||||
return this.mAddressSharing;
|
||||
};
|
||||
//add a new connection based on this websocket
|
||||
PeerPool.prototype.add = function (socket) {
|
||||
this.mConnections.push(new SignalingPeer(this, socket));
|
||||
};
|
||||
//Returns the SignalingClientConnection that opened a server using the given address
|
||||
//or null if address not in use
|
||||
PeerPool.prototype.getServerConnection = function (address) {
|
||||
return this.mServers[address];
|
||||
};
|
||||
//Tests if the address is available for use.
|
||||
//returns true in the following cases
|
||||
//the address is longer than the maxAddressLength and the server the address is not yet in use or address sharing is active
|
||||
PeerPool.prototype.isAddressAvailable = function (address) {
|
||||
if (address.length <= this.maxAddressLength // only allow addresses shorter than maxAddressLength
|
||||
&& (this.mServers[address] == null || this.mAddressSharing)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
//Adds the server. No checking is performed here! logic should be solely in the connection class
|
||||
PeerPool.prototype.addServer = function (client, address) {
|
||||
if (this.mServers[address] == null) {
|
||||
this.mServers[address] = new Array();
|
||||
}
|
||||
this.mServers[address].push(client);
|
||||
};
|
||||
//Removes an address from the server. No checks performed
|
||||
PeerPool.prototype.removeServer = function (client, address) {
|
||||
//supports address sharing. remove the client from the server list that share the address
|
||||
var index = this.mServers[address].indexOf(client);
|
||||
if (index != -1) {
|
||||
this.mServers[address].splice(index, 1);
|
||||
}
|
||||
//delete the whole list if the last one left
|
||||
if (this.mServers[address].length == 0) {
|
||||
delete this.mServers[address];
|
||||
}
|
||||
};
|
||||
//Removes a given connection from the pool
|
||||
PeerPool.prototype.removeConnection = function (client) {
|
||||
var index = this.mConnections.indexOf(client);
|
||||
if (index != -1) {
|
||||
this.mConnections.splice(index, 1);
|
||||
}
|
||||
else {
|
||||
console.warn("Tried to remove unknown SignalingClientConnection. Bug?" + client);
|
||||
}
|
||||
};
|
||||
PeerPool.prototype.count = function () {
|
||||
return this.mConnections.length;
|
||||
};
|
||||
return PeerPool;
|
||||
}());
|
||||
var SignalingConnectionState;
|
||||
(function (SignalingConnectionState) {
|
||||
SignalingConnectionState[SignalingConnectionState["Uninitialized"] = 0] = "Uninitialized";
|
||||
SignalingConnectionState[SignalingConnectionState["Connecting"] = 1] = "Connecting";
|
||||
SignalingConnectionState[SignalingConnectionState["Connected"] = 2] = "Connected";
|
||||
SignalingConnectionState[SignalingConnectionState["Disconnecting"] = 3] = "Disconnecting";
|
||||
SignalingConnectionState[SignalingConnectionState["Disconnected"] = 4] = "Disconnected"; //means the instance is destroyed and unusable
|
||||
})(SignalingConnectionState || (SignalingConnectionState = {}));
|
||||
;
|
||||
///note: all methods starting with "internal" might leave the system in an inconsistent state
|
||||
///e.g. peerA is connected to peerB means peerB is connected to peerA but internalRemoveConnection
|
||||
///could cause peerA being disconnected from peerB but peerB still thinking to be connected to peerA!!!
|
||||
var SignalingPeer = (function () {
|
||||
function SignalingPeer(pool, socket) {
|
||||
var _this = this;
|
||||
this.mState = SignalingConnectionState.Uninitialized;
|
||||
this.mConnections = {};
|
||||
//C# version uses short so 16384 is 50% of the positive numbers (maybe might make sense to change to ushort or int)
|
||||
this.mNextIncomingConnectionId = new inet.ConnectionId(16384);
|
||||
this.mConInfo = "[con info missing]";
|
||||
this.mConnectionPool = pool;
|
||||
this.mSocket = socket;
|
||||
//(this.mSocket as any).maxPayload = 16;
|
||||
this.mState = SignalingConnectionState.Connecting;
|
||||
this.mConInfo = this.mSocket.upgradeReq.connection.remoteAddress + ":" + this.mSocket.upgradeReq.connection.remotePort;
|
||||
WebsocketNetworkServer.log("[" + this.mConInfo + "]" + " connected ");
|
||||
socket.on('message', function (message, flags) {
|
||||
_this.onMessage(message, flags);
|
||||
});
|
||||
socket.on('error', function (error) {
|
||||
console.error(error);
|
||||
});
|
||||
socket.on('close', function (code, message) { _this.onClose(code, message); });
|
||||
socket.on('pong', function (data, flags) {
|
||||
WebsocketNetworkServer.log("[" + _this.mConInfo + "]" + "INC: pong ");
|
||||
});
|
||||
this.mState = SignalingConnectionState.Connected;
|
||||
this.mPingInterval = setInterval(function () { _this.doPing(); }, 30000);
|
||||
}
|
||||
SignalingPeer.prototype.doPing = function () {
|
||||
if (this.mState == SignalingConnectionState.Connected && this.mSocket.readyState == ws.OPEN) {
|
||||
this.mSocket.ping();
|
||||
WebsocketNetworkServer.log("[" + this.mConInfo + "]" + "OUT: ping");
|
||||
}
|
||||
};
|
||||
SignalingPeer.prototype.evtToString = function (evt) {
|
||||
var output = "[";
|
||||
output += "NetEventType: (";
|
||||
output += inet.NetEventType[evt.Type];
|
||||
output += "), id: (";
|
||||
output += evt.ConnectionId.id;
|
||||
if (evt.Info != null) {
|
||||
output += "), Data: (";
|
||||
output += evt.Info;
|
||||
}
|
||||
else if (evt.MessageData != null) {
|
||||
var chars = new Uint16Array(evt.MessageData.buffer, evt.MessageData.byteOffset, evt.MessageData.byteLength / 2);
|
||||
output += "), Data: (";
|
||||
var binaryString = "";
|
||||
for (var i = 0; i < chars.length; i++) {
|
||||
binaryString += String.fromCharCode(chars[i]);
|
||||
}
|
||||
output += binaryString;
|
||||
}
|
||||
output += ")]";
|
||||
return output;
|
||||
};
|
||||
SignalingPeer.prototype.onMessage = function (message, flags) {
|
||||
try {
|
||||
//unlike browsers ws will give a Uint8Array
|
||||
var evt = inet.NetworkEvent.fromByteArray(message);
|
||||
WebsocketNetworkServer.log("[" + this.mConInfo + "]" + "INC: " + this.evtToString(evt));
|
||||
this.handleIncomingEvent(evt);
|
||||
}
|
||||
catch (err) {
|
||||
WebsocketNetworkServer.log("[" + this.mConInfo + "]" + "Invalid message received: " + message + " \n Error: " + err);
|
||||
}
|
||||
};
|
||||
SignalingPeer.prototype.sendToClient = function (evt) {
|
||||
//this method is also called during cleanup after a disconnect
|
||||
//check first if we are still connected
|
||||
//bugfix: apprently 2 sockets can be closed at exactly the same time without
|
||||
//onclosed being called immediately -> socket has to be checked if open
|
||||
if (this.mState == SignalingConnectionState.Connected
|
||||
&& this.mSocket.readyState == this.mSocket.OPEN) {
|
||||
WebsocketNetworkServer.log("[" + this.mConInfo + "]" + "OUT: " + this.evtToString(evt));
|
||||
var msg = inet.NetworkEvent.toByteArray(evt);
|
||||
this.mSocket.send(msg);
|
||||
}
|
||||
};
|
||||
SignalingPeer.prototype.onClose = function (code, error) {
|
||||
this.mState = SignalingConnectionState.Disconnecting;
|
||||
this.Cleanup();
|
||||
};
|
||||
//so far only used if the socket was disconnected
|
||||
SignalingPeer.prototype.Cleanup = function () {
|
||||
if (this.mPingInterval != null) {
|
||||
clearInterval(this.mPingInterval);
|
||||
}
|
||||
this.mConnectionPool.removeConnection(this);
|
||||
WebsocketNetworkServer.log("[" + this.mConInfo + "]" + "disconnected "
|
||||
+ " " + this.mConnectionPool.count()
|
||||
+ " connections left.");
|
||||
//disconnect all connections
|
||||
var test = this.mConnections; //workaround for not having a proper dictionary yet...
|
||||
for (var v in this.mConnections) {
|
||||
if (this.mConnections.hasOwnProperty(v))
|
||||
this.disconnect(new inet.ConnectionId(+v));
|
||||
}
|
||||
//make sure the server address is freed
|
||||
if (this.mServerAddress != null) {
|
||||
this.stopServer();
|
||||
}
|
||||
this.mState = SignalingConnectionState.Disconnected;
|
||||
};
|
||||
SignalingPeer.prototype.handleIncomingEvent = function (evt) {
|
||||
//update internal state based on the event
|
||||
if (evt.Type == inet.NetEventType.NewConnection) {
|
||||
//client wants to connect to another client
|
||||
var address = evt.Info;
|
||||
//the id this connection should be addressed with
|
||||
var newConnectionId = evt.ConnectionId;
|
||||
this.connect(address, newConnectionId);
|
||||
}
|
||||
else if (evt.Type == inet.NetEventType.ConnectionFailed) {
|
||||
}
|
||||
else if (evt.Type == inet.NetEventType.Disconnected) {
|
||||
//peer tries to disconnect from another peer
|
||||
var otherPeerId = evt.ConnectionId;
|
||||
this.disconnect(otherPeerId);
|
||||
}
|
||||
else if (evt.Type == inet.NetEventType.ServerInitialized) {
|
||||
this.startServer(evt.Info);
|
||||
}
|
||||
else if (evt.Type == inet.NetEventType.ServerInitFailed) {
|
||||
}
|
||||
else if (evt.Type == inet.NetEventType.ServerClosed) {
|
||||
//stop server request
|
||||
this.stopServer();
|
||||
}
|
||||
else if (evt.Type == inet.NetEventType.ReliableMessageReceived) {
|
||||
this.sendData(evt.ConnectionId, evt.MessageData, true);
|
||||
}
|
||||
else if (evt.Type == inet.NetEventType.UnreliableMessageReceived) {
|
||||
this.sendData(evt.ConnectionId, evt.MessageData, false);
|
||||
}
|
||||
};
|
||||
SignalingPeer.prototype.internalAddIncomingPeer = function (peer) {
|
||||
//another peer connected to this (while allowing incoming connections)
|
||||
//store the reference
|
||||
var id = this.nextConnectionId();
|
||||
this.mConnections[id.id] = peer;
|
||||
//event to this (the other peer gets the event via addOutgoing
|
||||
this.sendToClient(new inet.NetworkEvent(inet.NetEventType.NewConnection, id, null));
|
||||
};
|
||||
SignalingPeer.prototype.internalAddOutgoingPeer = function (peer, id) {
|
||||
//this peer successfully connected to another peer. id was generated on the
|
||||
//client side
|
||||
this.mConnections[id.id] = peer;
|
||||
//event to this (the other peer gets the event via addOutgoing
|
||||
this.sendToClient(new inet.NetworkEvent(inet.NetEventType.NewConnection, id, null));
|
||||
};
|
||||
SignalingPeer.prototype.internalRemovePeer = function (id) {
|
||||
delete this.mConnections[id.id];
|
||||
this.sendToClient(new inet.NetworkEvent(inet.NetEventType.Disconnected, id, null));
|
||||
};
|
||||
//test this. might cause problems
|
||||
//the number is converted to string trough java script but we need get back the number
|
||||
//for creating the connection id
|
||||
SignalingPeer.prototype.findPeerConnectionId = function (otherPeer) {
|
||||
for (var peer in this.mConnections) {
|
||||
if (this.mConnections[peer] === otherPeer) {
|
||||
return new inet.ConnectionId(+peer);
|
||||
}
|
||||
}
|
||||
};
|
||||
SignalingPeer.prototype.nextConnectionId = function () {
|
||||
var result = this.mNextIncomingConnectionId;
|
||||
this.mNextIncomingConnectionId = new inet.ConnectionId(this.mNextIncomingConnectionId.id + 1);
|
||||
return result;
|
||||
};
|
||||
//public methods (not really needed but can be used for testing or server side deubgging)
|
||||
//this peer initializes a connection to a certain address. The connection id is set by the client
|
||||
//to allow tracking of the connection attempt
|
||||
SignalingPeer.prototype.connect = function (address, newConnectionId) {
|
||||
var serverConnections = this.mConnectionPool.getServerConnection(address);
|
||||
//
|
||||
if (serverConnections != null && serverConnections.length == 1) {
|
||||
//inform the server connection about the new peer
|
||||
//events will be send by these methods
|
||||
//shared addresses -> connect to everyone listening
|
||||
serverConnections[0].internalAddIncomingPeer(this);
|
||||
this.internalAddOutgoingPeer(serverConnections[0], newConnectionId);
|
||||
}
|
||||
else {
|
||||
//if address is not in use or it is in multi join mode -> connection fails
|
||||
this.sendToClient(new inet.NetworkEvent(inet.NetEventType.ConnectionFailed, newConnectionId, null));
|
||||
}
|
||||
};
|
||||
//join connection happens if another user joins a multi address. it will connect to every address
|
||||
//listening to that room
|
||||
SignalingPeer.prototype.connectJoin = function (address) {
|
||||
var serverConnections = this.mConnectionPool.getServerConnection(address);
|
||||
//in join mode every connection is incoming as everyone listens together
|
||||
if (serverConnections != null) {
|
||||
for (var _i = 0, serverConnections_1 = serverConnections; _i < serverConnections_1.length; _i++) {
|
||||
var v = serverConnections_1[_i];
|
||||
if (v != this) {
|
||||
v.internalAddIncomingPeer(this);
|
||||
this.internalAddIncomingPeer(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
SignalingPeer.prototype.disconnect = function (connectionId) {
|
||||
var otherPeer = this.mConnections[connectionId.id];
|
||||
if (otherPeer != null) {
|
||||
var idOfOther = otherPeer.findPeerConnectionId(this);
|
||||
//find the connection id the other peer uses to talk to this one
|
||||
this.internalRemovePeer(connectionId);
|
||||
otherPeer.internalRemovePeer(idOfOther);
|
||||
}
|
||||
else {
|
||||
}
|
||||
};
|
||||
SignalingPeer.prototype.startServer = function (address) {
|
||||
//what to do if it is already a server?
|
||||
if (this.mServerAddress != null)
|
||||
this.stopServer();
|
||||
if (this.mConnectionPool.isAddressAvailable(address)) {
|
||||
this.mServerAddress = address;
|
||||
this.mConnectionPool.addServer(this, address);
|
||||
this.sendToClient(new inet.NetworkEvent(inet.NetEventType.ServerInitialized, inet.ConnectionId.INVALID, address));
|
||||
if (this.mConnectionPool.hasAddressSharing()) {
|
||||
//address sharing is active. connect to every endpoint already listening on this address
|
||||
this.connectJoin(address);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.sendToClient(new inet.NetworkEvent(inet.NetEventType.ServerInitFailed, inet.ConnectionId.INVALID, address));
|
||||
}
|
||||
};
|
||||
SignalingPeer.prototype.stopServer = function () {
|
||||
if (this.mServerAddress != null) {
|
||||
this.mConnectionPool.removeServer(this, this.mServerAddress);
|
||||
this.sendToClient(new inet.NetworkEvent(inet.NetEventType.ServerClosed, inet.ConnectionId.INVALID, null));
|
||||
this.mServerAddress = null;
|
||||
}
|
||||
//do nothing if it wasnt a server
|
||||
};
|
||||
//delivers the message to the local peer
|
||||
SignalingPeer.prototype.forwardMessage = function (senderPeer, msg, reliable) {
|
||||
var id = this.findPeerConnectionId(senderPeer);
|
||||
if (reliable)
|
||||
this.sendToClient(new inet.NetworkEvent(inet.NetEventType.ReliableMessageReceived, id, msg));
|
||||
else
|
||||
this.sendToClient(new inet.NetworkEvent(inet.NetEventType.UnreliableMessageReceived, id, msg));
|
||||
};
|
||||
SignalingPeer.prototype.sendData = function (id, msg, reliable) {
|
||||
var peer = this.mConnections[id.id];
|
||||
if (peer != null)
|
||||
peer.forwardMessage(this, msg, reliable);
|
||||
};
|
||||
return SignalingPeer;
|
||||
}());
|
||||
//# sourceMappingURL=WebsocketNetworkServer.js.map
|
14
config.json
Normal file
14
config.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"httpConfig":
|
||||
{
|
||||
"port": 12776
|
||||
},
|
||||
"maxPayload":
|
||||
16777216,
|
||||
"apps": [
|
||||
{
|
||||
"name": "Main",
|
||||
"path": "/"
|
||||
}
|
||||
]
|
||||
}
|
21
config_template.json
Normal file
21
config_template.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"httpConfig":
|
||||
{
|
||||
"port": VALUE
|
||||
},
|
||||
"httpsConfig":
|
||||
{
|
||||
"port": VALUE,
|
||||
"ssl_key_file": FILE.key,
|
||||
"ssl_cert_file": FILE.crt
|
||||
},
|
||||
"maxPayload":
|
||||
SIZE_IN_BYTES,
|
||||
"apps": [
|
||||
{
|
||||
"name": NAME,
|
||||
"path": PATH,
|
||||
"address_sharing": BOOL
|
||||
}
|
||||
]
|
||||
}
|
19
package.json
Normal file
19
package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "websocket-signaling",
|
||||
"version": "0.97.4",
|
||||
"description": "A websocket signaling server for use with WebRTC Network and Video Chat assets for Unity.",
|
||||
"main": "server.js",
|
||||
"author": {
|
||||
"name": "Christoph Kutza",
|
||||
"email": "contact@because-why-not.com"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.13.4",
|
||||
"requirejs": "^2.2.0",
|
||||
"typescript": "1.8.9",
|
||||
"ws": "^1.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"tsc": "tsc"
|
||||
}
|
||||
}
|
36
server.js
Normal file
36
server.js
Normal file
@ -0,0 +1,36 @@
|
||||
"use strict";
|
||||
var config = require("./config.json");
|
||||
var http = require('http');
|
||||
var https = require('https');
|
||||
var ws = require('ws');
|
||||
var fs = require('fs');
|
||||
var wns = require('./WebsocketNetworkServer');
|
||||
//setup
|
||||
var httpServer = null;
|
||||
var httpsServer = null;
|
||||
if (config.httpConfig) {
|
||||
httpServer = http.createServer();
|
||||
httpServer.listen(config.httpConfig.port, function () { console.log('Listening on ' + httpServer.address().port); });
|
||||
}
|
||||
if (config.httpsConfig) {
|
||||
httpsServer = https.createServer({
|
||||
key: fs.readFileSync(config.httpsConfig.ssl_key_file),
|
||||
cert: fs.readFileSync(config.httpsConfig.ssl_cert_file)
|
||||
});
|
||||
httpsServer.listen(config.httpsConfig.port, function () { console.log('Listening on ' + httpsServer.address().port); });
|
||||
}
|
||||
var websocketSignalingServer = new wns.WebsocketNetworkServer();
|
||||
for (var _i = 0, _a = config.apps; _i < _a.length; _i++) {
|
||||
var app = _a[_i];
|
||||
if (httpServer) {
|
||||
//perMessageDeflate: false needs to be set to false turning off the compression. if set to true
|
||||
//the websocket library crashes if big messages are received (eg.128mb) no matter which payload is set!!!
|
||||
var webSocket = new ws.Server({ server: httpServer, path: app.path, maxPayload: config.maxPayload, perMessageDeflate: false });
|
||||
websocketSignalingServer.addSocketServer(webSocket, app);
|
||||
}
|
||||
if (httpsServer) {
|
||||
var webSocketSecure = new ws.Server({ server: httpsServer, path: app.path, maxPayload: config.maxPayload, perMessageDeflate: false }); //problem in the typings -> setup to only accept http not https so cast to any to turn off typechecks
|
||||
websocketSignalingServer.addSocketServer(webSocketSecure, app);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=server.js.map
|
Reference in New Issue
Block a user