tfw you realize js doesn't support 8byte ints
wow
This commit is contained in:
parent
e80c40a775
commit
085378b7dc
8 changed files with 230 additions and 66 deletions
|
@ -1,17 +1,61 @@
|
|||
class Connection {
|
||||
private static Sock: WebSocket = null;
|
||||
private static _IsOpen: boolean = false;
|
||||
public static get IsOpen(): boolean {
|
||||
return Connection._IsOpen;
|
||||
private static sock: WebSocket = null;
|
||||
private static _isOpen: boolean = false;
|
||||
private static onOpenFunc: () => void = null;
|
||||
private static onCloseFunc: () => void = null;
|
||||
public static get isOpen(): boolean {
|
||||
return Connection._isOpen;
|
||||
}
|
||||
|
||||
public static Initialize(): void {
|
||||
Connection.Sock
|
||||
public static open(onOpen: () => void = null): void {
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -28,13 +28,13 @@ String.prototype.replaceAll = function(needle: any, replace: any, ignoreCase: bo
|
|||
}
|
||||
return retval;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
String.prototype.contains = function(needle: string, ignoreCase: boolean = false): boolean {
|
||||
return ignoreCase
|
||||
? this.toLowerCase().indexOf(needle.toLowerCase()) != -1
|
||||
: this.indexOf(needle) != -1;
|
||||
};
|
||||
}
|
||||
|
||||
String.prototype.stripCharacters = function(chars: string) {
|
||||
var copy = this;
|
||||
|
@ -42,18 +42,18 @@ String.prototype.stripCharacters = function(chars: string) {
|
|||
copy = copy.replaceAll(chars.split(""), "");
|
||||
|
||||
return copy;
|
||||
};
|
||||
}
|
||||
|
||||
String.prototype.hasUnicodeCharacters = function() {
|
||||
for(var i = 0; i < this.length; i++) {
|
||||
if(this.charCodeAt(i) > 127) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
String.prototype.byteLength = function() {
|
||||
return utf8.encode(this).length;
|
||||
};
|
||||
}
|
||||
|
||||
String.prototype.toByteArray = function() {
|
||||
var str = utf8.encode(this);
|
||||
|
@ -62,7 +62,7 @@ String.prototype.toByteArray = function() {
|
|||
ret[i] = str.charCodeAt(i);
|
||||
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// ** DATE EXTENSIONS ** \\
|
||||
|
@ -78,27 +78,34 @@ interface Date {
|
|||
}
|
||||
|
||||
Date.unixNow = function() {
|
||||
return (new Date()).toUnixTime();
|
||||
};
|
||||
return (new Date).toUnixTime();
|
||||
}
|
||||
|
||||
Date.prototype.toUnixTime = function() {
|
||||
return Math.floor(this.getTime()/1000);
|
||||
};
|
||||
}
|
||||
|
||||
/*Date.prototype.ToDateTimeString = function() {
|
||||
return this.toDateString() +" @ "+ this.getHours().zeroPad() +":"+ this.getMinutes().zeroPad() +":"+ this.getSeconds().zeroPad();
|
||||
};
|
||||
}
|
||||
|
||||
Date.prototype.ToTimeString = function() {
|
||||
return this.getHours().zeroPad() +":"+ this.getMinutes().zeroPad() +":"+ this.getSeconds().zeroPad();
|
||||
};*/
|
||||
}*/
|
||||
|
||||
|
||||
// ** NUMBER EXTENSIONS ** \\
|
||||
|
||||
interface Number {
|
||||
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 {
|
||||
|
@ -106,28 +113,86 @@ Number.prototype.zeroPad = function(mag: number = 1): string {
|
|||
for(; this < Math.pow(10, mag) && mag != 0; --mag)
|
||||
ret = "0" + ret;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
Number.prototype.packBytes = function(bytes: number) {
|
||||
var ret = new Uint8Array(bytes);
|
||||
for(var i = 0; i < bytes; i++)
|
||||
ret[i] = (this & (0xFF << 8 * (bytes - 1 - i))) >>> 8 * (bytes - 1 - i);
|
||||
return ret;
|
||||
};
|
||||
Number.prototype.packInt16 = function(): Uint8Array {
|
||||
var buffer = new ArrayBuffer(2);
|
||||
new DataView(buffer).setInt16(0, this, false);
|
||||
return new Uint8Array(buffer);
|
||||
}
|
||||
|
||||
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 ** \\
|
||||
|
||||
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 {
|
||||
var ret = 0;
|
||||
for(var i = 0; i < this.length; i++)
|
||||
ret = ret | ((this[i] & 0xFF) << 8*(this.length - 1 - i));
|
||||
return ret;
|
||||
};
|
||||
Uint8Array.prototype.unpackInt16 = function(offset: number = 0): number {
|
||||
var buffer = this.buffer;
|
||||
return new DataView(buffer).getInt16(offset, false);
|
||||
}
|
||||
|
||||
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 {
|
||||
var chunkSize = 4096;
|
||||
|
@ -137,4 +202,4 @@ Uint8Array.prototype.toString = function(): string {
|
|||
raw += String.fromCharCode.apply(null, this.subarray(chunkSize*i, chunkSize*i + chunkSize));
|
||||
}
|
||||
return utf8.decode(raw);
|
||||
};
|
||||
}
|
|
@ -2,18 +2,17 @@ class FileCache {
|
|||
private static dbHandle: IDBDatabase = null;
|
||||
|
||||
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) => {
|
||||
var db: IDBDatabase = event.target.result;
|
||||
if(db.objectStoreNames.contains("hashes"))
|
||||
db.deleteObjectStore("hashes");
|
||||
|
||||
if(!db.objectStoreNames.contains("files"))
|
||||
db.createObjectStore("files", {keyPath: "Name", autoIncrement: false});
|
||||
var stores = db.objectStoreNames;
|
||||
for(var i in stores)
|
||||
db.deleteObjectStore(stores[i]);
|
||||
|
||||
if(!db.objectStoreNames.contains("metadata"))
|
||||
db.createObjectStore("metadata", {keyPath: "Name", autoIncrement: false});
|
||||
db.createObjectStore("files", {keyPath: "name", autoIncrement: false});
|
||||
db.createObjectStore("metadata", {keyPath: "name", autoIncrement: false});
|
||||
};
|
||||
|
||||
request.onerror = (event: any) => {
|
||||
|
@ -36,7 +35,7 @@ class FileCache {
|
|||
};
|
||||
|
||||
request.onerror = (event: any) => {
|
||||
error(event);
|
||||
error("Could not get metadata for file "+ fileName);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -46,27 +45,29 @@ class FileCache {
|
|||
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 store = query.objectStore("files");
|
||||
var request = store.get(fileName);
|
||||
|
||||
request.onsuccess = () => {
|
||||
success(request.result);
|
||||
success(request.result.name, request.result.data);
|
||||
};
|
||||
|
||||
request.onerror = (event: any) => {
|
||||
error(event);
|
||||
error("Could not get contents for file "+ fileName);
|
||||
};
|
||||
}
|
||||
|
||||
public static setFile(fileName: string, data: Uint8Array) {
|
||||
var query = FileCache.dbHandle.transaction("files", "readwrite");
|
||||
var store = query.objectStore("files");
|
||||
store.put(data, fileName);
|
||||
store.put({name: fileName, data: data});
|
||||
}
|
||||
}
|
||||
|
||||
class FileMeta {
|
||||
|
||||
name: string;
|
||||
type: string;
|
||||
hash: string;
|
||||
}
|
|
@ -10,11 +10,6 @@ class Packet {
|
|||
return this._id;
|
||||
}
|
||||
|
||||
private _isLegal: boolean = true;
|
||||
public get isLegal(): boolean {
|
||||
return this._isLegal;
|
||||
}
|
||||
|
||||
private _regions: Uint8Array[] = [];
|
||||
public get regions(): Uint8Array[] {
|
||||
return this._regions;
|
||||
|
@ -28,9 +23,50 @@ class Packet {
|
|||
public getRegionString(region: number): string {
|
||||
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 {
|
||||
var headerSize = 1, bodySize = 0;
|
||||
var headerSize = 2, bodySize = 0;
|
||||
this._regions.forEach(region => {
|
||||
bodySize += region.byteLength;
|
||||
|
||||
|
@ -42,10 +78,26 @@ class Packet {
|
|||
});
|
||||
|
||||
var buffer = new Uint8Array(headerSize + bodySize);
|
||||
var headerPtr = 1, bodyPtr = 0;
|
||||
var headerPtr = 2, bodyPtr = headerSize;
|
||||
buffer[0] = this._id % 256;
|
||||
buffer[1] = this._regions.length;
|
||||
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;
|
||||
|
|
3
client/src/RenderContext.ts
Normal file
3
client/src/RenderContext.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
class RenderContext {
|
||||
|
||||
}
|
|
@ -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:
|
||||
|
||||
* User IDs: 8 bytes, integer, unsigned
|
||||
* User IDs: Hex string, 8 bytes unsigned
|
||||
* Co-ordinates: 8 bytes, double-precision float
|
||||
* 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.
|
||||
|
||||
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.
|
||||
|
||||
|
|
|
@ -41,10 +41,7 @@ namespace Square {
|
|||
}
|
||||
|
||||
public static byte[] NetworkToHostOrder(this byte[] bytes) {
|
||||
if(BitConverter.IsLittleEndian)
|
||||
return bytes.Reverse().ToArray();
|
||||
else
|
||||
return bytes;
|
||||
return bytes.HostToNetworkOrder();
|
||||
}
|
||||
|
||||
public static Single UnpackFloat(this byte[] bytes)
|
||||
|
|
|
@ -91,15 +91,17 @@ namespace Server {
|
|||
return null;
|
||||
|
||||
var header = new List<byte>();
|
||||
header.Add((byte)Id);
|
||||
header.Add((byte)RegionCount);
|
||||
IEnumerable<byte> body = new byte[0];
|
||||
foreach(var region in Regions) {
|
||||
if(region.Length < 254)
|
||||
if(region.Length < 0xFE)
|
||||
header.Add((byte)region.Length);
|
||||
else if(region.Length <= 0xFFFF) {
|
||||
header.Add(254);
|
||||
header.Add(0xFE);
|
||||
header.AddRange(((UInt16)region.Length).Pack());
|
||||
} else {
|
||||
header.Add(255);
|
||||
header.Add(0xFF);
|
||||
header.AddRange(region.Length.Pack());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue