238 lines
6.8 KiB
JavaScript
238 lines
6.8 KiB
JavaScript
|
var Yaht = function() {
|
||
|
if(Yaht.socket !== undefined)
|
||
|
return;
|
||
|
|
||
|
var loading = document.getElementById('loading');
|
||
|
loading.textContent += '.';
|
||
|
|
||
|
Yaht.socket = new WebSocket(Yaht.ENDPOINT, 'fwahtzee');
|
||
|
Yaht.socket.binaryType = 'arraybuffer';
|
||
|
Yaht.socket.onopen = Yaht.onOpen;
|
||
|
Yaht.socket.onclose = Yaht.onClose;
|
||
|
Yaht.socket.onerror = Yaht.onError;
|
||
|
Yaht.socket.onmessage = Yaht.onMessage;
|
||
|
};
|
||
|
Yaht.ENDPOINT = 'ws://127.0.0.1:30566';
|
||
|
Yaht.socket = undefined;
|
||
|
Yaht.pingInterval = undefined;
|
||
|
Yaht.OP_PING = 0x01;
|
||
|
Yaht.OP_ERROR = 0x02;
|
||
|
Yaht.OP_RANDOM = 0x03;
|
||
|
Yaht.OP_CHAT_MSG = 0x21;
|
||
|
Yaht.OP_CHAT_DEL = 0x22;
|
||
|
Yaht.onOpen = function(ev) {
|
||
|
console.log('[+] Connected!');
|
||
|
|
||
|
if(Yaht.pingInterval)
|
||
|
clearInterval(Yaht.pingInterval);
|
||
|
Yaht.pingInterval = setInterval(Yaht.sendPing, 4.9 * 60 * 1000);
|
||
|
Yaht.sendPing();
|
||
|
};
|
||
|
Yaht.onClose = function(ev) {
|
||
|
if(Yaht.pingInterval)
|
||
|
clearInterval(Yaht.pingInterval);
|
||
|
console.log('[-] Disconnected: %s %s %s', ev.code, ev.reason, ev.wasClean.toString());
|
||
|
};
|
||
|
Yaht.onError = function(ev) {
|
||
|
console.error('[!]', ev);
|
||
|
};
|
||
|
Yaht.onMessage = function(ev) {
|
||
|
if(!ev.data)
|
||
|
return;
|
||
|
var data = new Uint8Array(ev.data),
|
||
|
packet = Yaht.decodePacket(data);
|
||
|
if(packet !== null)
|
||
|
Yaht.handlePacket(packet);
|
||
|
};
|
||
|
Yaht.send = function(data) {
|
||
|
Yaht.socket.send(data);
|
||
|
};
|
||
|
Yaht.writeInt = function(arr, val, width, off) {
|
||
|
val = parseInt(val);
|
||
|
off += width - 1;
|
||
|
for(var i = 0; i < width; ++i)
|
||
|
arr[off - i] = val / Math.pow(2, 8 * i);
|
||
|
};
|
||
|
Yaht.writeI16 = function(arr, val, off) {
|
||
|
Yaht.writeInt(arr, val, 2, off);
|
||
|
};
|
||
|
Yaht.writeI32 = function(arr, val, off) {
|
||
|
Yaht.writeInt(arr, val, 4, off);
|
||
|
};
|
||
|
Yaht.writeI48 = function(arr, val, off) {
|
||
|
Yaht.writeInt(arr, val, 6, off);
|
||
|
};
|
||
|
Yaht.readInt = function(arr, width, off) {
|
||
|
var val = 0;
|
||
|
off += width;
|
||
|
for(var i = width; i > 0; --i)
|
||
|
val |= arr[off - i] * Math.pow(2, 8 * (i - 1));
|
||
|
return val;
|
||
|
};
|
||
|
Yaht.readI16 = function(arr, off) {
|
||
|
return Yaht.readInt(arr, 2, off);
|
||
|
};
|
||
|
Yaht.readI32 = function(arr, off) {
|
||
|
return Yaht.readInt(arr, 4, off);
|
||
|
};
|
||
|
Yaht.readI48 = function(arr, off) {
|
||
|
return Yaht.readInt(arr, 6, off);
|
||
|
};
|
||
|
Yaht.sendPing = function() {
|
||
|
var now = Date.now() / 1000,
|
||
|
pack = new Uint8Array(5);
|
||
|
pack[0] = Yaht.OP_PING;
|
||
|
Yaht.writeI32(pack, now, 1);
|
||
|
Yaht.send(pack);
|
||
|
};
|
||
|
Yaht.randomIdCounter = 0;
|
||
|
Yaht.randomCallbacks = {};
|
||
|
Yaht.randomValues = {};
|
||
|
Yaht.getNextRandomId = function() { return Yaht.randomIdCounter = (Yaht.randomIdCounter + 1) % 0x7F; };
|
||
|
Yaht.registerRandomCallback = function(identifier, callback) { Yaht.randomCallbacks['_' + identifier] = callback; };
|
||
|
Yaht.doRandomCallback = function(identifier, buffer) {
|
||
|
identifier = '_' + identifier.toString();
|
||
|
Yaht.randomValues[identifier] = buffer;
|
||
|
if(Yaht.randomCallbacks[identifier]) {
|
||
|
Yaht.randomCallbacks[identifier](buffer);
|
||
|
delete Yaht.randomCallbacks[identifier];
|
||
|
}
|
||
|
};
|
||
|
Yaht.getRandomValue = function(identifier) {
|
||
|
return Yaht.randomValues['_' + identifier] || [];
|
||
|
};
|
||
|
Yaht.requestRandom = function(count, callback) {
|
||
|
var pack = new Uint8Array(3);
|
||
|
pack[0] = Yaht.OP_RANDOM;
|
||
|
pack[1] = Yaht.getNextRandomId();
|
||
|
pack[2] = parseInt(count);
|
||
|
if(callback)
|
||
|
Yaht.registerRandomCallback(pack[1], callback);
|
||
|
Yaht.socket.send(pack);
|
||
|
return pack[1];
|
||
|
};
|
||
|
Yaht.toUTF16 = function(str) {
|
||
|
str = str || '';
|
||
|
var length = str.length,
|
||
|
buffer = new ArrayBuffer(length * 2),
|
||
|
u16arr = new Uint16Array(buffer);
|
||
|
|
||
|
for(var i = 0; i < length; ++i)
|
||
|
u16arr[i] = str.charCodeAt(i);
|
||
|
|
||
|
return new Uint8Array(buffer);
|
||
|
};
|
||
|
Yaht.fromUTF16 = function(arr, offset, length) {
|
||
|
offset = typeof offset === 'undefined' ? 0 : offset;
|
||
|
length = typeof length === 'undefined' ? (arr.length - offset) : length;
|
||
|
var str = '';
|
||
|
for(var i = 0; i < length; i = i + 2)
|
||
|
str += String.fromCharCode((arr[i + offset + 1] << 8) || arr[i + offset]);
|
||
|
return str;
|
||
|
};
|
||
|
Yaht.sendChatMessage = function(roomId, text) {
|
||
|
if(text.length < 1 || text.length > 500)
|
||
|
return false;
|
||
|
|
||
|
var textBytes = Yaht.toUTF16(text),
|
||
|
pack = new Uint8Array(9 + textBytes.length);
|
||
|
|
||
|
pack[0] = Yaht.OP_CHAT_MSG;
|
||
|
Yaht.writeI48(pack, roomId, 1);
|
||
|
Yaht.writeI16(pack, textBytes.length, 7);
|
||
|
|
||
|
for(var i = 0; i < textBytes.length; ++i)
|
||
|
pack[9 + i] = textBytes[i];
|
||
|
|
||
|
Yaht.socket.send(pack);
|
||
|
return true;
|
||
|
};
|
||
|
Yaht.consoleBeep = function(freq, dur, finish) {
|
||
|
var ctx = new AudioContext,
|
||
|
osc = ctx.createOscillator();
|
||
|
osc.type = 'sine';
|
||
|
osc.frequency.value = freq;
|
||
|
osc.connect(ctx.destination);
|
||
|
osc.start();
|
||
|
setTimeout(function() {
|
||
|
osc.stop();
|
||
|
if(finish)
|
||
|
finish();
|
||
|
}, dur);
|
||
|
};
|
||
|
Yaht.removeElement = function(elem) {
|
||
|
elem.parentNode.removeChild(elem);
|
||
|
};
|
||
|
Yaht.decodePacket = function(data) {
|
||
|
var packet = null;
|
||
|
|
||
|
switch(data[0]) {
|
||
|
default:
|
||
|
console.debug(data);
|
||
|
break;
|
||
|
|
||
|
case Yaht.OP_PING:
|
||
|
packet = {
|
||
|
type: 'pong',
|
||
|
took: Yaht.readI32(data, 1),
|
||
|
};
|
||
|
break;
|
||
|
case Yaht.OP_ERROR:
|
||
|
packet = {
|
||
|
type: 'error',
|
||
|
msg: Yaht.fromUTF16(data, 3, Yaht.readI16(data, 1)),
|
||
|
};
|
||
|
break;
|
||
|
case Yaht.OP_RANDOM:
|
||
|
packet = {
|
||
|
type: 'random',
|
||
|
identifier: data[1],
|
||
|
values: data.slice(3, data[2] + 3),
|
||
|
};
|
||
|
break;
|
||
|
|
||
|
case Yaht.OP_CHAT_MSG:
|
||
|
packet = {
|
||
|
type: 'chat:msg',
|
||
|
room: Yaht.readI48(data, 1),
|
||
|
user: Yaht.readI48(data, 7),
|
||
|
msg: Yaht.readI48(data, 13),
|
||
|
text: Yaht.fromUTF16(data, 21, Yaht.readI16(data, 19)),
|
||
|
};
|
||
|
break;
|
||
|
case Yaht.OP_CHAT_DEL:
|
||
|
packet = {
|
||
|
type: 'chat:del',
|
||
|
msg: Yaht.readI48(data, 1),
|
||
|
};
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return packet;
|
||
|
};
|
||
|
Yaht.handlePacket = function(packet) {
|
||
|
switch(packet.type || null) {
|
||
|
default:
|
||
|
console.debug('[>] ' + (packet.type || ''), packet);
|
||
|
break;
|
||
|
|
||
|
case 'pong':
|
||
|
console.log('[>] ping received, took %dms.', packet.took);
|
||
|
break;
|
||
|
case 'error':
|
||
|
console.error('[!]', packet.msg);
|
||
|
alert(packet.msg);
|
||
|
break;
|
||
|
case 'random':
|
||
|
Yaht.doRandomCallback(packet.identifier, packet.values);
|
||
|
break;
|
||
|
|
||
|
case 'chat:msg':
|
||
|
console.log('[&] %d #%d <%d> %s', packet.msg, packet.room, packet.user, packet.text);
|
||
|
break;
|
||
|
case 'chat:del':
|
||
|
console.log('[&] %d deleted.', packet.msg);
|
||
|
break;
|
||
|
}
|
||
|
};
|