diff --git a/ARES/application/dispatch.lsl b/ARES/application/dispatch.lsl new file mode 100644 index 0000000..490fb95 --- /dev/null +++ b/ARES/application/dispatch.lsl @@ -0,0 +1,247 @@ +/* ========================================================================= + * + * Nanite Systems Advanced Research Encapsulation System + * + * Copyright (c) 2022–2025 Nanite Systems Corporation + * + * ========================================================================= + * + * Dispatch Program + * + * This program is covered under the terms of the ARES Software Copyright + * License, Section 1 (ASCL-i). It is offered to you on a limited basis to + * facilitate modification and customization. + * + * To see the full text of the ASCL, type 'help license' on any standard + * ARES distribution, or visit http://nanite-systems.com/ASCL for the + * current version. + * + * DISCLAIMER + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS + * IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DAMAGES HOWEVER CAUSED ON ANY THEORY OF LIABILITY ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * ========================================================================= + * + */ + +#include +#define CLIENT_VERSION "0.1.0" +#define CLIENT_VERSION_TAGS "alpha" + +integer charging; +integer ext_repairing; +integer auto_repairing; +float charge_ratio = 0.75; +float integrity = 1.0; +integer power_on = 1; + +string supported_triggers = "{ + \"shutdown\":\"\", + \"boot\":\"\", + \"power-10\":\"\", + \"power-20\":\"\", + \"power-up\":\"\", + \"power-100\":\"\", + \"charge-start\":\"\", + \"charge-end\":\"\", + \"autorepair-start\":\"\", + \"autorepair-end\":\"\", + \"ext-repair-start\":\"\", + \"ext-repair-end\":\"\", + \"integrity-100\":\"\", + \"device-add\":\"\", + \"device-remove\":\"\", + \"guest-allow\":\"\", + \"guest-reject\":\"\", + \"region-change\":\"\", + \"teleport\":\"\" +}"; + +list e_map = [ + EVENT_TELEPORT, "teleport", + EVENT_NEW_DEVICE, "device-add", + EVENT_REMOVE_DEVICE, "device-remove", + EVENT_REGION_CHANGE, "region-change" +]; + +event_trigger(string e_name, string e_args, key outs, key user) { + string command = getjs(supported_triggers, [e_name]); + if(command != "" && command != JSON_INVALID) { + if(e_args != "") + command += " " + e_args; + invoke(command, outs, NULL_KEY, user); + } + #ifdef DEBUG + else { + echo("[" + PROGRAM_NAME + "] no action configured for event " + e_name); + } + #endif +} + +main(integer src, integer n, string m, key outs, key ins, key user) { + if(n == SIGNAL_INVOKE) { + list argv = splitnulls(m, " "); + integer argc = count(argv); + string msg = ""; + string action = gets(argv, 1); + if(argc == 1 || action == "help") { + list event_names = jskeys(supported_triggers); + msg = "Syntax: " + PROGRAM_NAME + " on |nothing\nwhere is a system command without the @ prefix and is one of: " + concat(event_names, ", "); + } else if(action == "on") { + string e = gets(argv, 2); + string cmd = concat(delrange(argv, 0, 2), " "); + if(getjs(supported_triggers, [e]) != JSON_INVALID) { + if(cmd == "nothing") + cmd = ""; + + string new_supported_triggers = setjs(supported_triggers, [e], cmd); + if(new_supported_triggers == JSON_INVALID) { + msg = "Error: command '" + cmd + "' contains unencodable characters"; + } else { + if(cmd != "") + msg = "Updated event mapping."; + else + msg = "Removed event mapping."; + + setdbl("dispatch", ["trigger"], supported_triggers = new_supported_triggers); + } + } else { + msg = "Unsupported event: " + e + "."; + } + } else { + msg = "Run '@" + PROGRAM_NAME + " help' for instructions."; + } + + if(msg != "") + print(outs, user, msg); + } else if(n == SIGNAL_EVENT || n == SIGNAL_TIMER) { + list argv = splitnulls(m, " "); + integer ei = (integer)gets(argv, 0); + + string e_name; + + if(ei != EVENT_WARNING && n != SIGNAL_TIMER) { + e_name = gets(e_map, index(e_map, ei) + 1); + } else { + integer sloti = (integer)gets(argv, 1); + argv = []; + + if(sloti == 7 || n == SIGNAL_TIMER) { + integer new_charging = (integer)getdbl("status", ["charging"]); + float new_charge_ratio = (float)getdbl("status", ["charge-ratio"]); + + string power_event; + + if(new_charge_ratio > 0.99 && charge_ratio <= 0.99) { + power_event = "power-100"; + } else if(new_charge_ratio > 0.20 && charge_ratio <= 0.20) { + power_event = "power-up"; + } else if((new_charge_ratio <= 0.20 && charge_ratio > 0.20) + || (new_charge_ratio > 0.10 && charge_ratio <= 0.10)) { + power_event = "power-20"; + } else if(new_charge_ratio <= 0.10 && charge_ratio > 0.10) { + power_event = "power-10"; + } + + if(charging != new_charging) { + if(charging) { + e_name = "charge-end"; + } else { + e_name = "charge-start"; + } + } + + if(power_event != "") + event_trigger(power_event, "", avatar, avatar); + + charge_ratio = new_charge_ratio; + charging = new_charging; + + } else if(sloti == 0 || n == SIGNAL_TIMER) { + integer new_ext_repairing = (integer)getdbl("repair", ["ext-repair"]); + integer new_auto_repairing = (integer)getdbl("repair", ["autorepairing"]); + float new_integrity = (float)getdbl("repair", ["integrity"]); + + if(new_integrity == 1.00 && integrity < 1.00) { + event_trigger("integrity-100", "", avatar, avatar); + } + + if(new_ext_repairing != ext_repairing) { + string exte; + if(new_ext_repairing) + exte = "ext-repair-start"; + else + exte = "ext-repair-end"; + + event_trigger(exte, "", avatar, avatar); + } + + if(new_auto_repairing != auto_repairing) { + if(new_auto_repairing) + e_name = "autorepair-start"; + else + e_name = "autorepair-end"; + } + + integrity = new_integrity; + ext_repairing = new_ext_repairing; + auto_repairing = new_auto_repairing; + } + } + + if(e_name != "") { + event_trigger(e_name, concat(delitem(argv, 0), " "), avatar, avatar); + } + } else if(n == SIGNAL_NOTIFY) { + list argv = split(m, " "); + string action = gets(argv, 1); + if(action == "power") { + integer new_power_on = (integer)getdbl("status", ["on"]); + if(new_power_on != power_on) { + power_on = new_power_on; + if(power_on) { + event_trigger("boot", "", outs, user); + } else { + event_trigger("shutdown", "", outs, user); + } + } + } else if(action == "security") { + string subaction = gets(argv, 2); + if(subaction == "yes" || subaction == "trust") { + event_trigger("guest-allow", "", outs, user); + } else if(subaction == "no" || subaction == "block") { + event_trigger("guest-reject", "", outs, user); + } + } + } else if(n == SIGNAL_INIT) { + #ifdef DEBUG + echo("[" + PROGRAM_NAME + "] init signal"); + #endif + hook_events([ + EVENT_WARNING + ] + llList2ListStrided(e_map, 0, LAST, 2)); + + string new_supported_triggers = getdbl("dispatch", ["trigger"]); + if(new_supported_triggers == JSON_INVALID) { + setdbl("dispatch", ["trigger"], supported_triggers); + } else { + supported_triggers = new_supported_triggers; + } + + set_timer("dispatch", 5); + } else if(n == SIGNAL_UNKNOWN_SCRIPT) { + echo("[" + PROGRAM_NAME + "] failed to run '" + m + "' (kernel could not find the program specified)"); + } else { + echo("[" + PROGRAM_NAME + "] unimplemented signal " + (string)n + ": " + m); + } +} + +#include