From 02acadacc1e47f8a30cf6651e609ba879cbfd9e0 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sat, 4 Jan 2025 13:17:41 -0700 Subject: [PATCH 01/26] Added WebSocketUtil (and intenet folder) Planning on adding more stuff to it. --- libs.xml | 3 + project.xml | 2 + .../funkin/backend/internet/WebSocketUtil.hx | 106 ++++++++++++++++++ source/funkin/backend/system/macros/Macros.hx | 3 +- 4 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 source/funkin/backend/internet/WebSocketUtil.hx diff --git a/libs.xml b/libs.xml index f932da098..a443dfa3a 100644 --- a/libs.xml +++ b/libs.xml @@ -4,6 +4,9 @@ + + + diff --git a/project.xml b/project.xml index 6f01bc102..51a5ab17f 100644 --- a/project.xml +++ b/project.xml @@ -170,6 +170,8 @@ + + diff --git a/source/funkin/backend/internet/WebSocketUtil.hx b/source/funkin/backend/internet/WebSocketUtil.hx new file mode 100644 index 000000000..4368f93b2 --- /dev/null +++ b/source/funkin/backend/internet/WebSocketUtil.hx @@ -0,0 +1,106 @@ +package funkin.backend.internet; + +import haxe.io.Bytes; +import hx.ws.Log; +import hx.ws.WebSocket; + +import funkin.backend.system.Logs; + +class WebSocketUtil implements IFlxDestroyable { + + static var loggingEnabled:Bool = false; + static function toggleLogging(?INFO:Bool = true, ?DEBUG:Bool = true, ?DATA:Bool = true) { + loggingEnabled = !loggingEnabled; + if (!loggingEnabled) return Log.mask = 0; + + var _mask = Log.mask; + if (INFO) _mask = _mask | Log.INFO; + if (DEBUG) _mask = _mask | Log.DEBUG; + if (DATA) _mask = _mask | Log.DATA; + Log.mask = _mask; + + return Log.mask; + } + + public var onOpen:WebSocket->Void = (webSocket)->{}; + public var onMessage:Void->Void = ()->{}; + public var onClose:Void->Void = ()->{}; + public var onError:Dynamic->Void = (error)->{}; + + private var url:String; + private var webSocket:WebSocket; + public function new(url:String, ?onOpen:WebSocket->Void, ?immediateOpen:Bool = false) { + this.onOpen = (onOpen == null) ? this.onOpen : onOpen; + + this.url = url; + this.webSocket = new WebSocket(this.url, false); + + // TODO: make trace print colors with `Logs.hx` + this.webSocket.onopen = function() { + try { + this.onOpen(webSocket); + } catch(e) { + trace('Error: ${e}'); + } + }; + + this.webSocket.onmessage = function(message) { + try { + this.onMessage(); + } catch(e) { + trace('Error: ${e}'); + } + }; + + this.webSocket.onclose = function() { + try { + this.onClose(); + } catch(e) { + trace('Error: ${e}'); + } + }; + + this.webSocket.onerror = function(error) { + trace('Websocket error: ${error}'); + try { + this.onError(error); + } catch(e) { + trace('Error: ${e}'); + } + }; + + if (immediateOpen) this.open(); + } + + public function open() { + trace('[Connection Status] Connecting to ${this.url}'); + try { + this.webSocket.open(); + } catch(e) { + trace("Failed to open websocket: " + e); + this.onError(e); + } + } + + public function close() { + trace('[Connection Status] Closing connection to ${this.url}'); + try { + this.webSocket.close(); + } catch(e) { + trace("Failed to close websocket: " + e); + } + } + + public function send(data:String) { + try { + this.webSocket.send(data); + } catch(e) { + trace("Failed to send data to websocket: " + e); + } + } + + public function destroy() { + this.webSocket.close(); + } +} + diff --git a/source/funkin/backend/system/macros/Macros.hx b/source/funkin/backend/system/macros/Macros.hx index 9183e4a4e..dc56c359c 100644 --- a/source/funkin/backend/system/macros/Macros.hx +++ b/source/funkin/backend/system/macros/Macros.hx @@ -21,8 +21,9 @@ class Macros { // OTHER LIBRARIES & STUFF #if THREE_D_SUPPORT "away3d", "flx3d", #end #if VIDEO_CUTSCENES "hxvlc.flixel", "hxvlc.openfl", #end + // BASE HAXE - "DateTools", "EReg", "Lambda", "StringBuf", "haxe.crypto", "haxe.display", "haxe.exceptions", "haxe.extern", "scripting" + "DateTools", "EReg", "Lambda", "StringBuf", "haxe.crypto", "haxe.display", "haxe.exceptions", "haxe.extern", "scripting", ]) Compiler.include(inc); From b8d313272c0a3ce5a0961b0ed8fd202571a66f6c Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:28:36 -0700 Subject: [PATCH 02/26] WebSocketUtil document --- .../funkin/backend/internet/WebSocketUtil.hx | 66 ++++++++++++++++--- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/source/funkin/backend/internet/WebSocketUtil.hx b/source/funkin/backend/internet/WebSocketUtil.hx index 4368f93b2..b92fc8cd0 100644 --- a/source/funkin/backend/internet/WebSocketUtil.hx +++ b/source/funkin/backend/internet/WebSocketUtil.hx @@ -1,14 +1,20 @@ package funkin.backend.internet; import haxe.io.Bytes; -import hx.ws.Log; -import hx.ws.WebSocket; + +import hx.ws.*; import funkin.backend.system.Logs; class WebSocketUtil implements IFlxDestroyable { - + /** + * Used for the `toggleLogging` function. this is more of a data handler for the function. + **/ static var loggingEnabled:Bool = false; + + /** + * Call this function to toggle debugging for the WebSocket. + **/ static function toggleLogging(?INFO:Bool = true, ?DEBUG:Bool = true, ?DATA:Bool = true) { loggingEnabled = !loggingEnabled; if (!loggingEnabled) return Log.mask = 0; @@ -22,13 +28,36 @@ class WebSocketUtil implements IFlxDestroyable { return Log.mask; } + /** + * Function calls after the WebSocket has been opened. + * @param webSocket Returns the instance of the WebSocket. + **/ public var onOpen:WebSocket->Void = (webSocket)->{}; - public var onMessage:Void->Void = ()->{}; + + /** + * Whenever the WebSocket receives a message sent from the server. + * @param message Returns the message sent from the server. + **/ + public var onMessage:Dynamic->Void = (message)->{}; + + /** + * Runs whenever the WebSocket closes. + **/ public var onClose:Void->Void = ()->{}; + + /** + * Runs whenever the WebSocket encounters an error. + **/ public var onError:Dynamic->Void = (error)->{}; - private var url:String; - private var webSocket:WebSocket; + @:dox(hide) private var url:String; + @:dox(hide) private var webSocket:WebSocket; + + /** + * @param url The URL of the WebSocket. Usually `ws://localhost:port`. + * @param onOpen sets the `onOpen` function directly to the class. + * @param immediateOpen If true, the WebSocket will open immediately. Hence why `onOpen` is a function in the parameters. + **/ public function new(url:String, ?onOpen:WebSocket->Void, ?immediateOpen:Bool = false) { this.onOpen = (onOpen == null) ? this.onOpen : onOpen; @@ -45,8 +74,15 @@ class WebSocketUtil implements IFlxDestroyable { }; this.webSocket.onmessage = function(message) { + var data:Dynamic = null; try { - this.onMessage(); + switch(message) { + case StrMessage(str): + data = str; + case BytesMessage(bytes): + data = bytes; + } + this.onMessage(data); } catch(e) { trace('Error: ${e}'); } @@ -70,8 +106,12 @@ class WebSocketUtil implements IFlxDestroyable { }; if (immediateOpen) this.open(); + return this; } + /** + * Opens the WebSocket. + **/ public function open() { trace('[Connection Status] Connecting to ${this.url}'); try { @@ -82,6 +122,9 @@ class WebSocketUtil implements IFlxDestroyable { } } + /** + * Closes the WebSocket. + **/ public function close() { trace('[Connection Status] Closing connection to ${this.url}'); try { @@ -91,14 +134,21 @@ class WebSocketUtil implements IFlxDestroyable { } } - public function send(data:String) { + /** + * Sends data to the server + **/ + public function send(data) { try { this.webSocket.send(data); } catch(e) { trace("Failed to send data to websocket: " + e); + this.onError(e); } } + /** + * Closes the WebSocket and destroys the class instance. + **/ public function destroy() { this.webSocket.close(); } From 4a8af03d15e36300f4eb8e8cc96077310a5def62 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sat, 4 Jan 2025 15:42:57 -0700 Subject: [PATCH 03/26] Added the WebSockets and made `internet` lib Hi you Auto Action Download users! I can make your text red pretty cool right? Human... I remember your Commit History --- .../funkin/backend/internet/WebSocketUtil.hx | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/source/funkin/backend/internet/WebSocketUtil.hx b/source/funkin/backend/internet/WebSocketUtil.hx index b92fc8cd0..bcb054c5d 100644 --- a/source/funkin/backend/internet/WebSocketUtil.hx +++ b/source/funkin/backend/internet/WebSocketUtil.hx @@ -1,7 +1,5 @@ package funkin.backend.internet; -import haxe.io.Bytes; - import hx.ws.*; import funkin.backend.system.Logs; @@ -48,7 +46,17 @@ class WebSocketUtil implements IFlxDestroyable { /** * Runs whenever the WebSocket encounters an error. **/ - public var onError:Dynamic->Void = (error)->{}; + public var onError(default, set):Dynamic->Void = (error)->{}; + private function set_onError(_errorFunc):Dynamic->Void { + var func = (error)->{ + Logs.traceColored([ + Logs.logText("[WebSocket Error] ", RED), + Logs.logText('${error}'), + ], ERROR); + if (_errorFunc != null) _errorFunc(error); + }; + return this.onError = func; + } @:dox(hide) private var url:String; @:dox(hide) private var webSocket:WebSocket; @@ -60,16 +68,16 @@ class WebSocketUtil implements IFlxDestroyable { **/ public function new(url:String, ?onOpen:WebSocket->Void, ?immediateOpen:Bool = false) { this.onOpen = (onOpen == null) ? this.onOpen : onOpen; + this.onError = this.onError; this.url = url; this.webSocket = new WebSocket(this.url, false); - // TODO: make trace print colors with `Logs.hx` this.webSocket.onopen = function() { try { this.onOpen(webSocket); - } catch(e) { - trace('Error: ${e}'); + } catch(error) { + this.onError(error); } }; @@ -84,7 +92,7 @@ class WebSocketUtil implements IFlxDestroyable { } this.onMessage(data); } catch(e) { - trace('Error: ${e}'); + this.onError(e); } }; @@ -92,32 +100,26 @@ class WebSocketUtil implements IFlxDestroyable { try { this.onClose(); } catch(e) { - trace('Error: ${e}'); + this.onError(e); } }; - this.webSocket.onerror = function(error) { - trace('Websocket error: ${error}'); - try { - this.onError(error); - } catch(e) { - trace('Error: ${e}'); - } - }; + this.webSocket.onerror = this.onError; if (immediateOpen) this.open(); - return this; } /** * Opens the WebSocket. **/ public function open() { - trace('[Connection Status] Connecting to ${this.url}'); + Logs.traceColored([ + Logs.logText("[WebSocket Connection] ", BLUE), + Logs.logText('Connecting to ${this.url}'), + ], INFO); try { this.webSocket.open(); } catch(e) { - trace("Failed to open websocket: " + e); this.onError(e); } } @@ -126,11 +128,14 @@ class WebSocketUtil implements IFlxDestroyable { * Closes the WebSocket. **/ public function close() { - trace('[Connection Status] Closing connection to ${this.url}'); + Logs.traceColored([ + Logs.logText("[WebSocket Connection] ", BLUE), + Logs.logText('Closing connection to ${this.url}'), + ], INFO); try { this.webSocket.close(); } catch(e) { - trace("Failed to close websocket: " + e); + this.onError(e); } } @@ -141,7 +146,6 @@ class WebSocketUtil implements IFlxDestroyable { try { this.webSocket.send(data); } catch(e) { - trace("Failed to send data to websocket: " + e); this.onError(e); } } From ddf1a48c2d04e98cb7746ad428dd9d4ed0666c37 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sat, 4 Jan 2025 15:49:25 -0700 Subject: [PATCH 04/26] added "hx.ws" as an import (wont work neo but ok) --- source/funkin/backend/system/macros/Macros.hx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/funkin/backend/system/macros/Macros.hx b/source/funkin/backend/system/macros/Macros.hx index dc56c359c..73beaeb3d 100644 --- a/source/funkin/backend/system/macros/Macros.hx +++ b/source/funkin/backend/system/macros/Macros.hx @@ -22,6 +22,9 @@ class Macros { #if THREE_D_SUPPORT "away3d", "flx3d", #end #if VIDEO_CUTSCENES "hxvlc.flixel", "hxvlc.openfl", #end + // hxWebSocket + "hx.ws", + // BASE HAXE "DateTools", "EReg", "Lambda", "StringBuf", "haxe.crypto", "haxe.display", "haxe.exceptions", "haxe.extern", "scripting", ]) From 294fe6bb4ea42c39ef873fa366501e5a7d4e878f Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sat, 4 Jan 2025 15:50:51 -0700 Subject: [PATCH 05/26] fixed indentation --- .../funkin/backend/internet/WebSocketUtil.hx | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/source/funkin/backend/internet/WebSocketUtil.hx b/source/funkin/backend/internet/WebSocketUtil.hx index bcb054c5d..29aefc83a 100644 --- a/source/funkin/backend/internet/WebSocketUtil.hx +++ b/source/funkin/backend/internet/WebSocketUtil.hx @@ -66,22 +66,22 @@ class WebSocketUtil implements IFlxDestroyable { * @param onOpen sets the `onOpen` function directly to the class. * @param immediateOpen If true, the WebSocket will open immediately. Hence why `onOpen` is a function in the parameters. **/ - public function new(url:String, ?onOpen:WebSocket->Void, ?immediateOpen:Bool = false) { + public function new(url:String, ?onOpen:WebSocket->Void, ?immediateOpen:Bool = false) { this.onOpen = (onOpen == null) ? this.onOpen : onOpen; this.onError = this.onError; this.url = url; this.webSocket = new WebSocket(this.url, false); - this.webSocket.onopen = function() { + this.webSocket.onopen = function() { try { this.onOpen(webSocket); } catch(error) { this.onError(error); } - }; + }; - this.webSocket.onmessage = function(message) { + this.webSocket.onmessage = function(message) { var data:Dynamic = null; try { switch(message) { @@ -94,20 +94,20 @@ class WebSocketUtil implements IFlxDestroyable { } catch(e) { this.onError(e); } - }; + }; - this.webSocket.onclose = function() { + this.webSocket.onclose = function() { try { this.onClose(); } catch(e) { this.onError(e); } - }; + }; - this.webSocket.onerror = this.onError; + this.webSocket.onerror = this.onError; if (immediateOpen) this.open(); - } + } /** * Opens the WebSocket. @@ -156,5 +156,4 @@ class WebSocketUtil implements IFlxDestroyable { public function destroy() { this.webSocket.close(); } -} - +} \ No newline at end of file From f1e8d9754494bdb7ad416c13581e6bc1e31459e3 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sat, 4 Jan 2025 16:42:23 -0700 Subject: [PATCH 06/26] Fixed some issues --- source/funkin/backend/internet/WebSocketUtil.hx | 7 +++++++ source/funkin/backend/system/macros/Macros.hx | 4 +--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/source/funkin/backend/internet/WebSocketUtil.hx b/source/funkin/backend/internet/WebSocketUtil.hx index 29aefc83a..f6a889335 100644 --- a/source/funkin/backend/internet/WebSocketUtil.hx +++ b/source/funkin/backend/internet/WebSocketUtil.hx @@ -4,6 +4,13 @@ import hx.ws.*; import funkin.backend.system.Logs; +/** +* Basically a Utility for HScript to use WebSockets. Adds safeguards, error handling, and logging to debug your WebSockets. +* ItsLJcool wanted to make CodenameEngine Online / Multiplayer. This will make it easier to do so. +* +* This does NOT support making a Server Side WebSocket. Its only for Client Side WebSockets. If you want to make a Server you need to that yourself. +* I'd suggest using JavaScript for it. Though any program will do. +**/ class WebSocketUtil implements IFlxDestroyable { /** * Used for the `toggleLogging` function. this is more of a data handler for the function. diff --git a/source/funkin/backend/system/macros/Macros.hx b/source/funkin/backend/system/macros/Macros.hx index 73beaeb3d..b8e6fc6c2 100644 --- a/source/funkin/backend/system/macros/Macros.hx +++ b/source/funkin/backend/system/macros/Macros.hx @@ -22,9 +22,6 @@ class Macros { #if THREE_D_SUPPORT "away3d", "flx3d", #end #if VIDEO_CUTSCENES "hxvlc.flixel", "hxvlc.openfl", #end - // hxWebSocket - "hx.ws", - // BASE HAXE "DateTools", "EReg", "Lambda", "StringBuf", "haxe.crypto", "haxe.display", "haxe.exceptions", "haxe.extern", "scripting", ]) @@ -44,6 +41,7 @@ class Macros { } Compiler.include("funkin", [#if !UPDATE_CHECKING 'funkin.backend.system.updating' #end]); + // Compiler.include("hx.ws", [#if !UPDATE_CHECKING 'cs.system.net.sockets' #end]); // idk what im doing neo } public static function initMacros() { From 62f1569caf79d53f6128b901259f8850e2d58bc7 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sat, 4 Jan 2025 19:49:26 -0700 Subject: [PATCH 07/26] Added `WebSocketPacket` to be used with `WebSocketUtil`. Srt was the person behind hanlding the data when we did Codename Engine Multiplayer Test. --- .../backend/internet/WebSocketPacket.hx | 107 ++++++++++++++++++ .../funkin/backend/internet/WebSocketUtil.hx | 7 +- 2 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 source/funkin/backend/internet/WebSocketPacket.hx diff --git a/source/funkin/backend/internet/WebSocketPacket.hx b/source/funkin/backend/internet/WebSocketPacket.hx new file mode 100644 index 000000000..52dade846 --- /dev/null +++ b/source/funkin/backend/internet/WebSocketPacket.hx @@ -0,0 +1,107 @@ +package funkin.backend.internet; + +import String; +import StringBuf; +import Reflect; + +/** +* A data object that can be customized for `WebSocketUtil` to send data to the server. +* Srt was the person that made the data idea with first version of Codename Engine Multiplayer Test. +* +* Here is an example of how the default packet looks like: +* `value::ENDPACKET>` +* +* On the server side its considered as a string so you can use splits to get the data. +**/ +class WebSocketPacket { + + /** + * The starting prefix of the packet. + **/ + static var startPrefix(default, set):String = "value` + **/ + static var dataPointer(default, set):String = "=>"; + private static function set_dataPointer(value:String):String { return value.trim(); } + + /** + * Splits the packet from key / values. + * Default example: + * `key=>value::key2=>value2` + **/ + static var dataSplit(default, set):String = "::"; + private static function set_dataSplit(value:String):String { return value.trim(); } + + /** + * Just normal json data that is being held for the packet to stringify. + **/ + private var packetData:Dynamic = {}; + + /** + * The name of the event the server handles. + * If null it won't be added in the packet. + **/ + public var packetEventName:String; + + /** + * @param packetName The name of the event the server handles. + * @param packetData The data that is being sent to the server. Can also be a stringified JSON. + **/ + public function new(packetName:Null, packetData:Dynamic) { + this.packetEventName = (packetName == null) ? "" : packetName; + + if (packetData is String) packetData = haxe.Json.parse(packetData); + this.packetData = packetData; + } + + /** + * Checks if the packet has the field. + * @param field The field to check for + * @return If the packet has the field. + **/ + public function exists(field:String):Bool { + return Reflect.hasField(this.packetData, field); + } + + /** + * Gets the packet field. + * @param field The field to get the value. + * @return the value of the field. + **/ + public function get(field:String):Dynamic { + return Reflect.field(this.packetData, field); + } + + /** + * Sets a value to the packet. + * @param field The field to get the value. + * @param value The value to set. + * @return the packet data as a JSON structure. + **/ + public function set(field:String, value:Dynamic) { + Reflect.setField(this.packetData, field, value); + return this.packetData; + } + + /** + * Converts the packet to a string. + * @return The packet as a string. + **/ + public function toString():String { + var data:StringBuf = new StringBuf(); + if (WebSocketPacket.startPrefix != "" && this.packetEventName != "") data.add('${WebSocketPacket.startPrefix}${WebSocketPacket.dataSplit}${this.packetEventName}'); + for (field in Reflect.fields(this.packetData)) data.add('${WebSocketPacket.dataSplit}${field}${WebSocketPacket.dataPointer}${Reflect.getProperty(this.packetData, field)}'); + if (WebSocketPacket.endPrefix != "") data.add('${WebSocketPacket.dataSplit}${WebSocketPacket.endPrefix}'); + return data.toString(); + } +} \ No newline at end of file diff --git a/source/funkin/backend/internet/WebSocketUtil.hx b/source/funkin/backend/internet/WebSocketUtil.hx index f6a889335..abe905910 100644 --- a/source/funkin/backend/internet/WebSocketUtil.hx +++ b/source/funkin/backend/internet/WebSocketUtil.hx @@ -37,7 +37,7 @@ class WebSocketUtil implements IFlxDestroyable { * Function calls after the WebSocket has been opened. * @param webSocket Returns the instance of the WebSocket. **/ - public var onOpen:WebSocket->Void = (webSocket)->{}; + public var onOpen:WebSocketUtil->Void = (webSocket)->{}; /** * Whenever the WebSocket receives a message sent from the server. @@ -73,7 +73,7 @@ class WebSocketUtil implements IFlxDestroyable { * @param onOpen sets the `onOpen` function directly to the class. * @param immediateOpen If true, the WebSocket will open immediately. Hence why `onOpen` is a function in the parameters. **/ - public function new(url:String, ?onOpen:WebSocket->Void, ?immediateOpen:Bool = false) { + public function new(url:String, ?onOpen:WebSocketUtil->Void, ?immediateOpen:Bool = false) { this.onOpen = (onOpen == null) ? this.onOpen : onOpen; this.onError = this.onError; @@ -82,7 +82,7 @@ class WebSocketUtil implements IFlxDestroyable { this.webSocket.onopen = function() { try { - this.onOpen(webSocket); + this.onOpen(this); } catch(error) { this.onError(error); } @@ -150,6 +150,7 @@ class WebSocketUtil implements IFlxDestroyable { * Sends data to the server **/ public function send(data) { + if (data is WebSocketPacket) data = data.toString(); try { this.webSocket.send(data); } catch(e) { From 50aeecd7d8e0521fb27484bd243521ad42158bd6 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sat, 4 Jan 2025 21:39:18 -0700 Subject: [PATCH 08/26] clarification --- source/funkin/backend/internet/WebSocketUtil.hx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/funkin/backend/internet/WebSocketUtil.hx b/source/funkin/backend/internet/WebSocketUtil.hx index abe905910..5bcb4d2c6 100644 --- a/source/funkin/backend/internet/WebSocketUtil.hx +++ b/source/funkin/backend/internet/WebSocketUtil.hx @@ -6,6 +6,7 @@ import funkin.backend.system.Logs; /** * Basically a Utility for HScript to use WebSockets. Adds safeguards, error handling, and logging to debug your WebSockets. +* YOU WILL NEED TO HANDLE HOW THE WEBSOCKETS ARE CLOSED!!! calling `destroy` will close the WebSocket. * ItsLJcool wanted to make CodenameEngine Online / Multiplayer. This will make it easier to do so. * * This does NOT support making a Server Side WebSocket. Its only for Client Side WebSockets. If you want to make a Server you need to that yourself. @@ -162,6 +163,6 @@ class WebSocketUtil implements IFlxDestroyable { * Closes the WebSocket and destroys the class instance. **/ public function destroy() { - this.webSocket.close(); + this.close(); } } \ No newline at end of file From 23b4f595d061f08685d80a93ae6ec9e2afbce94f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=8D=9A=7ENex?= <87421482+NexIsDumb@users.noreply.github.com> Date: Sun, 5 Jan 2025 20:44:46 +0100 Subject: [PATCH 09/26] allow scripting the entire raw websocket api --- libs.xml | 4 +--- source/funkin/backend/system/macros/Macros.hx | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/libs.xml b/libs.xml index a443dfa3a..673982bca 100644 --- a/libs.xml +++ b/libs.xml @@ -4,9 +4,6 @@ - - - @@ -16,6 +13,7 @@ + diff --git a/source/funkin/backend/system/macros/Macros.hx b/source/funkin/backend/system/macros/Macros.hx index b8e6fc6c2..6d95a5c48 100644 --- a/source/funkin/backend/system/macros/Macros.hx +++ b/source/funkin/backend/system/macros/Macros.hx @@ -40,8 +40,8 @@ class Macros { } } + Compiler.include("hx.ws", ["hx.ws.cs", "hx.ws.java", "hx.ws.nodejs"]); Compiler.include("funkin", [#if !UPDATE_CHECKING 'funkin.backend.system.updating' #end]); - // Compiler.include("hx.ws", [#if !UPDATE_CHECKING 'cs.system.net.sockets' #end]); // idk what im doing neo } public static function initMacros() { From 4714cbf509af155e8f044eed8c92cc334b391baa Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sun, 5 Jan 2025 12:52:27 -0700 Subject: [PATCH 10/26] reformatted the `WebSocketPacket` to use `Serializer` instead of a long string. Also added Meta Data to the packet (can be disabled as a 3rd param) --- .../backend/internet/WebSocketPacket.hx | 101 ++++++++++-------- 1 file changed, 57 insertions(+), 44 deletions(-) diff --git a/source/funkin/backend/internet/WebSocketPacket.hx b/source/funkin/backend/internet/WebSocketPacket.hx index 52dade846..3c3884f13 100644 --- a/source/funkin/backend/internet/WebSocketPacket.hx +++ b/source/funkin/backend/internet/WebSocketPacket.hx @@ -1,49 +1,27 @@ package funkin.backend.internet; -import String; +import funkin.backend.assets.ModsFolder; +import funkin.backend.system.macros.GitCommitMacro; + +import haxe.Serializer; + +import Date; import StringBuf; +import String; import Reflect; /** * A data object that can be customized for `WebSocketUtil` to send data to the server. -* Srt was the person that made the data idea with first version of Codename Engine Multiplayer Test. +* You will need to handle the custom packet on your server yourself. * -* Here is an example of how the default packet looks like: -* `value::ENDPACKET>` +* GENERAL WEBSOCKET WARNING: Sending data to the server on `update` SHOULD NEVER BE DONE!! +* It will be slow and generally not a good idea. It might overload the server and cause unforseen issues. * -* On the server side its considered as a string so you can use splits to get the data. +* Why use a packet class instead of sending your own data? Well this Serializes the data and handles it for you, so all you do is just send the class in the `WebSocketUtil.send` and thats it. **/ class WebSocketPacket { - - /** - * The starting prefix of the packet. - **/ - static var startPrefix(default, set):String = "value` - **/ - static var dataPointer(default, set):String = "=>"; - private static function set_dataPointer(value:String):String { return value.trim(); } - - /** - * Splits the packet from key / values. - * Default example: - * `key=>value::key2=>value2` - **/ - static var dataSplit(default, set):String = "::"; - private static function set_dataSplit(value:String):String { return value.trim(); } - /** - * Just normal json data that is being held for the packet to stringify. + * Just normal data that is being held for the packet to get stringified. **/ private var packetData:Dynamic = {}; @@ -51,17 +29,41 @@ class WebSocketPacket { * The name of the event the server handles. * If null it won't be added in the packet. **/ - public var packetEventName:String; + public var packetEventName(default, set):String; + private function set_packetEventName(value:String):String { + if (value == null) return ""; + return this.packetEventName = value; + } + + @:dox(hide) private var add_meta_data:Bool = true; /** * @param packetName The name of the event the server handles. * @param packetData The data that is being sent to the server. Can also be a stringified JSON. + * @param add_meta_data If true, adds metadata to the packet. This is useful for data like the time it was sent, **/ - public function new(packetName:Null, packetData:Dynamic) { - this.packetEventName = (packetName == null) ? "" : packetName; + public function new(packetName:Null, packetData:Dynamic, ?_add_meta_data:Bool = true) { + this.packetEventName = packetName; + this.add_meta_data = _add_meta_data; - if (packetData is String) packetData = haxe.Json.parse(packetData); + // in case ig + try { + if (packetData is String) packetData = haxe.Json.parse(packetData); + } catch (e:Dynamic) { + trace("Error parsing string data to packet: " + e); + } + this.packetData = packetData; + + if (this.add_meta_data) { + try { + if (ModsFolder.currentModFolder != null) this.packetData.__mod = ModsFolder.currentModFolder; + this.packetData.__commitHash = GitCommitMacro.commitHash; // for checking outdated action builds on the server. its gonna be peak trust. + } catch (e:Dynamic) { + trace("Error adding metadata to packet: " + e); + } + } + } /** @@ -94,14 +96,25 @@ class WebSocketPacket { } /** - * Converts the packet to a string. + * Converts the packet to a string. Uses `Serializer` to convert the packet to a data string * @return The packet as a string. **/ public function toString():String { - var data:StringBuf = new StringBuf(); - if (WebSocketPacket.startPrefix != "" && this.packetEventName != "") data.add('${WebSocketPacket.startPrefix}${WebSocketPacket.dataSplit}${this.packetEventName}'); - for (field in Reflect.fields(this.packetData)) data.add('${WebSocketPacket.dataSplit}${field}${WebSocketPacket.dataPointer}${Reflect.getProperty(this.packetData, field)}'); - if (WebSocketPacket.endPrefix != "") data.add('${WebSocketPacket.dataSplit}${WebSocketPacket.endPrefix}'); - return data.toString(); + var cerial = new Serializer(); + var buffer = new StringBuf(); + + // if no name is associated with packet, just serialize the data + if (packetEventName != "") { + buffer.add('!HXP'); + buffer.add(this.packetEventName); + } else { + buffer.add('!HXp'); + } + buffer.add('=>'); + + if (add_meta_data) this.packetData.__timestamp = Date.now().getTime(); + + cerial.serialize(this.packetData); + return '${buffer.toString()}${cerial.toString()}'; } } \ No newline at end of file From 8c8f5320d77228ff63c9dac7d5287af756885291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=8D=9A=7ENex?= <87421482+NexIsDumb@users.noreply.github.com> Date: Sun, 5 Jan 2025 20:56:26 +0100 Subject: [PATCH 11/26] moving to .system.net instead of .internet so its all together with also yosh's socket (important to remember that socket and websocket are two different things though) --- .../{internet => system/net}/WebSocketPacket.hx | 8 ++++---- .../{internet => system/net}/WebSocketUtil.hx | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) rename source/funkin/backend/{internet => system/net}/WebSocketPacket.hx (98%) rename source/funkin/backend/{internet => system/net}/WebSocketUtil.hx (98%) diff --git a/source/funkin/backend/internet/WebSocketPacket.hx b/source/funkin/backend/system/net/WebSocketPacket.hx similarity index 98% rename from source/funkin/backend/internet/WebSocketPacket.hx rename to source/funkin/backend/system/net/WebSocketPacket.hx index 3c3884f13..923d8ece8 100644 --- a/source/funkin/backend/internet/WebSocketPacket.hx +++ b/source/funkin/backend/system/net/WebSocketPacket.hx @@ -1,4 +1,4 @@ -package funkin.backend.internet; +package funkin.backend.system.net; import funkin.backend.assets.ModsFolder; import funkin.backend.system.macros.GitCommitMacro; @@ -24,7 +24,7 @@ class WebSocketPacket { * Just normal data that is being held for the packet to get stringified. **/ private var packetData:Dynamic = {}; - + /** * The name of the event the server handles. * If null it won't be added in the packet. @@ -52,9 +52,9 @@ class WebSocketPacket { } catch (e:Dynamic) { trace("Error parsing string data to packet: " + e); } - + this.packetData = packetData; - + if (this.add_meta_data) { try { if (ModsFolder.currentModFolder != null) this.packetData.__mod = ModsFolder.currentModFolder; diff --git a/source/funkin/backend/internet/WebSocketUtil.hx b/source/funkin/backend/system/net/WebSocketUtil.hx similarity index 98% rename from source/funkin/backend/internet/WebSocketUtil.hx rename to source/funkin/backend/system/net/WebSocketUtil.hx index 5bcb4d2c6..d45d4ce7e 100644 --- a/source/funkin/backend/internet/WebSocketUtil.hx +++ b/source/funkin/backend/system/net/WebSocketUtil.hx @@ -1,4 +1,4 @@ -package funkin.backend.internet; +package funkin.backend.system.net; import hx.ws.*; @@ -17,7 +17,7 @@ class WebSocketUtil implements IFlxDestroyable { * Used for the `toggleLogging` function. this is more of a data handler for the function. **/ static var loggingEnabled:Bool = false; - + /** * Call this function to toggle debugging for the WebSocket. **/ @@ -39,18 +39,18 @@ class WebSocketUtil implements IFlxDestroyable { * @param webSocket Returns the instance of the WebSocket. **/ public var onOpen:WebSocketUtil->Void = (webSocket)->{}; - + /** * Whenever the WebSocket receives a message sent from the server. * @param message Returns the message sent from the server. **/ public var onMessage:Dynamic->Void = (message)->{}; - + /** * Runs whenever the WebSocket closes. **/ public var onClose:Void->Void = ()->{}; - + /** * Runs whenever the WebSocket encounters an error. **/ @@ -68,7 +68,7 @@ class WebSocketUtil implements IFlxDestroyable { @:dox(hide) private var url:String; @:dox(hide) private var webSocket:WebSocket; - + /** * @param url The URL of the WebSocket. Usually `ws://localhost:port`. * @param onOpen sets the `onOpen` function directly to the class. @@ -113,7 +113,7 @@ class WebSocketUtil implements IFlxDestroyable { }; this.webSocket.onerror = this.onError; - + if (immediateOpen) this.open(); } From bc44bbe8a6b0065381538310b1eddb96d4f14436 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sun, 5 Jan 2025 13:08:00 -0700 Subject: [PATCH 12/26] "The => should only be in the not empty part" - Neo 2025 --- source/funkin/backend/system/net/WebSocketPacket.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/backend/system/net/WebSocketPacket.hx b/source/funkin/backend/system/net/WebSocketPacket.hx index 923d8ece8..07910a5db 100644 --- a/source/funkin/backend/system/net/WebSocketPacket.hx +++ b/source/funkin/backend/system/net/WebSocketPacket.hx @@ -107,10 +107,10 @@ class WebSocketPacket { if (packetEventName != "") { buffer.add('!HXP'); buffer.add(this.packetEventName); + buffer.add('=>'); } else { buffer.add('!HXp'); } - buffer.add('=>'); if (add_meta_data) this.packetData.__timestamp = Date.now().getTime(); From 13d5ab77e8a21dde28bcf10c650dea143a86bc87 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sun, 5 Jan 2025 19:48:20 -0700 Subject: [PATCH 13/26] Added Discord Meta Data (Username, Global name and Premium Type only) --- source/funkin/backend/system/net/WebSocketPacket.hx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/funkin/backend/system/net/WebSocketPacket.hx b/source/funkin/backend/system/net/WebSocketPacket.hx index 07910a5db..8eb91117a 100644 --- a/source/funkin/backend/system/net/WebSocketPacket.hx +++ b/source/funkin/backend/system/net/WebSocketPacket.hx @@ -2,6 +2,7 @@ package funkin.backend.system.net; import funkin.backend.assets.ModsFolder; import funkin.backend.system.macros.GitCommitMacro; +import funkin.backend.utils.DiscordUtil; import haxe.Serializer; @@ -59,6 +60,11 @@ class WebSocketPacket { try { if (ModsFolder.currentModFolder != null) this.packetData.__mod = ModsFolder.currentModFolder; this.packetData.__commitHash = GitCommitMacro.commitHash; // for checking outdated action builds on the server. its gonna be peak trust. + this.packetData.__discord = { + username: DiscordUtil.user.username, + globalName: DiscordUtil.user.globalName, + premiumType: DiscordUtil.user.premiumType, + }; } catch (e:Dynamic) { trace("Error adding metadata to packet: " + e); } @@ -112,7 +118,7 @@ class WebSocketPacket { buffer.add('!HXp'); } - if (add_meta_data) this.packetData.__timestamp = Date.now().getTime(); + if (add_meta_data) this.packetData.__timestamp = Date.now(); cerial.serialize(this.packetData); return '${buffer.toString()}${cerial.toString()}'; From a6fad0abc5b5d40af561cace99c06fc9c80e1d54 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sun, 5 Jan 2025 21:54:46 -0700 Subject: [PATCH 14/26] updated `WebSocketPacket` to actually you know do its job and not crash under cases --- .../backend/system/net/WebSocketPacket.hx | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/source/funkin/backend/system/net/WebSocketPacket.hx b/source/funkin/backend/system/net/WebSocketPacket.hx index 8eb91117a..f4b4fdb16 100644 --- a/source/funkin/backend/system/net/WebSocketPacket.hx +++ b/source/funkin/backend/system/net/WebSocketPacket.hx @@ -9,7 +9,7 @@ import haxe.Serializer; import Date; import StringBuf; import String; -import Reflect; +import Type; /** * A data object that can be customized for `WebSocketUtil` to send data to the server. @@ -24,15 +24,20 @@ class WebSocketPacket { /** * Just normal data that is being held for the packet to get stringified. **/ - private var packetData:Dynamic = {}; + private var packetData(default, set):Dynamic = {}; + private function set_packetData(value:Dynamic):Dynamic { + if (value == null) return {}; + if (value is String) value = haxe.Json.parse(value); + return this.packetData = value; + } /** * The name of the event the server handles. * If null it won't be added in the packet. **/ public var packetEventName(default, set):String; - private function set_packetEventName(value:String):String { - if (value == null) return ""; + private function set_packetEventName(value:Null):String { + if (value == null) return this.packetEventName = ""; return this.packetEventName = value; } @@ -43,17 +48,10 @@ class WebSocketPacket { * @param packetData The data that is being sent to the server. Can also be a stringified JSON. * @param add_meta_data If true, adds metadata to the packet. This is useful for data like the time it was sent, **/ - public function new(packetName:Null, packetData:Dynamic, ?_add_meta_data:Bool = true) { + public function new(packetName:Null, ?packetData:Null, ?_add_meta_data:Bool = true) { this.packetEventName = packetName; this.add_meta_data = _add_meta_data; - // in case ig - try { - if (packetData is String) packetData = haxe.Json.parse(packetData); - } catch (e:Dynamic) { - trace("Error parsing string data to packet: " + e); - } - this.packetData = packetData; if (this.add_meta_data) { @@ -106,11 +104,11 @@ class WebSocketPacket { * @return The packet as a string. **/ public function toString():String { - var cerial = new Serializer(); var buffer = new StringBuf(); // if no name is associated with packet, just serialize the data - if (packetEventName != "") { + trace("this.packetEventName.trim(): " + this.packetEventName.trim()); + if (this.packetEventName.trim() != "") { buffer.add('!HXP'); buffer.add(this.packetEventName); buffer.add('=>'); @@ -120,7 +118,8 @@ class WebSocketPacket { if (add_meta_data) this.packetData.__timestamp = Date.now(); - cerial.serialize(this.packetData); + var cerial = new Serializer(); + if (this.packetData != {}) cerial.serialize(this.packetData); return '${buffer.toString()}${cerial.toString()}'; } } \ No newline at end of file From 6237a6d27c5459b4d43b87928d969d691b0d1574 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sun, 5 Jan 2025 21:58:39 -0700 Subject: [PATCH 15/26] erm i forgor about a trace line --- source/funkin/backend/system/net/WebSocketPacket.hx | 1 - 1 file changed, 1 deletion(-) diff --git a/source/funkin/backend/system/net/WebSocketPacket.hx b/source/funkin/backend/system/net/WebSocketPacket.hx index f4b4fdb16..da4fe71f2 100644 --- a/source/funkin/backend/system/net/WebSocketPacket.hx +++ b/source/funkin/backend/system/net/WebSocketPacket.hx @@ -107,7 +107,6 @@ class WebSocketPacket { var buffer = new StringBuf(); // if no name is associated with packet, just serialize the data - trace("this.packetEventName.trim(): " + this.packetEventName.trim()); if (this.packetEventName.trim() != "") { buffer.add('!HXP'); buffer.add(this.packetEventName); From 254e59e052d940d25b246ce7386fc9231f316e02 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Mon, 6 Jan 2025 18:39:35 -0700 Subject: [PATCH 16/26] No longer sends Discord Metadata if your not connected. --- source/funkin/backend/system/net/WebSocketPacket.hx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/source/funkin/backend/system/net/WebSocketPacket.hx b/source/funkin/backend/system/net/WebSocketPacket.hx index da4fe71f2..e61c5b79c 100644 --- a/source/funkin/backend/system/net/WebSocketPacket.hx +++ b/source/funkin/backend/system/net/WebSocketPacket.hx @@ -58,11 +58,8 @@ class WebSocketPacket { try { if (ModsFolder.currentModFolder != null) this.packetData.__mod = ModsFolder.currentModFolder; this.packetData.__commitHash = GitCommitMacro.commitHash; // for checking outdated action builds on the server. its gonna be peak trust. - this.packetData.__discord = { - username: DiscordUtil.user.username, - globalName: DiscordUtil.user.globalName, - premiumType: DiscordUtil.user.premiumType, - }; + // if Discord isn't active, dont send the metadata + if (DiscordUtil.ready) this.packetData.__discord = { username: DiscordUtil.user.username, globalName: DiscordUtil.user.globalName, premiumType: DiscordUtil.user.premiumType }; } catch (e:Dynamic) { trace("Error adding metadata to packet: " + e); } From d52a350859546ab0f13122ae8f6974a21c9a218d Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Tue, 7 Jan 2025 20:31:39 -0700 Subject: [PATCH 17/26] Added `ServerPacketData`. Messages recieved by the WebSocket will either parse it as a `ServerPacketData` if it can, if not it will be a string. Thanks to Neo for helping with that. --- source/funkin/backend/system/MainState.hx | 4 ++ .../backend/system/net/WebSocketPacket.hx | 29 ++++++++ .../backend/system/net/WebSocketUtil.hx | 72 +++++++++++++++++++ 3 files changed, 105 insertions(+) diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx index a5011d6d6..1e787754e 100644 --- a/source/funkin/backend/system/MainState.hx +++ b/source/funkin/backend/system/MainState.hx @@ -8,6 +8,7 @@ import funkin.menus.TitleState; import funkin.menus.BetaWarningState; import funkin.backend.chart.EventsData; import flixel.FlxState; +import funkin.backend.system.net.WebSocketPacket; /** * Simple state used for loading the game @@ -21,6 +22,9 @@ class MainState extends FlxState { Main.loadGameSettings(); initiated = true; + // Resetting to default because I doubt any modding the engine will reset it lmao + WebSocketPacket.packetTypes = WebSocketPacket.default_packetTypes; + #if sys CoolUtil.deleteFolder('./.temp/'); // delete temp folder #end diff --git a/source/funkin/backend/system/net/WebSocketPacket.hx b/source/funkin/backend/system/net/WebSocketPacket.hx index e61c5b79c..f5d89da4a 100644 --- a/source/funkin/backend/system/net/WebSocketPacket.hx +++ b/source/funkin/backend/system/net/WebSocketPacket.hx @@ -21,6 +21,24 @@ import Type; * Why use a packet class instead of sending your own data? Well this Serializes the data and handles it for you, so all you do is just send the class in the `WebSocketUtil.send` and thats it. **/ class WebSocketPacket { + + /** + * Packet Types that can be gathered from the server. Used by `WebSocketUtil` + * If your server doesn't use the Template ItsLJcool made, then you add your own here. + **/ + public static var packetTypes:Map = [ + "haxe" => {params: "!HXP", none: "!HXp"}, + "javascript" => {params: "!JSP", none: "!JSp"}, + "js" => {params: "!JSP", none: "!JSp"}, + ]; + + @:dox(hide) + public static var default_packetTypes(default, never):Map = [ + "haxe" => {params: "!HXP", none: "!HXp"}, + "javascript" => {params: "!JSP", none: "!JSp"}, + "js" => {params: "!JSP", none: "!JSp"}, + ]; + /** * Just normal data that is being held for the packet to get stringified. **/ @@ -118,4 +136,15 @@ class WebSocketPacket { if (this.packetData != {}) cerial.serialize(this.packetData); return '${buffer.toString()}${cerial.toString()}'; } + + public static function isServerPacket(data:Dynamic):Bool { + if ((data is ServerPacketData)) return true; + return false; + } +} + +@:structInit +class ServerPacketData { + public var name:String; + public var data:Dynamic; } \ No newline at end of file diff --git a/source/funkin/backend/system/net/WebSocketUtil.hx b/source/funkin/backend/system/net/WebSocketUtil.hx index d45d4ce7e..ab2ff7fb4 100644 --- a/source/funkin/backend/system/net/WebSocketUtil.hx +++ b/source/funkin/backend/system/net/WebSocketUtil.hx @@ -2,7 +2,10 @@ package funkin.backend.system.net; import hx.ws.*; +import funkin.backend.system.net.WebSocketPacket.ServerPacketData; import funkin.backend.system.Logs; +import haxe.Unserializer; +import Type; /** * Basically a Utility for HScript to use WebSockets. Adds safeguards, error handling, and logging to debug your WebSockets. @@ -62,10 +65,16 @@ class WebSocketUtil implements IFlxDestroyable { Logs.logText('${error}'), ], ERROR); if (_errorFunc != null) _errorFunc(error); + if (this.closeOnError) this.close(); }; return this.onError = func; } + /** + * If true, the WebSocket will close when an error occurs. + **/ + public var closeOnError:Bool = true; + @:dox(hide) private var url:String; @:dox(hide) private var webSocket:WebSocket; @@ -95,6 +104,8 @@ class WebSocketUtil implements IFlxDestroyable { switch(message) { case StrMessage(str): data = str; + var _data = this.attemptDeserialize(data); + if (WebSocketPacket.isServerPacket(_data)) data = _data; case BytesMessage(bytes): data = bytes; } @@ -117,6 +128,61 @@ class WebSocketUtil implements IFlxDestroyable { if (immediateOpen) this.open(); } + + /* + / javascript code for reference + + toString() { + if (this.add_meta_data) this.data.__timestamp = Date.now(); + var hasName = (this.name != null && this.name.trim() != ""); + var start = (hasName) ? "!JSP"+this.name : "!JSp"; + start += "=>"; + + var cerial = new Serializer(); + cerial.serialize(this.data); + + return start+cerial.toString(); + } + */ + + /** + * @param rawData The raw data from the server + * @return The packet data if it was found, otherwise null and WebSocketUtil will handle it. + */ + private function attemptDeserialize(rawData:String):Null { + if (!rawData.startsWith("!")) return null; + + for (key=>value in WebSocketPacket.packetTypes) { + var hasPacketData = rawData.startsWith(value.params); // PREFIXname=>DATA + var hasPacketNone = rawData.startsWith(value.none); // PREFIX=>DATA + + if (hasPacketNone) { + var data = rawData.substr(rawData.indexOf("=>") + 2); + var packetData:Dynamic = Unserializer.run(data); + if (packetData == null) packetData = {}; + var packet:ServerPacketData = { name: null, data: packetData }; + return packet; + } + + if (!hasPacketData) continue; + + try { + var data = rawData.substr(rawData.indexOf("=>") + 2); + var name = rawData.substring(value.params.length, rawData.indexOf("=>")); + var packetData:Dynamic = Unserializer.run(data); + if (packetData == null) packetData = {}; + var packet:ServerPacketData = { name: name, data: packetData }; + return packet; + } catch (e:Dynamic) { + trace('Error parsing packet: ${e}'); + return null; + } + break; + } + + return null; + } + /** * Opens the WebSocket. **/ @@ -140,8 +206,10 @@ class WebSocketUtil implements IFlxDestroyable { Logs.logText("[WebSocket Connection] ", BLUE), Logs.logText('Closing connection to ${this.url}'), ], INFO); + try { this.webSocket.close(); + this._isClosed = true; } catch(e) { this.onError(e); } @@ -159,10 +227,14 @@ class WebSocketUtil implements IFlxDestroyable { } } + private var _isClosed:Bool = false; + /** * Closes the WebSocket and destroys the class instance. **/ public function destroy() { + if (this._isClosed) return; + this.close(); } } \ No newline at end of file From a5bb1fc5b3111dcbc9258018cab80c6057d43955 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Fri, 10 Jan 2025 01:16:16 -0700 Subject: [PATCH 18/26] `closeOnError` is default since you can handle your errors by default. Basically the Server Template is done, and so I think WebSocket's are ready to be put into CNE. --- source/funkin/backend/system/net/WebSocketUtil.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/backend/system/net/WebSocketUtil.hx b/source/funkin/backend/system/net/WebSocketUtil.hx index ab2ff7fb4..fb1f0c733 100644 --- a/source/funkin/backend/system/net/WebSocketUtil.hx +++ b/source/funkin/backend/system/net/WebSocketUtil.hx @@ -73,7 +73,7 @@ class WebSocketUtil implements IFlxDestroyable { /** * If true, the WebSocket will close when an error occurs. **/ - public var closeOnError:Bool = true; + public var closeOnError:Bool = false; @:dox(hide) private var url:String; @:dox(hide) private var webSocket:WebSocket; @@ -206,7 +206,7 @@ class WebSocketUtil implements IFlxDestroyable { Logs.logText("[WebSocket Connection] ", BLUE), Logs.logText('Closing connection to ${this.url}'), ], INFO); - + try { this.webSocket.close(); this._isClosed = true; From a82d824b61cce6d04f9cbc4433469b3f82998df9 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Fri, 10 Jan 2025 18:37:51 -0700 Subject: [PATCH 19/26] Added Link in comments to Server Template --- source/funkin/backend/system/net/WebSocketUtil.hx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/funkin/backend/system/net/WebSocketUtil.hx b/source/funkin/backend/system/net/WebSocketUtil.hx index fb1f0c733..be07d34e4 100644 --- a/source/funkin/backend/system/net/WebSocketUtil.hx +++ b/source/funkin/backend/system/net/WebSocketUtil.hx @@ -14,6 +14,9 @@ import Type; * * This does NOT support making a Server Side WebSocket. Its only for Client Side WebSockets. If you want to make a Server you need to that yourself. * I'd suggest using JavaScript for it. Though any program will do. +* +* Check out the WebSocket Server Template for Codename Engine here: +* https://github.com/ItsLJcool/WebSocket-Server-Template-for-CNE **/ class WebSocketUtil implements IFlxDestroyable { /** From a80578b984763a36f615ad3ca313001721cab7b2 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Fri, 10 Jan 2025 20:33:35 -0700 Subject: [PATCH 20/26] Connecting to WebSocket is now Async. Can be disabled. --- .../backend/system/net/WebSocketUtil.hx | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/source/funkin/backend/system/net/WebSocketUtil.hx b/source/funkin/backend/system/net/WebSocketUtil.hx index be07d34e4..2fe9d492c 100644 --- a/source/funkin/backend/system/net/WebSocketUtil.hx +++ b/source/funkin/backend/system/net/WebSocketUtil.hx @@ -80,6 +80,12 @@ class WebSocketUtil implements IFlxDestroyable { @:dox(hide) private var url:String; @:dox(hide) private var webSocket:WebSocket; + + /** + * If true, when you call `open` the WebSocket will attempt to connect in a new thread. + * Usefull for trying to connect to a WebSocket Server whilst the game is running. + **/ + public var _threadedConnection:Bool = true; /** * @param url The URL of the WebSocket. Usually `ws://localhost:port`. @@ -194,11 +200,21 @@ class WebSocketUtil implements IFlxDestroyable { Logs.logText("[WebSocket Connection] ", BLUE), Logs.logText('Connecting to ${this.url}'), ], INFO); - try { - this.webSocket.open(); - } catch(e) { - this.onError(e); - } + + var _func = () -> { + try { + this.webSocket.open(); + } catch(e) { + this.onError(e); + return; + } + Logs.traceColored([ + Logs.logText("[WebSocket Connection] ", YELLOW), + Logs.logText('Connected to ${this.url}'), + ], INFO); + }; + if (_threadedConnection) Main.execAsync(_func); + else _func(); } /** From 3434bcdcb5d74f8f7d990f4cb5bd5ddce9fd4b88 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sat, 11 Jan 2025 00:42:26 -0700 Subject: [PATCH 21/26] forgor about stuff, also added Threaded Packet Sending (disabled by default) --- .../backend/system/net/WebSocketUtil.hx | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/source/funkin/backend/system/net/WebSocketUtil.hx b/source/funkin/backend/system/net/WebSocketUtil.hx index 2fe9d492c..e7f329a56 100644 --- a/source/funkin/backend/system/net/WebSocketUtil.hx +++ b/source/funkin/backend/system/net/WebSocketUtil.hx @@ -84,9 +84,16 @@ class WebSocketUtil implements IFlxDestroyable { /** * If true, when you call `open` the WebSocket will attempt to connect in a new thread. * Usefull for trying to connect to a WebSocket Server whilst the game is running. + * WARNING: CAN CAUSE ERRORS IN HSCRIPT!! **/ public var _threadedConnection:Bool = true; + /** + * If true, when you call `send` the WebSocket will attempt to send in a new thread. + * WARNING: CAN CAUSE ERRORS IN HSCRIPT!! + **/ + public var _threadedSend:Bool = false; + /** * @param url The URL of the WebSocket. Usually `ws://localhost:port`. * @param onOpen sets the `onOpen` function directly to the class. @@ -137,23 +144,6 @@ class WebSocketUtil implements IFlxDestroyable { if (immediateOpen) this.open(); } - - /* - / javascript code for reference - - toString() { - if (this.add_meta_data) this.data.__timestamp = Date.now(); - var hasName = (this.name != null && this.name.trim() != ""); - var start = (hasName) ? "!JSP"+this.name : "!JSp"; - start += "=>"; - - var cerial = new Serializer(); - cerial.serialize(this.data); - - return start+cerial.toString(); - } - */ - /** * @param rawData The raw data from the server * @return The packet data if it was found, otherwise null and WebSocketUtil will handle it. @@ -238,12 +228,16 @@ class WebSocketUtil implements IFlxDestroyable { * Sends data to the server **/ public function send(data) { - if (data is WebSocketPacket) data = data.toString(); - try { - this.webSocket.send(data); - } catch(e) { - this.onError(e); - } + var _func = () -> { + if (data is WebSocketPacket) data = data.toString(); + try { + this.webSocket.send(data); + } catch(e) { + this.onError(e); + } + }; + if (_threadedSend) Main.execAsync(_func); + else _func(); } private var _isClosed:Bool = false; From 7c480d2b4b7d8cdd4b88f8ca1a19482b2a2f1ab6 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sat, 11 Jan 2025 00:43:29 -0700 Subject: [PATCH 22/26] oop --- source/funkin/backend/system/net/WebSocketUtil.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/backend/system/net/WebSocketUtil.hx b/source/funkin/backend/system/net/WebSocketUtil.hx index e7f329a56..715286b1f 100644 --- a/source/funkin/backend/system/net/WebSocketUtil.hx +++ b/source/funkin/backend/system/net/WebSocketUtil.hx @@ -203,7 +203,7 @@ class WebSocketUtil implements IFlxDestroyable { Logs.logText('Connected to ${this.url}'), ], INFO); }; - if (_threadedConnection) Main.execAsync(_func); + if (this._threadedConnection) Main.execAsync(_func); else _func(); } @@ -236,7 +236,7 @@ class WebSocketUtil implements IFlxDestroyable { this.onError(e); } }; - if (_threadedSend) Main.execAsync(_func); + if (this._threadedSend) Main.execAsync(_func); else _func(); } From 13afcd1c4c185688216adc7da111e72cbfd04428 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sat, 11 Jan 2025 04:56:50 -0700 Subject: [PATCH 23/26] I forgot the `onmessage` is being sent from a thread, so updated the `_threadedSend` --- source/funkin/backend/system/net/WebSocketUtil.hx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/funkin/backend/system/net/WebSocketUtil.hx b/source/funkin/backend/system/net/WebSocketUtil.hx index 715286b1f..a7fd7664d 100644 --- a/source/funkin/backend/system/net/WebSocketUtil.hx +++ b/source/funkin/backend/system/net/WebSocketUtil.hx @@ -5,7 +5,8 @@ import hx.ws.*; import funkin.backend.system.net.WebSocketPacket.ServerPacketData; import funkin.backend.system.Logs; import haxe.Unserializer; -import Type; + +import flixel.util.FlxTimer; /** * Basically a Utility for HScript to use WebSockets. Adds safeguards, error handling, and logging to debug your WebSockets. @@ -236,8 +237,11 @@ class WebSocketUtil implements IFlxDestroyable { this.onError(e); } }; - if (this._threadedSend) Main.execAsync(_func); - else _func(); + // because its already threaded. + if (this._threadedSend) _func(); + else new FlxTimer().start(0.0001, (tmr:FlxTimer) -> { + _func(); + }); } private var _isClosed:Bool = false; From ab0e1c96c5680966fbb614134730788a637acb34 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sat, 11 Jan 2025 05:02:51 -0700 Subject: [PATCH 24/26] MORE!!! --- source/funkin/backend/system/net/WebSocketUtil.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/backend/system/net/WebSocketUtil.hx b/source/funkin/backend/system/net/WebSocketUtil.hx index a7fd7664d..62d021ecb 100644 --- a/source/funkin/backend/system/net/WebSocketUtil.hx +++ b/source/funkin/backend/system/net/WebSocketUtil.hx @@ -239,7 +239,7 @@ class WebSocketUtil implements IFlxDestroyable { }; // because its already threaded. if (this._threadedSend) _func(); - else new FlxTimer().start(0.0001, (tmr:FlxTimer) -> { + else new FlxTimer().start(0.01, (tmr:FlxTimer) -> { _func(); }); } From eecf1f39b8b3a5529a5e5cde67e7ad04df5c7036 Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sat, 11 Jan 2025 05:30:23 -0700 Subject: [PATCH 25/26] testing packet storing --- .../backend/system/net/WebSocketUtil.hx | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/source/funkin/backend/system/net/WebSocketUtil.hx b/source/funkin/backend/system/net/WebSocketUtil.hx index 62d021ecb..09ef27ee9 100644 --- a/source/funkin/backend/system/net/WebSocketUtil.hx +++ b/source/funkin/backend/system/net/WebSocketUtil.hx @@ -87,13 +87,15 @@ class WebSocketUtil implements IFlxDestroyable { * Usefull for trying to connect to a WebSocket Server whilst the game is running. * WARNING: CAN CAUSE ERRORS IN HSCRIPT!! **/ - public var _threadedConnection:Bool = true; + public var _threadedConnection:Bool = false; /** * If true, when you call `send` the WebSocket will attempt to send in a new thread. * WARNING: CAN CAUSE ERRORS IN HSCRIPT!! **/ public var _threadedSend:Bool = false; + + @:dox(hide) public var __packets:Array = []; /** * @param url The URL of the WebSocket. Usually `ws://localhost:port`. @@ -130,6 +132,7 @@ class WebSocketUtil implements IFlxDestroyable { } catch(e) { this.onError(e); } + __packets.push(data); }; this.webSocket.onclose = function() { @@ -145,6 +148,10 @@ class WebSocketUtil implements IFlxDestroyable { if (immediateOpen) this.open(); } + public function getRecentPacket():Dynamic { + return __packets.shift(); + } + /** * @param rawData The raw data from the server * @return The packet data if it was found, otherwise null and WebSocketUtil will handle it. @@ -229,19 +236,12 @@ class WebSocketUtil implements IFlxDestroyable { * Sends data to the server **/ public function send(data) { - var _func = () -> { - if (data is WebSocketPacket) data = data.toString(); - try { - this.webSocket.send(data); - } catch(e) { - this.onError(e); - } - }; - // because its already threaded. - if (this._threadedSend) _func(); - else new FlxTimer().start(0.01, (tmr:FlxTimer) -> { - _func(); - }); + if (data is WebSocketPacket) data = data.toString(); + try { + this.webSocket.send(data); + } catch(e) { + this.onError(e); + } } private var _isClosed:Bool = false; From ce99c1c5fedc1849e81e78caaa11700fa268702b Mon Sep 17 00:00:00 2001 From: ItsLJcool <54383469+ItsLJcool@users.noreply.github.com> Date: Sun, 12 Jan 2025 03:35:07 -0700 Subject: [PATCH 26/26] might fix compiling issue? --- source/funkin/backend/system/net/WebSocketUtil.hx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/source/funkin/backend/system/net/WebSocketUtil.hx b/source/funkin/backend/system/net/WebSocketUtil.hx index 09ef27ee9..a9e564d7e 100644 --- a/source/funkin/backend/system/net/WebSocketUtil.hx +++ b/source/funkin/backend/system/net/WebSocketUtil.hx @@ -235,10 +235,12 @@ class WebSocketUtil implements IFlxDestroyable { /** * Sends data to the server **/ - public function send(data) { - if (data is WebSocketPacket) data = data.toString(); + public function send(data) { + var _data = null; + if (data is WebSocketPacket) _data = data.toString(); + else _data = data; try { - this.webSocket.send(data); + this.webSocket.send(_data); } catch(e) { this.onError(e); }