diff --git a/docs/index.md b/docs/index.md index 49250bf..2443ab1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,14 +10,15 @@ pip install git+https://github.com/AURUMVORXX/PyG2O.git ``` 3. Launch websocket client in your Squirrel scripts ``` -// PyG2O_Start(url, reconnect, silent) -// reconnect - auto reconnect if server stopped +// PyG2O(url, silent, max_reconnect_attempts) // silent - disable information prints +// max_reconnect_attempts - maximum reconnect attempts if server will stop the connection (0 - infinite attempts). This value doesn't reset on connection -// Start server before any events -PyG2O_Start("ws://localhost:8080", true, false) - -addEventHandler("onInit"... +// Start server +local srv = PyG2O("ws://localhost:8080", true, 15) +srv.start() +// Stop server +srv.stop() ``` 4. In your application, launch asyncio event loop and websocket server ```python diff --git a/include/events.nut b/include/events.nut index 49c21f7..bbe0f9f 100644 --- a/include/events.nut +++ b/include/events.nut @@ -3,614 +3,571 @@ addEventHandler("onPlayerUseCheat", function(playerid, type) { local data = { event = "onPlayerUseCheat", - args = { - playerid = playerid, - type = type - } + playerid = playerid, + type = type } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onBan", function(banInfo) { local data = { event = "onBan", - args = { - ban = { - mac = "mac" in banInfo ? banInfo["mac"] : "-1", - ip = "ip" in banInfo ? banInfo["ip"] : "-1", - serial = "serial" in banInfo ? banInfo["serial"] : "-1", - name = "name" in banInfo ? banInfo["name"] : "-1", - timestamp = "timestamp" in banInfo ? banInfo["timestamp"] : -1 - } + ban = { + mac = "mac" in banInfo ? banInfo["mac"] : "-1", + ip = "ip" in banInfo ? banInfo["ip"] : "-1", + serial = "serial" in banInfo ? banInfo["serial"] : "-1", + name = "name" in banInfo ? banInfo["name"] : "-1", + timestamp = "timestamp" in banInfo ? banInfo["timestamp"] : -1 } } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onExit", function() { local data = { - event = "onExit", - args = {} + event = "onExit" } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onTick", function() { local data = { - event = "onTick", - args = {} + event = "onTick" } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onTime", function(day, hour, min) { local data = { event = "onTime", - args = { - day = day, - hour = hour, - min = min, - } + day = day, + hour = hour, + min = min } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onUnban", function(banInfo) { local data = { event = "onUnban", - args = { - ban = { - mac = "mac" in banInfo ? banInfo["mac"] : "-1", - ip = "ip" in banInfo ? banInfo["ip"] : "-1", - serial = "serial" in banInfo ? banInfo["serial"] : "-1", - name = "name" in banInfo ? banInfo["name"] : "-1", - timestamp = "timestamp" in banInfo ? banInfo["timestamp"] : -1 - } + ban = { + mac = "mac" in banInfo ? banInfo["mac"] : "-1", + ip = "ip" in banInfo ? banInfo["ip"] : "-1", + serial = "serial" in banInfo ? banInfo["serial"] : "-1", + name = "name" in banInfo ? banInfo["name"] : "-1", + timestamp = "timestamp" in banInfo ? banInfo["timestamp"] : -1 } } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onNpcActionFinished", function(npc_id, action_type, action_id, result) { local data = { event = "onNpcActionFinished", - args = { - npc_id = npc_id, - action_type = action_type, - action_id = action_id, - result = result - } + npc_id = npc_id, + action_type = action_type, + action_id = action_id, + result = result } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onNpcActionSent", function(npc_id, action_type, action_id) { local data = { event = "onNpcActionSent", - args = { - npc_id = npc_id, - action_type = action_type, - action_id = action_id, - } + npc_id = npc_id, + action_type = action_type, + action_id = action_id, } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onNpcChangeHostPlayer", function(npc_id, current_id, previous_id) { local data = { event = "onNpcChangeHostPlayer", - args = { - npc_id = npc_id, - current_id = current_id, - previous_id = previous_id - } + npc_id = npc_id, + current_id = current_id, + previous_id = previous_id } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onNpcCreated", function(npc_id) { local data = { event = "onNpcCreated", - args = { - npc_id = npc_id - } + npc_id = npc_id } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onNpcDestroyed", function(npc_id) { local data = { - event = "onNpcDestroyed", - args = { - npc_id = npc_id - } + event = "onNpcCreated", + npc_id = npc_id } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerChangeColor", function(playerid, r, g, b) { local data = { event = "onPlayerChangeColor", - args = { - playerid = playerid, - r = r, - g = g, - b = b - } + playerid = playerid, + r = r, + g = g, + b = b } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerChangeFocus", function(playerid, oldFocusId, newFocusId) { local data = { event = "onPlayerChangeFocus", - args = { - playerid = playerid, - oldFocusId = oldFocusId, - newFocusId = newFocusId - } + playerid = playerid, + oldFocusId = oldFocusId, + newFocusId = newFocusId } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerChangeHealth", function(playerid, previous, current) { local data = { event = "onPlayerChangeHealth", - args = { - playerid = playerid, - previous = previous, - current = current - } + playerid = playerid, + previous = previous, + current = current } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerChangeMana", function(playerid, previous, current) { local data = { event = "onPlayerChangeMana", - args = { - playerid = playerid, - previous = previous, - current = current - } + playerid = playerid, + previous = previous, + current = current } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerChangeMaxHealth", function(playerid, previous, current) { local data = { event = "onPlayerChangeMaxHealth", - args = { - playerid = playerid, - previous = previous, - current = current - } + playerid = playerid, + previous = previous, + current = current } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerChangeMaxMana", function(playerid, previous, current) { local data = { event = "onPlayerChangeMaxMana", - args = { - playerid = playerid, - previous = previous, - current = current - } + playerid = playerid, + previous = previous, + current = current } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerChangeWeaponMode", function(playerid, previous, current) { local data = { event = "onPlayerChangeWeaponMode", - args = { - playerid = playerid, - previous = previous, - current = current - } + playerid = playerid, + previous = previous, + current = current } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerChangeWorld", function(playerid, previous, current) { local data = { event = "onPlayerChangeWorld", - args = { - playerid = playerid, - previous = previous, - current = current - } + playerid = playerid, + previous = previous, + current = current } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerCommand", function(playerid, command, params) { local data = { event = "onPlayerCommand", - args = { - playerid = playerid, - command = command, - params = params - } + playerid = playerid, + command = command, + params = params } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerDamage", function(playerid, killerid, description) { local data = { event = "onPlayerDamage", - args = { - playerid = playerid, - killerid = killerid, - obj_DamageDescription = { - name = "desc", - data = _PyG2O_Serialize(description) - } + playerid = playerid, + killerid = killerid, + desc = { + obj_name = "DamageDescription", + obj_data = _globalInstance._serializeObject(description) } } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerDead", function(playerid, killerid) { local data = { event = "onPlayerDead", - args = { - playerid = playerid, - killerid = killerid - } + playerid = playerid, + killerid = killerid } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerDisconnect", function(playerid, reason) { local data = { event = "onPlayerDisconnect", - args = { - playerid = playerid, - reason = reason - } + playerid = playerid, + reason = reason } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerDropItem", function(playerid, itemGround) { local data = { event = "onPlayerDropItem", - args = { - playerid = playerid, - obj_ItemGround = { - name = "itemGround", - data = _PyG2O_Serialize(itemGround) - } + playerid = playerid, + itemGround = { + obj_name = "ItemGround", + obj_data = _globalInstance._serializeObject(itemGround) } } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerEnterWorld", function(playerid, world) { local data = { event = "onPlayerEnterWorld", - args = { - playerid = playerid, - world = world - } + playerid = playerid, + world = world } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerEquipAmulet", function(playerid, instance) { local data = { event = "onPlayerEquipAmulet", - args = { - playerid = playerid, - instance = instance - } + playerid = playerid, + instance = instance } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerEquipArmor", function(playerid, instance) { local data = { event = "onPlayerEquipArmor", - args = { - playerid = playerid, - instance = instance - } + playerid = playerid, + instance = instance } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerEquipBelt", function(playerid, instance) { local data = { event = "onPlayerEquipBelt", - args = { - playerid = playerid, - instance = instance - } + playerid = playerid, + instance = instance } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerEquipHandItem", function(playerid, hand, instance) { local data = { event = "onPlayerEquipHandItem", - args = { - playerid = playerid, - hand = hand, - instance = instance - } + playerid = playerid, + hand = hand, + instance = instance } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerEquipHelmet", function(playerid, instance) { local data = { event = "onPlayerEquipHelmet", - args = { - playerid = playerid, - instance = instance - } + playerid = playerid, + instance = instance } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerEquipMeleeWeapon", function(playerid, instance) { local data = { event = "onPlayerEquipMeleeWeapon", - args = { - playerid = playerid, - instance = instance - } + playerid = playerid, + instance = instance } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerEquipRangedWeapon", function(playerid, instance) { local data = { event = "onPlayerEquipRangedWeapon", - args = { - playerid = playerid, - instance = instance - } + playerid = playerid, + instance = instance } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerEquipRing", function(playerid, handId, instance) { local data = { event = "onPlayerEquipRing", - args = { - playerid = playerid, - handId = handId, - instance = instance - } + playerid = playerid, + handId = handId, + instance = instance } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerEquipShield", function(playerid, instance) { local data = { event = "onPlayerEquipShield", - args = { - playerid = playerid, - instance = instance - } + playerid = playerid, + instance = instance } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerEquipSpell", function(playerid, slotId, instance) { local data = { event = "onPlayerEquipSpell", - args = { - playerid = playerid, - slotId = slotId, - instance = instance - } + playerid = playerid, + slotId = slotId, + instance = instance } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerJoin", function(playerid) { local data = { event = "onPlayerJoin", - args = { - playerid = playerid - } + playerid = playerid } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerMessage", function(playerid, message) { local data = { event = "onPlayerMessage", - args = { - playerid = playerid, - message = message - } + playerid = playerid, + message = message } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerMobInteract", function(playerid, from, to) { local data = { event = "onPlayerMobInteract", - args = { - playerid = playerid, - from = from, - to = to - } + playerid = playerid, + from = from, + to = to } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerRespawn", function(playerid) { local data = { event = "onPlayerRespawn", - args = { - playerid = playerid - } + playerid = playerid } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerShoot", function(playerid, munition) { local data = { event = "onPlayerShoot", - args = { - playerid = playerid, - munition = munition - } + playerid = playerid, + munition = munition } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerSpellCast", function(playerid, instance, spellLevel) { local data = { event = "onPlayerSpellCast", - args = { - playerid = playerid, - instance = instance, - spellLevel = spellLevel - } + playerid = playerid, + instance = instance, + spellLevel = spellLevel } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerSpellSetup", function(playerid, instance) { local data = { event = "onPlayerSpellSetup", - args = { - playerid = playerid, - instance = instance - } + playerid = playerid, + instance = instance } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerTakeItem", function(playerid, itemGround) { local data = { event = "onPlayerTakeItem", - args = { - playerid = playerid, - obj_ItemGround = { - name = "itemGround", - data = _PyG2O_Serialize(itemGround) - } + playerid = playerid, + itemGround = { + obj_name = "ItemGround", + obj_data = _globalInstance._serializeObject(itemGround) } } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerTeleport", function(playerid, vobName) { local data = { event = "onPlayerTeleport", - args = { - playerid = playerid, - vobName = vobName - } + playerid = playerid, + vobName = vobName } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); addEventHandler("onPlayerToggleFaceAni", function(playerid, aniName, toggle) { local data = { event = "onPlayerToggleFaceAni", - args = { - playerid = playerid, - aniName = aniName, - toggle = toggle - } + playerid = playerid, + aniName = aniName, + toggle = toggle } - _PyG2O_Send(data); + if (_globalInstance != -1) + _globalInstance._send("event", data); }); \ No newline at end of file diff --git a/include/main.nut b/include/main.nut index 4c23b70..0095d9d 100644 --- a/include/main.nut +++ b/include/main.nut @@ -1,126 +1,153 @@ -local _pyg2o_server_connection = -1; -local _silent = false; -local _url = -1 -local _reconnect = false; +_globalInstance <- -1; -function PyG2O_Start(url, reconnect = false, silent = false) +class PyG2O { - _silent = silent; - _url = url; - _reconnect = reconnect; + _connection = -1; + _silent = false; + _url = -1; + _reconnect_attempts = 0; + _max_reconnect_attempts = 0; - _pyg2o_server_connection = WebsocketClient(); - _pyg2o_server_connection.silent = silent; - _pyg2o_server_connection.setUrl(url); - _pyg2o_server_connection.start(); + _constantsInitialized = false; + _messageHandlers = null; - if (!_silent) - print("[PyG2O] Initializing connection on " + url) -} - -function _PyG2O_Send(data) -{ - if (_pyg2o_server_connection == -1) - return; - - _pyg2o_server_connection.send(JSON.dump_ansi(data)); -} - -function _PyG2O_InitializeConstants() -{ - local const_data = { - "type": "const_init", - "args": getconsttable() - }; - - _PyG2O_Send(const_data); -} - -function _PyG2O_GetClassName(object) -{ - if (object instanceof DamageDescription) - return "DamageDescription"; - else if (object instanceof ItemGround) - return "ItemGround"; - - return null; -} - -function _PyG2O_Serialize(object) -{ - local cls = object.getclass(); - local tab = {}; - - if (object instanceof DamageDescription) + constructor(url, silent = false, max_reconnect_attempts = 0) { - tab["_flags"] <- object.flags; - tab["_damage"] <- object.damage; - tab["_item_instance"] <- object.item_instance; - tab["_distance"] <- object.distance; - tab["_spell_id"] <- object.spell_id; - tab["_spell_level"] <- object.spell_level; - tab["_node"] <- object.node; - } - else if (object instanceof ItemGround) - { - tab["_id"] <- object.id; - tab["_instance"] <- object.instance; - tab["_amount"] <- object.amount; - tab["_world"] <- object.world; - tab["_virtualWorld"] <- object.virtualWorld; - tab["_position"] <- object.getPosition(); - tab["_rotation"] <- object.getRotation(); + _url = url; + _max_reconnect_attempts = max_reconnect_attempts; + _silent = silent; + _messageHandlers = {}; + + _connection = WebsocketClient(); + _connection.silent = _silent; + _connection.setUrl(_url); + + _connection.onOpen = _onOpen.bindenv(this); + _connection.onClose = _onClose.bindenv(this); + _connection.onMessage = _onMessage.bindenv(this); + + _registerMessage("call", _message_call.bindenv(this)); + + if (_globalInstance == -1) + _globalInstance = this; } - return tab; -} - -addEventHandler("onWebsocketConnect", function(socket, url) -{ - if (socket != _pyg2o_server_connection) - return; - - _PyG2O_InitializeConstants(); - if (!_silent) - print("[PyG2O] Successfully connected to " + url); -}); - -addEventHandler("onWebsocketClose", function(socket, url, message) -{ - if (socket != _pyg2o_server_connection || _reconnect) - return; - - _pyg2o_server_connection = -1; -}); - -addEventHandler("onWebsocketMessage", function(socket, url, message) -{ - if (socket != _pyg2o_server_connection) - return; - - local request = JSON.parse_ansi(message); - if ("uuid" in request) + function start() { - local result = compilestring(request["args"])(); - local className = _PyG2O_GetClassName(result); - if (className != null) - { - className = format("obj_%s", className) + _connection.start(); + if (_connection.running) + print("[PyG2O] Initializing connection on " + _url); + } - request["args"] = {}; - request["args"].rawset(className, {}); - local objTab = request["args"].rawget(className); - objTab["name"] <- "result"; - objTab["data"] <- _PyG2O_Serialize(result); + function stop() + { + _connection.stop(); + if (!_connection.running) + print("[PyG2O] Stopped connection"); + } + + function _registerMessage(type, handler) + { + if (type in _messageHandlers) + return; + + _messageHandlers[type] <- handler; + } + + function _callMessage(type, data) + { + if(!(type in _messageHandlers)) + return; + + _messageHandlers[type](data); + } + + function _send(type, data, uuid = "none") + { + local sendData = { + "type": type, + "data": data, + "uuid": uuid } - else - { - request["args"] = - { - "result": result - }; - } - _pyg2o_server_connection.send(JSON.dump_ansi(request, 2)); + + _connection.send(JSON.dump_ansi(sendData, 2)); } -}); + + function _initializeConstants() + { + if (_constantsInitialized) + return; + + _send("init_constants", getconsttable()); + } + + function _getClassName(object) + { + if (object instanceof DamageDescription) + return "DamageDescription"; + else if (object instanceof ItemGround) + return "ItemGround"; + + return null; + } + + function _serializeObject(object) + { + local cls = object.getclass(); + local tab = {}; + + if (object instanceof DamageDescription) + { + tab["_flags"] <- object.flags; + tab["_damage"] <- object.damage; + tab["_item_instance"] <- object.item_instance; + tab["_distance"] <- object.distance; + tab["_spell_id"] <- object.spell_id; + tab["_spell_level"] <- object.spell_level; + tab["_node"] <- object.node; + } + else if (object instanceof ItemGround) + { + tab["_id"] <- object.id; + tab["_instance"] <- object.instance; + tab["_amount"] <- object.amount; + tab["_world"] <- object.world; + tab["_virtualWorld"] <- object.virtualWorld; + tab["_position"] <- object.getPosition(); + tab["_rotation"] <- object.getRotation(); + } + + return tab; + } + + function _onOpen(url) + { + _initializeConstants(); + if (!_silent) + print("[PyG2O] Successfully connected to " + url); + } + + function _onClose(url, message) + { + if (_max_reconnect_attempts == 0) + return; + + _reconnect_attempts += 1; + if (_reconnect_attempts < _max_reconnect_attempts) + return; + + _connection.stop(); + } + + function _onMessage(url, message) + { + local request = JSON.parse_ansi(message); + if (!("type" in request) || + !("uuid" in request) || + !("data" in request)) + return; + + _callMessage(request["type"], request); + } +} \ No newline at end of file diff --git a/include/messages.nut b/include/messages.nut new file mode 100644 index 0000000..ee16144 --- /dev/null +++ b/include/messages.nut @@ -0,0 +1,16 @@ + +function _message_call(data) +{ + local result = compilestring(data["data"])(); + local className = _getClassName(result); + if (className != null) + { + data["data"] = {}; + data["data"]["obj_name"] <- className; + data["data"]["obj_data"] <- _serializeObject(result); + } + else + data["data"] = result; + + _send("result", data["data"], data["uuid"]); +} \ No newline at end of file diff --git a/include/pyg2o.xml b/include/pyg2o.xml index 37df48a..2c40932 100644 --- a/include/pyg2o.xml +++ b/include/pyg2o.xml @@ -1,4 +1,5 @@ +