tfw you realize js doesn't support 8byte ints

wow
This commit is contained in:
Malloc of Kuzkycyziklistan 2017-05-23 16:00:51 -05:00
parent e80c40a775
commit 085378b7dc
8 changed files with 230 additions and 66 deletions

View file

@ -1,17 +1,61 @@
class Connection { class Connection {
private static Sock: WebSocket = null; private static sock: WebSocket = null;
private static _IsOpen: boolean = false; private static _isOpen: boolean = false;
public static get IsOpen(): boolean { private static onOpenFunc: () => void = null;
return Connection._IsOpen; private static onCloseFunc: () => void = null;
public static get isOpen(): boolean {
return Connection._isOpen;
} }
public static Initialize(): void { public static open(onOpen: () => void = null): void {
Connection.Sock if(Connection._isOpen)
return;
// FLAG replace hard coded url with one loaded from a config file
Connection.sock = new WebSocket("ws://localhost:6770");
Connection.onOpenFunc = onOpen;
Connection.sock.onopen = Connection.onOpen;
Connection.sock.onmessage = Connection.onMessage;
Connection.sock.onclose = Connection.onClose;
} }
public static Close(): void { private static onOpen(event: any): void {
Connection._isOpen = true;
if(Connection.onOpenFunc)
Connection.onOpenFunc();
} }
private static private static onMessage(event: any): void {
var msg = Packet.fromBytes(event.data);
console.log(msg);
switch(msg.id) {
case kPacketId.KeyExchange:
break;
case kPacketId.LoginAttempt:
break;
case kPacketId.RegistrationAttempt:
break;
}
}
private static onClose(event: any): void {
Connection._isOpen = false;
if(Connection.onCloseFunc)
Connection.onCloseFunc();
}
public static close(onClose: () => void = null): void {
if(!Connection._isOpen)
return;
Connection.onCloseFunc = onClose;
Connection.sock.close();
}
} }

View file

@ -28,13 +28,13 @@ String.prototype.replaceAll = function(needle: any, replace: any, ignoreCase: bo
} }
return retval; return retval;
} }
}; }
String.prototype.contains = function(needle: string, ignoreCase: boolean = false): boolean { String.prototype.contains = function(needle: string, ignoreCase: boolean = false): boolean {
return ignoreCase return ignoreCase
? this.toLowerCase().indexOf(needle.toLowerCase()) != -1 ? this.toLowerCase().indexOf(needle.toLowerCase()) != -1
: this.indexOf(needle) != -1; : this.indexOf(needle) != -1;
}; }
String.prototype.stripCharacters = function(chars: string) { String.prototype.stripCharacters = function(chars: string) {
var copy = this; var copy = this;
@ -42,18 +42,18 @@ String.prototype.stripCharacters = function(chars: string) {
copy = copy.replaceAll(chars.split(""), ""); copy = copy.replaceAll(chars.split(""), "");
return copy; return copy;
}; }
String.prototype.hasUnicodeCharacters = function() { String.prototype.hasUnicodeCharacters = function() {
for(var i = 0; i < this.length; i++) { for(var i = 0; i < this.length; i++) {
if(this.charCodeAt(i) > 127) return true; if(this.charCodeAt(i) > 127) return true;
} }
return false; return false;
}; }
String.prototype.byteLength = function() { String.prototype.byteLength = function() {
return utf8.encode(this).length; return utf8.encode(this).length;
}; }
String.prototype.toByteArray = function() { String.prototype.toByteArray = function() {
var str = utf8.encode(this); var str = utf8.encode(this);
@ -62,7 +62,7 @@ String.prototype.toByteArray = function() {
ret[i] = str.charCodeAt(i); ret[i] = str.charCodeAt(i);
return ret; return ret;
}; }
// ** DATE EXTENSIONS ** \\ // ** DATE EXTENSIONS ** \\
@ -78,27 +78,34 @@ interface Date {
} }
Date.unixNow = function() { Date.unixNow = function() {
return (new Date()).toUnixTime(); return (new Date).toUnixTime();
}; }
Date.prototype.toUnixTime = function() { Date.prototype.toUnixTime = function() {
return Math.floor(this.getTime()/1000); return Math.floor(this.getTime()/1000);
}; }
/*Date.prototype.ToDateTimeString = function() { /*Date.prototype.ToDateTimeString = function() {
return this.toDateString() +" @ "+ this.getHours().zeroPad() +":"+ this.getMinutes().zeroPad() +":"+ this.getSeconds().zeroPad(); return this.toDateString() +" @ "+ this.getHours().zeroPad() +":"+ this.getMinutes().zeroPad() +":"+ this.getSeconds().zeroPad();
}; }
Date.prototype.ToTimeString = function() { Date.prototype.ToTimeString = function() {
return this.getHours().zeroPad() +":"+ this.getMinutes().zeroPad() +":"+ this.getSeconds().zeroPad(); return this.getHours().zeroPad() +":"+ this.getMinutes().zeroPad() +":"+ this.getSeconds().zeroPad();
};*/ }*/
// ** NUMBER EXTENSIONS ** \\ // ** NUMBER EXTENSIONS ** \\
interface Number { interface Number {
zeroPad(mag?: number): string; zeroPad(mag?: number): string;
packBytes(bytes: number): Uint8Array;
packInt16(): Uint8Array;
packUint16(): Uint8Array;
packInt32(): Uint8Array;
packUint32(): Uint8Array;
packFloat(): Uint8Array;
packDouble(): Uint8Array;
} }
Number.prototype.zeroPad = function(mag: number = 1): string { Number.prototype.zeroPad = function(mag: number = 1): string {
@ -106,28 +113,86 @@ Number.prototype.zeroPad = function(mag: number = 1): string {
for(; this < Math.pow(10, mag) && mag != 0; --mag) for(; this < Math.pow(10, mag) && mag != 0; --mag)
ret = "0" + ret; ret = "0" + ret;
return ret; return ret;
}; }
Number.prototype.packBytes = function(bytes: number) { Number.prototype.packInt16 = function(): Uint8Array {
var ret = new Uint8Array(bytes); var buffer = new ArrayBuffer(2);
for(var i = 0; i < bytes; i++) new DataView(buffer).setInt16(0, this, false);
ret[i] = (this & (0xFF << 8 * (bytes - 1 - i))) >>> 8 * (bytes - 1 - i); return new Uint8Array(buffer);
return ret; }
};
Number.prototype.packUint16 = function(): Uint8Array {
var buffer = new ArrayBuffer(2);
new DataView(buffer).setUint16(0, this, false);
return new Uint8Array(buffer);
}
Number.prototype.packInt32 = function(): Uint8Array {
var buffer = new ArrayBuffer(4);
new DataView(buffer).setInt32(0, this, false);
return new Uint8Array(buffer);
}
Number.prototype.packUint32 = function(): Uint8Array {
var buffer = new ArrayBuffer(4);
new DataView(buffer).setUint32(0, this, false);
return new Uint8Array(buffer);
}
Number.prototype.packFloat = function(): Uint8Array {
var buffer = new ArrayBuffer(4);
new DataView(buffer).setFloat32(0, this, false);
return new Uint8Array(buffer);
}
Number.prototype.packDouble = function(): Uint8Array {
var buffer = new ArrayBuffer(8);
new DataView(buffer).setFloat64(0, this, false);
return new Uint8Array(buffer);
}
// ** UINT8ARRAY EXTENSIONS ** \\ // ** UINT8ARRAY EXTENSIONS ** \\
interface Uint8Array { interface Uint8Array {
unpackBytes(): number; unpackInt16(offset?: number): number;
unpackUint16(offset?: number): number;
unpackInt32(offset?: number): number;
unpackUint32(offset?: number): number;
unpackFloat(offset?: number): number;
unpackDouble(offset?: number): number;
} }
Uint8Array.prototype.unpackBytes = function(): number { Uint8Array.prototype.unpackInt16 = function(offset: number = 0): number {
var ret = 0; var buffer = this.buffer;
for(var i = 0; i < this.length; i++) return new DataView(buffer).getInt16(offset, false);
ret = ret | ((this[i] & 0xFF) << 8*(this.length - 1 - i)); }
return ret;
}; Uint8Array.prototype.unpackUint16 = function(offset: number = 0): number {
var buffer = this.buffer;
return new DataView(buffer).getUint16(offset, false);
}
Uint8Array.prototype.unpackInt32 = function(offset: number = 0): number {
var buffer = this.buffer;
return new DataView(buffer).getInt32(offset, false);
}
Uint8Array.prototype.unpackUint32 = function(offset: number = 0): number {
var buffer = this.buffer;
return new DataView(buffer).getUint32(offset, false);
}
Uint8Array.prototype.unpackFloat = function(offset: number = 0): number {
var buffer = this.buffer;
return new DataView(buffer).getFloat32(offset, false);
}
Uint8Array.prototype.unpackDouble = function(offset: number = 0): number {
var buffer = this.buffer;
return new DataView(buffer).getFloat64(offset, false);
}
Uint8Array.prototype.toString = function(): string { Uint8Array.prototype.toString = function(): string {
var chunkSize = 4096; var chunkSize = 4096;
@ -137,4 +202,4 @@ Uint8Array.prototype.toString = function(): string {
raw += String.fromCharCode.apply(null, this.subarray(chunkSize*i, chunkSize*i + chunkSize)); raw += String.fromCharCode.apply(null, this.subarray(chunkSize*i, chunkSize*i + chunkSize));
} }
return utf8.decode(raw); return utf8.decode(raw);
}; }

View file

@ -2,18 +2,17 @@ class FileCache {
private static dbHandle: IDBDatabase = null; private static dbHandle: IDBDatabase = null;
public static initCache(success: ()=>void, error: (error: string)=>void): void { public static initCache(success: ()=>void, error: (error: string)=>void): void {
var request = window.indexedDB.open("fileCache", 2); var request = window.indexedDB.open("fileCache", 3);
request.onupgradeneeded = (event: any) => { request.onupgradeneeded = (event: any) => {
var db: IDBDatabase = event.target.result; var db: IDBDatabase = event.target.result;
if(db.objectStoreNames.contains("hashes"))
db.deleteObjectStore("hashes");
if(!db.objectStoreNames.contains("files")) var stores = db.objectStoreNames;
db.createObjectStore("files", {keyPath: "Name", autoIncrement: false}); for(var i in stores)
db.deleteObjectStore(stores[i]);
if(!db.objectStoreNames.contains("metadata")) db.createObjectStore("files", {keyPath: "name", autoIncrement: false});
db.createObjectStore("metadata", {keyPath: "Name", autoIncrement: false}); db.createObjectStore("metadata", {keyPath: "name", autoIncrement: false});
}; };
request.onerror = (event: any) => { request.onerror = (event: any) => {
@ -36,7 +35,7 @@ class FileCache {
}; };
request.onerror = (event: any) => { request.onerror = (event: any) => {
error(event); error("Could not get metadata for file "+ fileName);
}; };
} }
@ -46,27 +45,29 @@ class FileCache {
store.put(meta); store.put(meta);
} }
public static getFile(fileName: string, success: (data: Uint8Array)=>void, error: (error: string)=>void): void { public static getFile(fileName: string, success: (name: string, data: Uint8Array)=>void, error: (error: string)=>void): void {
var query = FileCache.dbHandle.transaction("files"); var query = FileCache.dbHandle.transaction("files");
var store = query.objectStore("files"); var store = query.objectStore("files");
var request = store.get(fileName); var request = store.get(fileName);
request.onsuccess = () => { request.onsuccess = () => {
success(request.result); success(request.result.name, request.result.data);
}; };
request.onerror = (event: any) => { request.onerror = (event: any) => {
error(event); error("Could not get contents for file "+ fileName);
}; };
} }
public static setFile(fileName: string, data: Uint8Array) { public static setFile(fileName: string, data: Uint8Array) {
var query = FileCache.dbHandle.transaction("files", "readwrite"); var query = FileCache.dbHandle.transaction("files", "readwrite");
var store = query.objectStore("files"); var store = query.objectStore("files");
store.put(data, fileName); store.put({name: fileName, data: data});
} }
} }
class FileMeta { class FileMeta {
name: string;
type: string;
hash: string;
} }

View file

@ -10,11 +10,6 @@ class Packet {
return this._id; return this._id;
} }
private _isLegal: boolean = true;
public get isLegal(): boolean {
return this._isLegal;
}
private _regions: Uint8Array[] = []; private _regions: Uint8Array[] = [];
public get regions(): Uint8Array[] { public get regions(): Uint8Array[] {
return this._regions; return this._regions;
@ -28,9 +23,50 @@ class Packet {
public getRegionString(region: number): string { public getRegionString(region: number): string {
return this.getRegion(region).toString(); return this.getRegion(region).toString();
} }
public addRegion(data: Uint8Array): void {
this._regions.push(data);
}
public Packet(id: kPacketId, regions: any[]) {
this._id = id;
regions.forEach(region => {
if(typeof region == "string")
this._regions.push((<string>region).toByteArray());
else if(region instanceof Uint8Array)
this._regions.push(region);
});
}
public static fromBytes(bytes: Uint8Array): Packet {
var packet = new Packet;
packet._id = bytes[0];
var regionCount = bytes[1];
var regionLengths = [];
var ptr = 2;
for(var i = 0; i < regionCount; ++i) {
if(bytes[ptr] < 0xFE)
regionLengths.push(bytes[ptr]);
else if(bytes[ptr] == 0xFE) {
regionLengths.push(bytes.unpackUint16(ptr + 1));
ptr += 2;
} else {
regionLengths.push(bytes.unpackUint32(ptr + 1));
ptr += 4;
}
++ptr;
}
for(var i = 0; i < regionCount; ++i) {
packet.regions.push(bytes.subarray(ptr, ptr + regionLengths[i]));
ptr += regionLengths[i];
}
return packet;
}
public getBytes(): Uint8Array { public getBytes(): Uint8Array {
var headerSize = 1, bodySize = 0; var headerSize = 2, bodySize = 0;
this._regions.forEach(region => { this._regions.forEach(region => {
bodySize += region.byteLength; bodySize += region.byteLength;
@ -42,10 +78,26 @@ class Packet {
}); });
var buffer = new Uint8Array(headerSize + bodySize); var buffer = new Uint8Array(headerSize + bodySize);
var headerPtr = 1, bodyPtr = 0; var headerPtr = 2, bodyPtr = headerSize;
buffer[0] = this._id % 256; buffer[0] = this._id % 256;
buffer[1] = this._regions.length;
this._regions.forEach(region => { this._regions.forEach(region => {
var regionLength = region.byteLength;
if(regionLength < 0xFE)
buffer[headerPtr] = regionLength;
else if(regionLength >= 0xFE && regionLength <= 0xFFFF) {
buffer[headerPtr] = 0xFE;
buffer.set(regionLength.packUint16(), headerPtr + 1);
headerPtr += 2;
} else {
buffer[headerPtr] = 0xFF;
buffer.set(regionLength.packUint32(), headerPtr + 1);
headerPtr += 4;
}
++headerPtr;
buffer.set(region, bodyPtr);
bodyPtr += regionLength;
}); });
return buffer; return buffer;

View file

@ -0,0 +1,3 @@
class RenderContext {
}

View file

@ -27,7 +27,7 @@ The message body immediately follows the header with no separator, and consists
All numbers, unless otherwise specified, are the string representation of a base 10 number. Common exceptions are listed below: All numbers, unless otherwise specified, are the string representation of a base 10 number. Common exceptions are listed below:
* User IDs: 8 bytes, integer, unsigned * User IDs: Hex string, 8 bytes unsigned
* Co-ordinates: 8 bytes, double-precision float * Co-ordinates: 8 bytes, double-precision float
* Big Int: Hex string, variable size * Big Int: Hex string, variable size
@ -255,7 +255,7 @@ A packet ID may have a specific "direction" of communication, in that an endpoin
Because epoch time is not standardized across systems, an intermediate layer of date/time transmission must be used between the client and server so as to handle time dependent interactions. Therefore, a "sockstamp" will be used in place of the context-dependent implementations of epoch time. Because epoch time is not standardized across systems, an intermediate layer of date/time transmission must be used between the client and server so as to handle time dependent interactions. Therefore, a "sockstamp" will be used in place of the context-dependent implementations of epoch time.
A sockstamp is a sequence of six bytes that represent a fully qualified date and time on the Gregorian calendar. For the best use of space without obfuscating the data too much, the year's lower four bits and the four bits signifying the month are shared in the same byte, but no other components are joined. A sockstamp is a sequence of six bytes that represent a fully qualified UTC date and time on the Gregorian calendar. For the best use of space without obfuscating the data too much, the year's lower four bits and the four bits signifying the month are shared in the same byte, but no other components are joined.
The 12 bits signifying the year are an unsigned quanitity, and indicate the number of years since 0 AD; any date prior to the year of Christ's birth cannot be represented in this format, but this should never be necessary. The effective range of years that can be expressed by this format is 1 AD to 4095 AD. Because the year 0 AD is not a legal year in the Gregorian calendar, this value should never be zero. The 12 bits signifying the year are an unsigned quanitity, and indicate the number of years since 0 AD; any date prior to the year of Christ's birth cannot be represented in this format, but this should never be necessary. The effective range of years that can be expressed by this format is 1 AD to 4095 AD. Because the year 0 AD is not a legal year in the Gregorian calendar, this value should never be zero.

View file

@ -41,10 +41,7 @@ namespace Square {
} }
public static byte[] NetworkToHostOrder(this byte[] bytes) { public static byte[] NetworkToHostOrder(this byte[] bytes) {
if(BitConverter.IsLittleEndian) return bytes.HostToNetworkOrder();
return bytes.Reverse().ToArray();
else
return bytes;
} }
public static Single UnpackFloat(this byte[] bytes) public static Single UnpackFloat(this byte[] bytes)

View file

@ -91,15 +91,17 @@ namespace Server {
return null; return null;
var header = new List<byte>(); var header = new List<byte>();
header.Add((byte)Id);
header.Add((byte)RegionCount);
IEnumerable<byte> body = new byte[0]; IEnumerable<byte> body = new byte[0];
foreach(var region in Regions) { foreach(var region in Regions) {
if(region.Length < 254) if(region.Length < 0xFE)
header.Add((byte)region.Length); header.Add((byte)region.Length);
else if(region.Length <= 0xFFFF) { else if(region.Length <= 0xFFFF) {
header.Add(254); header.Add(0xFE);
header.AddRange(((UInt16)region.Length).Pack()); header.AddRange(((UInt16)region.Length).Pack());
} else { } else {
header.Add(255); header.Add(0xFF);
header.AddRange(region.Length.Pack()); header.AddRange(region.Length.Pack());
} }