-
-
Save endel/504705002901e2e4218fdfa5b20fff49 to your computer and use it in GitHub Desktop.
`colyseus-unity3d/Assets/Plugins/WebSocket/`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* TextEncoder polyfill for IE | |
*/ | |
if (typeof TextEncoder === "undefined") { | |
TextEncoder=function TextEncoder(){}; | |
TextEncoder.prototype.encode = function encode(str) { | |
"use strict"; | |
var Len = str.length, resPos = -1; | |
// The Uint8Array's length must be at least 3x the length of the string because an invalid UTF-16 | |
// takes up the equivelent space of 3 UTF-8 characters to encode it properly. However, Array's | |
// have an auto expanding length and 1.5x should be just the right balance for most uses. | |
var resArr = typeof Uint8Array === "undefined" ? new Array(Len * 1.5) : new Uint8Array(Len * 3); | |
for (var point=0, nextcode=0, i = 0; i !== Len; ) { | |
point = str.charCodeAt(i), i += 1; | |
if (point >= 0xD800 && point <= 0xDBFF) { | |
if (i === Len) { | |
resArr[resPos += 1] = 0xef/*0b11101111*/; resArr[resPos += 1] = 0xbf/*0b10111111*/; | |
resArr[resPos += 1] = 0xbd/*0b10111101*/; break; | |
} | |
// https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae | |
nextcode = str.charCodeAt(i); | |
if (nextcode >= 0xDC00 && nextcode <= 0xDFFF) { | |
point = (point - 0xD800) * 0x400 + nextcode - 0xDC00 + 0x10000; | |
i += 1; | |
if (point > 0xffff) { | |
resArr[resPos += 1] = (0x1e/*0b11110*/<<3) | (point>>>18); | |
resArr[resPos += 1] = (0x2/*0b10*/<<6) | ((point>>>12)&0x3f/*0b00111111*/); | |
resArr[resPos += 1] = (0x2/*0b10*/<<6) | ((point>>>6)&0x3f/*0b00111111*/); | |
resArr[resPos += 1] = (0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/); | |
continue; | |
} | |
} else { | |
resArr[resPos += 1] = 0xef/*0b11101111*/; resArr[resPos += 1] = 0xbf/*0b10111111*/; | |
resArr[resPos += 1] = 0xbd/*0b10111101*/; continue; | |
} | |
} | |
if (point <= 0x007f) { | |
resArr[resPos += 1] = (0x0/*0b0*/<<7) | point; | |
} else if (point <= 0x07ff) { | |
resArr[resPos += 1] = (0x6/*0b110*/<<5) | (point>>>6); | |
resArr[resPos += 1] = (0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/); | |
} else { | |
resArr[resPos += 1] = (0xe/*0b1110*/<<4) | (point>>>12); | |
resArr[resPos += 1] = (0x2/*0b10*/<<6) | ((point>>>6)&0x3f/*0b00111111*/); | |
resArr[resPos += 1] = (0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/); | |
} | |
} | |
if (typeof Uint8Array !== "undefined") return resArr.subarray(0, resPos + 1); | |
// else // IE 6-9 | |
resArr.length = resPos + 1; // trim off extra weight | |
return resArr; | |
}; | |
TextEncoder.prototype.toString = function(){return "[object TextEncoder]"}; | |
try { // Object.defineProperty only works on DOM prototypes in IE8 | |
Object.defineProperty(TextEncoder.prototype,"encoding",{ | |
get:function(){if(TextEncoder.prototype.isPrototypeOf(this)) return"utf-8"; | |
else throw TypeError("Illegal invocation");} | |
}); | |
} catch(e) { /*IE6-8 fallback*/ TextEncoder.prototype.encoding = "utf-8"; } | |
if(typeof Symbol!=="undefined")TextEncoder.prototype[Symbol.toStringTag]="TextEncoder"; | |
} | |
var LibraryWebSocket = { | |
$textEncoder: new TextEncoder(), | |
$webSocketState: { | |
/* | |
* Map of instances | |
* | |
* Instance structure: | |
* { | |
* url: string, | |
* ws: WebSocket | |
* } | |
*/ | |
instances: {}, | |
/* Last instance ID */ | |
lastId: 0, | |
/* Event listeners */ | |
onOpen: null, | |
onMesssage: null, | |
onError: null, | |
onClose: null, | |
/* Debug mode */ | |
debug: false | |
}, | |
/** | |
* Set onOpen callback | |
* | |
* @param callback Reference to C# static function | |
*/ | |
WebSocketSetOnOpen: function(callback) { | |
webSocketState.onOpen = callback; | |
}, | |
/** | |
* Set onMessage callback | |
* | |
* @param callback Reference to C# static function | |
*/ | |
WebSocketSetOnMessage: function(callback) { | |
webSocketState.onMessage = callback; | |
}, | |
/** | |
* Set onError callback | |
* | |
* @param callback Reference to C# static function | |
*/ | |
WebSocketSetOnError: function(callback) { | |
webSocketState.onError = callback; | |
}, | |
/** | |
* Set onClose callback | |
* | |
* @param callback Reference to C# static function | |
*/ | |
WebSocketSetOnClose: function(callback) { | |
webSocketState.onClose = callback; | |
}, | |
/** | |
* Allocate new WebSocket instance struct | |
* | |
* @param url Server URL | |
*/ | |
WebSocketAllocate: function(url) { | |
var urlStr = Pointer_stringify(url); | |
var id = webSocketState.lastId++; | |
webSocketState.instances[id] = { | |
url: urlStr, | |
ws: null | |
}; | |
return id; | |
}, | |
/** | |
* Remove reference to WebSocket instance | |
* | |
* If socket is not closed function will close it but onClose event will not be emitted because | |
* this function should be invoked by C# WebSocket destructor. | |
* | |
* @param instanceId Instance ID | |
*/ | |
WebSocketFree: function(instanceId) { | |
var instance = webSocketState.instances[instanceId]; | |
if (!instance) return 0; | |
// Close if not closed | |
if (instance.ws && instance.ws.readyState < 2) | |
instance.ws.close(); | |
// Remove reference | |
delete webSocketState.instances[instanceId]; | |
return 0; | |
}, | |
/** | |
* Connect WebSocket to the server | |
* | |
* @param instanceId Instance ID | |
*/ | |
WebSocketConnect: function(instanceId) { | |
var instance = webSocketState.instances[instanceId]; | |
if (!instance) return -1; | |
if (instance.ws !== null) | |
return -2; | |
instance.ws = new WebSocket(instance.url); | |
instance.ws.binaryType = 'arraybuffer'; | |
instance.ws.onopen = function() { | |
if (webSocketState.debug) | |
console.log("[JSLIB WebSocket] Connected."); | |
if (webSocketState.onOpen) | |
Runtime.dynCall('vi', webSocketState.onOpen, [ instanceId ]); | |
}; | |
instance.ws.onmessage = function(ev) { | |
if (webSocketState.debug) | |
console.log("[JSLIB WebSocket] Received message:", ev.data); | |
if (webSocketState.onMessage === null) | |
return; | |
if (ev.data instanceof ArrayBuffer) { | |
var dataBuffer = new Uint8Array(ev.data); | |
var buffer = _malloc(dataBuffer.length); | |
HEAPU8.set(dataBuffer, buffer); | |
try { | |
Runtime.dynCall('viii', webSocketState.onMessage, [ instanceId, buffer, dataBuffer.length ]); | |
} finally { | |
_free(buffer); | |
} | |
} else { | |
var dataBuffer = textEncoder.encode(ev.data); | |
var buffer = _malloc(dataBuffer.length); | |
HEAPU8.set(dataBuffer, buffer); | |
try { | |
Runtime.dynCall('viii', webSocketState.onMessage, [ instanceId, buffer, dataBuffer.length ]); | |
} finally { | |
_free(buffer); | |
} | |
} | |
}; | |
instance.ws.onerror = function(ev) { | |
if (webSocketState.debug) | |
console.log("[JSLIB WebSocket] Error occured."); | |
if (webSocketState.onError) { | |
var msg = "WebSocket error."; | |
var msgBytes = lengthBytesUTF8(msg); | |
var msgBuffer = _malloc(msgBytes + 1); | |
stringToUTF8(msg, msgBuffer, msgBytes); | |
try { | |
Runtime.dynCall('vii', webSocketState.onError, [ instanceId, msgBuffer ]); | |
} finally { | |
_free(msgBuffer); | |
} | |
} | |
}; | |
instance.ws.onclose = function(ev) { | |
if (webSocketState.debug) | |
console.log("[JSLIB WebSocket] Closed."); | |
if (webSocketState.onClose) | |
Runtime.dynCall('vii', webSocketState.onClose, [ instanceId, ev.code ]); | |
delete instance.ws; | |
}; | |
return 0; | |
}, | |
/** | |
* Close WebSocket connection | |
* | |
* @param instanceId Instance ID | |
* @param code Close status code | |
* @param reasonPtr Pointer to reason string | |
*/ | |
WebSocketClose: function(instanceId, code, reasonPtr) { | |
var instance = webSocketState.instances[instanceId]; | |
if (!instance) return -1; | |
if (!instance.ws) | |
return -3; | |
if (instance.ws.readyState === 2) | |
return -4; | |
if (instance.ws.readyState === 3) | |
return -5; | |
var reason = ( reasonPtr ? Pointer_stringify(reasonPtr) : undefined ); | |
try { | |
instance.ws.close(code, reason); | |
} catch(err) { | |
return -7; | |
} | |
return 0; | |
}, | |
/** | |
* Send message over WebSocket | |
* | |
* @param instanceId Instance ID | |
* @param bufferPtr Pointer to the message buffer | |
* @param length Length of the message in the buffer | |
*/ | |
WebSocketSend: function(instanceId, bufferPtr, length) { | |
var instance = webSocketState.instances[instanceId]; | |
if (!instance) return -1; | |
if (!instance.ws) | |
return -3; | |
if (instance.ws.readyState !== 1) | |
return -6; | |
instance.ws.send(HEAPU8.buffer.slice(bufferPtr, bufferPtr + length)); | |
return 0; | |
}, | |
/** | |
* Send text message over WebSocket | |
* | |
* @param instanceId Instance ID | |
* @param bufferPtr Pointer to the message buffer | |
* @param length Length of the message in the buffer | |
*/ | |
WebSocketSendText: function(instanceId, message) { | |
var instance = webSocketState.instances[instanceId]; | |
if (!instance) return -1; | |
if (!instance.ws) | |
return -3; | |
if (instance.ws.readyState !== 1) | |
return -6; | |
instance.ws.send(Pointer_stringify(message)); | |
return 0; | |
}, | |
/** | |
* Return WebSocket readyState | |
* | |
* @param instanceId Instance ID | |
*/ | |
WebSocketGetState: function(instanceId) { | |
var instance = webSocketState.instances[instanceId]; | |
if (!instance) return -1; | |
if (instance.ws) | |
return instance.ws.readyState; | |
else | |
return 3; | |
} | |
}; | |
autoAddDeps(LibraryWebSocket, '$webSocketState'); | |
autoAddDeps(LibraryWebSocket, '$textEncoder'); | |
mergeInto(LibraryManager.library, LibraryWebSocket); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment