P2P Export - Nodes - Patch

This commit is contained in:
2026-10-06 02:10:08 -07:00
parent 9fee67d231
commit 4ab646015d
49 changed files with 2478 additions and 1164 deletions

View File

@ -1,79 +0,0 @@
package leenkx.logicnode;
import leenkx.system.Event;
#if js
import leenkx.network.Leenkx;
#end
import iron.object.Object;
class AddTorrentNode extends LogicNode {
public var title: String;
public var values: Array<Dynamic>;
public var net_Url: String;
public var data: Dynamic;
public var runner: Bool = false;
public function new(tree:LogicTree) {
super(tree);
}
public function promiseResult(connection,torrentid,net_Url){
#if js
if (runner == false){
values = [];
var script = 'var torrentId = "' + torrentid + '";
lx_' + net_Url +'.wt.add(torrentId, function (torrent) {
leenkx.network.Leenkx.torrent.set("' + net_Url +'", torrent.files);
torrent.on("done", function () {
leenkx.network.Leenkx.connections.h["' + net_Url +'"].ontorrentdone();
})
});
';
js.Syntax.code('(1, eval)({0})', script.toString());
runner = true;
promiseResult(connection,torrentid,net_Url);
return;
}else{
try{
values = leenkx.network.Leenkx.torrent.get(net_Url);
if(values.length > 0){
runOutput(0);
runner = false;
return;
}
}
catch(error){
haxe.Timer.delay(function () {
promiseResult(connection,torrentid,net_Url);
return;
}, 100);
}
}
#end
}
override function run(from:Int) {
var connection = inputs[1].get();
if (connection == null) return;
var torrentid = inputs[2].get();
if (torrentid == null) return;
#if js
net_Url = connection._url;
promiseResult(connection,torrentid,net_Url);
#end
}
override function get(from: Int): Dynamic {
#if js
return switch (from) {
case 1: Leenkx.id.get(net_Url);
case 2: leenkx.network.Leenkx.torrent.get(net_Url);
default: throw "Unreachable";
}
#else
return null;
#end
}
}

View File

@ -1,131 +1,168 @@
package leenkx.logicnode;
import iron.object.Object;
#if js
import leenkx.network.Types;
import leenkx.network.Util;
#if lnx_torrent
import leenkx.network.Leenkx;
import leenkx.network.torrent.LeenkxClient;
import leenkx.network.torrent.TorrentClient;
import leenkx.network.torrent.peer.RtcPeerPool;
#end
import leenkx.system.Event;
class CreateLeenkxNode extends LogicNode {
public var property0: String;
public var property1: Bool;
public var property2: String;
public var property3: String;
public var net_Url: String;
public var data: Dynamic;
public var client: Dynamic;
public var value: String;
public var listener: TEvent = null;
public function new(tree:LogicTree) {
super(tree);
super(tree);
}
override function run(from:Int) {
#if js
net_Url = inputs[1].get();
if (net_Url == null) return;
if(Leenkx.connections[net_Url] == null){
var object = tree.object;
value = Leenkx.onLoadEvent;
listener = Event.add(value, onEvent, tree.object.uid);
client = new leenkx.network.Leenkx(net_Url, object);
} else {
return;
}
#end
}
function onEvent() {
#if js
var lnxjs:Dynamic = js.Lib.global;
var lnxCxNew = lnxjs.lnxNew;
if (lnxCxNew == null) {
trace("ERROR: lnxNew not available");
return;
#if lnx_torrent
net_Url = inputs[1].get();
if (net_Url == null) return;
if (Leenkx.connections[net_Url] == null) {
value = Leenkx.onLoadEvent;
listener = Event.add(value, onEvent, tree.object.uid);
new leenkx.network.Leenkx(net_Url, tree.object);
}
var lnxCn:Dynamic = lnxCxNew(net_Url);
lnxCn.on("connections", function(c) {
leenkx.network.Leenkx.data.set(net_Url, c + 1);
leenkx.network.Leenkx.connections[net_Url].onconnections();
});
lnxCn.on("message", function(address, message) {
leenkx.network.Leenkx.data.set(net_Url, message);
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onmessage();
});
lnxCn.on("seen", function(address) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onseen();
});
lnxCn.on("left", function(address) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onleft();
});
lnxCn.on("server", function(address) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onserver();
});
lnxCn.on("ping", function(address) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onping();
});
lnxCn.on("timeout", function(address) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].ontimeout();
});
lnxCn.on("rpc", function(address, call, args, nonce) {
leenkx.network.Leenkx.data.set(net_Url, call);
leenkx.network.Leenkx.id.set(net_Url, address);
call(args);
leenkx.network.Leenkx.connections[net_Url].onrpc();
});
lnxCn.on("rpc-response", function(address, nonce, response) {
leenkx.network.Leenkx.id.set(net_Url, address);
leenkx.network.Leenkx.connections[net_Url].onrpcresponse();
});
lnxCn.on("wireleft", function(wirecount, wire) {
leenkx.network.Leenkx.data.set(net_Url, wirecount);
leenkx.network.Leenkx.id.set(net_Url, wire.peerId);
leenkx.network.Leenkx.connections[net_Url].onwireleft();
});
lnxCn.on("wireseen", function(wirecount, wire) {
leenkx.network.Leenkx.data.set(net_Url, wirecount);
leenkx.network.Leenkx.id.set(net_Url, wire.peerId);
leenkx.network.Leenkx.connections[net_Url].onwireseen();
});
lnxCn.on("torrent", function(identifier, torrent) {
leenkx.network.Leenkx.data.set(net_Url, torrent);
leenkx.network.Leenkx.id.set(net_Url, identifier);
leenkx.network.Leenkx.connections[net_Url].ontorrent();
});
lnxCn.on("tracker", function(identifier) {
leenkx.network.Leenkx.id.set(net_Url, identifier);
leenkx.network.Leenkx.connections[net_Url].ontracker();
});
lnxCn.on("announce", function(identifier) {
leenkx.network.Leenkx.id.set(net_Url, identifier);
leenkx.network.Leenkx.connections[net_Url].onannounce();
});
Reflect.setField(lnxjs, "lnx_" + net_Url, lnxCn);
Leenkx.connections[net_Url].client = lnxCn;
var script = 'globalThis.addEventListener("beforeunload", function (e) {
leenkx.network.Leenkx.connections.h["' + net_Url + '"].client.destroy();
delete e["returnValue"];
});';
js.Syntax.code('(1, eval)({0})', script);
runOutput(0);
#end
}
function onEvent() {
if (listener != null) {
Event.removeListener(listener);
listener = null;
}
#if lnx_torrent
var sock = Leenkx.connections[net_Url];
if (sock == null) return;
if (sock.client != null) {
runOutput(0);
return;
}
var opts:leenkx.network.torrent.Types.LeenkxOptions = {deferJoin: true};
var nextInput = 2;
if (property0 == "datachannel") {
opts.transport = "datachannel";
var imode = property2 != null ? property2 : "inherit";
if (imode != "inherit") {
if (inputs.length > nextInput) {
var ice = TorrentClient.trackerUrls(inputs[nextInput].get());
if (ice != null) {
opts.iceServers = imode == "override" ? ice : RtcPeerPool.defaultIceServers.concat(ice);
}
}
nextInput++;
}
}
var tmode = property3 != null ? property3 : "inherit";
var trackers:Array<String> = null;
if (tmode != "inherit" && inputs.length > nextInput) {
trackers = TorrentClient.trackerUrls(inputs[nextInput].get());
}
opts.announce = tmode == "override" && trackers != null ? trackers
: trackers != null ? TorrentClient.defaultTrackers.concat(trackers)
: TorrentClient.defaultTrackers;
var lnxCn = new LeenkxClient(net_Url, opts);
lnxCn.isRelay = property1 == true;
lnxCn.onOpen = function() {
Leenkx.data.set(net_Url, lnxCn.lastPeerCount);
Leenkx.id.set(net_Url, lnxCn.lastPeerId);
sock.onopen();
};
lnxCn.onConnections = function(c) {
Leenkx.data.set(net_Url, c + 1);
sock.onconnections();
};
lnxCn.onMessage = function(address, message, nonce) {
Leenkx.data.set(net_Url, message);
Leenkx.id.set(net_Url, address);
sock.onmessage();
};
lnxCn.onSeen = function(address) {
Leenkx.id.set(net_Url, address);
sock.onseen();
};
lnxCn.onLeft = function(address) {
Leenkx.id.set(net_Url, address);
sock.onleft();
};
lnxCn.onServer = function(address) {
Leenkx.id.set(net_Url, address);
sock.onserver();
};
lnxCn.onPing = function(address) {
Leenkx.id.set(net_Url, address);
sock.onping();
};
lnxCn.onTimeout = function(address) {
Leenkx.id.set(net_Url, address);
sock.ontimeout();
};
lnxCn.onWireLeft = function(wirecount, wire) {
Leenkx.data.set(net_Url, wirecount);
if (wire != null && wire.peerId != null) {
Leenkx.id.set(net_Url, wire.peerId);
}
sock.onwireleft();
};
lnxCn.onWireSeen = function(wirecount, wire) {
Leenkx.data.set(net_Url, wirecount);
if (wire != null && wire.peerId != null) {
Leenkx.id.set(net_Url, wire.peerId);
}
sock.onwireseen();
};
lnxCn.onTorrent = function(identifier, torrent) {
Leenkx.data.set(net_Url, torrent);
Leenkx.id.set(net_Url, identifier);
sock.ontorrent();
};
lnxCn.onTracker = function(identifier, tracker) {
Leenkx.id.set(net_Url, identifier);
sock.ontracker();
};
lnxCn.onAnnounce = function(identifier) {
Leenkx.id.set(net_Url, identifier);
sock.onannounce();
};
lnxCn.onError = function(msg) {
Leenkx.data.set(net_Url, msg);
sock.onerror();
};
lnxCn.onTorrentAdded = function(t) {
Leenkx.torrent.set(net_Url, t);
Leenkx.file.set(net_Url, t.files);
Leenkx.data.set(net_Url, t);
sock.ontorrentadded();
};
lnxCn.onTorrentDone = function(t) {
Leenkx.data.set(net_Url, t);
sock.ontorrentdone();
};
sock.client = lnxCn;
lnxCn.connect();
#if (js && !kha_krom)
js.Browser.window.addEventListener("beforeunload", function(e) {
lnxCn.destroy();
});
#end
#end
runOutput(0);
}
override function get(from: Int): Dynamic {
#if js
#if lnx_torrent
return switch (from) {
case 1: Leenkx.connections[net_Url];
case 2: Leenkx.id.get(net_Url);

View File

@ -1,43 +1,100 @@
package leenkx.logicnode;
import leenkx.system.Event;
#if lnx_torrent
import leenkx.network.Leenkx;
import iron.object.Object;
import leenkx.network.torrent.Magnet;
import leenkx.network.torrent.TorrentClient;
#end
class CreateTorrentNode extends LogicNode {
public var torrent: Dynamic;
public var net_Url: String;
public var meta:Dynamic;
public var metaBytes:Dynamic;
public var magnetUri:Dynamic;
public var infoHashHex:String;
public var property0:String;
public var property1:String;
public function new(tree:LogicTree) {
super(tree);
}
}
override function run(from:Int) {
#if lnx_torrent
meta = null;
metaBytes = null;
magnetUri = null;
infoHashHex = null;
var connection = inputs[1].get();
if (connection == null) return;
var data = inputs[2].get();
if (data == null) return;
net_Url = connection._url;
var data = inputs[1].get();
if (data == null) return;
var mode = property1 != null ? property1 : "file";
var opts:Dynamic = {};
var mv = property0 != null ? Std.parseInt(property0) : null;
if (mv != null) Reflect.setField(opts, "metaVersion", mv);
field(2, opts, "name");
field(3, opts, "comment");
field(4, opts, "createdBy");
field(5, opts, "source");
field(6, opts, "announce");
field(7, opts, "private");
field(8, opts, "pieceLength");
field(9, opts, "urlList");
var script = 'var file = ' + data + ';
lx_' + net_Url +'.wt.add(file, function (torrent) {
leenkx.network.Leenkx.file.set("' + net_Url +'", torrent.files);
torrent.on("done", function () {
leenkx.network.Leenkx.connections.h["' + net_Url +'"].ontorrentdone();
});
});
';
if (mode == "folder") {
#if sys
data = TorrentClient.readFolder(Std.string(data));
var first:Dynamic = data[0];
if (first != null && Reflect.field(opts, "name") == null) {
var p = Std.string(first.path);
var i = p.indexOf("/");
Reflect.setField(opts, "name", p.substring(0, i >= 0 ? i : p.length));
}
#else
trace("Folder mode requires a sys target";
#end
}
js.Syntax.code('(1, eval)({0})', script.toString());
var m;
try {
m = TorrentClient.buildMeta(data, opts);
} catch(e:Dynamic) {
trace(e);
return;
}
meta = m;
metaBytes = m.toTorrentBytes();
infoHashHex = m.infoHashHex != null ? m.infoHashHex : m.infoHashV2Hex;
magnetUri = Magnet.build(m.infoHash, m.name, m.announce, m.urlList, m.infoHashV2);
#end
runOutput(0);
}
#if lnx_torrent
function field(i:Int, opts:Dynamic, name:String):Void {
if (i >= inputs.length) return;
var v:Dynamic = inputs[i].get();
if (v == null || Std.string(v) == "") {
return;
}
if (name == "announce") {
v = TorrentClient.trackerUrls(v);
if (v == null) return;
}
Reflect.setField(opts, name, v);
}
#end
override function get(from: Int): Dynamic {
return switch (from) {
case 1: Leenkx.file.get(net_Url);
default: throw "Unreachable";
}
}
override function get(from:Int):Dynamic {
#if lnx_torrent
return switch (from) {
case 1: meta;
case 2: metaBytes;
case 3: infoHashHex;
case 4: magnetUri;
default: throw "Unreachable";
}
#else
return null;
#end
}
}

View File

@ -0,0 +1,88 @@
package leenkx.logicnode;
#if (lnx_torrent && (sys || kha_krom))
import leenkx.network.torrent.tracker.HttpTracker.HttpTrackerServer;
import leenkx.network.torrent.tracker.UdpTracker.UdpTrackerServer;
import leenkx.network.torrent.transport.WsTrackerClient.WsTrackerServer;
#end
#if (lnx_torrent && sys)
import leenkx.network.torrent.transport.WsTrackerClient.WssTrackerServer;
#end
class CreateTrackerNode extends LogicNode {
public var property0:String;
public var tracker:Dynamic;
public var url:String;
public function new(tree:LogicTree) {
super(tree);
}
override function run(from:Int) {
tracker = null;
url = null;
startServer();
runOutput(0);
}
function startServer():Void {
#if (lnx_torrent && (sys || kha_krom))
var hv = inputs[1].get();
var host = hv != null ? Std.string(hv) : "127.0.0.1";
if (host == "") host = "127.0.0.1";
var pv = inputs[2].get();
var port = pv != null ? Std.int(Std.parseFloat(Std.string(pv))) : 0;
if (port <= 0) port = 6969;
var scheme = property0 != null ? property0 : "udp";
switch (scheme) {
case "http":
var s = new HttpTrackerServer();
s.onError = function(e) trace(e);
s.start(port);
tracker = s;
url = "http://" + host + ":" + port + "/announce";
case "ws":
var s = new WsTrackerServer(host, port);
s.onError = function(e) trace(e);
s.start();
tracker = s;
url = "ws://" + host + ":" + port;
case "wss":
#if sys
var cert = inputs.length > 3 ? Std.string(inputs[3].get()) : null;
var key = inputs.length > 4 ? Std.string(inputs[4].get()) : null;
if (cert == null || cert == "" || key == null || key == "") {
trace("wss server needs Cert File and Key File");
return;
}
var s = new WssTrackerServer(host, port, cert, key);
s.onError = function(e) trace(e);
s.start();
tracker = s;
url = "wss://" + host + ":" + port;
#else
trace("wss server requires a sys target");
#end
default:
var s = new UdpTrackerServer();
s.onError = function(e) trace(e);
s.start(port);
tracker = s;
url = "udp://" + host + ":" + port;
}
#else
trace("server mode needs a sys or krom target");
#end
}
override function get(from:Int):Dynamic {
#if lnx_torrent
return switch (from) {
case 1: tracker;
case 2: url;
default: throw "Unreachable";
}
#else
return null;
#end
}
}

View File

@ -0,0 +1,101 @@
package leenkx.logicnode;
import leenkx.system.Event;
#if lnx_torrent
import leenkx.network.Leenkx;
import leenkx.network.torrent.TorrentClient;
#end
class DownloadTorrentNode extends LogicNode {
public var net_Url:String;
public var torrent:Dynamic;
public var property0:String;
public var property1:String;
var listener:TEvent = null;
public function new(tree:LogicTree) {
super(tree);
}
public function promiseResult(connection, torrentid, net_Url, ?opts:Dynamic) {
#if lnx_torrent
var socket = Leenkx.connections[net_Url];
if (socket == null) {
if (listener == null) {
var self = this;
listener = Event.add(Leenkx.onLoadEvent, function() {
if (Leenkx.connections[net_Url] == null) return;
if (self.listener != null) {
Event.removeListener(self.listener);
self.listener = null;
}
self.promiseResult(connection, torrentid, net_Url, opts);
});
}
return;
}
var client = socket.client;
if (client == null) return;
var self = this;
client.add(torrentid, opts, function(torrent) {
self.torrent = torrent;
var onReady = function() {
self.runOutput(0);
};
var onDone = function() {
self.runOutput(4);
};
if (torrent.ready) onReady();
else torrent.on("ready", onReady);
if (torrent.done) onDone();
else torrent.on("done", onDone);
});
#end
}
function onRemove() {
if (listener != null) {
Event.removeListener(listener);
listener = null;
}
}
override function run(from:Int) {
var connection = inputs[1].get();
if (connection == null) return;
var v = inputs[2].get();
if (v == null) return;
#if lnx_torrent
net_Url = connection._url;
var torrentId:Dynamic = switch (property0 != null ? property0 : "magnet") {
case "file": v;
case "id": TorrentClient.torrentIdFor(v);
default: Std.string(v);
};
var opts:Dynamic = null;
var tmode = property1 != null ? property1 : "inherit";
if (tmode != "inherit" && inputs.length > 3) {
var announce = TorrentClient.trackerUrls(inputs[3].get());
opts = {};
if (announce != null) opts.announce = announce;
if (tmode == "override") opts.announceOverride = true;
}
promiseResult(connection, torrentId, net_Url, opts);
#end
}
override function get(from:Int):Dynamic {
#if lnx_torrent
var t:leenkx.network.torrent.Torrent = torrent;
return switch (from) {
case 1: torrent;
case 2: t != null && t.meta != null ? t.meta.name : null;
case 3: t != null ? t.files : null;
case 5: t != null ? t.infoHashHex : null;
case 6: t != null ? t.magnetURI : null;
default: throw "Unreachable";
}
#else
return null;
#end
}
}

View File

@ -0,0 +1,83 @@
package leenkx.logicnode;
#if lnx_torrent
import leenkx.network.Leenkx;
import leenkx.network.torrent.Torrent;
import leenkx.network.torrent.TorrentFile;
#end
class GetTorrentFileNode extends LogicNode {
public var property0:String;
public var file:Dynamic;
var net_Url:String;
public function new(tree:LogicTree) {
super(tree);
}
override function run(from:Int) {
#if lnx_torrent
file = null;
var id = inputs[2].get();
var match = inputs[3].get();
if (id != null && match != null) {
var connection = inputs[1].get();
if (connection != null) net_Url = connection._url;
var socket = net_Url != null ? Leenkx.connections[net_Url] : null;
var client = socket != null ? socket.client : null;
var t = client != null ? client.get(id) : (Std.isOfType(id, Torrent) ? id : null);
if (t != null) file = findFile(t, match);
}
#end
runOutput(0);
}
#if lnx_torrent
function findFile(t:Torrent, match:Dynamic):TorrentFile {
var files = t.files;
switch (property0) {
case "index":
var i = Std.parseInt(Std.string(match));
if (i != null && i >= 0 && i < files.length) {
return files[i];
}
case "name":
var s = Std.string(match);
for (f in files) {
if (f.name == s) return f;
}
case "path":
var s = Std.string(match);
for (f in files) {
if (f.path == s) return f;
}
default:
var s = Std.string(match);
if (!StringTools.startsWith(s, ".")) s = "." + s;
for (f in files) {
if (StringTools.endsWith(f.name, s)) return f;
}
}
return null;
}
#end
override function get(from:Int):Dynamic {
#if lnx_torrent
var f:TorrentFile = file;
return switch (from) {
case 1: file;
case 2: f != null ? f.name : null;
case 3: f != null ? f.path : null;
case 4: f != null ? f.length : 0;
case 5: f != null ? f.type : null;
case 6: f != null ? f.getBytes() : null;
case 7: f != null;
case 8: f != null ? f.downloaded : 0.0;
case 9: f != null ? f.progress : 0.0;
default: throw "Unreachable";
}
#else
return null;
#end
}
}

View File

@ -1,8 +1,8 @@
package leenkx.logicnode;
import leenkx.system.Event;
import leenkx.network.Connect;
import iron.object.Object;
#if lnx_torrent
import leenkx.network.Leenkx;
import leenkx.network.torrent.LeenkxClient;
#end
class LeenkxCloseConnectionNode extends LogicNode {
public var property0: String;
@ -12,41 +12,29 @@ class LeenkxCloseConnectionNode extends LogicNode {
}
override function run(from:Int) {
#if lnx_torrent
var connection = inputs[1].get();
if (connection == null) return;
if (connection == null) return;
var socket = Leenkx.connections[connection._url];
if (socket == null) return;
if(property0 == "client") {
var script = '
for (p in leenkx.network.Leenkx.connections.h["' + connection._url + '"].client.torrent._peers){
try{
leenkx.network.Leenkx.id.set("' + connection._url + '",p);
leenkx.network.Leenkx.connections.h["' + connection._url + '"].client.torrent._peers[p].conn._pc.close();
leenkx.network.Leenkx.connections.h["' + connection._url + '"].client.torrent._peers[p].conn.destroy();
leenkx.network.Leenkx.connections.h["' + connection._url + '"].onclose();
}catch(error){
console.log("Error: " + error);
}
}
';
js.Syntax.code('(1, eval)({0})', script.toString());
} else {
var script = '
try{
var lnxConn = leenkx.network.Leenkx.connections.h["' + connection._url + '"];
if (lnxConn && lnxConn.client && lnxConn.client.torrent && lnxConn.client.torrent._peers) {
for (var p in lnxConn.client.torrent._peers) {
lnxConn.client.torrent._peers[p].conn._pc.close();
lnxConn.client.torrent._peers[p].conn.destroy();
leenkx.network.Leenkx.id.set("' + connection._url + '", p);
lnxConn.onclose();
}
}
}catch(error){
console.log("Error: " + error);
}
';
js.Syntax.code('(1, eval)({0})', script.toString());
var lnxCn:LeenkxClient = socket.client;
if (property0 == "peer") {
var id:String = inputs.length > 2 ? inputs[2].get() : null;
if (id != null && lnxCn != null) {
lnxCn.peers.remove(id);
lnxCn.seenFired.remove(id);
}
runOutput(0);
return;
}
if (lnxCn != null) {
lnxCn.destroy();
socket.client = null;
}
Leenkx.connections.remove(connection._url);
socket.onclose();
#end
runOutput(0);
}
}

View File

@ -1,10 +1,10 @@
package leenkx.logicnode;
import leenkx.system.Event;
#if lnx_torrent
import leenkx.network.Leenkx;
import iron.object.Object;
#end
class LeenkxEventNode extends LogicNode {
public var property0: String;
public var property1: String;
public var value: String;
@ -14,90 +14,63 @@ class LeenkxEventNode extends LogicNode {
public function new(tree:LogicTree) {
super(tree);
tree.notifyOnInit(init);
tree.notifyOnRemove(onRemove);
}
function init() {
if (property0 == "client") {
net_Url = inputs[0].get();
switch (property1) {
case "onopen": value = Leenkx.onOpenEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onmessage": value = Leenkx.onMessageEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onerror": value = Leenkx.onErrorEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onclose": value = Leenkx.onCloseEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onseen": value = Leenkx.onSeenEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onserver": value = Leenkx.onServerEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onconnections": value = Leenkx.onConnectionsEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onping": value = Leenkx.onPingEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onleft": value = Leenkx.onLeftEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "ontimeout": value = Leenkx.onTimeOutEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onrpc": value = Leenkx.onRpcEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onrpcresponse": value = Leenkx.onRpcResponseEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onwireleft": value = Leenkx.onWireLeftEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onwireseen": value = Leenkx.onWireSeenEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "ontorrent": value = Leenkx.onTorrentEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "ontracker": value = Leenkx.onTrackerEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onannounce": value = Leenkx.onAnnounceEvent;listener = Event.add(value, onEvent, tree.object.uid);
default: throw "Failed to set client event type.";
}
} else if (property0 == "host") {
#if (sys || kha_krom)
var net_Domain = inputs[0].get();
var net_Port = inputs[1].get();
net_Url = "ws://" + net_Domain + ":" + Std.string(net_Port);
switch (property1) {
case "onopen": value = Leenkx.onOpenEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onmessage": value = Leenkx.onMessageEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onerror": value = Leenkx.onErrorEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onclose": value = Leenkx.onCloseEvent;listener = Event.add(value, onEvent, tree.object.uid);
default: throw "Failed to set host event type.";
}
#end
} else if (property0 == "securehost"){
#if (sys || kha_krom)
var net_Domain = inputs[0].get();
var net_Port = inputs[1].get();
net_Url = "wss://" + net_Domain + ":" + Std.string(net_Port);
switch (property1) {
case "onopen": value = Leenkx.onOpenEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onmessage": value = Leenkx.onMessageEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onerror": value = Leenkx.onErrorEvent;listener = Event.add(value, onEvent, tree.object.uid);
case "onclose": value = Leenkx.onCloseEvent;listener = Event.add(value, onEvent, tree.object.uid);
default: throw "Failed to set host event type.";
}
#end
#if lnx_torrent
var u:Dynamic = inputs[0].get();
net_Url = Std.isOfType(u, String) ? u : (u != null ? u._url : null);
switch (property1) {
case "onopen": value = Leenkx.onOpenEvent;
case "onmessage": value = Leenkx.onMessageEvent;
case "onerror": value = Leenkx.onErrorEvent;
case "onclose": value = Leenkx.onCloseEvent;
case "onseen": value = Leenkx.onSeenEvent;
case "onserver": value = Leenkx.onServerEvent;
case "onconnections": value = Leenkx.onConnectionsEvent;
case "onping": value = Leenkx.onPingEvent;
case "onleft": value = Leenkx.onLeftEvent;
case "ontimeout": value = Leenkx.onTimeOutEvent;
case "onwireleft": value = Leenkx.onWireLeftEvent;
case "onwireseen": value = Leenkx.onWireSeenEvent;
case "ontorrent": value = Leenkx.onTorrentEvent;
case "ontorrentadded": value = Leenkx.onTorrentAddedEvent;
case "ontracker": value = Leenkx.onTrackerEvent;
case "onannounce": value = Leenkx.onAnnounceEvent;
case "ontorrentdone": value = Leenkx.onTorrentDoneEvent;
case "onload": value = Leenkx.onLoadEvent;
default:
trace("Failed to set client event type");
}
listener = Event.add(value, onEvent, tree.object.uid);
#end
}
function onEvent() {
runOutput(0);
}
override function get(from: Int): Dynamic {
if (property0 == "host") {
return switch (from) {
case 1: Leenkx.id.get(net_Url);
case 2: Leenkx.data.get(net_Url);
default: throw "Unreachable";
}
}
else if (property0 == "securehost") {
return switch (from) {
case 1: Leenkx.id.get(net_Url);
case 2: Leenkx.data.get(net_Url);
default: throw "Unreachable";
}
}
else {
return switch (from) {
case 1: Leenkx.id.get(net_Url);
case 2: Leenkx.data.get(net_Url);
default: throw "Unreachable";
}
function onRemove() {
if (listener != null) {
Event.removeListener(listener);
listener = null;
}
}
override function get(from: Int): Dynamic {
#if lnx_torrent
return switch (from) {
case 1: Leenkx.id.get(net_Url);
case 2: Leenkx.data.get(net_Url);
default: throw "Unreachable";
}
#else
return null;
#end
}
}

View File

@ -1,8 +1,6 @@
package leenkx.logicnode;
import leenkx.network.Connect;
import leenkx.network.Buffer;
import haxe.io.Bytes;
import iron.object.Object;
import iron.math.Vec4;
import iron.math.Quat;
import iron.math.Mat4;
@ -22,140 +20,107 @@ class LeenkxMessageParserNode extends LogicNode {
if (data == null) return;
api = inputs[1].get();
if (api == null) api = "";
var buffer:Buffer;
if (Std.isOfType(data, Buffer)) {
buffer = cast data;
} else {
var rawBytes:Bytes;
try {
rawBytes = haxe.crypto.Base64.decode(Std.string(data));
} catch(e) {
trace("Base64 decode failed: " + e);
return;
}
buffer = new Buffer();
buffer.writeBytes(rawBytes);
}
/**
var buffer = cast(data, Buffer);
api = inputs[1].get();
if(buffer.endsWith(api) == false){
return;
}
**/
var buffer = new Buffer();
if(!StringTools.startsWith(data,api)){
return;
}
switch (property0) {
case "string":
parsed = data.substr(api.length, data.length);
runOutput(0);
case "vector":
var vec = data.substr(api.length, data.length);
vec = StringTools.replace(vec, "(", "");
vec = StringTools.replace(vec, ")", "");
vec = StringTools.replace(vec, " ", "");
vec = StringTools.trim(vec);
var array = vec.split(",").map(Std.parseFloat);
parsed = new Vec4( array[0], array[1], array[2], array[3] );
runOutput(0);
case "float":
var flt:String = data.substr(api.length, data.length);
var float = StringTools.trim(flt);
parsed = Std.parseFloat(float);
runOutput(0);
case "integer":
var int:String = data.substr(api.length, data.length);
var integer = StringTools.trim(int);
parsed = Std.parseInt(integer);
runOutput(0);
case "boolean":
var boolean:String = data.substr(api.length, data.length);
if(boolean == "true"){
parsed = true;
} else {
parsed = false;
}
runOutput(0);
case "transform":
var tsfm = data.substr(api.length, data.length);
tsfm = StringTools.replace(tsfm, "[", "");
tsfm = StringTools.replace(tsfm, "]", "");
tsfm = StringTools.replace(tsfm, " ", "");
tsfm = StringTools.trim(tsfm);
var array = tsfm.split(",").map(Std.parseFloat);
parsed = new Mat4(
array[0], array[1], array[2], array[3],
array[4], array[5], array[6], array[7],
array[8], array[9], array[10], array[11],
array[12], array[13], array[14], array[15]
);
runOutput(0);
case "rotation":
var bytes = new haxe.io.BytesInput(buffer.readUntil(api));
var rot = new Quat();
for (f in Reflect.fields(rot)) {
Reflect.setField(rot, f, bytes.readFloat());
}
parsed = rot;
runOutput(0);
default: throw "Failed to parse data.";
}
return;
switch (property0) {
case "string":
case "string":
var bytes = buffer.readAllAvailableBytes();
parsed = bytes.toString().substr(0, bytes.length - api.length);
trace(parsed);
runOutput(0);
case "vector":
var bytes = new haxe.io.BytesInput(buffer.readUntil(api));
case "vector":
var readBytes = buffer.readUntil(api);
if (readBytes == null) return;
var bytes = new haxe.io.BytesInput(readBytes);
var vec = new Vec4();
for (f in Reflect.fields(vec)) {
Reflect.setField(vec, f, bytes.readFloat());
}
vec.x = bytes.readFloat();
vec.y = bytes.readFloat();
vec.z = bytes.readFloat();
vec.w = bytes.readFloat();
parsed = vec;
runOutput(0);
case "float":
var bytes = new haxe.io.BytesInput(buffer.readUntil(api));
case "float":
var readBytes = buffer.readUntil(api);
if (readBytes == null) return;
var bytes = new haxe.io.BytesInput(readBytes);
var float: Float = bytes.readFloat();
parsed = float;
runOutput(0);
case "integer":
var bytes = new haxe.io.BytesInput(buffer.readUntil(api));
case "integer":
var readBytes = buffer.readUntil(api);
if (readBytes == null) return;
var bytes = new haxe.io.BytesInput(readBytes);
var integer: Int = bytes.readInt32();
parsed = integer;
runOutput(0);
case "boolean":
case "boolean":
var bytes = buffer.readAllAvailableBytes();
var boolean = bytes.toString().substr(0, bytes.length - api.length);
if(boolean == "true"){
if (boolean == "true") {
parsed = true;
} else {
parsed = false;
}
runOutput(0);
case "transform":
var bytes = new haxe.io.BytesInput(buffer.readUntil(api));
var readBytes = buffer.readUntil(api);
if (readBytes == null) return;
var bytes = new haxe.io.BytesInput(readBytes);
var loc: Vec4 = new Vec4();
var rot: Quat = new Quat();
var scl: Vec4 = new Vec4();
for (f in Reflect.fields(loc)) {
Reflect.setField(loc, f, bytes.readFloat());
try {
loc.x = bytes.readFloat();
loc.y = bytes.readFloat();
loc.z = bytes.readFloat();
loc.w = bytes.readFloat();
rot.x = bytes.readFloat();
rot.y = bytes.readFloat();
rot.z = bytes.readFloat();
rot.w = bytes.readFloat();
scl.x = bytes.readFloat();
scl.y = bytes.readFloat();
scl.z = bytes.readFloat();
scl.w = bytes.readFloat();
} catch(e: Dynamic) {
trace("readFloat error: " + e);
return;
}
for (f in Reflect.fields(rot)) {
Reflect.setField(rot, f, bytes.readFloat());
}
for (f in Reflect.fields(scl)) {
Reflect.setField(scl, f, bytes.readFloat());
}
var transform = Mat4.identity();
var transform = Mat4.identity();
parsed = transform.compose(loc, rot, scl);
runOutput(0);
case "rotation":
var bytes = new haxe.io.BytesInput(buffer.readUntil(api));
case "rotation":
var readBytes = buffer.readUntil(api);
if (readBytes == null) return;
var bytes = new haxe.io.BytesInput(readBytes);
var rot = new Quat();
for (f in Reflect.fields(rot)) {
Reflect.setField(rot, f, bytes.readFloat());
}
rot.x = bytes.readFloat();
rot.y = bytes.readFloat();
rot.z = bytes.readFloat();
rot.w = bytes.readFloat();
parsed = rot;
runOutput(0);
default: throw "Failed to parse data.";
}
}
}
override function get(from: Int): Dynamic {
return switch (from) {

View File

@ -1,401 +1,124 @@
package leenkx.logicnode;
import leenkx.network.Connect;
import leenkx.network.Leenkx;
import leenkx.network.Buffer;
import haxe.io.Bytes;
import iron.object.Object;
#if lnx_torrent
import leenkx.network.Leenkx;
import leenkx.network.torrent.LeenkxClient;
#end
import iron.math.Vec4;
import iron.math.Quat;
import iron.math.Mat4;
class LeenkxSendMessageNode extends LogicNode {
public var property0: String;
public var property1: String;
public var id: String;
public var all: Bool;
public var property2: Bool;
public function new(tree:LogicTree) {
super(tree);
}
override function run(from:Int) {
#if js
var connection = inputs[1].get();
if (connection == null) return;
var api: String = inputs[2].get();
if (connection == null) return;
var api: String = inputs[2].get();
var message: Dynamic = inputs[3].get();
if (message == null) return;
var broadcast: Bool = inputs.length > 4 && inputs[4].get() == true;
var targetAddress: String = inputs.length > 5 ? inputs[5].get() : null;
if (!broadcast && (targetAddress == null || targetAddress == "")) {
return;
}
#if lnx_torrent
var socket = Leenkx.connections[connection._url];
if (socket == null) return;
var lnxCn:LeenkxClient = socket.client;
if (lnxCn == null) return;
var buffer = new Buffer();
switch (property1) {
case "string":
buffer.writeBytes(Bytes.ofString(message));
case "vector":
var bytesOut = new haxe.io.BytesOutput();
var f:Null<Float>;
if ((f = message.x) != null) bytesOut.writeFloat(f);
if ((f = message.y) != null) bytesOut.writeFloat(f);
if ((f = message.z) != null) bytesOut.writeFloat(f);
if ((f = message.w) != null) bytesOut.writeFloat(f);
buffer.writeBytes(bytesOut.getBytes());
case "float":
var bytesOut = new haxe.io.BytesOutput();
bytesOut.writeFloat(message);
buffer.writeBytes(bytesOut.getBytes());
case "integer":
var bytesOut = new haxe.io.BytesOutput();
bytesOut.writeInt32(message);
buffer.writeBytes(bytesOut.getBytes());
case "boolean":
if (message == true) {
buffer.writeBytes(Bytes.ofString("true"));
} else {
buffer.writeBytes(Bytes.ofString("false"));
}
case "transform":
var bytesOut = new haxe.io.BytesOutput();
var transform:Mat4 = message;
var loc = new Vec4();
var rot = new Quat();
var scale = new Vec4();
transform.decompose(loc, rot, scale);
bytesOut.writeFloat(loc.x);
bytesOut.writeFloat(loc.y);
bytesOut.writeFloat(loc.z);
bytesOut.writeFloat(loc.w);
bytesOut.writeFloat(rot.x);
bytesOut.writeFloat(rot.y);
bytesOut.writeFloat(rot.z);
bytesOut.writeFloat(rot.w);
bytesOut.writeFloat(scale.x);
bytesOut.writeFloat(scale.y);
bytesOut.writeFloat(scale.z);
bytesOut.writeFloat(scale.w);
buffer.writeBytes(bytesOut.getBytes());
case "rotation":
var bytesOut = new haxe.io.BytesOutput();
var f:Null<Float>;
if ((f = message.x) != null) bytesOut.writeFloat(f);
if ((f = message.y) != null) bytesOut.writeFloat(f);
if ((f = message.z) != null) bytesOut.writeFloat(f);
if ((f = message.w) != null) bytesOut.writeFloat(f);
buffer.writeBytes(bytesOut.getBytes());
default: trace("Failed to send data.");
}
if (api != null && api != "") {
buffer.writeBytes(Bytes.ofString(api));
}
var rawBytes = buffer.readAllAvailableBytes();
try {
if(inputs[5].get() == true){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lnx_' + connection._url +'.torrent._peers[p].conn.send(`'+ api + message + '`);
}catch(error){
console.log("Error: " + error);
}
}';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
if (property2) {
var payload = haxe.crypto.Base64.encode(rawBytes);
if (broadcast) {
lnxCn.send(null, payload);
} else {
lnxCn.send(targetAddress, payload);
}
} else {
var script = 'lnx_' + connection._url +'.send(`' + inputs[4].get() + '`, `'+ api + message + '` );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
lnxCn.sendRawMessage(rawBytes, broadcast ? null : targetAddress);
}
} catch(error) {
trace("Error: " + error);
}
return;
runOutput(0);
switch (property1) {
case "string":
if(property0 == "client"){
var buffer = new Buffer();
buffer.writeBytes(Bytes.ofString(message));
if(api != "" || api != null){
buffer.writeBytes(Bytes.ofString(api));
try {
connection.buffer = buffer;
if(inputs[5].get() == true){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
}catch(error){
console.log("Error: " + error);
}
}';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
} catch(error) {
trace("Error: " + error);
}
}
} else {
if(inputs[5].get() == true){
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}else{
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}
runOutput(0);
return;
}
case "vector":
if(property0 == "client"){
var bytesOut = new haxe.io.BytesOutput();
var i = Reflect.fields(message);
for (b in i) {
bytesOut.writeFloat(Reflect.field(message, b));
}
var buffer = new Buffer();
buffer.writeBytes(bytesOut.getBytes());
if(api != "" || api != null){
buffer.writeBytes(Bytes.ofString(api));
}
#else
runOutput(0);
try {
connection.buffer = buffer;
if(inputs[5].get() == true){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lnx_' + connection._url +'.torrent._peers[p].conn.send("' + api + message + '");
}catch(error){
console.log("Error: " + error);
}
}';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
} catch(error) {
trace("Error: " + error);
}
} else {
if(inputs[5].get() == true){
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}else{
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}
runOutput(0);
return;
}
case "float":
if(property0 == "client"){
var bytesOut = new haxe.io.BytesOutput();
bytesOut.writeFloat(message);
var buffer = new Buffer();
buffer.writeBytes(bytesOut.getBytes());
if(api != "" || api != null){
buffer.writeBytes(Bytes.ofString(api));
}
try {
connection.buffer = buffer;
if(inputs[5].get() == true){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
}catch(error){
console.log("Error: " + error);
}
}';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
} catch(error) {
trace("Error: " + error);
}
} else {
if(inputs[5].get() == true){
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}else{
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}
runOutput(0);
return;
}
case "integer":
if(property0 == "client"){
var bytesOut = new haxe.io.BytesOutput();
bytesOut.writeInt32(message);
var buffer = new Buffer();
buffer.writeBytes(bytesOut.getBytes());
if(api != "" || api != null){
buffer.writeBytes(Bytes.ofString(api));
}
try {
connection.buffer = buffer;
if(inputs[5].get() == true){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
}catch(error){
console.log("Error: " + error);
}
}';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
} catch(error) {
trace("Error: " + error);
}
} else {
if(inputs[5].get() == true){
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}else{
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}
runOutput(0);
return;
}
case "boolean":
if(property0 == "client"){
var buffer = new Buffer();
if(message == true){
buffer.writeBytes(Bytes.ofString("true"));
} else {
buffer.writeBytes(Bytes.ofString("false"));
}
if(api != "" || api != null){
buffer.writeBytes(Bytes.ofString(api));
}
try {
connection.buffer = buffer;
if(inputs[5].get() == true){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
}catch(error){
console.log("Error: " + error);
}
}';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
} catch(error) {
trace("Error: " + error);
}
} else {
if(inputs[5].get() == true){
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}else{
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}
runOutput(0);
return;
}
case "transform":
if(property0 == "client"){
var bytesOut = new haxe.io.BytesOutput();
var transform:Mat4 = message;
var loc = new Vec4();
var rot = new Quat();
var scale = new Vec4();
transform.decompose(loc, rot, scale);
var l = Reflect.fields(loc);
for (b in l) {
bytesOut.writeFloat(Reflect.field(loc, b));
}
var r = Reflect.fields(rot);
for (b in r) {
bytesOut.writeFloat(Reflect.field(rot, b));
}
var s = Reflect.fields(scale);
for (b in s) {
bytesOut.writeFloat(Reflect.field(scale, b));
}
var buffer = new Buffer();
buffer.writeBytes(bytesOut.getBytes());
if(api != "" || api != null){
buffer.writeBytes(Bytes.ofString(api));
}
try {
connection.buffer = buffer;
if(inputs[5].get() == true){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
//console.log("Mine: " + lx_8001.torrent.discovery.peerId + " || Incomming: " + p);
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
}catch(error){
console.log("Error: " + error);
}
}';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
//var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
var script = 'lnx_' + connection._url +'.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
} catch(error) {
trace("Error: " + error);
}
}
case "rotation":
if(property0 == "client"){
var bytesOut = new haxe.io.BytesOutput();
var i = Reflect.fields(message);
for (b in i) {
bytesOut.writeFloat(Reflect.field(message, b));
}
var buffer = new Buffer();
buffer.writeBytes(bytesOut.getBytes());
if(api != "" || api != null){
buffer.writeBytes(Bytes.ofString(api));
}
try {
connection.buffer = buffer;
if(inputs[5].get() == true){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
}catch(error){
console.log("Error: " + error);
}
}';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
} catch(error) {
trace("Error: " + error);
}
} else {
if(inputs[5].get() == true){
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}else{
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
js.Syntax.code('(1, eval)({0})', script);
}
runOutput(0);
return;
}
}
#end
}
}
/*******_________________________________________
try {
if(inputs[5].get() == true){
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lnx_' + connection._url +'.torrent._peers[p].conn.send("' + api + message + '");
}catch(error){
console.log("Error: " + error);
}
}';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
} else {
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
try{
lx_' + connection._url +'.torrent._peers[' + inputs[4].get() + '].conn.send("' + api + message + '");
}catch(error){
console.log("Error: " + error);
}
}';
js.Syntax.code('(1, eval)({0})', script.toString());
runOutput(0);
}
} catch(error) {
trace("Error: " + error);
}
return;
_________________________________________*****/

View File

@ -23,6 +23,7 @@ class NetworkMessageParserNode extends LogicNode {
var buffer = cast(data, Buffer);
api = inputs[1].get();
if (api == null) api = "";
if(buffer.endsWith(api) == false){
return;
}

View File

@ -0,0 +1,65 @@
package leenkx.logicnode;
#if lnx_torrent
import leenkx.network.Leenkx;
import leenkx.network.torrent.Torrent;
#end
class OnTorrentEventNode extends LogicNode {
public var property0:String;
public var eventValue:Dynamic;
var net_Url:String;
var torrent:Dynamic;
var registeredTorrents:Map<String, Bool> = new Map();
public function new(tree:LogicTree) {
super(tree);
}
override function run(from:Int) {
#if lnx_torrent
var connection = inputs[1].get();
if (connection == null) return;
var id = inputs[2].get();
if (id == null) return;
net_Url = connection._url;
var socket = Leenkx.connections[connection._url];
var client = socket != null ? socket.client : null;
var t = client != null ? client.get(id) : (Std.isOfType(id, Torrent) ? id : null);
if (t == null) return;
var event = property0 != null ? property0 : "done";
var self = this;
var fire = function(v:Dynamic) {
self.eventValue = v;
self.runOutput(0);
};
var key = t.infoHashHex != null ? t.infoHashHex : "v2:" + t.infoHashV2Hex;
var alreadyRegistered = registeredTorrents.exists(key);
if (!alreadyRegistered) registeredTorrents.set(key, true);
switch (event) {
case "ready":
if (t.ready) fire(null);
else if (!alreadyRegistered) t.on("ready", function() fire(null));
case "piece" | "wire" | "metadata" | "error":
if (!alreadyRegistered) t.on(event, function(v) fire(v));
default:
if (t.done) fire(null);
else if (!alreadyRegistered) t.on("done", function() fire(null));
}
torrent = t;
#end
}
override function get(from:Int):Dynamic {
#if lnx_torrent
return switch (from) {
case 1: torrent;
case 2: eventValue;
default: throw "Unreachable";
}
#else
return null;
#end
}
}

View File

@ -0,0 +1,73 @@
package leenkx.logicnode;
#if lnx_torrent
import haxe.io.Bytes;
import leenkx.network.torrent.Magnet;
import leenkx.network.torrent.MetaInfo;
#end
class ParseTorrentNode extends LogicNode {
var meta:Dynamic;
var magnet:Dynamic;
var idHex:String;
public var property0:String;
public function new(tree:LogicTree) {
super(tree);
}
override function run(from:Int) {
#if lnx_torrent
meta = null;
magnet = null;
idHex = null;
var src = inputs[1].get();
if (src == null) return;
switch (property0 != null ? property0 : "magnet") {
case "file":
if (Std.isOfType(src, MetaInfo)) {
meta = src;
} else {
var b = Std.isOfType(src, Bytes) ? src : Bytes.ofString(Std.string(src));
try {
meta = MetaInfo.fromTorrentBytes(b);
} catch(e:Dynamic) {
trace(e);
return;
}
}
case "id":
var s = Std.string(src);
idHex = s.length == 64 ? "v2:" + s : s;
default:
var m = Magnet.parse(Std.string(src));
if (m == null) {
return;
}
magnet = m;
idHex = m.infoHashHex != null ? m.infoHashHex : m.infoHashV2Hex;
}
#end
runOutput(0);
}
override function get(from:Int):Dynamic {
#if lnx_torrent
var m:MetaInfo = meta;
var mg:Magnet = magnet;
return switch (from) {
case 1: m;
case 2: m != null ? (m.infoHashHex != null ? m.infoHashHex : m.infoHashV2Hex) : idHex;
case 3: m != null ? m.name : (mg != null ? mg.name : null);
case 4: m != null ? m.announce : (mg != null ? mg.trackers : null);
case 5: m != null ? m.urlList : (mg != null ? mg.webSeeds : null);
case 6: m != null ? m.files : null;
case 7: m != null ? m.totalLength : 0;
case 8: m != null ? m.toTorrentBytes() : null;
default: throw "Unreachable";
}
#else
return null;
#end
}
}

View File

@ -1,52 +1,124 @@
package leenkx.logicnode;
import leenkx.system.Event;
import iron.object.Object;
#if lnx_torrent
import haxe.io.Bytes;
import leenkx.network.Leenkx;
import leenkx.network.torrent.MetaInfo;
import leenkx.network.torrent.Torrent;
#end
class SeedTorrentNode extends LogicNode {
public var torrent: Dynamic;
public var net_Url: Dynamic;
public var torrent:Dynamic;
public var net_Url:String;
public function new(tree:LogicTree) {
super(tree);
}
}
override function run(from:Int) {
#if js
#if lnx_torrent
var connection = inputs[1].get();
if (connection == null) return;
trace("got connection");
var data = inputs[3].get();
if (data == null) return;
trace("got data");
var opts = inputs[2].get();
if (connection == null) return;
var src = inputs[2].get();
net_Url = connection._url;
var cx = "lx_" + net_Url;
var lx:Dynamic = untyped js.Browser.window[cx];
var args = opts.toString();
trace(args);
var func = Reflect.field(lx.wt, "seed");
if (func != null) {
torrent = func(data, function (torrent) {
trace('Client is seeding ' + torrent.magnetURI);
});
//trace(torrent);
var socket = Leenkx.connections[connection._url];
if (socket == null || socket.client == null) return;
var client = socket.client;
var self = this;
var onSeeded = function(t:Torrent) {
if (t == null) return;
self.torrent = t;
var onDone = function() {
self.runOutput(2);
};
if (t.done) onDone();
else t.on("done", onDone);
};
if (Std.isOfType(src, Torrent)) {
var t:Torrent = src;
t.start();
torrent = t;
runOutput(0);
return;
}
var meta:MetaInfo = null;
if (Std.isOfType(src, MetaInfo)) {
meta = cast src;
} else if (Std.isOfType(src, Bytes)) {
try {
meta = MetaInfo.fromTorrentBytes(cast src);
} catch(e:Dynamic) {
return;
}
}
if (meta == null) {
return;
}
var files = filesFor(meta, inputs.length > 3 ? inputs[3].get() : null);
if (files == null) {
return;
}
client.seedMeta(meta, files, null, onSeeded);
runOutput(0);
#end
}
override function get(from: Int): Dynamic {
return switch (from) {
case 1: torrent;
default: throw "Unreachable";
}
}
#if lnx_torrent
function filesFor(meta:MetaInfo, data:Dynamic):Array<{path:String, data:Bytes}> {
if (data == null) return null;
var entries = meta.files;
if (Std.isOfType(data, Bytes)) {
var blob:Bytes = data;
if (blob.length != meta.totalLength) return null;
var files:Array<{path:String, data:Bytes}> = [];
var cursor = 0;
for (f in entries) {
files.push({path: f.path, data: blob.sub(cursor, f.length)});
cursor += f.length;
}
return files;
}
if (Std.isOfType(data, Array)) {
var items:Array<Dynamic> = data;
var files:Array<{path:String, data:Bytes}> = [];
for (it in items) {
var p = Reflect.field(it, "path");
var d:Bytes = Reflect.field(it, "data");
if (d == null) d = Reflect.field(it, "content");
if (p == null || d == null) continue;
var sp = Std.string(p);
var matched = false;
for (f in entries) {
if (f.path == sp || StringTools.endsWith(f.path, "/" + sp)) {
files.push({path: f.path, data: d});
matched = true;
break;
}
}
if (!matched) files.push({path: sp, data: d});
}
if (files.length == entries.length) return files;
return null;
}
return null;
}
#end
override function get(from:Int):Dynamic {
#if lnx_torrent
var t:Torrent = torrent;
return switch (from) {
case 1: torrent;
case 3: t != null ? t.infoHashHex : null;
case 4: t != null ? t.magnetURI : null;
default: throw "Unreachable";
}
#else
return null;
#end
}
}

View File

@ -0,0 +1,90 @@
package leenkx.logicnode;
#if lnx_torrent
import leenkx.network.Leenkx;
import leenkx.network.torrent.Torrent;
import leenkx.network.torrent.TorrentClient;
#end
class SetTrackersNode extends LogicNode {
public var property0:String;
public var target:Dynamic;
public function new(tree:LogicTree) {
super(tree);
}
override function run(from:Int) {
#if lnx_torrent
var p = property0 != null ? property0 : "add";
switch (p) {
case "add" | "override":
setTorrentTrackers(p == "override");
default:
setTrackerProperty(p);
}
#end
runOutput(0);
}
#if lnx_torrent
function setTorrentTrackers(trackerOverride:Bool):Void {
var id = inputs[2].get();
var urls = TorrentClient.trackerUrls(inputs[3].get());
if (urls == null) return;
var t:Torrent = Std.isOfType(id, Torrent) ? id : null;
if (t == null && id != null) {
var connection = inputs[1].get();
var socket = connection != null ? Leenkx.connections[connection._url] : null;
var client = socket != null ? socket.client : null;
if (client != null) t = client.get(id);
}
if (t != null) {
target = t;
if (trackerOverride) {
t.setTrackers(urls);
} else {
for (u in urls) t.addTracker(u);
}
} else if (id == null) {
target = null;
if (trackerOverride) {
TorrentClient.defaultTrackers = urls;
} else {
TorrentClient.mergeTrackers(TorrentClient.defaultTrackers, urls);
}
}
}
function setTrackerProperty(prop:String):Void {
var tracker:Dynamic = inputs[1].get();
if (tracker == null) return;
var v = inputs[2].get();
if (v == null) return;
var field = prop == "interval" ? "intervalSec" : prop;
var val:Dynamic = field == "trackerId" ? Std.string(v)
: Std.int(Std.parseFloat(Std.string(v)));
var setter:Dynamic = field == "intervalSec"
&& Reflect.field(tracker, "intervalSec") == null
? Reflect.field(tracker, "setAnnounceInterval") : null;
if (setter != null) {
try {
Reflect.callMethod(tracker, setter, [(val:Int) * 1000]);
} catch(e:Dynamic) {}
} else {
Reflect.setField(tracker, field, val);
}
target = tracker;
}
#end
override function get(from:Int):Dynamic {
#if lnx_torrent
return switch (from) {
case 1: target;
default: throw "Unreachable";
}
#else
return null;
#end
}
}

View File

@ -0,0 +1,74 @@
package leenkx.logicnode;
#if lnx_torrent
import leenkx.network.Leenkx;
import leenkx.network.torrent.Torrent;
#end
class TorrentClientInfoNode extends LogicNode {
var stats:Dynamic;
var list:Array<Dynamic>;
public function new(tree:LogicTree) {
super(tree);
}
override function run(from:Int) {
#if lnx_torrent
list = [];
stats = {downRate: 0.0, upRate: 0.0, progress: 0.0, ratio: 0.0};
var connection = inputs[1].get();
if (connection != null) {
var socket = Leenkx.connections[connection._url];
var client = socket != null ? socket.client : null;
if (client != null) collect(client);
}
#end
runOutput(0);
}
#if lnx_torrent
function collect(client:Dynamic) {
var down = 0.0;
var up = 0.0;
var received = 0.0;
var sent = 0.0;
var total = 0.0;
var left = 0.0;
var torrents:Map<String, Torrent> = client.torrents;
for (t in torrents) {
list.push(t);
var s = t.getStats();
down += s.downRate;
up += s.upRate;
received += s.downloaded;
sent += s.uploaded;
if (t.meta != null) {
total += t.meta.totalLength;
left += s.left;
}
}
stats = {
downRate: down,
upRate: up,
progress: total > 0 ? 1.0 - left / total : 0.0,
ratio: received > 0 ? sent / received : 0.0
};
}
#end
override function get(from:Int):Dynamic {
#if lnx_torrent
return switch (from) {
case 1: list;
case 2: list.length;
case 3: stats.downRate;
case 4: stats.upRate;
case 5: stats.progress;
case 6: stats.ratio;
default: throw "Unreachable";
}
#else
return null;
#end
}
}

View File

@ -0,0 +1,93 @@
package leenkx.logicnode;
#if lnx_torrent
import leenkx.network.Leenkx;
import leenkx.network.torrent.Torrent;
#end
class TorrentControlNode extends LogicNode {
public var property0:String;
var net_Url:String;
public function new(tree:LogicTree) {
super(tree);
}
override function run(from:Int) {
#if lnx_torrent
var connection = inputs[1].get();
if (connection == null) return;
var id = inputs[2].get();
if (id == null) return;
net_Url = connection._url;
var socket = Leenkx.connections[connection._url];
if (socket == null) return;
var client = socket.client;
var t = client != null ? client.get(id) : (Std.isOfType(id, Torrent) ? id : null);
var action = property0 != null ? property0 : "remove";
var file:Dynamic = inputs.length > 3 ? inputs[3].get() : null;
var t2 = t;
var startB = 0;
var endB = -1;
if (file != null) {
startB = file.offset;
endB = file.offset + file.length - 1;
if (t2 == null) t2 = file.torrent;
} else {
var sv = inputs.length > 4 ? inputs[4].get() : null;
var ev = inputs.length > 5 ? inputs[5].get() : null;
if (sv != null && Std.string(sv) != "") {
startB = Std.int(Std.parseFloat(Std.string(sv)));
}
if (ev != null && Std.string(ev) != "") {
endB = Std.int(Std.parseFloat(Std.string(ev)));
}
}
var pv = inputs.length > 6 ? inputs[6].get() : null;
var priority = pv != null && Std.string(pv) != "" ? Std.int(Std.parseFloat(Std.string(pv))) : null;
switch (action) {
case "start":
if (t != null) t.start();
case "stop":
if (t != null) t.stop();
case "select", "critical", "deselect":
if (t2 != null) {
if (endB < 0) {
endB = t2.meta != null ? t2.meta.totalLength - 1 : 0;
}
if (action == "critical") {
t2.critical(startB, endB);
} else if (action == "select") {
t2.select(startB, endB, priority);
} else {
t2.deselect(startB, endB, priority);
}
}
case "add-peer":
var peer = inputs.length > 3 ? inputs[3].get() : null;
if (t != null && peer != null) {
t.addPeer(Std.string(peer));
}
case "add-web-seed":
var url = inputs.length > 3 ? inputs[3].get() : null;
if (t != null && url != null) {
t.addWebSeed(Std.string(url));
}
case "remove", "remove-data":
var hex = t != null ? t.infoHashHex : Std.string(id);
if (hex == null) hex = Std.string(id);
if (client != null) {
client.remove(hex, action == "remove-data");
}
if (t != null && Leenkx.torrent.get(net_Url) == t) {
Leenkx.torrent.remove(net_Url);
Leenkx.file.remove(net_Url);
}
default:
}
#end
runOutput(0);
}
}

View File

@ -0,0 +1,74 @@
package leenkx.logicnode;
#if lnx_torrent
import leenkx.network.Leenkx;
import leenkx.network.torrent.Torrent;
import leenkx.network.torrent.TorrentClient;
#end
class TorrentInfoNode extends LogicNode {
public var torrent:Dynamic;
public var net_Url:String;
public function new(tree:LogicTree) {
super(tree);
}
override function run(from:Int) {
#if lnx_torrent
var id = inputs[2].get();
if (id == null) return;
torrent = null;
if (Std.isOfType(id, Torrent)) {
torrent = id;
} else {
var connection = inputs[1].get();
if (connection == null) return;
net_Url = connection._url;
var socket = Leenkx.connections[net_Url];
if (socket == null || socket.client == null) return;
torrent = socket.client.get(id);
}
#end
runOutput(0);
}
override function get(from:Int):Dynamic {
#if lnx_torrent
var t:Torrent = torrent;
return switch (from) {
case 1: t != null ? t.infoHashHex : null;
case 2: t != null && t.meta != null ? t.meta.name : null;
case 3: t != null ? t.numPeers : 0;
case 4: progress(t);
case 5: t != null ? t.getStats().downloaded : 0.0;
case 6: t != null ? t.getStats().uploaded : 0.0;
case 7: t != null ? t.done : false;
case 8: t != null ? t.magnetURI : null;
case 9: t != null ? t.files : null;
case 10: t != null && t.meta != null ? t.meta.numPieces : 0;
case 11: t != null && t.meta != null ? t.meta.pieceLength : 0;
case 12: t != null && t.meta != null ? t.meta.totalLength : 0;
case 13: t != null && t.meta != null ? t.meta.comment : null;
case 14: t != null && t.meta != null ? t.meta.createdBy : null;
case 15: t != null && t.meta != null ? t.meta.protocolVersion : 1;
case 16: t != null ? t.infoHashV2Hex : null;
case 17: t != null ? t.getStats().downRate : 0.0;
case 18: t != null ? t.getStats().upRate : 0.0;
case 19: t != null ? t.stopped : false;
case 20: t;
default: throw "Unreachable";
}
#else
return null;
#end
}
#if lnx_torrent
function progress(t:Torrent):Float {
if (t == null || t.meta == null || t.meta.totalLength == 0) return 0;
var stats = t.getStats();
return 1.0 - stats.left / t.meta.totalLength;
}
#end
}

View File

@ -121,7 +121,6 @@ class Leenkx {
class LeenkxSocket {
public var _url:String;
public var client:LeenkxClient;
public var torrentClient:leenkx.network.torrent.TorrentClient;
public var buffer:Dynamic;
public var onopen:Void->Void;
public var onclose:Void->Void;

View File

@ -9,7 +9,8 @@ import leenkx.network.torrent.TorrentInfo;
class LeenkxClient {
static var PEERTIMEOUT:Float = 5 * 60 * 1000;
public static var PEERTIMEOUT:Float = 5 * 60 * 1000;
public static var defaultHeartbeat:Int = 30000;
public var identifier:String;
public var seed:String;
@ -53,6 +54,54 @@ class LeenkxClient {
private var deferredOpts:TransportOpts = null;
private var heartbeatInterval:Int = 30000;
public var torrents(get, never):Map<String, Torrent>;
public var onTorrentAdded:Torrent -> Void;
public var onTorrentDone:Torrent -> Void;
var swarm:TorrentClient = null;
function get_torrents():Map<String, Torrent> {
return swarm != null ? swarm.torrents : [];
}
function getSwarm():TorrentClient {
if (swarm == null) {
swarm = new TorrentClient();
swarm.onError = function(e) {
if (onError != null) onError(e);
};
swarm.onTorrentAdded = function(t) {
if (onTorrentAdded != null) onTorrentAdded(t);
};
swarm.onTorrentDone = function(t) {
if (onTorrentDone != null) onTorrentDone(t);
};
}
return swarm;
}
public function add(torrentId:Dynamic, ?opts:Dynamic, ?cb:Torrent -> Void):Torrent {
return getSwarm().add(torrentId, opts, cb);
}
public function seedMeta(meta:MetaInfo,
files:Array<{path:String, data:Bytes}>,
?opts:Dynamic, ?cb:Torrent -> Void):Torrent {
return getSwarm().seedMeta(meta, files, opts, cb);
}
public function get(id:Dynamic):Torrent {
if (Std.isOfType(id, Torrent)) return id;
return swarm != null ? swarm.get(id) : null;
}
public function remove(infoHashHex:String, ?deleteData:Bool):Void {
if (swarm != null) swarm.remove(infoHashHex, deleteData);
}
public function listen(port:Int):Void {
getSwarm().listen(port);
}
public function new(?identifier:String, ?opts:LeenkxOptions) {
if (identifier != null && !Std.isOfType(identifier, String)) {
opts = cast identifier;
@ -60,7 +109,7 @@ class LeenkxClient {
}
opts = opts != null ? opts : {};
this.announce = opts.announce != null ? opts.announce : ["wss://ws1.leenkx.com"];
this.announce = opts.announce != null ? opts.announce : TorrentClient.defaultTrackers;
this.timeout = opts.timeout != null ? opts.timeout : PEERTIMEOUT;
if (opts.seed != null) {
@ -90,7 +139,7 @@ class LeenkxClient {
createTransport(opts, opts.deferJoin == true);
this.heartbeatInterval = opts.heartbeat != null ? opts.heartbeat : 30000;
this.heartbeatInterval = opts.heartbeat != null ? opts.heartbeat : defaultHeartbeat;
}
function createTransport(opts:LeenkxOptions, defer:Bool):Void {
@ -281,6 +330,10 @@ class LeenkxClient {
}
var packet = makePacket({y: PacketType.Leave});
sendRaw(packet);
if (swarm != null) {
swarm.destroy();
swarm = null;
}
if (transport != null) {
transport.destroy(cb);
} else if (cb != null) {

View File

@ -39,12 +39,9 @@ class Torrent {
public var announcePort:Int = 0;
public var downloadDir:String = null;
public function getStats():{downloaded:Float, uploaded:Float,
left:Float, downRate:Float, upRate:Float} {
public function getStats():{downloaded:Float, uploaded:Float, left:Float, downRate:Float, upRate:Float} {
var total = meta != null ? meta.totalLength : 0;
var downloaded = pieces != null
? Math.min(pieces.numHave * (meta.pieceLength:Float), total)
: 0;
var downloaded = pieces != null ? Math.min(pieces.numHave * (meta.pieceLength:Float), total) : 0;
var now = haxe.Timer.stamp();
var dt = now - lastStatT;
var downRate:Float = 0;
@ -93,8 +90,7 @@ class Torrent {
public function new(infoHash:Bytes, peerId:Bytes, trackers:Array<String>, ?meta:MetaInfo, ?storage:IStorage, ?downloadDir:String) {
TorrentClient.initMain();
this.infoHash = infoHash;
this.infoHashHex = infoHash != null
? Crypto.toHex(infoHash) : null;
this.infoHashHex = infoHash != null ? Crypto.toHex(infoHash) : null;
this.peerId = peerId;
this.trackers = trackers;
this.storage = storage;
@ -121,12 +117,9 @@ class Torrent {
function setMeta(m:MetaInfo):Void {
meta = m;
if (storage == null) {
var sz = m.storageLength > 0 ? m.storageLength
: m.totalLength;
var sz = m.storageLength > 0 ? m.storageLength : m.totalLength;
#if (sys || kha_krom)
storage = downloadDir != null
? new FileTreeStorage(downloadDir, m.files)
: new MemoryStorage(sz);
storage = downloadDir != null ? new FileTreeStorage(downloadDir, m.files) : new MemoryStorage(sz);
#else
storage = new MemoryStorage(sz);
#end
@ -139,8 +132,7 @@ class Torrent {
if (m.fileHashes == null && m.files != null) {
m.fileHashes = [for (f in m.files) null];
}
files = [for (f in m.files)
new TorrentFile(f, storage, this)];
files = [for (f in m.files) new TorrentFile(f, storage, this)];
infoHashV2 = m.infoHashV2;
infoHashV2Hex = m.infoHashV2Hex;
if (infoHash == null && m.infoHash != null) {
@ -151,8 +143,7 @@ class Torrent {
if (m.urlList == null && pendingWebSeeds != null) {
m.urlList = pendingWebSeeds;
}
magnetURI = Magnet.build(m.infoHash, m.name, trackers,
m.urlList, m.infoHashV2);
magnetURI = Magnet.build(m.infoHash, m.name, trackers, m.urlList, m.infoHashV2);
ready = true;
initWebSeeds();
emit1("metadata", m);
@ -214,6 +205,24 @@ class Torrent {
return startTracker(url);
}
public function setTrackers(urls:Array<String>):Void {
if (stopped) {
trackers = urls != null ? urls.copy() : [];
return;
}
if (rtcPool != null) {
rtcPool.close();
rtcPool = null;
}
for (t in httpTrackers) t.stop();
for (t in udpTrackers) t.stop();
httpTrackers = [];
udpTrackers = [];
trackerObjects = [];
trackers = urls != null ? urls.copy() : [];
startTrackers();
}
function startTracker(url:String):Dynamic {
var infoHashBin = TorrentInfo.toBinaryString(infoHash);
var peerIdBin = TorrentInfo.toBinaryString(peerId);
@ -231,9 +240,7 @@ class Torrent {
}
#if (sys || kha_krom)
else if (url.indexOf("http") == 0) {
var t = new leenkx.network.torrent.tracker.HttpTracker(
url, infoHash, peerId,
announcePort > 0 ? announcePort : 6881);
var t = new leenkx.network.torrent.tracker.HttpTracker(url, infoHash, peerId, announcePort > 0 ? announcePort : 6881);
t.statsProvider = getStats;
t.onPeers = onClassicPeers;
t.onError = function(e) {
@ -246,9 +253,7 @@ class Torrent {
#end
#if (sys || kha_krom)
else if (url.indexOf("udp") == 0) {
var t = new leenkx.network.torrent.tracker.UdpTracker(
url, infoHash, peerId,
announcePort > 0 ? announcePort : 6881);
var t = new leenkx.network.torrent.tracker.UdpTracker(url, infoHash, peerId, announcePort > 0 ? announcePort : 6881);
t.statsProvider = getStats;
t.onPeers = onClassicPeers;
t.onError = function(e) {
@ -272,8 +277,7 @@ class Torrent {
for (p in list) {
var key = "tcp:" + p.host + ":" + p.port;
if (wires.exists(key)) continue;
var ch = new leenkx.network.torrent.peer.TcpPeer(
p.host, p.port);
var ch = new leenkx.network.torrent.peer.TcpPeer(p.host, p.port);
addWire(key, ch);
}
}
@ -308,8 +312,7 @@ class Torrent {
requestBlocks(key);
};
wire.onHave = function(index) {
if (meta == null || index < 0
|| index >= meta.numPieces) return;
if (meta == null || index < 0 || index >= meta.numPieces) return;
var bf = peerBitfields.get(key);
if (bf != null && index < bf.length) bf[index] = true;
requestBlocks(key);
@ -317,17 +320,13 @@ class Torrent {
wire.onPiece = function(index, begin, data) {
var n = outstanding.get(key);
if (n != null && n > 0) outstanding.set(key, n - 1);
if (pieces != null && index >= 0
&& index < meta.numPieces
&& pieces.addBlock(index, begin, data)) {
if (pieces != null && index >= 0 && index < meta.numPieces && pieces.addBlock(index, begin, data)) {
broadcastHave(index);
}
requestBlocks(key);
};
wire.onRequest = function(index, begin, length) {
if (wire.amChoking || pieces == null || index < 0
|| index >= meta.numPieces || begin < 0 || length <= 0
|| begin + length > meta.pieceSize(index)) return;
if (wire.amChoking || pieces == null || index < 0 || index >= meta.numPieces || begin < 0 || length <= 0 || begin + length > meta.pieceSize(index)) return;
if (!pieces.have[index]) return;
var data = pieces.readBlock(index, begin, length);
uploadedBytes += data.length;
@ -384,8 +383,7 @@ class Torrent {
var out:Array<Bool> = [];
for (i in 0...n) {
var byte = i >> 3;
out.push(byte < bf.length
&& (bf.get(byte) & (0x80 >> (i & 7))) != 0);
out.push(byte < bf.length && (bf.get(byte) & (0x80 >> (i & 7))) != 0);
}
return out;
}
@ -479,8 +477,7 @@ class Torrent {
if (host == "" || port == null) return false;
var key = "tcp:" + host + ":" + port;
if (wires.exists(key)) return false;
addWire(key,
new leenkx.network.torrent.peer.TcpPeer(host, port));
addWire(key, new leenkx.network.torrent.peer.TcpPeer(host, port));
return true;
#else
return false;
@ -509,8 +506,7 @@ class Torrent {
var pendingLayers:Map<Int, {arr:Array<Bytes>, got:Int}> = [];
function pieceLayerBase():Int {
return Std.int(Math.log(meta.pieceLength / 16384)
/ Math.log(2));
return Std.int(Math.log(meta.pieceLength / 16384) / Math.log(2));
}
function requestPieceLayer(wire:WireProtocol, fi:Int):Void {
@ -528,8 +524,7 @@ class Torrent {
if (chunk > 512) chunk = 512;
var len = 2;
while (len < chunk) len <<= 1;
wire.sendHashRequest(root, pieceLayerBase(), sent,
len, 0);
wire.sendHashRequest(root, pieceLayerBase(), sent, len, 0);
hashReqNext.set(fi, sent + len);
hashReqTime.set(fi, haxe.Timer.stamp());
}
@ -547,11 +542,9 @@ class Torrent {
if (count <= 0) return;
for (fi in 0...meta.files.length) {
var f = meta.files[fi];
if (f.piecesRoot == null
|| !bytesEq(root, f.piecesRoot)) continue;
if (f.piecesRoot == null || !bytesEq(root, f.piecesRoot)) continue;
if (meta.fileHashes[fi] != null) return;
var np = Std.int(Math.ceil(f.length
/ meta.pieceLength));
var np = Std.int(Math.ceil(f.length / meta.pieceLength));
var pl = pendingLayers.get(fi);
if (pl == null) {
pl = {arr: [for (i in 0...np) null], got: 0};
@ -585,8 +578,7 @@ class Torrent {
var root = payload.sub(0, 32);
for (fi in 0...meta.files.length) {
var f = meta.files[fi];
if (f.piecesRoot != null
&& bytesEq(root, f.piecesRoot)) {
if (f.piecesRoot != null && bytesEq(root, f.piecesRoot)) {
hashReqNext.remove(fi);
pendingLayers.remove(fi);
return;
@ -604,10 +596,8 @@ class Torrent {
var np = Std.int(Math.ceil(f.length / meta.pieceLength));
l = [];
for (p in 0...np) {
var size = Std.int(Math.min(meta.pieceLength,
f.length - p * meta.pieceLength));
l.push(meta.pieceRootV2(storage.read(
f.offset + p * meta.pieceLength, size)));
var size = Std.int(Math.min(meta.pieceLength, f.length - p * meta.pieceLength));
l.push(meta.pieceRootV2(storage.read(f.offset + p * meta.pieceLength, size)));
}
layerCache.set(fi, l);
return l;
@ -620,13 +610,11 @@ class Torrent {
t = [level];
var depth = 0;
while (level.length > 1) {
var zero = MetaInfo.layerZero(pieceLayerBase()
+ depth + 1);
var zero = MetaInfo.layerZero(pieceLayerBase() + depth + 1);
var next:Array<Bytes> = [];
var i = 0;
while (i < level.length) {
var b = i + 1 < level.length
? level[i + 1] : zero;
var b = i + 1 < level.length ? level[i + 1] : zero;
next.push(MetaInfo.hashPair(level[i], b));
i += 2;
}

View File

@ -5,9 +5,7 @@ import leenkx.network.torrent.storage.Storage;
import leenkx.network.torrent.storage.Storage.IStorage;
class TorrentClient {
public static var defaultTrackers:Array<String> = [
"wss://ws1.leenkx.com"
];
public static var defaultTrackers:Array<String> = [];
public static var defaultDownloadDir:String = null;
public var downloadDir:String = null;
@ -247,8 +245,7 @@ class TorrentClient {
files.push({path: name, data: Bytes.ofString(Std.string(data))});
}
return {meta: MetaInfo.create(name, files, trackers, opts),
files: files};
return {meta: MetaInfo.create(name, files, trackers, opts), files: files};
}
public static function readFolder(path:String, ?base:String):Array<{path:String, data:Bytes}> {
@ -259,8 +256,7 @@ class TorrentClient {
root = root.substr(0, root.length - 1);
}
if (base == null) {
var i = Std.int(Math.max(root.lastIndexOf("/"),
root.lastIndexOf("\\")));
var i = Std.int(Math.max(root.lastIndexOf("/"), root.lastIndexOf("\\")));
base = i >= 0 ? root.substr(i + 1) : root;
}
var files:Array<{path:String, data:Bytes}> = [];
@ -317,20 +313,79 @@ class TorrentClient {
return t;
}
public function remove(infoHashHex:String, ?deleteData:Bool):Void {
var t = torrents.get(infoHashHex);
if (t == null) {
t = torrents.get("v2:" + infoHashHex);
if (t != null) infoHashHex = "v2:" + infoHashHex;
public function get(id:Dynamic):Torrent {
if (Std.isOfType(id, Torrent)) return id;
if (id == null) return null;
var hex = Std.string(id);
if (StringTools.startsWith(hex, "v2:")) {
hex = hex.substr(3);
}
if (t != null) {
t.destroy();
if (deleteData) t.deleteData();
torrents.remove(infoHashHex);
var t = torrents.get(hex);
if (t == null) t = torrents.get("v2:" + hex);
if (t == null) {
for (f in torrents) {
if (f.infoHashHex == hex || f.infoHashV2Hex == hex || (f.infoHash != null && Crypto.toHex(f.infoHash) == hex)) {
t = f;
break;
}
}
}
return t;
}
public static function torrentIdFor(v:Dynamic):Dynamic {
if (v == null) return null;
if (Std.isOfType(v, haxe.io.Bytes)) return v;
var s = Std.string(v);
if (s.indexOf("magnet:") == 0) return s;
if (s.indexOf("v2:") == 0) {
var h = s.substr(3);
return h.length == 64 ? "magnet:?xt=urn:btmh:1220" + h : s;
}
switch (s.length) {
case 40 | 32:
return "magnet:?xt=urn:btih:" + s;
case 64:
return "magnet:?xt=urn:btmh:1220" + s;
case 68:
return "magnet:?xt=urn:btmh:" + s;
default:
return s;
}
}
static function mergeTrackers(trackers:Array<String>, urls:Array<String>):Void {
public static function trackerUrls(v:Dynamic):Array<String> {
if (v == null) return null;
var items:Array<Dynamic> = Std.isOfType(v, Array)
? (v:Array<Dynamic>) : [v];
var out:Array<String> = [];
for (item in items) {
var u:Dynamic = null;
if (!Std.isOfType(item, String)) {
try {
u = Reflect.field(item, "url");
} catch(e:Dynamic) {}
}
var s = u != null ? Std.string(u) : Std.string(item);
if (s != "" && s != "null") out.push(s);
}
return out.length > 0 ? out : null;
}
public function remove(infoHashHex:String, ?deleteData:Bool):Void {
var t = get(infoHashHex);
if (t != null) {
t.destroy();
if (deleteData) t.deleteData();
var key:String = null;
for (k in torrents.keys()) {
if (torrents.get(k) == t) { key = k; break; }
}
if (key != null) torrents.remove(key);
}
}
public static function mergeTrackers(trackers:Array<String>, urls:Array<String>):Void {
if (urls == null) return;
for (u in urls) {
if (u != null && Lambda.indexOf(trackers, u) < 0) {

View File

@ -51,10 +51,12 @@ class RtcPeerPool {
var rtcInitialized = false;
#end
public static var defaultIceServers:Array<String> =
["stun:stun.l.google.com:19302", "stun:global.stun.twilio.com:3478"];
public function new(?iceServers:Array<String>, ?channelLabel:String, unordered:Bool = false, maxRetransmits:Int = -1) {
this.iceServers = iceServers != null ? iceServers
: ["stun:stun.l.google.com:19302",
"stun:global.stun.twilio.com:3478"];
: defaultIceServers.copy();
if (channelLabel != null) this.channelLabel = channelLabel;
this.unordered = unordered;
this.maxRetransmits = maxRetransmits;
@ -72,8 +74,7 @@ class RtcPeerPool {
}
#end
var signaling = new RtcSignaling(trackerUrl, infoHashBinary,
peerIdBinary);
var signaling = new RtcSignaling(trackerUrl, infoHashBinary, peerIdBinary);
signalings.push(signaling);
signaling.statsProvider = statsProvider;
signaling.onConnected = function() {
@ -223,8 +224,7 @@ class RtcPeerPool {
}
};
var dc = pc.createDataChannel(channelLabel,
{ordered: !unordered,
maxRetransmits: unordered ? maxRetransmits : null});
{ordered: !unordered, maxRetransmits: unordered ? maxRetransmits : null});
attachOfferChannel(offerIdHex, new DataChannelPeer(dc));
var iceComplete = false;
var iceTimer:haxe.Timer = null;
@ -271,8 +271,7 @@ class RtcPeerPool {
iceComplete = true;
if (iceTimer != null) iceTimer.stop();
if (!offerPeers.exists(offerIdHex)) return;
sig.sendAnswer(peerIdHex, offerIdHex,
pc.getLocalDescription());
sig.sendAnswer(peerIdHex, offerIdHex, pc.getLocalDescription());
};
pc.onGatheringStateChange = function(state) {
if (state == RTC_GATHERING_COMPLETE) sendAnswerSdp();
@ -312,8 +311,7 @@ class RtcPeerPool {
iceComplete = true;
if (iceTimer != null) iceTimer.stop();
if (!offerPeers.exists(offerIdHex)) return;
sig.sendAnswer(peerIdHex, offerIdHex,
RtcNative.getLocalDescription(pcId));
sig.sendAnswer(peerIdHex, offerIdHex, RtcNative.getLocalDescription(pcId));
};
RtcNative.setGatheringStateCallback(pcId, function(state:Int) {
if (state == RtcNative.RTC_GATHERING_COMPLETE) sendAnswerSdp();

View File

@ -36,6 +36,10 @@ class DatachannelTransport implements ITransport {
if (opts.announce != null && opts.announce.length > 0) {
url = opts.announce[0];
}
if (url == null || url == "") {
if (onError != null) onError("no announce tracker configured");
return;
}
if (opts.iceServers != null) iceServers = opts.iceServers;
pk = opts.pk != null ? opts.pk : "";
ek = opts.ek != null ? opts.ek : "";

View File

@ -48,7 +48,7 @@ class WebTorrentTransport implements ITransport {
var blob:Dynamic = createBlob(identifier);
var torrentOpts:Dynamic = {
name: identifier,
announce: opts.announce != null ? opts.announce : ["wss://ws1.leenkx.com"]
announce: opts.announce != null ? opts.announce : leenkx.network.torrent.TorrentClient.defaultTrackers
};
if (opts.torrentOpts != null) {
torrentOpts = js.lib.Object.assign(

View File

@ -43,6 +43,10 @@ class WireTransport implements ITransport {
if (opts.announce != null && opts.announce.length > 0) {
url = opts.announce[0];
}
if (url == null || url == "") {
if (onError != null) onError("no announce tracker configured");
return;
}
if (opts.iceServers != null) iceServers = opts.iceServers;
pk = opts.pk != null ? opts.pk : "";
ek = opts.ek != null ? opts.ek : "";