Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions building/libs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<git name="hxdiscord_rpc" url="https://github.com/CodenameCrew/cne-hxdiscord_rpc" ref="old" skipDeps="true" />
<lib name="funkin-modchart" skipDeps="true" />
<lib name="hxvlc" version="1.9.3" skipDeps="true" />
<lib name="hxWebSockets" />

<!-- Documentation and other features -->
<git name="away3d" url="https://github.com/CodenameCrew/away3d" />
Expand Down
2 changes: 2 additions & 0 deletions project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@
<haxelib name="flixel" />
<haxelib name="flixel-addons" />

<haxelib name="hxWebSockets" />

<haxelib name="hxvlc" if="VIDEO_CUTSCENES" />
<haxelib name="away3d" if="THREE_D_SUPPORT" />
<haxelib name="format" />
Expand Down
76 changes: 76 additions & 0 deletions source/funkin/backend/system/net/FunkinPacket.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package funkin.backend.system.net;

import flixel.util.typeLimit.OneOfTwo;

import haxe.io.Bytes;
import haxe.io.BytesOutput;

class FunkinPacket {
public var status:Int = -1;
public var head:String;
public var fields:Map<String, String> = new Map<String, String>();
public var body:OneOfTwo<String, Bytes>;

public function new(_head:String, ?_body:OneOfTwo<String, Bytes> = "", ?_status:Int = -1) {
this.head = _head.trim();
this.body = (_body is String) ? _body.trim() : _body;
this.status = _status;
}

public function set(name:String, value:String):FunkinPacket { fields.set(name, value); return this; }
inline public function get(name:String):String { return fields.get(name); }
inline public function exists(name:String):Bool { return fields.exists(name); }
inline public function remove(name:String):Bool { return fields.remove(name); }
inline public function keys():Iterator<String> { return fields.keys(); }

public function toString(?includeBody:Bool = true):String {
var str:String = '';
if (head.length > 0) str += '$head\r\n';
for (key in keys()) str += '$key: ${get(key)}\r\n';
if (!includeBody) return str;
if (body is String) str += body;
else str += (cast body : Bytes).toString();
return str;
}
public function toBytes():Bytes {
var bytes:BytesOutput = new BytesOutput();
bytes.writeString(toString(false)); // Absolute Cinema, thanks AbstractAndrew for the Revolutionary Idea 🔥🔥
if (body is String) bytes.writeString(body);
else if (body is Bytes) bytes.write(body);
return bytes.getBytes();
}

public static function fromBytes(bytes:Bytes):Null<FunkinPacket> {
var status:Int = -1;

var header_length:Int = -1;
var header:String = "";

var body_is_string:Bool = false;
var body_length:Int = -1;
var body:OneOfTwo<String, Bytes> = null;

try {
var offset:Int = 0;
status = bytes.getInt32(0); offset += 4;
header_length = bytes.getInt32(offset); offset += 4;
header = bytes.getString(offset, header_length); offset += header_length;
body_is_string = (bytes.get(offset) == 1); offset += 1;
body_length = bytes.getInt32(offset); offset += 4;
if (body_is_string) body = bytes.getString(offset, body_length);
else body = bytes.sub(offset, body_length);
} catch(e) {
FlxG.log.error('FunkinPacket.fromBytes() failed to parse packet: $e');
return null;
}
var packet:FunkinPacket = new FunkinPacket(null, body, status);
for (line in header.split("\r\n")) {
var data = line.split(": ");
if (data.length < 2) continue;
var key:String = data.shift().trim();
var value:String = data.shift().trim();
packet.set(key, value);
}
return packet;
}
}
129 changes: 129 additions & 0 deletions source/funkin/backend/system/net/FunkinSocket.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package funkin.backend.system.net;

#if sys
import sys.net.Host;
import sys.net.Socket as SysSocket;
import haxe.io.Bytes;

@:keep
class FunkinSocket implements IFlxDestroyable {
public var socket:SysSocket = new SysSocket();

public var metrics:Metrics = new Metrics();

public var FAST_SEND(default, set):Bool = true;
private function set_FAST_SEND(value:Bool):Bool {
FAST_SEND = value;
socket.setFastSend(value);
return value;
}
public var BLOCKING(default, set):Bool = false;
private function set_BLOCKING(value:Bool):Bool {
BLOCKING = value;
socket.setBlocking(value);
return value;
}

public var host:Host;
public var port:Int;

public function new(?_host:String = "127.0.0.1", ?_port:Int = 5000) {
FAST_SEND = true;
BLOCKING = false;
this.host = new Host(_host);
this.port = _port;
}

// Reading Area
public function readAll():Null<Bytes> {
try {
var bytes = this.socket.input.readAll();
if (bytes == null) return null;
metrics.updateBytesReceived(bytes.length);
return bytes;
} catch(e) { }
return null;
}
public function readLine():Null<String> {
try {
var bytes = this.socket.input.readLine();
if (bytes == null) return null;
metrics.updateBytesReceived(bytes.length);
return bytes;
} catch(e) { }
return null;
}
public function read(nBytes:Int):Null<Bytes> {
try {
var bytes = this.socket.input.read(nBytes);
if (bytes == null) return null;
metrics.updateBytesReceived(bytes.length);
return bytes;
} catch(e) { }
return null;
}
public function readBytes(bytes:Bytes):Int {
try {
var length = this.socket.input.readBytes(bytes, 0, bytes.length);
metrics.updateBytesReceived(length);
return length;
} catch(e) { }
return 0;
}

// Writing Area
public function prepare(nbytes:Int):Void { socket.output.prepare(nbytes); }
public function write(bytes:Bytes):Bool {
try {
this.socket.output.write(bytes);
metrics.updateBytesSent(bytes.length);
return true;
} catch (e) { }
return false;
}
public function writeString(str:String):Bool {
try {
this.socket.output.writeString(str);
metrics.updateBytesSent(Bytes.ofString(str).length);
return true;
} catch(e) { }
return false;
}

public function bind(?expectingConnections:Int = 1):FunkinSocket {
Logs.traceColored([
Logs.logText('[FunkinSocket] ', BLUE),
Logs.logText('Binding to ', NONE), Logs.logText(host.toString(), YELLOW), Logs.logText(':', NONE), Logs.logText(Std.string(port), CYAN),
]);
socket.bind(host, port);
socket.listen(expectingConnections);
return this;
}

public function connect():FunkinSocket {
Logs.traceColored([
Logs.logText('[FunkinSocket] ', BLUE),
Logs.logText('Connecting to ', NONE), Logs.logText(host.toString(), YELLOW), Logs.logText(':', NONE), Logs.logText(Std.string(port), CYAN),
]);
socket.connect(host, port);
return this;
}

public function close() {
try {
if (socket != null) socket.close();
Logs.traceColored([
Logs.logText('[FunkinSocket] ', BLUE),
Logs.logText('Closing socket from ', NONE), Logs.logText(host.toString(), YELLOW), Logs.logText(':', NONE), Logs.logText(Std.string(port), CYAN),
]);
} catch(e) {
Logs.traceColored([
Logs.logText('[FunkinSocket] ', BLUE),
Logs.logText('Failed to close socket: ${e}', NONE),
]);
}
}

public function destroy() { close(); }
}
#end
Loading