Rebuilt repo

main
Samantha Wright 2024-06-28 15:44:37 -07:00
commit a68267b5cf
88 changed files with 22463 additions and 0 deletions

331
ARES/a Normal file
View File

@ -0,0 +1,331 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222023 Nanite Systems Corporation
*
* =========================================================================
*
* A.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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 <utils.lsl>
#include <objects.lsl>
// some programs (e.g. LSLisp) need this to determine the target platform:
#define ARES
// version that OS components will report:
#define ARES_VERSION "0.5.0"
#define ARES_VERSION_TAGS "beta 3"
// copyright year for the OS:
#define ARES_YEAR "2022-2024"
// ring definitions (link numbers):
#define R_KERNEL 1
#define R_DAEMON 2
#define R_PROGRAM 3
// populated with llGetLinkKey():
key KERNEL = NULL_KEY;
key DAEMON = NULL_KEY;
key PROGRAM = NULL_KEY;
// populated with llGetOwner():
key avatar = NULL_KEY;
// populated with llGetScriptName():
string PROGRAM_NAME = "program";
// channel definitions
#define C_UNASSIGNED 0x7a000000
// the kernel takes messages via llMessageLinked using the system() API call
// so it doesn't need a channel
// #define C_KERNEL 0x7a001000
#define C_DAEMON_BASE 0x7a002000
#define C_STATUS 0x7a002001
#define E_STATUS "!\""
#define C_BASEBAND 0x7a002002
#define E_BASEBAND "!#"
#define C_IO 0x7a002003
#define E_IO "!$"
#define C_HARDWARE 0x7a002004
#define E_HARDWARE "!%"
#define C_INTERFACE 0x7a002005
#define E_INTERFACE "!&"
#define C_VARIATYPE 0x7a002006
#define E_VARIATYPE "!'"
#define C_EFFECTOR 0x7a002007
#define E_EFFECTOR "!("
#define C_REPAIR 0x7a002008
#define E_REPAIR "!)"
#define C_SCHEDULER 0x7a002009
#define E_SCHEDULER "!*"
#define C_SEXUALITY 0x7a00200a
#define E_SEXUALITY "!+"
#define C_WARRIOR 0x7a00200b
#define E_WARRIOR "!,"
#define C_STORAGE 0x7a00200c
#define E_STORAGE "!-"
#define C_PROGRAM_BASE 0x7a003000
#define C_LIBFS 0x7a003ffd
#define C_PROC 0x7a003ffe
#define FILESYSTEM_ADDRESS 0xffd
#define DELEGATE_ADDRESS 0xffe
#define INPUT_HANDLER "00000000-0000-0000-0000-000000000001"
#define VOX_LISTENER "00000000-0000-0000-0000-000000000002"
#define OUTPUT_WHISPER "00000000-0000-0000-0000-000000000010"
#define OUTPUT_SAY "00000000-0000-0000-0000-000000000020"
#define OUTPUT_SHOUT "00000000-0000-0000-0000-000000000100"
#define OUTPUT_NULL NULL_KEY
integer PROGRAM_NUMBER = 0;
string E_PROGRAM_NUMBER = "!!";
// signal number encoding (0-4095, 0x0000-0x0fff)
#define ares_encode(_number) llChar( ((_number & 0x0fc0) >> 6) + 33 ) + llChar( (_number & 0x3f) + 33 )
#define ares_decode(_code) (((llOrd(_code, 0) - 33) << 6) + (llOrd(_code, 1) - 33))
// placeholder for procs with no known name (used in system messages):
#define E_UNKNOWN "!!"
/*
SIGNAL DEFINITIONS
Signals are assigned spaces in the following format:
0x000 - 0x0ff: standard program interactions
0x100 - 0x1ff: daemon interactions
...
0xf00 - 0xfff: kernel interactions
*/
// 0x000-0x0ff: standard program interactions
#define SIGNAL_INVOKE 0x000
#define E_SIGNAL_INVOKE "!!"
// call from kernel to program:
#define SIGNAL_EVENT 0x002
#define E_SIGNAL_EVENT "!#"
// call from daemon to program (avoid at all costs):
#define SIGNAL_SUMMON 0x003
#define E_SIGNAL_SUMMON "!$"
// receipt for successful invoke:
#define SIGNAL_DONE 0x004
#define E_SIGNAL_DONE "!%"
// bidirectional timer creation and trigger:
#define SIGNAL_TIMER 0x005
#define E_SIGNAL_TIMER "!&"
// 0x100-0x1ff: daemon interactions
#define SIGNAL_DATA_REQUEST 0x100
#define E_SIGNAL_DATA_REQUEST "%!"
#define SIGNAL_DATA_UNAVAILABLE 0x101
#define E_SIGNAL_DATA_UNAVAILABLE "%\""
#define SIGNAL_DATA_VALUE 0x102
#define E_SIGNAL_DATA_VALUE "%#"
#define SIGNAL_DATA_LIST 0x103
#define E_SIGNAL_DATA_LIST "%$"
#define SIGNAL_DATA_SET 0x104
#define E_SIGNAL_DATA_SET "%%"
#define SIGNAL_DATA_DELETE 0x105
#define E_SIGNAL_DATA_DELETE "%&"
// call from program to daemon:
#define SIGNAL_CALL 0x110
#define E_SIGNAL_CALL "%1"
#define SIGNAL_CREATE_RULE 0x111
#define E_SIGNAL_CREATE_RULE "%2"
#define SIGNAL_DELETE_RULE 0x112
#define E_SIGNAL_DELETE_RULE "%3"
#define SIGNAL_NOTIFY 0x113
#define E_SIGNAL_NOTIFY "%4"
#define SIGNAL_QUERY_RULES 0x114
#define E_SIGNAL_QUERY_RULES "%5"
// messy commands to daemon-bound functions
// that do not fit neatly into the design:
#define SIGNAL_SECURITY 0x120
#define E_SIGNAL_SECURITY "%A"
#define SIGNAL_VOX 0x121
#define E_SIGNAL_VOX "%B"
#define SIGNAL_TELL 0x122
#define E_SIGNAL_TELL "%C"
// pipe interactions for FLOW (stream-processing) programs:
// (not implemented in this version)
#define SIGNAL_STREAM_OPEN 0x130
#define E_SIGNAL_STREAM_OPEN "%Q"
#define SIGNAL_STREAM_DATA 0x131
#define E_SIGNAL_STREAM_DATA "%R"
#define SIGNAL_STREAM_CLOSED 0x132
#define E_SIGNAL_STREAM_CLOSED "%S"
// 0xf00-0xfff: kernel interactions
#define SIGNAL_SOLICIT_ADDRESS 0xf00
#define SIGNAL_ASSIGN_ADDRESS 0xf01
#define E_SIGNAL_ASSIGN_ADDRESS "]\""
#define SIGNAL_TERMINATE 0xf02
#define E_SIGNAL_TERMINATE "]#"
#define SIGNAL_WAKE 0xf03
#define E_SIGNAL_WAKE "]$"
#define SIGNAL_WOKE 0xf04
#define E_SIGNAL_WOKE "]%"
#define SIGNAL_OVERVIEW 0xf05
#define E_SIGNAL_OVERVIEW "]&"
#define SIGNAL_OVERVIEW_REPORT 0xf06
#define E_SIGNAL_OVERVIEW_REPORT "]'"
#define SIGNAL_HOOK_EVENT 0xf07
#define E_SIGNAL_HOOK_EVENT "]("
#define SIGNAL_UNHOOK_EVENT 0xf08
#define E_SIGNAL_UNHOOK_EVENT "])"
#define SIGNAL_TERMINATED 0xf09
#define E_SIGNAL_TERMINATED "]*"
#define SIGNAL_UNKNOWN_SCRIPT 0xf0a
#define E_SIGNAL_UNKNOWN_SCRIPT "]+"
// these require callback numbers:
#define SIGNAL_QUERY_MODULES 0xf0b
#define E_SIGNAL_QUERY_MODULES "],"
#define SIGNAL_QUERY_HOOKS 0xf0c
#define E_SIGNAL_QUERY_HOOKS "]-"
#define SIGNAL_QUERY_DAEMONS 0xf0d
#define E_SIGNAL_QUERY_DAEMONS "]."
// parse the provided string as an input pipe:
#define SIGNAL_MODULES_REPORT 0xf0e
#define E_SIGNAL_MODULES_REPORT "]/"
#define SIGNAL_HOOKS_REPORT 0xf0f
#define E_SIGNAL_HOOKS_REPORT "]0"
#define SIGNAL_DAEMONS_REPORT 0xf10
#define E_SIGNAL_DAEMONS_REPORT "]1"
// from delegate or daemon to kernel:
#define SIGNAL_TRIGGER_EVENT 0xf11
#define E_SIGNAL_TRIGGER_EVENT "]2"
#define SIGNAL_DAEMON_ANNOUNCE 0xf12
#define SIGNAL_MODE 0xf13
#define E_SIGNAL_MODE "]4"
// program ready to be sent commands:
#define SIGNAL_READY 0xf14
#define E_SIGNAL_READY "]5"
// program was awoken but already awake:
#define SIGNAL_BUSY 0xf15
#define E_SIGNAL_BUSY "]6"
// stop program without resetting it:
#define SIGNAL_SLEEP 0xf16
#define E_SIGNAL_SLEEP "]7"
#define SIGNAL_RECHECK_MODULE 0xf17
#define E_SIGNAL_RECHECK_MODULE "]8"
// special message for combat performance:
#define SIGNAL_COLLISION 0xf18
#define E_SIGNAL_COLLISION "]9"
#define SIGNAL_INVENTORY_DROP 0xf19
#define E_SIGNAL_INVENTORY_DROP "]:"
// daemons can now call programs safely:
#define SIGNAL_SYSTEM_READY 0xffc
#define E_SIGNAL_SYSTEM_READY "`]"
#define SIGNAL_INIT 0xffd
#define E_SIGNAL_INIT "`^"
// this requires a callback number:
#define SIGNAL_DAEMON_RESET 0xffe
#define E_SIGNAL_DAEMON_RESET "`_"
#define SIGNAL_KERNEL_RESET 0xfff
#define E_SIGNAL_KERNEL_RESET "``"
/*
EVENT DEFINITIONS
Most events have no parameters.
Exceptions are noted.
*/
// Touch events are for the ARES HUD only. They are sent in the format: 1 <id> <link> <face> <ST coordinates>
#define EVENT_TOUCH 0x001
// Teleport events ARE NOT sent when the system teleports to a new region.
#define EVENT_TELEPORT 0x002
// Generated by _fs after a refresh, whether or not any files actually changed
#define EVENT_INVENTORY_CHANGE 0x003
// 0x004 was EVENT_TIMER, which has been replaced by SIGNAL_TIMER for brevity
// Device events specify the name and key of the device, e.g. 5 battery <id>
// However, they may be sent with no parameters during a device probe.
#define EVENT_NEW_DEVICE 0x005
#define EVENT_REMOVE_DEVICE 0x006
// Region change events are sent when the system crosses a region boundary OR teleports to a new region.
#define EVENT_REGION_CHANGE 0x007
// The on_rez event is generated by _proc when it resets and when the system is rezzed.
#define EVENT_ON_REZ 0x008
// The interface refresh event triggers whenever the whole UI needs to be repositioned, such as when the HUD subsystem is enabled, the unit is powered on, the _interface daemon is reset, or mouselook is entered or left.
#define EVENT_INTERFACE 0x009
// The rez_object event is generated by _proc when an object is rezzed. The object's key is passed as a parameter.
#define EVENT_REZ_OBJECT 0x00a
// The warning event triggers whenever a warning is issued by a daemon. This is meant only to be monitored by the _display program. Ideally this would be a notify message, but using an event hook fails gracefully if a warning is generated before _display is registered with the kernel. Specified as 11 <slot> <message>, e.g. "11 2 6" displays 'RADIATION DANGER' in slot 2; see interface.consts.h.lsl for list.
#define EVENT_WARNING 0x00b
/*
MODE DEFINITIONS
see ARES/program for details
*/
#define MODE_NORMAL 0x000
#define MODE_THREADING 0x001
#define MODE_NON_VOLATILE 0x002
#define MODE_MASK_SLEEPLESS 0x003
#define MODE_ACCEPT_DONE 0x004
#define MODE_MASK_BATCH 0x006
#define MODE_FLOW 0x008
#include <ARES/api/api.h.lsl>

97
ARES/api/api.h.lsl Normal file
View File

@ -0,0 +1,97 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* API.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
/*
FOUNDATIONAL SYSTEM CALLS
IMPORTANT: When required to provide a UUID to the ARES API by these or any other functions, never pass an empty string. Instead use NULL_KEY. Some parts of the OS assume fixed-length message prefixes and will break if fed the wrong thing.
*/
#ifndef _ARES_API_H_
#define _ARES_API_H_
// send a command to the kernel:
#define kernel(_message_number, _message) linked(R_KERNEL, _message_number, E_PROGRAM_NUMBER + _message, "")
// send a message to the system (via the kernel):
#define system(_message_number, _message) linked(R_KERNEL, _message_number, _message, "")
// daemon call (message below 0xf00):
#define call(_daemon_channel, _message_number, _message) tell(DAEMON, _daemon_channel, ares_encode(_message_number) + E_PROGRAM_NUMBER + _message)
// pre-encoded daemon call (message below 0xf00):
#define e_call(_daemon_channel, _e_message_number, _message) tell(DAEMON, _daemon_channel, _e_message_number + E_PROGRAM_NUMBER + _message)
// run a program via command line (as though called by a user):
#define invoke(_command, _output_pipe, _input_pipe, _user) system(SIGNAL_INVOKE, E_UNKNOWN + E_PROGRAM_NUMBER + (string)(_output_pipe) + " " + (string)(_input_pipe) + " " + (string)(_user) + " " + _command)
// request a program be terminated:
#define terminate(_program) system(SIGNAL_TERMINATE, E_UNKNOWN + E_PROGRAM_NUMBER + _program)
// send a notification to a program (used for file i/o and sharing status changes):
#define notify_program(_command, _output_pipe, _input_pipe, _user) system(SIGNAL_NOTIFY, E_UNKNOWN + E_PROGRAM_NUMBER + (string)(_output_pipe) + " " + (string)(_input_pipe) + " " + (string)(_user) + " " + _command)
// send a notification to a daemon (see C_* constants in ARES/a for channels):
#define notify_daemon(_daemon_channel, _command, _output_pipe, _input_pipe, _user) tell(DAEMON, _daemon_channel, E_SIGNAL_NOTIFY + E_PROGRAM_NUMBER + (string)(_output_pipe) + " " + (string)(_input_pipe) + " " + (string)(_user) + " " + _command)
// send a message from one daemon to another (message number is unencoded)
#define daemon_to_daemon(_e_daemon, _message_number, _message) system(_message_number, _e_daemon + E_PROGRAM_NUMBER + _message)
/* Inclusions of ARES API components */
#include <ARES/api/io.h.lsl>
#include <ARES/api/hardware.h.lsl>
#include <ARES/api/status.h.lsl>
#include <ARES/api/effector.h.lsl>
#include <ARES/api/repair.h.lsl>
#include <ARES/api/interface.h.lsl>
#include <ARES/api/kernel.h.lsl>
#include <ARES/api/scheduler.h.lsl>
#include <ARES/api/storage.h.lsl>
#include <ARES/api/tasks.h.lsl>
#include <ARES/api/database.h.lsl>
#include <ARES/api/request.h.lsl>
/*
The following modules provide function implementations rather than convenient ways to make existing system calls, and are therefore not automatically included:
auth.h.lsl
file.h.lsl
interface.consts.h.lsl
version-compare.h.lsl
*/
#endif // _ARES_API_H_

145
ARES/api/auth.h.lsl Normal file
View File

@ -0,0 +1,145 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 2023 Nanite Systems Corporation
*
* =========================================================================
*
* AUTH.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_AUTH_H_
#define _ARES_AUTH_H_
#include <utils.lsl>
#define ALLOWED TRUE
#define DENIED FALSE
#define PENDING NOWHERE
#define SEC_NEVER 0
#define SEC_ALWAYS 1
#define SEC_CONSENT 2
#define SEC_USER 3
#define SEC_MANAGER 4
#define SEC_OWNER 5
#define SEC_SELF 6
/*
sec_check() return values:
ALLOWED (1) on access granted,
DENIED (0) on access denied, or
PENDING (-1) if a consent prompt was generated
if sec_check() returns PENDING, the program should abruptly halt
one of deny_cmd or retry_cmd is executed after the unit denies or grants consent, preserving the original user and outs
if you implement a structure like...
if (check == ALLOWED) { // do the task here ... } else if (check == DENIED) { // print error message here ... }
...in your program, then you can pass the original command as both deny_cmd and retry_cmd, e.g. sec_check(user, "manage", outs, m, m)
almost all of the permissions are customizable; the default ones are:
menu: access the menus
local: send commands in local chat
remote: send commands using a remote access device
yank: force the unit to teleport
arouse: trigger TESI events
chat: send chat as the unit
manage: adjust unit settings
identity: adjust unit's name etc.
add-user: add a registered user at rank 3
add-manager: promote a registered user to rank 4
add-owner: promote a registered user to rank 5
demote-self: lower own user rank
demote-manager: lower rank of a manager (rank 4)
demote-owner: lower rank of an owner (rank 5)
run-away: clear all users
safeword: restore the unit's autonomy
database: alter database entries directly
(the db program requires rank 5 to affect the 'security' section or run a load)
owner: this is a special permission that just checks the owner rank (used for configuring security settings)
the standard interpretations of the security levels are:
0: no one may do this
1: everyone may do this except for the banned
2: consent must be given to do this
3: authorized users of rank 3 or higher (all) may do this
4: authorized users of rank 4 or higher (managers) may do this
5: authorized users of rank 5 or higher (owners) may do this
6: only the unit may do this
*/
// #define SEC_DEBUG
integer sec_check(key user, string permission, key outs, string deny_cmd, string retry_cmd) {
string sec_section = llLinksetDataRead("security");
integer req = (integer)getjs(sec_section, ["rule", permission]);
#ifdef SEC_DEBUG
echo("[sec_check]\nrequired access: " + (string)req
+ "\nuser rank: " + getjs(sec_section, ["user", user])
+ "\nuser guest? " + getjs(sec_section, ["guest", user])
+ "\nuser ban? " + getjs(sec_section, ["ban", user])
);
#endif
if(req == SEC_SELF && user == avatar)
return ALLOWED;
string ban = getjs(sec_section, ["ban", user]);
string guest = getjs(sec_section, ["guest", user]);
integer rank = (integer)getjs(sec_section, ["user", user]);
if(permission == "owner")
return (rank == SEC_OWNER);
if(ban != JSON_INVALID && ((integer)ban == 1 || (integer)ban > llGetUnixTime()))
return DENIED;
else if(rank >= req && req > SEC_NEVER)
return ALLOWED;
else if(req == SEC_CONSENT) {
if(guest != JSON_INVALID && ((integer)guest == 1 || (integer)guest > llGetUnixTime()))
return ALLOWED;
else {
notify_program("_security consent "
+ jsarray([retry_cmd, deny_cmd]),
outs,
NULL_KEY,
user
);
return PENDING;
}
} else if(req == SEC_ALWAYS)
return ALLOWED;
else
return DENIED;
}
#endif // _ARES_AUTH_H_

64
ARES/api/database.h.lsl Normal file
View File

@ -0,0 +1,64 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* DATABASE.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
/*
LSD DATABASE I/O
Read, write, and delete database entries.
There are two sets of macros for interacting with the Linkset datastore. The first set has names ending in 'dbl', and the second set has names ending in 'db'.
The 'dbl' macros assume the key name is already split into a list (the same as LSL's JSON functions) and are therefore more efficient.
The 'db' macros will do this for you; you should only need these for handling user input.
Occasionally when working with many entries in one section, you may still find it memory-efficient to use the llLinksetData* functions with getjs(). There are no macros for this because you should only be doing it once or twice per program.
*/
#ifndef _ARES_DATABASE_H_
#define _ARES_DATABASE_H_
// use as setdbl(section, ["list", "of", "subkey", "names"], value)
#define setdbl(_section, ...) llLinksetDataWrite(_section, setjs(llLinksetDataRead(_section), __VA_ARGS__))
#define getdbl(_section, ...) getjs(llLinksetDataRead(_section), __VA_ARGS__)
#define deletedbl(_section, ...) llLinksetDataWrite(_section, setjs(llLinksetDataRead(_section), __VA_ARGS__, JSON_DELETE))
// use as setdbl(section, "string.of.subkey.names", value)
#define setdb(_section, _entry, _value) llLinksetDataWrite(_section, setjs(llLinksetDataRead(_section), splitnulls(_entry, "."), _value))
#define getdb(_section, _entry) getjs(llLinksetDataRead(_section), splitnulls(_entry, "."))
#define deletedb(_section, _entry) llLinksetDataWrite(_section, setjs(llLinksetDataRead(_section), splitnulls(_entry, "."), JSON_DELETE))
#endif // _ARES_DATABASE_H_

76
ARES/api/effector.h.lsl Normal file
View File

@ -0,0 +1,76 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* EFFECTOR.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
// **** effector daemon ****
#ifndef _ARES_EFFECTOR_H_
#define _ARES_EFFECTOR_H_
/*
* ARES RLV
*
* The main role of the effector daemon is in managing RLV restrictions and preventing them from conflicting, similar to an RLV relay. However, there are some slight differences from regular RLV:
*
* - The '@' is stripped off.
* - To format _rule_values for restrictions (those rules ending in =n/y/add/rem), put '?' instead. This will be replaced with 'y' and 'n' as appropriate by the effector daemon.
* - If a rule's name starts with 'a:' then its contents will be treated as an animation from ring 2 (the daemon ring)
* - The new rule 'move=?' causes the system to intercept movement keys while active.
* - The rule 'recvchat=?' will also trigger 'recvemote=?' and cause the _input program to attempt to filter quoted text inside emotes.
*
* As of Alpha 1, only a few RLV rules can be applied multiple times. These are 'move=?', 'sendchat=?', 'recvemote=?', and 'recvchat=?'. Attempting to remove any other rule will release the restriction entirely, breaking other sources of the same restriction. This will be improved when the RLV relay program 'restraint' is finished in Alpha 3.
*
*/
// effector_restrict(rule-name, rule-value): applies an RLV restriction (e.g. 'recvchat=?') with the specified rule name (see details at start of effector.h.lsl)
#define effector_restrict(_rule_name, _rule_value) e_call(C_EFFECTOR, E_SIGNAL_CREATE_RULE, _rule_name + " " + _rule_value)
// effector_release(rule-name): releases an RLV restriction (see details at start of effector.h.lsl); if the rule name ends in "*" then all rules matching the prefix will be removed, e.g. "power_*" to remove all rules starting with "power_"
#define effector_release(_rule_name) e_call(C_EFFECTOR, E_SIGNAL_DELETE_RULE, _rule_name)
// this just sticks "@" on the front:
#define effector_rlv(_rule) e_call(C_EFFECTOR, E_SIGNAL_CALL, NULL_KEY + " " + NULL_KEY + " effector rlv " + _rule)
// teleports to region and (vector) position; if _external is TRUE, doesn't consume power or play fx
#define effector_teleport(_region, _position, _external) e_call(C_EFFECTOR, E_SIGNAL_CALL, NULL_KEY + " " + NULL_KEY + " effector teleport " + (string)(_external) + "|" + (string)avatar + "|" + (_region) + "|" + (string)(_position))
// tell the controller hardware to make noises:
// play_sound(): play a sound specified by UUID
// announce(): play an announcer voice sample, as defined in the LSD section 'announcer'
#define play_sound(_name) e_call(C_EFFECTOR, E_SIGNAL_CALL, (string)avatar + " " + (string)avatar + " effector sound " + _name)
#define daemon_play_sound(_name) daemon_to_daemon(E_EFFECTOR, SIGNAL_CALL, (string)avatar + " " + (string)avatar + " effector sound " + _name)
#define announce(_announcement) e_call(C_EFFECTOR, E_SIGNAL_CALL, (string)avatar + " " + (string)avatar + " effector announce " + _announcement)
#define daemon_announce(_announcement) daemon_to_daemon(E_EFFECTOR, SIGNAL_CALL, (string)avatar + " " + (string)avatar + " effector announce " + _announcement)
#endif // _ARES_EFFECTOR_H_

249
ARES/api/file.h.lsl Normal file
View File

@ -0,0 +1,249 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 2023 Nanite Systems Corporation
*
* =========================================================================
*
* FILE.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_FILE_H_
#define _ARES_FILE_H_
#include <utils.lsl>
#define FILE_R 0
#define FILE_INS 1
#define FILE_OUTS 2
#define FILE_USER 3
#define FILE_NAME 4
#define FILE_OFFSET 5
#define FILE_LENGTH 6
#define FILE_UNIT 7
/*
NEW UNIFORM FILE ACCESS SYSTEM - ARES 0.4.4
replaces ARES/api/file.h.lsl
*/
/*
To use this script, call fopen() with the 'outs', 'ins', and 'user' from main()
When file contents are available, the program will receive:
SIGNAL_NOTIFY
message: PROGRAM_NAME + " file"
ins: matching the file_handle returned by fopen()
You can then call fread(file_handle) to get contents.
fread() has the following special return values:
JSON_FALSE: file does not exist or file is empty
JSON_TRUE: file was opened successfully and data will arrive soon
If fread() returns any other value, it is file data.
fread() reads files in buffer chunks of around 1024 bytes, or one line for notecards, and always advances to the next chunk afterward.
fread() will close the file automatically when it is done reading, at which point getjs(tasks_queue, [file_handle]) will return JSON_INVALID.
By default, this API will automatically try to resolve tasks for you when the file is closed. This is good for simple file readers but wrong if you intend to do more processing afterward. To suppress this behavior, set '_resolved = 0' before calling fopen(), and pass NULL_KEY for ins.
*/
#ifndef FILE_STEP_SIZE
// define this as a small integer for speed-ups (don't go too far, though)
#define FILE_STEP_SIZE 1
#endif
// fopen(output_stream, input_stream, user_key, file_name): returns file_handle
key fopen(key outs, key ins, key user, string filename) {
key q = llGenerateKey();
// send request to io daemon to create pipe and fetch file length:
file_open(q, filename);
// track file information and prevent job from sleeping during load process
string metadata = list2js(JSON_ARRAY, [
_resolved, // callback number for program that summoned us
ins, // original input stream (often used as a callback handle)
outs, // output stream
user, // user that initiated the request
filename, // filename being worked on
NOWHERE, // file offset (haven't started reading yet)
NOWHERE, // file length (loaded first)
"" // file unit (loaded with file length)
]);
task_begin(q, metadata);
// metadata is stored in the global JSON variable tasks_queue, and can be accessed later
_resolved = 0;
return q;
}
// fread(file_handle): returns JSON_TRUE if file opened successfully, JSON_FALSE if file empty/missing, otherwise file text
// note that it may return up to 1025 bytes for a perfectly full notecard line + linebreak, but will otherwise always return 1024 bytes or fewer
string fread(key q) {
// split apart file metadata into a list (smaller code)
list file = js2list(getjs(tasks_queue, [q]));
integer file_length = geti(file, FILE_LENGTH);
string file_unit = gets(file, FILE_UNIT);
string fn = gets(file, FILE_NAME);
// copy contents of LSD pipe into local variable:
string buffer;
pipe_read(q, buffer);
integer offset = geti(file, FILE_OFFSET);
_resolved = 0;
if(!~file_length) {
// on the first successful call we load the file length and unit
list stat_parts = split(buffer, " ");
file_length = (integer)gets(stat_parts, 0);
file_unit = gets(stat_parts, 1);
tasks_queue = setjs(setjs(tasks_queue,
[q, FILE_LENGTH], (string)file_length),
[q, FILE_UNIT], (string)file_unit);
// for notecards, file length is measured as the number of lines,
// NOT the actual character count
if(file_length > 0) {
// a length greater than 0 indicates the file exists
// start reading file
offset = 0;
integer read_length = FILE_STEP_SIZE;
if(file_unit == "b")
read_length *= FILE_PAGE_LENGTH;
if(read_length > file_length)
read_length = file_length;
file_read(q, fn, (string)offset + " " + (string)read_length);
tasks_queue = setjs(tasks_queue, [q, FILE_OFFSET], (string)offset);
buffer = JSON_TRUE;
} else {
// no file was found, or file was empty
file_close(q);
// job can now end:
// resolve_io(geti(file, FILE_R), getk(file, FILE_OUTS), getk(file, FILE_INS));
resolve_i(geti(file, FILE_R), getk(file, FILE_INS));
task_end(q);
buffer = JSON_FALSE;
}
} else {
// got length earlier, so this must be file data
// move forward to next page:
if(file_unit == "b")
offset += (FILE_PAGE_LENGTH * FILE_STEP_SIZE);
else
offset += (FILE_STEP_SIZE);
// is there more file?
if(offset < file_length) {
// record new offset:
tasks_queue = setjs(tasks_queue, [q, FILE_OFFSET], (string)offset);
// get next section:
#ifdef DEBUG
echo("file.h.lsl: loading offset " + (string)offset + "/" + (string)file_length);
#endif
integer read_length = FILE_STEP_SIZE;
if(file_unit == "b")
read_length *= FILE_PAGE_LENGTH;
if(read_length + offset > file_length)
read_length = file_length - offset;
file_read(q, fn, (string)offset + " " + (string)read_length);
} else {
// reached end of file
file_close(q);
// job can now end:
// resolve_io(geti(file, FILE_R), getk(file, FILE_OUTS), getk(file, FILE_INS));
resolve_i(geti(file, FILE_R), getk(file, FILE_INS));
// delete file handle:
task_end(q);
}
}
// send data to the program
return buffer;
}
// preview what fread() will return without actually initiating the next file request
string fpeek(key q) {
// split apart file metadata into a list (smaller code)
list file = js2list(getjs(tasks_queue, [q]));
integer file_length = geti(file, FILE_LENGTH);
// copy contents of LSD pipe into local variable:
string buffer = llLinksetDataRead("p:" + (string)q);
if(!~file_length) {
// on the first successful call we load the file length
file_length = (integer)buffer;
// tasks_queue = setjs(tasks_queue, [q, FILE_LENGTH], (string)file_length);
// for notecards, file length is measured as 256 * (number of lines),
// NOT the actual character count
if(file_length > 0) {
// a length greater than 0 indicates the file exists
buffer = JSON_TRUE;
} else {
// no file was found, or file was empty
buffer = JSON_FALSE;
}
}
// send data to the program
return buffer;
}
// abort file reading (only needed when using fpeek()):
fclose(key q) {
// split apart file metadata into a list (smaller code)
list file = js2list(getjs(tasks_queue, [q]));
file_close(q);
// job can now end:
// resolve_io(geti(file, FILE_R), getk(file, FILE_OUTS), getk(file, FILE_INS));
resolve_i(geti(file, FILE_R), getk(file, FILE_INS));
// delete file handle:
task_end(q);
}
#endif // _ARES_FILE_H_

211
ARES/api/file.h.lsl.old Normal file
View File

@ -0,0 +1,211 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 2023 Nanite Systems Corporation
*
* =========================================================================
*
* FILE.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_FILE_H_
#define _ARES_FILE_H_
#include <utils.lsl>
#define FILE_R 0
#define FILE_INS 1
#define FILE_OUTS 2
#define FILE_USER 3
#define FILE_NAME 4
#define FILE_OFFSET 5
#define FILE_LENGTH 6
/*
To use this script, call fopen() with the 'outs', 'ins', and 'user' from main()
When file contents are available, the program will receive:
SIGNAL_NOTIFY
message: PROGRAM_NAME + " file"
ins: matching the file_handle returned by fopen()
You can then call fread(file_handle) to get contents.
fread() has the following special return values:
JSON_FALSE: file does not exist or file is empty
JSON_TRUE: file was opened successfully and data will arrive soon
If fread() returns any other value, it is file data.
fread() reads files in buffer chunks of FILE_PAGE_LENGTH and always advances to the next chunk.
FILE_PAGE_LENGTH is 1024 in ARES alpha 1.
For notecards, which are not read in bytes, this is actually translated into 4 lines.
fread() will close the file automatically when it is done reading, at which point getjs(tasks_queue, [file_handle]) will return JSON_INVALID.
By default, this API will automatically try to resolve tasks for you when the file is closed. This is good for simple file readers but wrong if you intend to do more processing afterward. To suppress this behavior, set '_resolved = 0' before calling fopen(), and pass NULL_KEY for ins.
*/
// fopen(output_stream, input_stream, user_key, file_name): returns file_handle
key fopen(key outs, key ins, key user, string filename) {
key q = llGenerateKey();
// send request to io daemon to create pipe and fetch file length:
file_open(q, filename);
// track file information and prevent job from sleeping during load process
string metadata = list2js(JSON_ARRAY, [
_resolved, // callback number for program that summoned us
ins, // original input stream (often used as a callback handle)
outs, // output stream
user, // user that initiated the request
filename, // filename being worked on
NOWHERE, // file offset (haven't started reading yet)
NOWHERE // file length (loaded first)
]);
task_begin(q, metadata);
// metadata is stored in the global JSON variable tasks_queue, and can be accessed later
_resolved = 0;
return q;
}
// fread(file_handle): returns JSON_TRUE if file opened successfully, JSON_FALSE if file empty/missing, otherwise file text
string fread(key q) {
// split apart file metadata into a list (smaller code)
list file = js2list(getjs(tasks_queue, [q]));
integer file_length = geti(file, FILE_LENGTH);
string fn = gets(file, FILE_NAME);
// copy contents of LSD pipe into local variable:
string buffer;
pipe_read(q, buffer);
integer offset = geti(file, FILE_OFFSET);
_resolved = 0;
if(!~file_length) {
// on the first successful call we load the file length
file_length = (integer)buffer;
tasks_queue = setjs(tasks_queue, [q, FILE_LENGTH], (string)file_length);
// for notecards, file length is measured as 256 * (number of lines),
// NOT the actual character count
if(file_length > 0) {
// a length greater than 0 indicates the file exists
// start reading file
file_read(q, fn, offset = 0);
tasks_queue = setjs(tasks_queue, [q, FILE_OFFSET], (string)offset);
buffer = JSON_TRUE;
} else {
// no file was found, or file was empty
file_close(q);
// job can now end:
// resolve_io(geti(file, FILE_R), getk(file, FILE_OUTS), getk(file, FILE_INS));
resolve_i(geti(file, FILE_R), getk(file, FILE_INS));
task_end(q);
buffer = JSON_FALSE;
}
} else {
// got length earlier, so this must be file data
// move forward to next page:
offset += FILE_PAGE_LENGTH;
// is there more file?
if(offset < file_length) {
// record new offset:
tasks_queue = setjs(tasks_queue, [q, FILE_OFFSET], (string)offset);
// get next section:
file_read(q, fn, offset);
} else {
// reached end of file
file_close(q);
// job can now end:
// resolve_io(geti(file, FILE_R), getk(file, FILE_OUTS), getk(file, FILE_INS));
resolve_i(geti(file, FILE_R), getk(file, FILE_INS));
// delete file handle:
task_end(q);
}
}
// send data to the program
return buffer;
}
// preview what fread() will return without actually initiating the next file request
string fpeek(key q) {
// split apart file metadata into a list (smaller code)
list file = js2list(getjs(tasks_queue, [q]));
integer file_length = geti(file, FILE_LENGTH);
// copy contents of LSD pipe into local variable:
string buffer = llLinksetDataRead("p:" + (string)q);
if(!~file_length) {
// on the first successful call we load the file length
file_length = (integer)buffer;
// tasks_queue = setjs(tasks_queue, [q, FILE_LENGTH], (string)file_length);
// for notecards, file length is measured as 256 * (number of lines),
// NOT the actual character count
if(file_length > 0) {
// a length greater than 0 indicates the file exists
buffer = JSON_TRUE;
} else {
// no file was found, or file was empty
buffer = JSON_FALSE;
}
}
// send data to the program
return buffer;
}
// abort file reading (only needed when using fpeek()):
fclose(key q) {
// split apart file metadata into a list (smaller code)
list file = js2list(getjs(tasks_queue, [q]));
file_close(q);
// job can now end:
// resolve_io(geti(file, FILE_R), getk(file, FILE_OUTS), getk(file, FILE_INS));
resolve_i(geti(file, FILE_R), getk(file, FILE_INS));
// delete file handle:
task_end(q);
}
#endif // _ARES_FILE_H_

69
ARES/api/fs.h.lsl.old Normal file
View File

@ -0,0 +1,69 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* FS.H.LSL Header Component (DEPRECATED)
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_FS_H_
#define _ARES_FS_H_
/*
UNIFORM FILE ACCESS - OBSOLETE
DO NOT USE - DEFUNCT IN ARES 0.4.4
file handling via io (with fs as back end)
see also file.h.lsl for a simple (but memory-intensive) wrapper to these calls
*/
#define FILE_PAGE_LENGTH 2048
#define FILE_SIZE NOWHERE
#define FILE_LINE_WIDTH 1024
#define file_read(_pipe, _filename, _offset) e_call(C_IO, E_SIGNAL_DATA_REQUEST, (string)(_pipe) + " " + PROGRAM_NAME + " " + (_filename) + " " + (string)(_offset))
#define file_open(_pipe, _filename) file_read(_pipe, _filename, NOWHERE);
#define file_close(_pipe) e_call(C_IO, E_SIGNAL_DELETE_RULE, "[\"" + (string)(_pipe) + "\"]")
// OTHER FILESYSTEM ACTIVITIES
// get all filenames in a view:
#define list_files(_rule) js2list(llLinksetDataRead("fs:"+_rule))
// get all filenames (careful; this is a lot of data):
#define list_all_files() jskeys(llLinksetDataRead("fs:root"))
// trigger a filesystem refresh (no way of checking when this finishes):
#define fs_refresh() invoke("fs refresh", avatar, NULL_KEY, avatar)
#endif // _ARES_FS_H_

46
ARES/api/hardware.h.lsl Normal file
View File

@ -0,0 +1,46 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* HARDWARE.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_HARDWARE_H_
#define _ARES_HARDWARE_H_
// **** hardware daemon ****
#define device_list() e_call(C_HARDWARE, E_SIGNAL_DATA_LIST, "")
#define device_command(_device_address, _command, _outs, _user) e_call(C_HARDWARE, E_SIGNAL_CALL, (string)(_outs) + " " + (string)(_user) + " hardware " + (_device_address) + " " + (_command))
#define device_probe() e_call(C_HARDWARE, E_SIGNAL_CALL, NULL_KEY + " " + NULL_KEY + " hardware probe")
#endif

View File

@ -0,0 +1,253 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Interface Constants
*
* 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.
*
* =========================================================================
*
*/
// This file is *not* included by default.
#ifndef _ARES_INTERFACE_CONSTS_H_
#define _ARES_INTERFACE_CONSTS_H_
// 2: unused (system daemons)
#define MODEL_BADGE 3
// 13-14, 4, and 249-250: mouselook combat stuff
#define CROSSHAIR 4
// 5-10: gauges
#define POWER_GAUGE 5
#define RATE_GAUGE 6
#define HEAT_GAUGE 7
#define FAN_GAUGE 8
#define INTEGRITY_GAUGE 9
#define LUBE_GAUGE 10
#define GAUGE_SIZE <256, 32, 0>
#define BADGE_CLADDING_LEFT 11
#define BADGE_CLADDING_RIGHT 12
// 13-14, 86, and 249-250: mouselook combat stuff
#define INTEGRITY_LABEL 13
#define AMMO_LABEL 14
// 15-22: unused (formerly fixed warnings)
// 23-28: unused
// 23 used for experimental MOAP widget
#define WORKING 28
#define BOOT_PROGRESS 30
#define BOOT_LOGO 31
// 32-63: device icons
#define DEVICE_ICON_BASE 32
#define ALERT_OPTIONS 64
#define ALERT_ANCHOR 67
#define COMPASS_PRIM_DIAL 65
#define COMPASS_PRIM_FRAME 66
#define SPEEDOMETER 68
#define SPEEDOMETER_TEXT 69
#define ALTIMETER_BAR 70
#define ALTIMETER_MARKER 71
#define WARRIOR_START 72
// 72-78: Warrior paper doll
#define WEAPON_SELECT_START 79
// 79-85: Warrior weapon select
#define CLADDING_BACKDROP 86
// 87-91: sexuality
#define LUST_CLADDING 87
#define LUST_GAUGE 88
#define SENSITIVITY_GAUGE 89
#define PLATEAU_MARKER 90
#define ORGASM_MARKER 91
// 92-95: sexuality reserved
// 96-111: sitrep
#define SITREP_BASE 96
#define CPU_BAR 96
#define CPU_LABEL 97
#define FTL_RECHARGE_BAR 98
#define FTL_RECHARGE_LABEL 99
#define SIM_LAG_BAR 100
#define SIM_LAG_LABEL 101
#define SIM_POP_BAR 102
#define SIM_POP_LABEL 103
#define AUX_POWER_BAR 104
#define AUX_POWER_LABEL 105
#define HUMIDITY_BAR 106
#define HUMIDITY_LABEL 107
#define PRESSURE_BAR 108
#define PRESSURE_LABEL 109
#define RADIATION_BAR 110
#define RADIATION_LABEL 111
// 112-126: apps & stuff
// 112-120: unused
#define LL_TARGET_LOCK 121
#define LL_NAV_DEST 122
// 123-126: reserved for modal apps
#define WIZARD 123
#define WIZARD_TEXT 124
#define WIZARD_TEXT_2 125
#define WIZARD_TEXT_3 126
#define APP_0 123
#define APP_1 124
#define APP_2 125
#define APP_3 126
#define SCREEN_ANCHOR 127
#define SCREEN 128
#define TEXT_START 129
#define MSG_TEXT_START 195
#define MSG_TEXT_PRIM_LIMIT 12
// 195-206: alert message text
#define FIXED_WARNING_LIST 207
/* 207: fixed warnings
FW assignments
activate with:
system(SIGNAL_TRIGGER_EVENT, (string)EVENT_WARNING + " " + (string)(_slot) + " " + (string)(_msg))
send msg 0 to clear a slot
slot 0 (prim 15): damage (messages 1-3) applied by repair
1: CHECK HARDWARE
2: REPAIRS REQUIRED
3: REPAIRING
slot 1 (prim 16): non-combat modes (messages 4-5) applied by repair
4: OUT OF CHARACTER
5: DEGREELESSNESS MODE
slot 2 (prim 17): threats to homeostasis (messages 6-13) applied by status
6: RADIATION DANGER
7: HEAT DANGER
8: ICE DANGER
9: DUMP HEAT!
10: BAROMETER FAULT
11: VACUUM
12: IN WATER
13: CRYOLUBRICANT LOW
slot 3 (prim 18): processor status (messages 14 and 19) applied by baseband
14: WORKING
19: KERNEL INITIALIZING
slot 4 (prim 19): movement status (messages 15-18, 21) applied by ???
15: NAVIGATING
16: FOLLOWING
17: ANCHORED
18: IMMOBILIZED
21: CARRIED
slot 5 (prim 20): dive status (message 20) applied by ???
20: UNDER REMOTE CONTROL
slot 6 (prim 21): weapon status (messages 22-24) applied by ??? (device?)
22: LOW AMMO
23: NO AMMO
24: RELOADING
slot 7 (prim 22): battery status (messages 25-26) applied by status
25: LOW BATTERY
26: CHARGING
*/
#define CONFIG_CONTROLS 208
// 209-248: available
// 13-14, 86, and 249-250: mouselook combat stuff
#define INTEGRITY 249
#define AMMO 250
#define ARENA_SCOREBOARD_START 251
#define ARENA_CLOCK 256
#define VISIBLE <0.50000, -0.50000, -0.50000, 0.50000>
#define INVISIBLE <0.00000, -0.00000, -0.70711, 0.70711>
// for FIXED_WARNING_LIST only:
#define VISIBLE_FWL <0.70711, 0.00000, -0.70711, 0.00000>
#define INVISIBLE_FWL <0.50000, -0.50000, -0.50000, -0.50000>
#define OFFSCREEN <0, 0, -1.25>
#define OFFSCREEN_RIGHT <0, -3, 0>
#define OFFSCREEN_LEFT <0, 3, 0>
#define MOVER_TEX "cdf9347e-3a47-08d1-0ebc-1e2d27aa802c"
#define METER_TEX "8c782efc-cee7-c616-af9d-9a196cdd87c7"
#define SITREP_METER_TEX "e28a35b0-1581-4cc3-d2ab-9721b3ee3a19"
// #define AURA_TEX "1e1f83bc-968d-57a7-88a3-b88fc1a77a70"
#define AURA_TEX "cac27a60-722b-c653-c806-2c985ff34cf3"
#define BADGE_DEFAULT "0a1ebe09-5691-357e-e68a-cc90a2466fe2"
#define ALTIMETER_MARKER_TEX "4a860b55-2513-015a-63ba-f928c985bd06"
// #define BOOT_LOGO_TEX llGetInventoryKey("i_boot")
// #define COMPASS_TEX llGetInventoryKey("i_compass")
// #define MENU_TEX llGetInventoryKey("m_main")
// #define ANCHOR_TEX llGetInventoryKey("m_anchor")
// #define CLADDING_LEFT llGetInventoryKey("i_cladding-left")
// #define CLADDING_RIGHT llGetInventoryKey("i_cladding-right")
// #define MLOOK_TEX llGetInventoryKey("i_mlook")
// #define BIGNUM_TEX llGetInventoryKey("i_bignums")
// #define CROSSHAIR_TEX llGetInventoryKey("i_crosshair")
// #define ALTIMETER_TEX llGetInventoryKey("i_altimeter")
#define LUST_TEX llGetInventoryKey("i_sexuality")
// #define SITREP_TEX llGetInventoryKey("i_sitrep")
// #define TARGET_TEX llGetInventoryKey("i_target")
// #define CLADDING_BACKDROP_TEX llGetInventoryKey("i_cladding-backdrop")
// #define ALERT_TEX llGetInventoryKey("i_alert")
// #define WORKING_TEX llGetInventoryKey("i_working")
// moved to utils.lsl:
// #define str2vec(__str) (vector)("<" + replace(__str, " ", ",") + ">")
// #define vec2str(__vec) (__vec.x + " " + __vec.y + " " + __vec.z)
#endif // _ARES_INTERFACE_CONSTS_H_

88
ARES/api/interface.h.lsl Normal file
View File

@ -0,0 +1,88 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* INTERFACE.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_INTERFACE_H_
#define _ARES_INTERFACE_H_
// **** interface daemon ****
// interface_sound(): play a system sound effect to the unit only (must be named in LSD:interface.sound)
#define interface_sound(_name) e_call(C_INTERFACE, E_SIGNAL_CALL, \
(string)avatar + " " + (string)avatar + " interface sound " + _name)
// fixed_warning(slot, msg): display a fixed warning message on the UI (see values in interface.consts.h.lsl)
#define fixed_warning(_slot, _msg) \
system(SIGNAL_TRIGGER_EVENT, (string)EVENT_WARNING + " " + (string)(_slot) + " " + (string)(_msg))
// **** variatype daemon ****
/* alert(): create an alert message on the HUD
_message: the text to show (must fit in 12 variatype cells, approx. 96 chars)
_icon: 0-4 (icon 0 does not play sound)
_color: 0 for normal (color d), 1 for bad (color c)
_buttons: 0 for consent prompt, 1 for menu prompt, 2 for OK/details, 3 for OK only, 4 for ignore/help/run/delete
then: a list of actions to be invoked, one for each button
every alert acknowledgement will always clear the message
provide "!clear" as an action for just closing the prompt
any _buttons other than 0 will cause the alert to time out after a few seconds
example: alert("hi", 0, 0, 3, ["!clear"]) simply shows "hi" with dismissal option
the constants below can help make your alerts easier to read in code
*/
#define ALERT_ICON_INFO 0
#define ALERT_ICON_PERMISSION 1
#define ALERT_ICON_DATA 2
#define ALERT_ICON_HARDWARE 3
#define ALERT_ICON_ERROR 4
#define ALERT_COLOR_NORMAL 0
#define ALERT_COLOR_BAD 1
#define ALERT_BUTTONS_CONSENT 0
#define ALERT_BUTTONS_MENU 1
#define ALERT_BUTTONS_DETAILS 2
#define ALERT_BUTTONS_DISMISS 3
#define ALERT_BUTTONS_AUTOEXEC 6
#define alert(_message, _icon, _color, _buttons, ...) e_call(C_VARIATYPE, E_SIGNAL_CALL, NULL_KEY + " " + NULL_KEY + " variatype alert " + _message + "\n" + (string)(_icon) + "\n" + (string)(_color) + "\n" + (string)(_buttons) + "\n" + jsarray(__VA_ARGS__))
// send a HUD alert message from a daemon:
#define daemon_alert(_message, _icon, _color, _buttons, ...) daemon_to_daemon(E_VARIATYPE, E_SIGNAL_CALL, NULL_KEY + " " + NULL_KEY + " variatype alert " + _message + "\n" + (string)(_icon) + "\n" + (string)(_color) + "\n" + (string)(_buttons) + "\n" + jsarray(__VA_ARGS__))
#endif // _ARES_INTERFACE_H_

130
ARES/api/io.h.lsl Normal file
View File

@ -0,0 +1,130 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* IO.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_IO_H_
#define _ARES_IO_H_
// **** IO daemon ****
// create a pipeline; parameter should be a list of strings, each of which describes a pipe to create
/*
// individual pipes use the format:
[p:<key>] [n:<key>] <type-spec>
- p:<key> is optional; it specifies a UUID to use for the pipe (otherwise this is generated randomly)
- n:<key> is optional; it overrides the pipe this will send to (required if you want complete control over the UUIDs of a pipeline)
- the <type-spec> is one of:
notify <program>: triggers the program using SIGNAL_NOTIFY (volatile)
invoke <program>: triggers the program using SIGNAL_INVOKE (volatile)
signal <program>: triggers the program using SIGNAL_NOTIFY (non-volatile)
permanent <program>: triggers the program using SIGNAL_INVOKE (non-volatile)
to <channel> <UUID>: sends messages to <UUID> on <channel> (non-volatile) - will not send messages to subsequent pipes
from <channel> <UUID> <program>: listens for messages from <UUID> on <channel> and invokes <program> (non-volatile) - will not pass on messages from prior pipes; specify <UUID> as NULL_KEY to listen to messages from any object; be aware that the user key provided by 'from' will be the object UUID rather than the owning avatar
transport: simply passes messages to the output pipe (non-volatile)
print: simply passes messages to the output pipe (volatile)
<program>: if no type identifier is included, the pipe is assumed to be a volatile SIGNAL_INVOKE pipe
file_open() automatically creates a special pipe which is functionally identical to a volatile SIGNAL_NOTIFY pipe, but automatically removes the linebreaks at the start of the buffer caused by print()
volatile pipes are purged on rez by io
invokes will purge their pipes when they finish, unless the pipes are non-volatile or the input stream's UUID is currently assigned to a task
non-volatile pipes are never purged or destroyed.
after creation, the program will receive SIGNAL_NOTIFY with the message "* pipe <definition>" for each pipe generated, with ins == the pipe key and outs == the next key.
*/
#define pipe_open(...) e_call(C_IO, E_SIGNAL_CREATE_RULE, jsarray((list)(__VA_ARGS__)))
// delete a series of pipes (provide as a list of keys):
#define pipe_close(...) e_call(C_IO, E_SIGNAL_DELETE_RULE, jsarray((list)(__VA_ARGS__)))
// as pipe_close, but only remove volatile invoke pipes:
#define pipe_close_volatile(...) e_call(C_IO, E_SIGNAL_DELETE_RULE, "V" + jsarray((list)(__VA_ARGS__)))
// assign a new next pipe to an existing pipe:
#define pipe_extend(__pipe, __next) e_call(C_IO, E_SIGNAL_CREATE_RULE, jsarray((list)("p:" + (string)(__pipe) + " n:" + (string)(__next))))
#undef SAFE_EOF
#define SAFE_EOF ""
// read a message from a pipe and store it in the destination variable:
#define pipe_read(_pipe_key, _dest_var) { _dest_var = read(_pipe_key); }
string read(key pipe) {
string spipe = "p:" + (string)pipe;
string buffer = llLinksetDataRead(spipe);
integer eor = strpos(buffer, SAFE_EOF);
if(~eor) {
if(eor == strlen(buffer) - 1)
llLinksetDataDelete(spipe);
else
llLinksetDataWrite(spipe, delstring(buffer, 0, eor));
buffer = delstring(buffer, eor, LAST);
} else {
llLinksetDataDelete(spipe);
}
return buffer;
}
// send a list of pipe chains to _outs, starting from the specified head pipe(s):
#define pipe_list(_outs, _user, ...) e_call(C_IO, E_SIGNAL_QUERY_RULES, (string)(_outs) + " " + (string)(_user) + " " + concat((list)(__VA_ARGS__), " "))
// print(outs, user, message): print message to pipe outs on behalf of user
#if defined(RING_NUMBER) && RING_NUMBER <= R_DAEMON
#define print(_pipe_key, _user, _message) { llLinksetDataWrite("p:" + (string)(_pipe_key), llLinksetDataRead("p:" + (string)(_pipe_key)) + _message + SAFE_EOF); linked(R_KERNEL, SIGNAL_CALL, E_IO + E_PROGRAM_NUMBER + (string)(_pipe_key) + " " + (string)(_user) + " io", ""); }
#else
#define print(_pipe_key, _user, _message) { llLinksetDataWrite("p:" + (string)(_pipe_key), llLinksetDataRead("p:" + (string)(_pipe_key)) + _message + SAFE_EOF); tell(DAEMON, C_IO, E_SIGNAL_CALL + E_PROGRAM_NUMBER + (string)(_pipe_key) + " " + (string)(_user)); }
#endif
// send messages directly through io on an arbitrary channel (needed for RLV relay, AX, etc.):
#define io_tell(_target, _channel, _message) tell(DAEMON, C_IO, E_SIGNAL_TELL + E_PROGRAM_NUMBER + (string)(_target) + " " + (string)(_channel) + " " + _message)
#define io_broadcast(_channel, _message) io_tell(NULL_KEY, _channel, _message)
// silently add a message to a pipe so it can be found later:
#define pipe_write(_pipe_key, _message) llLinksetDataWrite("p:" + (string)(_pipe_key), llLinksetDataRead("p:" + (string)(_pipe_key)) + _message + SAFE_EOF)
// tell _io to process a pipe without adding anything to it:
#if defined(RING_NUMBER) && RING_NUMBER <= R_DAEMON
#define pipe_push(_pipe_key, _user) { linked(R_KERNEL, SIGNAL_CALL, E_IO + E_PROGRAM_NUMBER + (string)(_pipe_key) + " " + (string)(_user) + " io", ""); }
#else
#define pipe_push(_pipe_key, _user) { tell(DAEMON, C_IO, E_SIGNAL_CALL + E_PROGRAM_NUMBER + (string)(_pipe_key) + " " + (string)(_user)); }
#endif
#endif // _ARES_IO_H_

99
ARES/api/io.h.lsl.bak Normal file
View File

@ -0,0 +1,99 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222023 Nanite Systems Corporation
*
* =========================================================================
*
* IO.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_IO_H_
#define _ARES_IO_H_
// **** IO daemon ****
// create a pipeline; parameter should be a list of strings, each of which describes a pipe to create
/*
// individual pipes use the format:
[p:<key>] [n:<key>] <type-spec>
- p:<key> is optional; it specifies a UUID to use for the pipe (otherwise this is generated randomly)
- n:<key> is optional; it overrides the pipe this will send to (required if you want complete control over the UUIDs of a pipeline)
- the <type-spec> is one of:
notify <program>: triggers the program using SIGNAL_NOTIFY (volatile)
invoke <program>: triggers the program using SIGNAL_INVOKE (volatile)
signal <program>: triggers the program using SIGNAL_NOTIFY (non-volatile)
permanent <program>: triggers the program using SIGNAL_INVOKE (non-volatile)
to <channel> <UUID>: sends messages to <UUID> on <channel> (non-volatile) - will not send messages to subsequent pipes
from <channel> <UUID> <program>: listens for messages from <UUID> on <channel> and invokes <program> (non-volatile) - will not pass on messages from prior pipes; specify <UUID> as NULL_KEY to listen to messages from any object; be aware that the user key provided by 'from' will be the object UUID rather than the owning avatar
transport: simply passes messages to the output pipe (non-volatile)
print: simply passes messages to the output pipe (volatile)
<program>: if no type identifier is included, the pipe is assumed to be a volatile SIGNAL_INVOKE pipe
file_open() automatically creates a special pipe which is functionally identical to a volatile SIGNAL_NOTIFY pipe, but automatically removes the linebreaks at the start of the buffer caused by print()
volatile pipes are purged on rez by io
invokes will purge their pipes when they finish, unless the pipes are non-volatile or the input stream's UUID is currently assigned to a task
non-volatile pipes are never purged or destroyed.
after creation, the program will receive SIGNAL_NOTIFY with the message "* pipe <definition>" for each pipe generated, with ins == the pipe key and outs == the next key.
*/
#define pipe_open(...) e_call(C_IO, E_SIGNAL_CREATE_RULE, jsarray((list)(__VA_ARGS__)))
// delete a series of pipes (provide as a list of keys):
#define pipe_close(...) e_call(C_IO, E_SIGNAL_DELETE_RULE, jsarray((list)(__VA_ARGS__)))
// as pipe_close, but only remove volatile invoke pipes:
#define pipe_close_volatile(...) e_call(C_IO, E_SIGNAL_DELETE_RULE, "V" + jsarray((list)(__VA_ARGS__)))
// assign a new next pipe to an existing pipe:
#define pipe_extend(__pipe, __next) e_call(C_IO, E_SIGNAL_CREATE_RULE, jsarray((list)("p:" + (string)(__pipe) + " n:" + (string)(__next))))
// read a message from a pipe and store it in the destination variable:
#define pipe_read(_pipe_key, _dest_var) { _dest_var = llLinksetDataRead("p:" + (string)(_pipe_key)); llLinksetDataDelete("p:" + (string)_pipe_key); }
// send a list of pipe chains to _outs, starting from the specified head pipe(s):
#define pipe_list(_outs, _user, ...) e_call(C_IO, E_SIGNAL_QUERY_RULES, (string)(_outs) + " " + (string)(_user) + " " + concat((list)(__VA_ARGS__), " "))
// print a message to a pipe on behalf of a user (a linebreak will be prefixed):
#define print(_pipe_key, _user, _message) { llLinksetDataWrite("p:" + (string)(_pipe_key), llLinksetDataRead("p:" + (string)(_pipe_key)) + "\n" + _message); tell(DAEMON, C_IO, E_SIGNAL_CALL + E_PROGRAM_NUMBER + (string)(_pipe_key) + " " + (string)(_user)); }
// print a message to a pipe (special syntax when called from a daemon):
#define daemon_print(_pipe_key, _user, _message) { llLinksetDataWrite("p:" + (string)(_pipe_key), llLinksetDataRead("p:" + (string)(_pipe_key)) + "\n" + _message); linked(R_KERNEL, SIGNAL_CALL, E_IO + E_PROGRAM_NUMBER + (string)(_pipe_key) + " " + (string)(_user) + " io", ""); }
// send messages directly through io on an arbitrary channel (needed for RLV relay, AX, etc.):
#define io_tell(_target, _channel, _message) tell(DAEMON, C_IO, E_SIGNAL_TELL + E_PROGRAM_NUMBER + (string)(_target) + " " + (string)(_channel) + " " + _message)
#define io_broadcast(_channel, _message) io_tell(NULL_KEY, _channel, _message)
// silently add a message to a pipe so it can be found later:
#define pipe_write(_pipe_key, _message) llLinksetDataWrite("p:" + (string)(_pipe_key), llLinksetDataRead("p:" + (string)(_pipe_key)) + "\n" + _message)
#endif // _ARES_IO_H_

51
ARES/api/kernel.h.lsl Normal file
View File

@ -0,0 +1,51 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* KERNEL.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_KERNEL_H_
#define _ARES_KERNEL_H_
/*
KERNEL INFORMATION QUERIES
*/
// kernel queries (each SIGNAL_QUERY_<topic> yields a SIGNAL_<topic>_REPORT reply):
#define query_modules() kernel(SIGNAL_QUERY_MODULES, "")
#define query_daemons() kernel(SIGNAL_QUERY_DAEMONS, "")
// control inventory drop (must be done from root prim):
#define inventory_drop(_enabled) kernel(SIGNAL_INVENTORY_DROP, (string)_enabled)
#endif // _ARES_KERNEL_H_

55
ARES/api/repair.h.lsl Normal file
View File

@ -0,0 +1,55 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* REPAIR.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_REPAIR_H_
#define _ARES_REPAIR_H_
// **** repair daemon ****
/* deal_damage(): deal ATOS damge to the unit
_type: one of: "projectile", "crash", "heat", "cold", "special"
_amount: number of HP to remove; multiply by 4 for projectile and crash damage; may be affected by shields
_source: the UUID of the object causing the damage
negative numbers apply repair
use "special" damage if you want to bypass defenses
if DQD or OOC mode is enabled, the unit's integrity number will not change, but other consequences like masochistic arousal and shield damage may still occur
*/
#define deal_damage(_amount, _type, _source) e_call(C_REPAIR, E_SIGNAL_CALL, (string)_source + " " + (string)_source + " repair inflict " + (_type) + " " + (string)(_amount));
#endif // _ARES_REPAIR_H_

110
ARES/api/request.h.lsl Normal file
View File

@ -0,0 +1,110 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* REQUEST.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_REQUEST_H_
#define _ARES_REQUEST_H_
// lookup functions - implemented in proc
// returns data to a pipe; create a pipe to receive results, or use OUTPUT_SAY as a placeholder for testing
// in all of these lookup functions, _user is relayed passively, and can be replaced with a handle
// http_get(): GETs a web address
#define http_get(_url, _outs, _user) notify_program("proc fetch " + _url, _outs, NULL_KEY, _user)
// http_get but with headers (use llHTTPRequest() format):
#define http_get_extended(_url, _outs, _user, ...) notify_program("proc fetch " + _url + " " + jsarray((list)(__VA_ARGS__)), _outs, NULL_KEY, _user)
// http_post(): POSTs the input stream to a web address - use pipe_write() with a generated key to populate the input stream
#define http_post(_url, _outs, _ins, _user) notify_program("proc fetch " + _url, _outs, _ins, _user)
// http_post() but with headers (use llHTTPRequest() format):
#define http_post_extended(_url, _outs, _ins, _user, ...) notify_program("proc fetch " + _url + " " + jsarray((list)(__VA_ARGS__)), _outs, _ins, _user)
// request_uuid(): returns agent key
#define request_uuid(_agent, _outs, _user) notify_program("proc uuid " + _agent, _outs, NULL_KEY, _user)
// request_name(): returns modern-style username
#define request_name(_agent, _outs, _user) notify_program("proc name " + _agent, _outs, NULL_KEY, _user)
// request_avatar(): returns vintage-style Firstname Lastname
#define request_avatar(_agent, _outs, _user) notify_program("proc avatar " + _agent, _outs, NULL_KEY, _user)
// request_online(): returns 1 if online, 0 if offline
#define request_online(_agent, _outs, _user) notify_program("proc online " + _agent, _outs, NULL_KEY, _user)
// request_born(): returns birthdate in SLT
#define request_born(_agent, _outs, _user) notify_program("proc born " + _agent, _outs, NULL_KEY, _user)
// request_payinfo(): returns 1 if payment info on file, 0 if not
#define request_payinfo(_agent, _outs, _user) notify_program("proc payinfo " + _agent, _outs, NULL_KEY, _user)
// request_regionpos(): returns coordinates of a region
#define request_regionpos(_region, _outs, _user) notify_program("proc regionpos " + _region, _outs, NULL_KEY, _user)
// request_regionstatus(): returns status of a region
#define request_regionstatus(_region, _outs, _user) notify_program("proc regionstatus " + _region, _outs, NULL_KEY, _user)
// request_regionrating(): returns rating of a region
#define request_regionrating(_region, _outs, _user) notify_program("proc regionrating " + _region, _outs, NULL_KEY, _user)
/*
_proc http server API (all done with SIGNAL_NOTIFY)
your program must generate the messages:
_proc listen <callback> (to start listening)
_proc release <callback> (to end listening)
_proc reply <condition> (when a query comes in - set ins to Q)
// <condition> is the HTTP status code, ideally 200
(the macros below will take care of these)
you will receive:
<callback> http://<URL> (when created or re-created)
<callback> URL_REQUEST_DENIED (when things fail)
<callback> <any other URL fragment, may be blank or start with / or ?> (when receiving a message, check input pipe, ins=request body, outs=Q)
<callback> will be activated with ins=request body and outs=Q
parse request body with read(), then send your response to Q with pipe_write() (don't use print())
then call _proc reply <condition> to send the message
servers are automatically recreated on region change, which will cause a new http://<URL> to be provided
HTTPS is not supported (yet)
*/
#define http_listen(_callback, _user) notify_program("_proc listen " + PROGRAM_NAME + " " + (_callback), NULL_KEY, NULL_KEY, _user)
#define http_release(_callback) notify_program("_proc release " + PROGRAM_NAME + " " + (_callback), NULL_KEY, NULL_KEY, NULL_KEY)
#define http_reply(_Q, _condition, _body) { pipe_write(_Q, _body); notify_program("_proc reply " + (string)(_condition), NULL_KEY, _Q, NULL_KEY); }
#endif // _ARES_REQUEST_H_

64
ARES/api/scheduler.h.lsl Normal file
View File

@ -0,0 +1,64 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* SCHEDULER.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_SCHEDULER_H_
#define _ARES_SCHEDULER_H_
/*
EVENT HOOKS
This asks the scheduler to notify your program when something important happens. The list of supported events can be found in the <ARES/a> header.
*/
// hooking and unhooking events (provide a list of EVENT_* constants):
#if defined(RING_NUMBER) && RING_NUMBER <= R_DAEMON
#define query_hooks() kernel(SIGNAL_QUERY_HOOKS, "")
#define hook_events(...) kernel(SIGNAL_HOOK_EVENT, PROGRAM_NAME + "," + concat(__VA_ARGS__, ","))
#define unhook_events(...) kernel(SIGNAL_UNHOOK_EVENT, PROGRAM_NAME + "," + concat(__VA_ARGS__, ","))
#else
#define query_hooks() tell(DAEMON, C_SCHEDULER, E_SIGNAL_QUERY_HOOKS + PROGRAM_NAME)
#define hook_events(...) tell(DAEMON, C_SCHEDULER, E_SIGNAL_HOOK_EVENT + PROGRAM_NAME + "," + concat(__VA_ARGS__, ","))
#define unhook_events(...) tell(DAEMON, C_SCHEDULER, E_SIGNAL_UNHOOK_EVENT + PROGRAM_NAME + "," + concat(__VA_ARGS__, ","))
#endif
// create a timer (interval must be an integer)
// delete by setting interval to 0
// tag is an arbitrary string used to disambiguate multiple timers for the same program
// note that timers must be recreated if the kernel resets, since they are PID-based
// (non-timer events survive)
#define set_timer(_tag, _interval) tell(DAEMON, C_SCHEDULER, E_SIGNAL_TIMER + E_PROGRAM_NUMBER + (string)(_interval) + " " + _tag)
#endif // _ARES_SCHEDULER_H_

45
ARES/api/sexuality.h.lsl Normal file
View File

@ -0,0 +1,45 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* SEXUALITY.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_SEXUALITY_H_
#define SEXUALITY_VERSION "0.9.2"
#define SEXUALITY_VERSION_TAGS "preview 2"
#define C_LUST -9999969
#define C_LUST_FX -1010101
list SEXUALITY_MODES = ["disabled", "narrative", "interactive", "masochism"];
#endif // #ifndef _ARES_SEXUALITY_H_

70
ARES/api/status.h.lsl Normal file
View File

@ -0,0 +1,70 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* STATUS.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_STATUS_H_
#define _ARES_STATUS_H_
/* status daemon: power loads & updates
1. create a power load with:
setdbl("chassis", ["load", device + "__" + load_name], (string)wattage);
where 'device' should be 'system' or the address of a connected device, and load_name is a single-word lower-case description of why power is being used
2. trigger the status daemon to reload the status section with:
(from a program) e_call(C_STATUS, E_SIGNAL_CALL, NULL_KEY + " " + NULL_KEY + " status update");
(from a daemon) daemon_to_daemon(E_DAEMON, SIGNAL_CALL, NULL_KEY + " " + NULL_KEY + " status update");
3. when finished, delete the power load:
deletedbl("chassis", ["load", device + "__" + load_name]);
4. repeat step 2
*/
// create/modify or remove a power load:
#define set_power_load(_device, _load_name, _wattage) setdbl("chassis", ["load", _device + "__" + _load_name], (string)_wattage)
#define delete_power_load(_device, _load_name) deletedbl("chassis", ["load", _device + "__" + _load_name])
// (remember to trigger a status update afterward, as described in the comment above)
#define status_update() e_call(C_STATUS, E_SIGNAL_CALL, NULL_KEY + " " + NULL_KEY + " status update");
// #define external_teleport() e_call(C_STATUS, E_SIGNAL_CALL, NULL_KEY + " " + NULL_KEY + " status external-tp");
#endif // _ARES_STATUS_H_

109
ARES/api/storage.h.lsl Normal file
View File

@ -0,0 +1,109 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* STORAGE.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_STORAGE_H_
#define _ARES_STORAGE_H_
/*
NEW UNIFORM FILE ACCESS SYSTEM - ARES 0.4.4
replaces ARES/api/fs.h.lsl
*/
#define FILE_PAGE_LENGTH 1024
#define FILE_LINE_WIDTH 1024
// open a pipe and performs a stat (requests file info):
#define file_open(_pipe, _filename) file_read(_pipe, _filename, NOWHERE);
/*
file stat in ARES 0.4.4
When a file is opened, _storage will send the following fields to your application via a notify "<program> file" pipe:
<size> <unit> <type> <description>
The fields are space-separated and have the following definitions:
<size> is the length of the file, measured in <unit>.
<unit> is b for bytes, p for pages, or l for lines.
<type> is d for directory, o for unreadable objects, and f for readable text files.
<description> is a freeform string (e.g. llGetInventoryDesc()) and may not be present.
Attempting to read an object file will cause an error. (These were added to represent SL inventory items with no interpretable contents.) Attempting to read a directory will give a linebreak-separated list of names therein. The special file "<source>:*" will list all files in <source>, e.g. "local:*".
The "<source>:" prefix can also be used to disambiguate files with identical names, but finding them in the first place may be nontrivial; the behavior of fs:root when multiple files with the same name are present in different sources is currently undefined and will generally cause problems.
*/
/*
file read in ARES 0.4.4
Once the file is opened and you have received its stat values (above), you can then ask _storage to start sending data. To successfully iterate through a file, you need to look at the unit and then request appropriate sections with file_read().
The offset parameter of file_read() may be a single number or <start> <length> (with a " " in the middle). If no <length> is missing, then only 1 unit will be returned. If your application can handle it, you should simply request the entire file using the <size> given by the file_open() macro.
If the <unit> is b, then you will only get one byte at a time if you fail to specify <length>, which is not very useful. Instead use a larger window, like 1024 or 2048, and iterate over the file to the best of your program's memory-handling abilities.
If you request data from an "l" file, a linebreak will always be added on the end so that the text can be concatenated properly without worrying about the underlying format.
*/
#define file_read(_pipe, _filename, _offset) e_call(C_IO, E_SIGNAL_DATA_REQUEST, (string)(_pipe) + " " + PROGRAM_NAME + " " + (_filename) + " " + (string)(_offset))
// just closes the pipe; the storage protocol is stateless:
#define file_close(_pipe) e_call(C_IO, E_SIGNAL_DELETE_RULE, "[\"" + (string)(_pipe) + "\"]")
// write text directly into a filename (provided the target filesystem supports and allows it).
// fill _pipe using pipe_write(), then call this to send the data.
// NOTE: the file will be OVERWRITTEN. Use file_append() if you do not want this.
#define file_write(_filename, _pipe) e_call(C_STORAGE, E_SIGNAL_CALL, NULL_KEY + " " + NULL_KEY + " storage write " + (_filename) + " " + (string)(_pipe))
#define file_append(_filename, _pipe) e_call(C_STORAGE, E_SIGNAL_CALL, NULL_KEY + " " + NULL_KEY + " storage append " + (_filename) + " " + (string)(_pipe))
// delete a file, provided the filesystem supports and allows it.
#define file_delete(_filename) e_call(C_STORAGE, E_SIGNAL_CALL, NULL_KEY + " " + NULL_KEY + " storage delete " + (_filename))
// OTHER FILESYSTEM ACTIVITIES
// get all filenames in a view:
#define view_files(_rule) js2list(llLinksetDataRead("fs:"+_rule))
// get all filenames in a source:
#define source_files(_rule) split(llLinksetDataRead("fs:"+_rule), "\n")
// get all filenames (careful; this can be huge):
#define list_all_files() jskeys(llLinksetDataRead("fs:root"))
// refresh a source:
#define storage_refresh(_source, _callback, _user) e_call(C_STORAGE, E_SIGNAL_CALL, (string)(_callback) + " " + (string)(_user) + " storage refresh " + (_source))
#endif // _ARES_STORAGE_H_

86
ARES/api/tasks.h.lsl Normal file
View File

@ -0,0 +1,86 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* TASKS.H.LSL Header Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_TASKS_H_
#define _ARES_TASKS_H_
/*
MODES AND TASK MANAGEMENT
Every program has a bitfield called '_mode' (note the underscore) which defines when the OS will try to put it to sleep. The most common mode flag is MODE_THREADING, which is set automatically while the program has an active task. This should be used whenever the program is waiting for a response from another module, to avoid the 2 second delay of starting back up. Threading jobs will be reset on rez (including sim change, login, attach).
You can begin and end tasks whenever you want.
MODE_NON_VOLATILE is set on programs with '#define NON_VOLATILE' at the top. These programs never go to sleep, and the kernel will not even double-check to make sure they are still flagged as awake when sending them messages. The NON_VOLATILE flag also means the program never gets reset during a rezzing event.
MODE_ACCEPT_DONE means the program will be informed whenever an invoke() or notify() has completed.
*/
string tasks_queue = "{}";
// create a task with the specified tags
// in most cases, you should use ins (the input stream) for _id, as this will protect both the input and output stream from being recycled if they are volatile
#define task_begin(_id, _message) { tasks_queue = setjs(tasks_queue, [_id], _message); system(SIGNAL_MODE, PROGRAM_NAME + "," + (string)(_mode = _mode | MODE_THREADING)); }
#define task_end(_id) (tasks_queue = setjs(tasks_queue, [_id], JSON_DELETE))
#define task_count() count(jskeys(tasks_queue))
// [DEPRECATED/NICHE] politely shut down program (kernel removes MODE_THREADING when this happens, so we don't need to tell it):
// (you may still need this when e.g. ending a real timer-based task)
#define exit() { if(_resolved) system(SIGNAL_DONE, ares_encode(_resolved) + NULL_KEY + " " + PROGRAM_NAME); system(SIGNAL_TERMINATED, PROGRAM_NAME); _mode = _mode & ~MODE_THREADING; llSetScriptState(PROGRAM_NAME, FALSE); llSleep(0.022); }
// [DEPRECATED/NICHE] send a callback key with exit:
#define exitc(_ins) { if(_resolved) system(SIGNAL_DONE, ares_encode(_resolved) + (string)(_ins) + " " + PROGRAM_NAME); system(SIGNAL_TERMINATED, PROGRAM_NAME); _mode = _mode & ~MODE_THREADING; llSetScriptState(PROGRAM_NAME, FALSE); llSleep(0.022); }
// [DEPRECATED] send a receipt for a completed task, without shutting down:
#define resolve(_R) if(_R) system(SIGNAL_DONE, ares_encode(_R) + NULL_KEY + " " + PROGRAM_NAME)
// [DEPRECATED] send a receipt for a completed task (with callback), without shutting down - deprecated:
#define resolvec(_R, _ins) if(_R) system(SIGNAL_DONE, ares_encode(_R) + (string)(_ins) + " " + PROGRAM_NAME)
// [QUESTIONABLE] send a receipt for a completed task, including callback, and close streams (if they are volatile invoke pipes):
#define resolve_io(_R, _outs, _ins) { pipe_close_volatile([_ins, _outs]); if(_R) system(SIGNAL_DONE, ares_encode(_R) + (string)(_ins) + " " + PROGRAM_NAME); }
// [RECOMMENDED] send a receipt for a completed task, including callback, and close only the input stream (if it is a volatile invoke pipe):
#define resolve_i(_R, _ins) { if(_R) system(SIGNAL_DONE, ares_encode(_R) + (string)(_ins) + " " + PROGRAM_NAME); pipe_close_volatile(_ins); }
// set the program's MODE_* flags and inform the kernel:
#define set_mode(_M) system(SIGNAL_MODE, PROGRAM_NAME + "," + (string)(_mode = _M))
// delay_relock(): informs the policy manager to push back auto-lock, if auto-lock is enabled
#define delay_relock() if((integer)getdbl("policy", ["autolock", "enabled"]) && !(integer)getdbl("policy", ["lock"])) notify_program("policy delay", avatar, NULL_KEY, avatar)
#endif // _ARES_TASKS_H_

View File

@ -0,0 +1,86 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* VERSION-COMPARE.H.LSL
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
#ifndef _ARES_VERSION_COMPARE_H_
#define _ARES_VERSION_COMPARE_H_
// compares two version numbers
// returns a negative number if the first argument is greater
// returns a positive number if the second argument is greater
// returns zero if they are equal
integer version_compare(string version_a, string version_b) {
if(version_a == version_b)
return 0;
list pa = splitnulls(version_a, ".");
list pb = splitnulls(version_b, ".");
integer ca;
integer cb;
while(geti(pa, LAST) == 0 && ((ca = count(pa)) > 0))
pa = delitem(pa, LAST);
while(geti(pb, LAST) == 0 && ((cb = count(pb)) > 0))
pb = delitem(pb, LAST);
if(ca == 0) {
ca = 1;
pa = ["0"];
}
if(cb == 0) {
cb = 1;
pb = ["0"];
}
integer i;
while(i < ca && i < cb) {
integer ja = (integer)gets(pa, i);
integer jb = (integer)gets(pb, i);
if(ja > jb)
return -(i + 1);
else if(ja < jb)
return (i + 1);
++i;
}
if(ca > cb)
return -(cb+1);
else if(ca < cb)
return (ca+1);
return 0;
}
#endif // _ARES_VERSION_COMPARE_H_

466
ARES/application/calc.lsl Normal file
View File

@ -0,0 +1,466 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Calculator Utility
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#define CLIENT_VERSION "1.2.0"
#define CLIENT_VERSION_TAGS "release"
#define PUSH(_list, _item) _list += (list)(_item)
#define SHIFT(_list) gets(_list, 0); _list = delitem(_list, 0)
#define POP(_list) gets(_list, LAST); _list = delitem(_list, LAST)
#define UNSHIFT(_list, _item) _list = (list)(_item) + _list
#define SYMBOLS ["<<", ">>", "+", "-", "**", "*", "/", "%", "^", "~", "&&", "||", "&", "|", "(", ")"]
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
llSetMemoryLimit(0x10000);
llScriptProfiler(amm_profiling = PROFILE_SCRIPT_MEMORY);
list argv = split(m, " ");
integer argc = count(argv);
string msg;
if(argc == 1) {
msg = "Usage: " + PROGRAM_NAME + " [-x|-d|-e] <calculation>\n -x: Return result in hexadecimal\n (input hex values as 0xff)\n -d: Decode from ARES base64\n -e: Encode to ARES base64\n -r [[<min>] <max>]: generate a random integer between <min> (or 0) and (<max> - 1); if no bounds are specified, generates a random float between 0.0 and 1.0\n\nSupported symbols: " + concat(SYMBOLS, ", ");
} else {
integer hex_output;
integer ARES_decode;
integer ARES_encode;
string first = gets(argv, 1);
if(first == "-r") {
if(argc == 2)
msg = (string)llFrand(1.0);
else if(argc == 3)
msg = (string)((integer)llFrand((float)gets(argv, 2)));
else if(argc == 4)
msg = (string)((integer)(llFrand((float)gets(argv, 3) - (float)gets(argv, 2))) + (integer)gets(argv, 2));
jump error;
} else if(first == "-x") {
hex_output = 1;
argv = delitem(argv, 1);
#ifdef DEBUG
echo("hexadecimal output enabled if result is integer");
#endif
} else if(first == "-d") {
ARES_decode = 1;
string t0 = gets(argv, 2);
argv = alter(argv, [ares_decode(t0)], 1, 2);
} else if(first == "-e") {
ARES_encode = 1;
argv = delitem(argv, 1);
}
list tokens = llParseString2List(concat(delitem(argv, 0), " "), [" "], sublist(SYMBOLS, 0, 7));
// n.b. there is a hard limit of 8 separators
integer ti = 0;
integer imax = count(tokens);
list symbol_block_2 = sublist(SYMBOLS, 8, 15);
while(ti < imax) {
list tr = llParseString2List(gets(tokens, ti), [], symbol_block_2);
tokens = alter(tokens, tr, ti, ti);
integer tri = count(tr);
if(tri) {
ti += tri;
imax += tri - 1;
} else {
++ti;
}
}
#ifdef DEBUG
echo("Initial tokens: " + concat(tokens, ", "));
#endif
#define SYMBOL_TEXT 0
#define SYMBOL_CLASS 1
#define C_IS_ATOM 0x001
#define C_INTEGER 0x03
#define C_FLOAT 0x05
#define C_OPEN_PAREN 0x0400
#define C_CLOSE_PAREN 0x0600
#define C_IS_OPERATOR 0x1000
#define C_IS_UNARY 0x0010
#define C_UNARY 0x1710
// C_UNARY consists of unary - ~
#define C_EXP 0x1600
// C_EXP consists of ** << >>
#define C_MUL 0x1500
// C_MUL consists of * / %
#define C_SUM 0x1400
// C_SUM consists of + -
#define C_BIT 0x1300
// C_BIT consists of ^ & |
#define C_NOT 0x1210
// C_NOT consists of unary !
#define C_BOOL 0x1100
// C_BOOL consists of && ||
tokens = ["("] + tokens + [")"];
imax += 2;
// each token is represented as a doublet: [text,class]
list stack = [];
list parse;
integer i = 0;
#define T_OPEN_PAREN "[\"\",64]"
while(i < imax) {
string token_text = SHIFT(tokens);
integer first = llOrd(token_text, 0);
string top = gets(stack, LAST);
string top_text;
integer top_class;
if(top != "") {
top_class = (integer)getjs(top, [SYMBOL_CLASS]);
top_text = getjs(top, [SYMBOL_TEXT]);
} else {
top_class = NOWHERE;
top_text = "?";
}
integer token_class;
if(first == 0x30 || first == 0x2e || (float)token_text != 0) { // '0' or '.'
if(~strpos(token_text, "."))
token_class = C_FLOAT;
else if(~strpos(token_text, "x"))
token_class = C_INTEGER;
else if(~strpos(token_text, "e"))
token_class = C_FLOAT;
else
token_class = C_INTEGER;
} else if(token_text == "(") {
token_text = "";
token_class = C_OPEN_PAREN;
} else if(contains(SYMBOLS, token_text)) {
if(token_text == "-") {
if(top_class == C_OPEN_PAREN && ((integer)getjs(gets(parse, LAST), [SYMBOL_CLASS]) & C_IS_ATOM)) {
token_class = C_SUM;
#ifdef DEBUG
echo("interpreted - as first subtraction in pg");
#endif
} else if(top_class == C_OPEN_PAREN) {
token_class = C_UNARY;
#ifdef DEBUG
echo("interpreted - as negative at start of pg");
#endif
} else if(top_class & C_IS_OPERATOR) {
token_class = C_UNARY;
#ifdef DEBUG
echo("interpreted - as negative after op " + top_text);
#endif
} else {
token_class = C_SUM;
#ifdef DEBUG
echo("interpreted - as base case subtraction");
#endif
}
} else if(token_text == "~")
token_class = C_UNARY;
else if(token_text == "**" || token_text == "<<" || token_text == ">>")
token_class = C_EXP;
else if(token_text == "*" || token_text == "/" || token_text == "%")
token_class = C_MUL;
else if(token_text == "+")
token_class = C_SUM;
else if(token_text == "^" || token_text == "&" || token_text == "|")
token_class = C_BIT;
else if(token_text == "!")
token_class = C_NOT;
else if(token_text == "&&" || token_text == "||")
token_class = C_BOOL;
else if(token_text == ")")
token_class = C_CLOSE_PAREN;
} else {
msg = "Unknown symbol: " + token_text;
jump error;
}
string token = jsarray([token_text, token_class]);
#ifdef DEBUG
echo("evaluating token " + token + " and stack " + concat(stack, " · "));
#endif
if(token_class & C_IS_ATOM) {
// numeric atom
#ifdef DEBUG
echo("top when evaluating atom " + token_text + ": " + (string)top_class);
#endif
if(top_class & C_IS_ATOM) {
msg = "Bad syntax: two consecutive atoms " + top_text + " " + token_text;
jump error;
} else if(top_class == C_OPEN_PAREN || top_class == 0) {
PUSH(parse, token);
#ifdef DEBUG
echo("emitted first token of paren group: " + token);
#endif
} else {
PUSH(stack, token);
#ifdef DEBUG
echo("stacked " + token);
#endif
}
} else if(token_class == C_OPEN_PAREN) {
PUSH(stack, token);
#ifdef DEBUG
echo("stacked open paren");
#endif
} else { // must be an operator or C_CLOSE_PAREN
string second = gets(stack, -2);
integer second_class = (integer)getjs(second, [SYMBOL_CLASS]);
/*
possibilities for an operator (using /):
input stack current parse correct action final parse
1/2 ( 1 stack / 2 1 2 /
(1+2)/3 ( 1 2 + stack / 3 1 2 + 3 /
1**3/2 ( ** 3 1 emit 3 **, stack / 2 1 3 ** 2 /
1+2/3 ( + 2 1 stack / 3 1 2 3 / +
1+2/3**4 ( + 2 1 stack / 3 1 2 3 4 ** / +
1+2/3/4 1 2 3 / 4 / +
1+2/3+4 1 2 3 / 4 + +
1*2/3*4 1 2 * 3 / 4 *
1%2/3 ( % 2 1 emit 2 %, stack / 3 1 2 % 3 /
1+(1+2/3)**3 ( + ( + 2 1 1 stack / 3 1 1 2 3 / + + 3 **
shift leftovers
*/
if(token_class & C_IS_OPERATOR) {
if(second_class & C_IS_OPERATOR) {
// emit [top, second] only if second_class is at least token_class
if(second_class >= token_class && top_class != C_OPEN_PAREN) {
PUSH(parse, top);
PUSH(parse, second);
#ifdef DEBUG
echo("precedence decision: emitted " + top + " and " + second + " instead of " + token);
#endif
stack = delrange(stack, -2, -1);
}
}
// convert unary operators into binary operators:
if(token_class & C_IS_UNARY) {
PUSH(stack, top = jsarray([top_text = "0", top_class = C_INTEGER]));
}
PUSH(stack, token);
}
}
++i;
if(token_class == C_CLOSE_PAREN || i == imax) {
string arg;
string a2;
while(count(stack) && (i == imax || arg != T_OPEN_PAREN)) {
arg = POP(stack);
if((integer)getjs(arg, [SYMBOL_CLASS]) & C_IS_ATOM) {
a2 = arg;
arg = POP(stack);
string a3 = gets(stack, LAST);
if((integer)getjs(a3, [SYMBOL_CLASS]) & C_IS_ATOM) {
POP(stack);
PUSH(parse, a3);
#ifdef DEBUG
echo("consume a3 " + a3);
#endif
}
if((integer)getjs(a2, [SYMBOL_CLASS]) != C_OPEN_PAREN) {
PUSH(parse, a2);
#ifdef DEBUG
echo("a2 emit " + a2);
#endif
}
#ifdef DEBUG
else {
echo("a2 no emit paren");
}
#endif
}
if((integer)getjs(arg, [SYMBOL_CLASS]) != C_OPEN_PAREN) {
PUSH(parse, arg);
#ifdef DEBUG
echo("arg emit " + arg);
#endif
}
#ifdef DEBUG
else {
echo("arg no emit paren");
}
#endif
}
#ifdef DEBUG
echo("drainage stopped on " + arg);
#endif
}
}
#ifdef DEBUG
echo("Final parse: " + concat(parse, " · "));
#endif
list execution_stack;
while(count(parse)) {
string token = SHIFT(parse);
string text = getjs(token, [SYMBOL_TEXT]);
integer class = (integer)getjs(token, [SYMBOL_CLASS]);
if(class & C_IS_ATOM) {
PUSH(execution_stack, token);
} else if(class & C_IS_OPERATOR) {
string right = POP(execution_stack);
string left = POP(execution_stack);
string left_text = getjs(left, [SYMBOL_TEXT]);
string right_text = getjs(right, [SYMBOL_TEXT]);
integer li = (integer)left_text;
integer ri = (integer)right_text;
float lf = (float)left_text;
float rf = (float)right_text;
integer left_is_float = (llFabs(lf - (float)li) > 0.0001) || ~strpos(left_text, ".");
integer right_is_float = (llFabs(rf - (float)ri) > 0.0001) || ~strpos(right_text, ".");
integer have_floats = left_is_float || right_is_float;
string result_text;
integer result_type = C_INTEGER;
if(have_floats)
result_type = C_FLOAT;
if(text == "<<") {
result_text = (string)(li << ri);
} else if(text == ">>") {
result_text = (string)(li >> ri);
} else if(text == "+") {
if(have_floats)
result_text = (string)(lf + rf);
else
result_text = (string)(li + ri);
} else if(text == "-") {
if(have_floats)
result_text = (string)(lf - rf);
else
result_text = (string)(li - ri);
} else if(text == "**") {
if(have_floats)
result_text = (string)llPow(lf, rf);
else
result_text = (string)llPow(li, ri);
} else if(text == "*") {
if(have_floats)
result_text = (string)(lf * rf);
else
result_text = (string)(li * ri);
} else if(text == "/") {
if(have_floats)
result_text = (string)(lf / rf);
else
result_text = (string)(li / ri);
} else if(text == "%") {
result_text = (string)(li % ri);
} else if(text == "^") {
result_text = (string)(li ^ ri);
} else if(text == "~") {
result_text = (string)(~ri);
} else if(text == "&&") {
result_text = (string)(li && ri);
} else if(text == "||") {
result_text = (string)(li || ri);
} else if(text == "&") {
result_text = (string)(li & ri);
} else if(text == "|") {
result_text = (string)(li | ri);
} else {
msg = "Unknown operator " + text;
jump error;
}
PUSH(execution_stack, jsarray([result_text, result_type]));
} else {
msg = "Parse invalid: found token " + token + " in execution stack";
jump error;
}
}
if(count(execution_stack) != 1) {
msg = "Error: multiple results: " + concat(execution_stack, " · ");
} else {
string final_output = gets(execution_stack, 0);
integer final_class = (integer)getjs(final_output, [SYMBOL_CLASS]);
string final_text = getjs(final_output, [SYMBOL_TEXT]);
/* #ifdef DEBUG
echo("hex output? " + (string)hex_output + "; final class = " + (string)final_class);
#endif*/
if(substr(final_text, 0, 1) == "0x")
final_text = (string)((integer)final_text);
if(hex_output && final_class == C_INTEGER) {
msg = "0x" + hex((integer)final_text);
} else if(ARES_encode && final_class == C_INTEGER) {
integer v = (integer)final_text;
msg = ares_encode(v);
} else {
msg = final_text;
}
}
}
@error;
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
} else {
echo("[" + PROGRAM_NAME + "] unimplemented signal " + (string)n + ": " + m);
}
}
#include <ARES/program>

View File

@ -0,0 +1,635 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Corrado Utility
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#define CLIENT_VERSION "0.1.1"
#define CLIENT_VERSION_TAGS "alpha"
// argsplit() from find.lsl
list argsplit(string m) {
// splits a string based on word boundaries, but groups terms inside double and single quotes
// tabs and newlines are converted to spaces
list results;
string TAB = llChar(9);
list atoms = llParseStringKeepNulls(m, [], [" ", "\\", "'", "\"", "\n", TAB]);
integer in_quotes; // 1 = single, 2 = double
integer ti = 0;
integer tmax = count(atoms);
string buffer;
while(ti < tmax) {
string t = gets(atoms, ti);
integer c = llOrd(t, 0);
if(c == 0x22) { // '"'
if(in_quotes == 0) {
in_quotes = 2;
} else if(in_quotes == 2) {
in_quotes = 0;
} else {
buffer += t;
}
} else if(c == 0x27) { // '\''
if(in_quotes == 0) {
in_quotes = 1;
} else if(in_quotes == 1) {
in_quotes = 0;
} else {
buffer += t;
}
} else if(c == 0x5c) { // '\\'
string t1 = gets(atoms, ti + 2);
if(t1 == "\"" || t1 == "'") {
buffer += t1;
ti += 2;
} else {
buffer += t;
}
} else if(c == 0x20 || c == 0x0a || c == 0x09) { // ' ', '\n', '\t'
if(in_quotes) {
buffer += " ";
} else {
results += buffer;
buffer = "";
}
} else {
buffer += t;
}
++ti;
}
if(tmax)
results += buffer;
return results;
}
string bot_address;
key bot;
string group;
string password;
string callback;
key http_fetch_reply;
list waiting_queries; // [src, ins, handle, outs, user, post_body_pipe, post_reply_pipe, bot_address]
list active_queries; // [src, ins, handle, outs, user]
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_NOTIFY) {
list argv = split(m, " ");
string arg0 = gets(argv, 0);
if(arg0 == PROGRAM_NAME || arg0 == "*") {
string arg1 = gets(argv, 1);
if(arg1 == "http") {
string path = gets(argv, 2);
if(substr(path, 0, 0) == "/") {
string buffer = read(ins);
// echo(PROGRAM_NAME + ": received query at " + path);
if(buffer != "") {
print(user, user, buffer);
}
http_reply(outs, 200, "OK");
/*
pipe_write(outs, "OK");
notify_program("_proc reply 200", NULL_KEY, outs, user);
*/
} else if(substr(path, 0, 3) == "http") {
echo(PROGRAM_NAME + ": got URL " + path);
callback = path + "/";
} else if(path == URL_REQUEST_DENIED) {
echo(PROGRAM_NAME + ": URL recruitment failed.");
callback = "";
}
} else if(arg1 == "fetched") {
string buffer = read(ins);
key handle = gets(argv, 2);
integer aqi = index(active_queries, handle);
if(~aqi) {
integer r = geti(active_queries, aqi - 2);
key r_ins = getk(active_queries, aqi - 1);
key r_outs = getk(active_queries, aqi + 1);
key r_user = getk(active_queries, aqi + 2);
active_queries = delrange(active_queries, aqi - 2, aqi + 2);
print(r_outs, r_user, buffer);
resolvec(r, r_ins);
} else {
echo("invalid aqi: " + m);
}
pipe_close(ins);
} else if(arg1 == "pipe") {
// notify <PROGRAM_NAME> fetched <handle>
if(gets(argv, 2) == "notify"
&& gets(argv, 3) == PROGRAM_NAME
&& gets(argv, 4) == "fetched") {
key handle = gets(argv, 5);
integer wqi = index(waiting_queries, handle);
if(~wqi) {
active_queries += sublist(waiting_queries, wqi - 2, wqi + 2);
string q_addr = gets(waiting_queries, wqi + 5);
key q_reply_p = getk(waiting_queries, wqi + 4);
key q_body_p = getk(waiting_queries, wqi + 3);
http_post(q_addr, q_reply_p, q_body_p, handle);
waiting_queries = delrange(waiting_queries, wqi - 2, wqi + 5);
// echo("dispatched query");
} else {
echo("invalid wqi: " + m);
}
} else {
echo("unexpected pipe open: " + m);
}
}
}
} else if(n == SIGNAL_INVOKE) {
list argv = argsplit(m); // non-standard
integer argc = count(argv);
string msg = "";
if(argc == 1) {
msg = "Syntax: " + PROGRAM_NAME + " [-] [-t] [-v] [-f] [-a <key>|-u <HTTP URL>] [-g <group>] [-p <password>] [--<arg> <value> [...]] [<command> [<message>]]\n\nConfigures and sends commands to a Corrade agent. Messages are formatted as JSON and sent over IM.\n\n -a <key>: Sets the agent UUID.\n -u <HTTP URL>: Sets the Corrade HTTP URL.\n -g <group>: Sets the authentication group name.\n -p <password>: Sets the authentication group password.\n\nIf any of the above settings are missing, the previous values will be re-used. -a and -u are mutually exclusive.\n\n <command>: One of https://grimore.org/secondlife/scripted_agents/corrade/api/commands\n <message>: Contextual; usually mapped to the third parameter listed on the Complete List of Commands page.\n --<arg> <value>: Sets additional arguments. Use \"double quotes\" to wrap values containing spaces.\n -t: Test only; do not send. Useful for determining what <message> maps to.\n -v: Verbose (warn about missing or empty <message> parameters)\n -f: Don't wait for response when using HTTP; just send command and quit.\n -: Read <message> from standard input, discarding <message> if non-empty.";
/*} else if(gets(argv, 1) == "-F") {
if(http_fetch_reply != "") {
pipe_close(http_fetch_reply);
}
http_fetch_reply = llGenerateKey();
pipe_open("p:" + (string)http_fetch_reply + " notify " + PROGRAM_NAME + " fetched"); */
} else if(gets(argv, 1) == "-L") {
// notify_program("_proc listen " + PROGRAM_NAME + " http", NULL_KEY, NULL_KEY, user);
http_listen("http", user);
callback = "";
} else if(gets(argv, 1) == "-R") {
// notify_program("_proc release " + PROGRAM_NAME + " http", NULL_KEY, NULL_KEY, user);
http_release("http");
callback = "";
} else {
integer no_http_wait = 0;
integer warn = 0;
integer test = 0;
integer pipe_in = 0;
string command;
string message;
integer argi = 1;
string json = "{}";
if(group != "")
json = setjs(json, ["group"], group);
if(password != "")
json = setjs(json, ["password"], password);
if(callback != "")
json = setjs(json, ["callback"], callback);
while(argi < argc) {
string term = gets(argv, argi);
if(message == "") {
if(term == "-") {
pipe_in = 1;
} else if(term == "-f") {
no_http_wait = 1;
} else if(term == "-v") {
warn = 1;
} else if(term == "-t") {
test = 1;
} else if(term == "-a") {
bot = (key)gets(argv, ++argi);
bot_address = "";
} else if(term == "-u") {
bot_address = gets(argv, ++argi);
bot = "";
} else if(term == "-g") {
group = gets(argv, ++argi);
json = setjs(json, ["group"], group);
} else if(term == "-p") {
password = gets(argv, ++argi);
json = setjs(json, ["password"], password);
} else if(substr(term, 0, 1) == "--") {
json = setjs(json, [delstring(term, 0, 1)], gets(argv, ++argi));
} else if(command == "") {
command = term;
json = setjs(json, ["command"], command);
} else {
message = term;
}
} else {
message += " " + term;
}
++argi;
}
if(pipe_in) {
string pipe_buffer = read(ins);
if(pipe_buffer != "")
message = pipe_buffer;
}
if(command == "") {
msg = "No command specified. See https://grimore.org/secondlife/scripted_agents/corrade/api/commands for a list of available commands.";
} else if(bot == "" && bot_address == "") {
msg = "No Corrade bot specified. Please set with -a <UUID> or -u <HTTP URL>";
} else if(group == "") {
msg = "No authentication group specified. Please set with -a <name> (use quotes)";
} else if(password == "") {
msg = "No authentication password specified. Please set with -p <password> (use quotes)";
} else if(json == JSON_INVALID) {
msg = "JSON encoding failed, likely due to imbalanced braces or square brackets in one or more parameters.";
} else {
string message_key;
{
string message_mappings = jsobject([
"addclassified", "name",
"addpick", "name",
"addtorole", "agent",
"agentaccess", "action",
"animation", "item",
"attach", "attachments",
"attachobject", "item",
"autopilot", "position",
"avatarnotes", "data",
"avatarzoffset", "offset",
"away", "action",
"ban", "avatars",
"batchaddtorole", "avatars",
"batchanimation", "item",
"batchattachobjects", "attachments",
"batchavatarkeytoname", "avatars",
"batchavatarnametokey", "avatars",
"batchdeletefromrole", "avatars",
"batchderez", "item",
"batchdropobject", "attachments",
"batcheject", "avatars",
"batchgetavatarappearancedata", "agents",
"batchgetavatardisplayname", "agents",
"batchgetavatarseat", "agents",
"batchgetprofiledata", "data",
"batchgive", "item",
"batchgroupkeytoname", "groups",
"batchgroupnametokey", "groups",
"batchinvite", "avatars",
"batchlure", "avatars",
"batchmute", "mutes",
"batchsetinventorydata", "data",
"batchsetobjectgroup", "item",
"batchsetobjectpermissions", "item",
"batchsetobjectpositions", "item",
"batchsetobjectrotations", "item",
"batchsetparcellist", "avatars",
"batchsetprimitivedescriptions", "item",
"batchsetprimitivenames", "item",
"batchsetprimitivepositions", "item",
"batchsetprimitiverotations", "item",
"batchtell", "message",
"batchupdateprimitiveinventory", "entity",
"busy", "action",
"changeappearance", "folder",
"changeprimitivelink", "item",
"click", "item",
"compilescript", "data",
"conference", "avatars",
"configuration", "path",
"copynotecardasset", "item",
"creategrass", "position",
"creategroup", "data",
"createlandmark", "name",
"createnotecard", "name",
"createprimitive", "name",
"createrole", "role",
"createtree", "position",
"crouch", "action",
"deleteclassified", "name",
"deletefromrole", "agent",
"deletepick", "name",
"deleterole", "role",
"deleteviewereffect", "id",
"derez", "item",
"detach", "attachments",
"directoryquery", "name",
"directorysearch", "name",
"displayname", "name",
"download", "item",
"dropobject", "item",
"eject", "agent",
"estateteleportusershome", "avatars",
"execute", "file",
"exportdae", "item",
"exportoar", "item",
"exportxml", "item",
"fly", "action",
"flyto", "position",
"getassetdata", "item",
"getavatarappearancedata", "agent",
"getavatarclassifieddata", "item",
"getavatarclassifieds", "agent",
"getavatardata", "agent",
"getavatardisplayname", "agent",
"getavatargroupdata", "agent",
"getavatargroupsdata", "agent",
"getavatarpickdata", "item",
"getavatarpicks", "agent",
"getavatarpositions", "entity",
"getavatarsappearancedata", "entity",
"getavatarsdata", "entity",
"getavatarseat", "agents",
"getavatarsseats", "agents",
"getcameradata", "data",
"getconferencememberdata", "agent",
"getconferencemembersdata", "session",
"getconfigurationdata", "data",
"getcurrentgroupsdata", "data",
"getestatebanlist", "type",
"getestateinfodata", "data",
"getestatelist", "type",
"geteventinfodata", "id",
"getfrienddata", "agent",
"getgridregiondata", "data",
"getgroupaccountsummarydata", "target",
"getgroupdata", "target",
"getgrouplandinfodata", "target",
"getgroupmemberdata", "target",
"getgroupmembersdata", "target",
"getgroupsdata", "target",
"getheartbeatdata", "data",
"getinventorydata", "item",
"getinventorypath", "path",
"getmapavatarpositions", "region",
"getmemberroles", "target",
"getmembers", "target",
"getmembersoffline", "target",
"getmembersonline", "target",
"getmovementdata", "data",
"getnetworkdata", "data",
"getobjectdata", "data",
"getobjectlink", "item",
"getobjectmediadata", "data",
"getobjectpermissions", "item",
"getobjectsdata", "data",
"getparceldata", "data",
"getparceldwell", "position",
"getparcelinfodata", "data",
"getparcellist", "type",
"getparcelobjectresourcedetaildata", "data",
"getparcelobjectsresourcedetaildata", "data",
"getparticlesystem", "item",
"getprimitivedata", "data",
"getprimitiveflexibledata", "data",
"getprimitiveinventory", "item",
"getprimitiveinventorydata", "data",
"getprimitivelightdata", "data",
"getprimitiveowners", "position",
"getprimitivepayprices", "item",
"getprimitivephysicsdata", "data",
"getprimitivepropertiesdata", "data",
"getprimitivescripttext", "target",
"getprimitivesculptdata", "data",
"getprimitivesdata", "data",
"getprimitiveshapedata", "data",
"getprimitivetexturedata", "data",
"getprofiledata", "data",
"getprofilesdata", "data",
"getregiondata", "data",
"getregionparcellocations", "region",
"getregionparcelsboundingbox", "region",
"getregiontop", "type",
"getremoteparcelinfodata", "data",
"getrolemembers", "role",
"getrolepowers", "role",
"getroles", "target",
"getrolesmembers", "target",
"getscriptrunning", "entity",
"getselfdata", "data",
"getterrainheight", "region",
"gettitles", "target",
"getviewereffects", "effect",
"give", "item",
"grab", "item",
"grantfriendrights", "rights",
"http", "URL",
"importxml", "data",
"inventory", "action",
"invite", "agent",
"join", "target",
"jump", "action",
"leave", "target",
"login", "location",
"logs", "search",
"look", "position",
"lure", "agent",
"mapfriend", "agent",
"moderate", "agent",
"mqtt", "payload",
"mute", "agent",
"notice", "message",
"notify", "tag",
"nudge", "direction",
"objectdeed", "item",
"offerfriendship", "agent",
"parcelbuy", "position",
"parceldeed", "position",
"parceleject", "agent",
"parcelfreeze", "agent",
"parcelreclaim", "position",
"parcelrelease", "position",
"pay", "description",
"playgesture", "item",
"playsound", "item",
"primitivebuy", "item",
"readfile", "path",
"recompilescript", "target",
"removeconfigurationgroup", "target",
"removeitem", "item",
"renameitem", "item",
"replytofriendshiprequest", "agent",
"replytogroupinvite", "session",
"replytoinventoryoffer", "session",
"replytoscriptdialog", "dialog",
"replytoscriptpermissionrequest", "task",
"replytoteleportlure", "session",
"requestlure", "agent",
"restartregion", "action",
"returnprimitives", "agent",
"rez", "item",
"run", "action",
"scriptreset", "item",
"searchinventory", "pattern",
"setcameradata", "data",
"setconfigurationdata", "data",
"setestatecovenant", "item",
"setestatelist", "action",
"setgroupdata", "data",
"setinventorydata", "data",
"setmovementdata", "data",
"setobjectgroup", "item",
"setobjectmediadata", "data",
"setobjectpermissions", "permissions",
"setobjectposition", "position",
"setobjectrotation", "rotation",
"setobjectsaleinfo", "price",
"setobjectscale", "scale",
"setparceldata", "data",
"setparcellist", "agent",
"setprimitivedescription", "description",
"setprimitiveflags", "item",
"setprimitiveflexibledata", "data",
"setprimitiveinventorydata", "data",
"setprimitivelightdata", "data",
"setprimitivematerial", "material",
"setprimitivename", "name",
"setprimitiveposition", "position",
"setprimitiverotation", "rotation",
"setprimitivescale", "scale",
"setprimitivesculptdata", "data",
"setprimitiveshapedata", "data",
"setprimitivetexturedata", "data",
"setprofiledata", "data",
"setregionterrainheights", "data",
"setregionterraintextures", "data",
"setrolepowers", "role",
"setscriptrunning", "entity",
"setviewereffect", "item",
"simulatorpause", "region",
"simulatorresume", "region",
"sit", "item",
"softban", "avatars",
"startproposal", "text",
"tag", "title",
"teleport", "position",
"tell", "message",
"terminatefriendship", "agent",
"terrain", "data",
"toggleparcelflags", "flags",
"touch", "item",
"trashitem", "item",
"turn", "radians",
"turnto", "position",
"typing", "action",
"unwear", "wearables",
"updatenotecard", "data",
"updateprimitiveinventory", "item",
"updatescript", "data",
"upload", "data",
"walkto", "position",
"wear", "wearables",
"writefile", "data"
]);
message_key = getjs(message_mappings, [command]);
}
if((message_key == "" || message_key == JSON_INVALID)) {
if(message != "")
msg = "Warning: discarded <message> value.";
} else if(message == "" && (message_key != "" && message_key != JSON_INVALID)) {
if(getjs(json, [message_key]) != JSON_INVALID) {
// already set manually; we're fine!
} else if(warn) {
msg = "Warning: <message> was expected but not provided.";
}
} else {
json = setjs(json, [message_key], message);
}
if(test)
msg = setjs(json, ["password"], "[REDACTED]");
else {
if(bot_address != "") {
key post_body_pipe = llGenerateKey();
pipe_write(post_body_pipe, json);
if(no_http_wait) {
http_post(bot_address, NULL_KEY, post_body_pipe, NULL_KEY);
} else {
key handle = llGenerateKey();
key post_reply_pipe = llGenerateKey();
waiting_queries += [
src, ins, handle, outs, user, post_body_pipe, post_reply_pipe, bot_address
];
pipe_open("p:" + (string)post_reply_pipe + " notify " + PROGRAM_NAME + " fetched " + (string)handle);
_resolved = 0;
}
/*
if(http_fetch_reply != "" || no_http_wait) {
key post_body_pipe = llGenerateKey();
key handle = llGenerateKey();
pipe_write(post_body_pipe, json);
if(!no_http_wait) {
http_post(bot_address, http_fetch_reply, post_body_pipe, handle);
_resolved = 0;
active_queries += [
src, ins, handle, outs, user
];
} else {
http_post(bot_address, NULL_KEY, post_body_pipe, handle);
}
} else {
// msg = "Run '" + PROGRAM_NAME + " -F' first.";
}
*/
} else if(bot == "") {
llInstantMessage(avatar, json);
} else {
llInstantMessage(bot, json);
}
}
}
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
} 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 <ARES/program>

487
ARES/application/db.lsl Normal file
View File

@ -0,0 +1,487 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Database Utility
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#define FILE_STEP_SIZE 10
#include <ARES/api/file.h.lsl>
#include <ARES/api/auth.h.lsl>
#define CLIENT_VERSION "1.2.0"
#define CLIENT_VERSION_TAGS "beta"
key dbload_q;
string read_buffer;
/*
replaces (string) number with (integer)number
and (string)\"number\" with (string)number
*/
list process_keyname(list keyname) {
integer ki = count(keyname);
while(ki--) {
string ks = gets(keyname, ki);
if((string)((integer)ks) == ks)
keyname = alter(keyname, [(integer)ks], ki, ki);
else if(llOrd(ks, 0) == 0x22 && llOrd(ks, LAST) == 0x22)
keyname = alter(keyname, [substr(ks, 1, -2)], ki, ki);
}
return keyname;
}
string db_set(string section, list keyname, string new_value) {
string old_value = getdbl(section, keyname);
setdbl(section, keyname, new_value);
if(old_value == JSON_INVALID)
old_value = "(undefined)";
else if(strlen(old_value) > 100)
old_value = "(" + (string)strlen(old_value) + " bytes)";
if(strlen(new_value) > 100)
new_value = "(" + (string)strlen(new_value) + " bytes)";
if(count(keyname) == 0)
return "overwrote ENTIRE section " + section + ": " + old_value + " --> " + new_value;
else
return "modified " + section + " setting " + concat(keyname, ".") + ": " + old_value + " --> " + new_value;
}
string db_show_2(string value, integer sp) {
if(llOrd(value, 0) == 0x7b && llOrd(value, LAST) == 0x7d) { // { and }
list keys = jskeys(value);
integer kmax = count(keys);
if(kmax == 0) {
if(strlen(value) > 100)
return "(" + (string)strlen(value) + " bytes)";
else
return value;
}
string sps = " ";
if(sp) {
integer spi = sp;
while(spi--)
sps += " ";
}
list vout;
integer ki = 0;
while(ki < kmax) {
string k = gets(keys, ki);
string subval = getjs(value, [k]);
if(llGetFreeMemory() > 2000)
vout += k + ": " + db_show_2(subval, sp + 1);
else
vout += k + ": (out of memory)";
++ki;
}
return "\n" + sps + concat(vout, "\n" + sps);
} else {
return value;
}
}
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
string action = gets(argv, 1);
integer argc = count(argv);
string msg;
integer allowed = sec_check(user, "database", outs, m, m);
if(allowed == DENIED) {
print(outs, user, "secondlife:///app/agent/" + (string)user + "/about is not authorized to access the database directly.");
return;
} else if(allowed == PENDING) {
return;
}
list parts;
string section;
list keyname;
integer user_rank = (integer)getdbl("security", ["user", user]);
if(action == "clone") {
parts = splitnulls(gets(argv, 2), ".");
section = gets(parts, 0);
list parts2 = splitnulls(gets(argv, 3), ".");
string section2 = gets(parts2, 0);
if(section2 == "security" && user_rank != SEC_OWNER) {
jump security_fail;
} else {
string text = getdbl(section, process_keyname(delitem(parts, 0)));
if(text == JSON_INVALID) {
msg = "Could not clone from " + gets(argv, 2) + " to " + gets(argv, 3) + ": entry " + gets(argv, 2) + " does not exist.";
} else {
setdbl(section2, process_keyname(delitem(parts2, 0)), text);
integer tl = strlen(text);
msg = "Cloned data from " + gets(argv, 2) + " to " + gets(argv, 3) + ".";
if(tl != 1)
msg += " (" + (string)tl + " bytes)";
else
msg += " (1 byte)";
}
}
} else if(action == "load") {
if(user_rank != SEC_OWNER) {
msg = "Only owners may perform database imports.";
} else if(gets(argv, 2) == "cancel") {
msg = "Aborted.";
resolve((integer)getjs(tasks_queue, [dbload_q, FILE_R]));
task_end(dbload_q);
dbload_q = "";
} else if(dbload_q) {
msg = "A database import is already in progress; run 'db load cancel' to abort.";
} else {
read_buffer = "";
string dbload_fn = gets(argv, 2);
dbload_q = fopen(outs, ins, user, dbload_fn);
msg = "Importing " + dbload_fn;
}
} else if(action == "delete" || action == "remove") {
parts = splitnulls(gets(argv, 2), ".");
section = gets(parts, 0);
if(section == "security" && user_rank != SEC_OWNER)
jump security_fail;
keyname = process_keyname(delitem(parts, 0));
if(getdbl(section, keyname) != JSON_INVALID) {
deletedbl(section, keyname);
msg = "Deleted " + concat(keyname, ".") + " in section " + section;
} else {
msg = concat(keyname, ".") + " in section " + section + " not present";
}
} else if(action == "toggle") {
parts = splitnulls(gets(argv, 2), ".");
section = gets(parts, 0);
if(section == "security" && user_rank != SEC_OWNER)
jump security_fail;
keyname = process_keyname(delitem(parts, 0));
integer val = !(integer)getdbl(section, keyname);
if(val)
msg = "Enabled ";
else
msg = "Disabled ";
setdbl(section, keyname, (string)val);
msg += gets(argv, 2);
} else if(action == "drop") {
section = gets(argv, 2);
if(section == "security" && user_rank != SEC_OWNER)
jump security_fail;
llLinksetDataDelete(section);
msg = "Deleted ENTIRE section " + section;
} else if(action == "set") {
parts = splitnulls(gets(argv, 2), ".");
section = gets(parts, 0);
if(section == "security" && user_rank != SEC_OWNER)
jump security_fail;
keyname = process_keyname(delitem(parts, 0));
string new_value = concat(delrange(argv, 0, 2), " ");
msg = db_set(section, keyname, new_value);
} else if(action == "append") {
parts = splitnulls(gets(argv, 2), ".");
section = gets(parts, 0);
if(section == "security" && user_rank != SEC_OWNER)
jump security_fail;
keyname = process_keyname(delitem(parts, 0)) + [JSON_APPEND];
string new_value = concat(delrange(argv, 0, 2), " ");
setdbl(section, keyname, new_value);
msg = "Added '" + new_value + "' to section " + section + " key " + concat(keyname, ".");
} else if(action == "") {
list all_keys = llLinksetDataListKeys(0, 0);
integer aki = count(all_keys);
// limited regex support can't handle ^(?!p:).+$
while(aki--)
if(substr(gets(all_keys, aki), 0, 1) == "p:")
all_keys = delitem(all_keys, aki);
msg = "Stored sections: " + concat(all_keys, ", ")
+ "\nLSD status: "
+ (string)llLinksetDataCountKeys() + " keys total, "
+ (string)llLinksetDataAvailable() + " bytes free.";
} else if(action == "u" || action == "usage") {
// list all_keys = llLinksetDataListKeys(0, 0);
integer akmax;
integer aki = akmax = llLinksetDataCountKeys(); // count(all_keys);
list fields;
while(aki--) {
string kn = gets(llLinksetDataListKeys(aki, 1), 0);
// msg += " - " + kn + " " + (string)strlen(llLinksetDataRead(kn)) + "\n";
fields += [kn, strlen_byte_inline(llLinksetDataRead(kn))];
}
fields = llListSortStrided(fields, 2, 1, TRUE);
aki = akmax;
while(aki--) {
integer L = geti(fields, (aki << 1) + 1);
string line = " - " + gets(fields, (aki << 1)) + " " + (string)L;
if(L != 1)
line += " bytes";
else
line += " byte";
// fields = alter(fields, [line], (aki << 1), (aki << 1) + 1);
fields = delrange(fields, (aki << 1), (aki << 1) + 1);
print(outs, user, line);
llSleep(0.05);
}
msg = /*concat(fields, "\n")
+ */"\nLSD status: "
+ (string)llLinksetDataCountKeys() + " keys total, "
+ (string)llLinksetDataAvailable() + " bytes free.";
} else if(action == "show") {
parts = splitnulls(gets(argv, 2), ".");
section = gets(parts, 0);
keyname = process_keyname(delitem(parts, 0));
// msg = db_show(section, keyname);
jump db_show;
} else if(action == "json") {
parts = splitnulls(gets(argv, 2), ".");
section = gets(parts, 0);
keyname = process_keyname(delitem(parts, 0));
msg = getdbl(section, keyname);
if(msg == JSON_INVALID)
msg = "(undefined)";
} else {
parts = splitnulls(action, ".");
section = gets(parts, 0);
if(llLinksetDataRead(section) != "") {
keyname = process_keyname(delitem(parts, 0));
if(argc > 2) {
if(count(keyname) > 0) {
if(section == "security" && user_rank != SEC_OWNER)
jump security_fail;
string new_value = concat(delrange(argv, 0, 1), " ");
msg = db_set(section, keyname, new_value);
} else {
msg = "Did you mean to do that? For safety, you must use 'db set' to replace an entire section.";
}
} else {
// msg = db_show(section, keyname);
jump db_show;
}
} else {
msg = PROGRAM_NAME + ": No section: " + action + "; see 'help db' for syntax.";
}
}
jump security_pass;
@db_show;
// (section and keyname are already populated)
string value;
if(count(keyname)) {
value = getdbl(section, keyname);
} else {
value = llLinksetDataRead(section);
}
if(value == JSON_INVALID) {
value = "(undefined)";
} else if(strlen(value) > 800 && count(jskeys(value))) {
value = "(keys only) " + concat(jskeys(value), ", ");
} else {
value = db_show_2(value, 0);
}
if(!count(keyname))
value = "(entire section) " + value;
msg = concat([section] + keyname, ".") + " " + value;
jump security_pass;
@security_fail;
msg = "Only the unit's owner may directly modify the 'security' section of the database.";
@security_pass;
if(msg != "")
print(outs, user, msg);
return;
} else if(n == SIGNAL_NOTIFY) {
if(m == PROGRAM_NAME + " file") {
if(ins == dbload_q) {
user = getjs(tasks_queue, [dbload_q, FILE_USER]);
outs = getjs(tasks_queue, [dbload_q, FILE_OUTS]);
string fn = getjs(tasks_queue, [dbload_q, FILE_NAME]);
string unit = getjs(tasks_queue, [dbload_q, FILE_UNIT]);
string result = fread(dbload_q);
string msg;
list parts;
string section;
list keyname;
if(result == JSON_FALSE) {
msg = "No file: " + fn;
dbload_q = "";
} else if(result == JSON_TRUE) {
msg = "Loading " + getjs(tasks_queue, [dbload_q, FILE_LENGTH]) + " " + getjs(tasks_queue, [dbload_q, FILE_UNIT]);
} else {
list lines = split(result, "\n");
if(unit != "l") {
lines = alter(lines, [read_buffer + gets(lines, 0)], 0, 0);
if(getjs(tasks_queue, [dbload_q]) == JSON_INVALID) {
// the end of the file; last line can be trusted
read_buffer = "";
} else {
read_buffer = gets(lines, LAST);
lines = delitem(lines, LAST);
}
}
integer li = 0;
integer lmax = count(lines);
while(li < lmax) {
string m = llStringTrim(gets(lines, li++), STRING_TRIM_HEAD);
if(m != "" && substr(m, 0, 0) == "#") {
// skip comments
} else {
list argv = splitnulls(m, " ");
integer mode = 0; // set
string mode_name = llToUpper(gets(argv, 0));
if(mode_name == "DELETE" || mode_name == "-") {
mode = 1;
argv = delitem(argv, 0);
} else if(mode_name == "CREATE" || mode_name == "+") {
mode = 2;
argv = delitem(argv, 0);
} else if(mode_name == "APPEND" || mode_name == "++") {
mode = 3;
argv = delitem(argv, 0);
// echo(m);
} else if(mode_name == "MERGE" || mode_name == "+=") {
mode = 5;
argv = delitem(argv, 0);
} else if(mode_name == "SET" || mode_name == "=") {
mode = 0;
argv = delitem(argv, 0);
} else if(mode_name == "DROP") {
mode = 4;
argv = delitem(argv, 0);
}
string varkey = gets(argv, 0);
if(varkey != "") {
string varvalue = concat(delitem(argv, 0), " ");
keyname = split(varkey, ".");
section = gets(keyname, 0);
keyname = process_keyname(delitem(keyname, 0));
string section_data = llLinksetDataRead(section);
if(mode == 1 && section_data == "") {
// nothing to delete
} else if(mode == 4) {
if(section_data != "") {
msg += "Deleted ENTIRE section " + section + "\n";
}
llLinksetDataDelete(section);
} else {
if(section_data == "") {
section_data = "{}";
msg += "Created section: " + section + "\n";
}
if(mode == 1)
varvalue = JSON_DELETE;
else if(mode == 3)
keyname += [JSON_APPEND];
if(mode == 2 && getjs(section_data, keyname) != JSON_INVALID) {
// value already exists; not replacing
} else {
// perform the update in memory:
if(mode == 5) {
list keys = jskeys(varvalue);
integer ki = count(keys);
while(ki--) {
string k = gets(keys, ki);
section_data = setjs(section_data, keyname + [k], getjs(varvalue, [k]));
}
} else {
section_data = setjs(section_data, keyname, varvalue);
}
// apply the update if successful:
if(section_data != JSON_INVALID) {
// only commit on successful update:
llLinksetDataWrite(section, section_data);
} else if(mode != 1) {
msg += "Operation failed: " + m + "\n";
} // else DELETEs fail silently
}
}
}
}
}
if(getjs(tasks_queue, [dbload_q]) == JSON_INVALID) {
msg += "Finished database import of " + fn
+ "\nLSD status: " + (string)llLinksetDataCountKeys() + " sections, " + (string)llLinksetDataAvailable() + " bytes free.";
dbload_q = "";
}
}
if(msg != "")
print(outs, user, msg);
} else {
echo("[" + PROGRAM_NAME + "] file data offered via unexpected pipe: " + (string)ins);
}
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
} 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 <ARES/program>

154
ARES/application/define.lsl Normal file
View File

@ -0,0 +1,154 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* define Dictionary Lookup
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#define CLIENT_VERSION "1.1.1"
#define CLIENT_VERSION_TAGS "release"
string DEFAULT_DOMAIN = "en.wikipedia.org";
#define QUERY_STRING "/w/api.php?action=query&format=json&prop=extracts&redirects=1&utf8=1&formatversion=2&exchars=900&explaintext=1&exsectionformat=plain&titles="
key define_receive_pipe;
string waiting_queries = "{}";
string queries_in_progress = "{}";
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
if(argc == 1) {
msg = "Syntax: " + PROGRAM_NAME + " [-w <domain>] <page>\n\nLooks up <page> on the [https://www.mediawiki.org MediaWiki] at <domain> (default: " + DEFAULT_DOMAIN + ").\n\nThe target wiki must have the [https://www.mediawiki.org/wiki/Extension:TextExtracts TextExtracts API extension] installed.";
} else {
string domain;
if(gets(argv, 1) == "-w") {
domain = gets(argv, 2);
argv = delrange(argv, 1, 2);
} else {
domain = DEFAULT_DOMAIN;
}
string question = concat(delitem(argv, 0), " ");
if(!~strpos(domain, "://"))
domain = "https://" + domain;
key handle = llGenerateKey();
string query = jsarray([domain, question, outs, ins, user, _resolved]);
_resolved = 0;
llSetMemoryLimit(0x10000);
if(define_receive_pipe) {
queries_in_progress = setjs(queries_in_progress, [handle], query);
http_get(domain + QUERY_STRING + llEscapeURL(question), define_receive_pipe, handle);
} else {
define_receive_pipe = llGenerateKey();
waiting_queries = setjs(waiting_queries, [handle], query);
pipe_open(["p:"+(string)define_receive_pipe + " notify " + PROGRAM_NAME + " response"]);
}
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_NOTIFY) {
list argv = split(m, " ");
string cmd = gets(argv, 1);
if(cmd == "pipe") {
list queries = jskeys(waiting_queries);
integer qi = 0;
integer qmax = count(queries);
while(qi < qmax) {
key handle = gets(queries, qi);
string query = getjs(waiting_queries, [(string)handle]);
waiting_queries = setjs(waiting_queries, [(string)handle], JSON_DELETE);
string domain = getjs(query, [0]);
string question = getjs(query, [1]);
queries_in_progress = setjs(queries_in_progress, [(string)handle], query);
http_get(domain + QUERY_STRING + llEscapeURL(question), define_receive_pipe, handle);
++qi;
}
} else if(cmd == "response") {
string query = getjs(queries_in_progress, [(string)user]);
if(query != JSON_INVALID) {
key o_outs = getjs(query, [2]);
key o_ins = getjs(query, [3]);
key o_user = getjs(query, [4]);
integer o_rc = (integer)getjs(query, [5]);
string buffer;
pipe_read(ins, buffer);
string fieldset = getjs(buffer, ["query", "pages", 0]);
string field;
if(fieldset != JSON_INVALID) {
if(getjs(fieldset, ["missing"]) == JSON_TRUE) {
field = getjs(fieldset, ["title"]) + " does not exist (" + getjs(query, [0]) + "/)";
} else {
field = getjs(fieldset, ["extract"]);
}
} else {
field = buffer;
}
print(o_outs, o_user, field);
// resolve_io(o_rc, o_outs, o_ins);
queries_in_progress = setjs(queries_in_progress, [(string)user], JSON_DELETE);
resolve_i(o_rc, o_ins);
}
if(queries_in_progress == "{}") {
pipe_close(define_receive_pipe);
define_receive_pipe = "";
llSetMemoryLimit(0x2000);
}
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
llSetMemoryLimit(0x2000);
} 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 <ARES/program>

905
ARES/application/filter.lsl Normal file
View File

@ -0,0 +1,905 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Text Filter Utilities (Standard Bundle)
*
* 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 <ARES/a>
#define CLIENT_VERSION ARES_VERSION
#define CLIENT_VERSION_TAGS ARES_VERSION_TAGS
list filters = [
// filter layer
"censor", 0,
"replace", 0,
"slang", 0,
"nonverbal", 1,
"translate", 1,
"stutter", 2,
"serpentine", 2,
"lisp", 3,
"mumble", 4,
"caps", 5,
"rot13", 5,
"slow", 5,
"bimbo", 6,
"superscript", 6,
"corrupted", 7,
"glitch", 7
];
string f_censor(string message, string flags) {
if(flags == "") {
echo("No censor dictionary specified. See 'help censor'");
return message;
} else {
list words = js2list(getdbl("filter", ["censor", flags]));
// echo("Censor word list: " + (string)concat(words, ", "));
integer wi = 0;
integer wmax = count(words);
string new_message = message;
string search_space = llToLower(new_message);
while(wi < wmax) {
string word = gets(words, wi++);
if(word == JSON_INVALID) {
echo("Censor dictionary not found: " + flags + ". See 'help censor'");
return message;
}
string replacement;
integer ci;
integer wlen = 0;
while(~(ci = strpos(search_space, word))) {
if(!wlen) {
wlen = strlen(word) - 1;
replacement = substr("********************************", 0, wlen);
}
new_message = llInsertString(delstring(new_message, ci, ci + wlen), ci, replacement);
search_space = llInsertString(delstring(search_space, ci, ci + wlen), ci, replacement);
// echo("Replaced " + word + " with " + replacement + ": " + new_message);
}
// new_message = replace(new_message, word, replacement);
}
return new_message;
}
}
string f_replace(string message, string dictionary_name) {
string dictionary = getdbl("filter", ["replace", dictionary_name]);
if(dictionary == JSON_INVALID) {
echo("[filter] warning: replacement dictionary " + dictionary_name + " does not exist. See 'help replace'");
return message;
}
list EOS = [".", "?", "!"];
list separators = EOS + [" ", ",", "\"", "'", "-", ":", ";", "(", ")", "*", "~", "/", "@", "", "—"];
list tokens = llParseString2List(message, [], separators); // not sure if this would be better as llParseStringKeepNulls()
list cases;
list ltokens;
integer tmax = count(tokens);
list headwords = jskeys(dictionary);
integer ti = 0;
while(ti < tmax) {
string t = gets(tokens, ti);
string lt = llToLower(t);
ltokens += lt;
integer original_case;
if(lt == t) {
original_case = 0; // plain
} else if(t == "I") {
original_case = 3; // unique
} else if(delstring(lt, 0, 0) == delstring(t, 0, 0)) {
original_case = 1; // first-letter
} else if(llToUpper(t) == t) {
original_case = 2; // all-caps
} else {
original_case = 3;
}
integer di = index(headwords, lt);
if(~di) {
string replacement = getjs(dictionary, [lt]);
if(llToLower(replacement) == replacement) { // only alter case for lower-case replacements
if(original_case == 3) {
string prequel = gets(ltokens, ti - 2);
if(contains(EOS, prequel) || prequel == "") // are we exactly two tokens after the end of a sentence?
original_case = 1;
else
original_case = 0;
}
if(original_case == 1) {
replacement = llToUpper(substr(replacement, 0, 0)) + delstring(replacement, 0, 0);
} else if(original_case == 2) {
replacement = llToUpper(replacement);
}
} else if(original_case == 2) { // actually, let's pass on all-caps so shouting looks right
replacement = llToUpper(replacement);
}
tokens = alter(tokens, [replacement], ti, ti);
}
++ti;
}
message = concat(tokens, "");
integer hwi = count(headwords);
while(hwi--) {
string hw = gets(headwords, hwi);
if(~strpos(hw, "'")) {
string replacement = getjs(dictionary, [hw]);
message = replace(message, hw, replacement);
}
}
return message;
}
string f_slang(string message, string flags) {
if(flags == "") {
echo("Warning: no slang table specified; see 'help slang'.");
return message;
}
string dictionary = getdbl("filter", ["slang", flags]);
if(dictionary == JSON_INVALID) {
echo("Warning: slang table " + flags + " does not exist; see 'help slang'.");
return message;
}
list prefixes = js2list(getjs(dictionary, ["pre"])); integer pc = count(prefixes);
list infixes = js2list(getjs(dictionary, ["mid"])); integer ic = count(infixes);
list suffixes = js2list(getjs(dictionary, ["post"])); integer sc = count(suffixes);
list sentences = llParseStringKeepNulls(message + " ", [], [". ", "? ", "! "]);
// echo(concat(sentences, "|"));
integer si = count(sentences);
while(si--) {
string sentence = gets(sentences, si);
if(strlen(sentence) > 2) {
list words = splitnulls(sentence, " ");
integer wc = count(words);
if(wc > 1) {
string prefix = gets(prefixes, (integer)llFrand(pc));
string suffix = gets(suffixes, (integer)llFrand(sc)) + " ";
string infix = gets(infixes, (integer)llFrand(ic));
integer wi = (integer)llFrand(wc);
words = alter(words, [llToLower(gets(words, 0))], 0, 0);
words = [prefix] + llListInsertList(words, [infix], wi) + [suffix];
}
sentences = alter(sentences, [concat(words, " ")], si, si + 1);
}
}
return llStringTrim(concat(sentences, ""), STRING_TRIM);
}
/*
string transfer_capitals(string source, string dest) {
string result;
integer ci = 0;
integer cimax = strlen(source);
integer d = strlen(dest);
integer c;
while(ci < cimax) {
integer a = llOrd(source, ci);
integer b = llOrd(dest, ci);
if(a < 0x80 && b < 0x80) { // if ASCII, work with integers
if(a > 0x40 && a < 0x5b) {
++c;
if(b > 0x60 && b < 0x7b) {
result += llChar(b - 0x20);
} else {
result += llChar(b);
}
} else {
result += llChar(b);
}
} else { // otherwise fall back to llToUpper()/llToLower()
string ac = llChar(a);
string bc = llChar(b);
string caps = llToUpper(ac);
string small = llToLower(ac);
if(caps != small && ac == caps) {
++c;
result += llToUpper(bc);
} else {
result += bc;
}
}
++ci;
}
// deal with extended words:
if(d > cimax) {
if(c == cimax && c > 1)
result += llToUpper(substr(dest, cimax, d));
else
result += substr(dest, cimax, d);
}
// yield:
return result;
} */
string last_nonverbal_flags = "m o o";
string f_nonverbal(string message, string flags) {
if(flags == "")
flags = last_nonverbal_flags;
else
last_nonverbal_flags = flags;
list parts = split(llToLower(flags), " ");
string prefix = gets(parts, 0); integer prelen = strlen(prefix);
string infix = gets(parts, 1); integer inlen = strlen(infix);
string suffix = gets(parts, 2); integer suflen = strlen(suffix);
string out;
integer fmax = strlen(message);
integer fi = 0;
string word;
while(fi < fmax) {
string char = substr(message, fi, fi);
if(llToUpper(char) != llToLower(char)) {
word += char;
} else {
if(word) {
integer wmax = strlen(word);
integer wi = 0;
string raword;
if(wmax <= prelen + inlen + suflen) {
raword = prefix + infix + suffix;
} else {
raword = prefix;
integer jl = wmax - prelen - suflen;
while(jl > 0) {
raword += infix;
jl -= inlen;
}
raword += suffix;
}
if(llToUpper(word) == word)
raword = llToUpper(raword);
else if(llToLower(word) != word)
raword = llToUpper(substr(raword, 0, 0)) + substr(word, 1, LAST);
/* else
word = raword; */
// word = transfer_capitals(word, raword);
out += raword;
word = raword = "";
}
out += char;
}
++fi;
}
return out;
}
#define TRANSLATION_URL "http://mymemory.translated.net/api/get?q=" + llEscapeURL(message) + "&langpair=" + replace(flags, ":", "|") + "&de=" + (string)avatar + "@lsl.secondlife.com"
key translate_pipe;
list translation_queue;
f_translate(string message, string flags, key outs, key ins, key user, integer rc) {
if(flags == "") {
echo("Cannot translate; no translation language pair set: " + message + ". See 'help translate'");
print(outs, user, message);
// resolve_io(rc, outs, ins);
resolve_i(rc, ins);
return;
}
key handle = llGenerateKey();
translation_queue += [message, flags, handle, outs, ins, user, rc];
if(translate_pipe) {
#ifdef DEBUG
echo("sending message " + message + " for translation immediately");
#endif
http_get(TRANSLATION_URL, translate_pipe, handle);
} else {
#ifdef DEBUG
echo("opening translation pipe");
#endif
translate_pipe = llGenerateKey();
pipe_open(["p:" + (string)translate_pipe + " notify " + PROGRAM_NAME + " translation"]);
}
}
/* string match_case(string question, string answer) {
if(llToLower(answer) == answer) {
return llToLower(question);
} else {
return llToUpper(question);
}
} */
string f_stutter(string message, string flags) {
float stutter_level = (float)flags;
if(flags == "" || stutter_level == 0)
return message;
list tokens = [" "] + llParseString2List(message, [], [" ", "a", "e", "i", "o", "u", "y", "A", "E", "I", "O", "U", "Y"]) + [" "];
integer tc = count(tokens);
integer ti = tc - 1;
while(ti--) {
if(gets(tokens, ti) == " ") {
string nt = gets(tokens, ti + 1);
string nt2 = gets(tokens, ti + 2);
if(llFrand(100) <= stutter_level) {
string pnt = substr(nt, 0, 0);
if(llToLower(pnt) != llToUpper(pnt)) {
string ntx;
if(llToLower(nt2) == nt2)
ntx = llToLower(nt);
else
ntx = llToUpper(nt);
if(~strpos("aeiouy", llToLower(nt))) { // starting with a vowel
nt += "-" + ntx;
if(llFrand(100) <= stutter_level) {
nt += "-" + ntx;
if(llFrand(100) <= stutter_level) {
nt += "-" + ntx;
}
}
tokens = alter(tokens, [nt], ti + 1, ti + 1);
} else if(nt2 != " ") { // consonants before a vowel
if(llFrand(100) < 50) // extend consonants only
nt2 = "-" + nt;
nt += nt2 + "-" + ntx;
if(llFrand(100) <= stutter_level) {
nt += nt2 + "-" + ntx;
if(llFrand(100) <= stutter_level) {
nt += nt2 + "-" + ntx;
}
}
tokens = alter(tokens, [nt], ti + 1, ti + 1);
}
}
}
}
}
return substr(concat(tokens, ""), 1, -2);
}
string f_serpentine(string message, string flags) {
if(flags == "") flags = "100";
float serpentine_strength = 0.01 * (float)flags;
if(serpentine_strength > 0.0) {
integer i = 0;
integer j = 0;
while(i < strlen(message)) {
string e = substr(message, i, i);
string f = substr(message, i, i + 1);
if(f == "ci" || f == "CI" || f == "CE" || f == "ce") {
e = e + e;
j = 1;
} else if(f == "SH") {
e = f;
} else if(f == "sh" || f == "Sh") {
e = "sh";
} else if(e == "s" || e == "z" || e == "S" || e == "Z" || e == "ß") {
e = e + e;
j = 1;
} else
e = "";
if(llFrand(1.0) < serpentine_strength && e != "") {
message = substr(message, 0, i) + e + substr(message, i + 1, LAST);
++i;
if(j) {
j = 0;
++i;
}
} else {
++i;
}
}
}
return message;
}
string f_lisp(string message, string flags) {
if(flags == "") flags = "100";
float lisp_strength = 0.01 * (float)flags;
integer i = 0;
integer j = 0;
while(i <= strlen(message) - 1) {
string e = substr(message, i, i);
string f = substr(message, i, i+1);
string g = substr(message, i+1, i+1);
if(g == "") g = e;
j = 1;
if(e == "s" || e == "z" || e == "ß") {
e = "th";
j = 0;
} else if(f == "ci" || f == "ce" || f == "sh" || f == "cc" || f == "zh") {
e = "th";
} else if(f == "Ci" || f == "Ce" || f == "Sh" || f == "Cc" || f == "Zh" || (llToLower(g) == g && ( e == "S" || e == "Z"))) {
e = "Th";
} else if(f == "CI" || f == "CE" || f == "SH" || f == "CC" || f == "ZH" || e == "S" || e == "Z") {
e = "TH";
} else {
e = "";
j = 0;
}
if(llFrand(1.0) < lisp_strength && e != "") {
if(strlen(message) == 1)
message = e;
else if(i == strlen(message) - 1)
message = substr(message, 0, i - 1) + e;
else if(i > 0)
message = substr(message, 0, i - 1) + e + substr(message, i + 1, -1);
else
message = e + substr(message, i + 1, -1);
++i;
if(j) {
i += j;
}
} else {
++i;
}
}
return message;
}
// based on fp_gagvox from the NS Ballgag
list orig_4 = ["tion", "tial"];
list repl_4 = ["fhion", "fho"];
list orig_3 = ["sch"];
list repl_3 = ["fh"];
list orig_2 = ["ss", "ci", "th", "sh", "ge", "ch"];
list repl_2 = ["ffh", "fi", "w", "hw", "wye", "tfh"];
list orig_1 = ["s", "l", "t", "r", "p", "b", "j", "z"];
list repl_1 = ["fh", "w", "ht", "w", "k", "w", "dh", "v"];
#define MIXED 0
#define LOWER 1
#define UPPER 2
string f_mumble(string message, string flags) {
integer i = 0;
integer L = strlen(message);
message = message + " ";
list out_tokens;
while(i < L) {
string src_4 = substr(message, i, i + 3);
string src_3 = substr(message, i, i + 2);
string src_2 = substr(message, i, i + 1);
string src_1 = substr(message, i, i + 0);
integer case = MIXED;
if(llToLower(src_2) == src_2)
case = LOWER;
else if(llToUpper(src_2) == src_2)
case = UPPER;
integer i4 = index(orig_4, llToLower(src_4));
integer i3 = index(orig_3, llToLower(src_3));
integer i2 = index(orig_2, llToLower(src_2));
integer i1 = index(orig_1, llToLower(src_1));
integer ll = 0;
string out_token;
if(~i4) {
out_token = gets(repl_4, i4);
i += 4;
} else if(~i3) {
out_token = gets(repl_3, i3);
i += 3;
} else if(~i2) {
out_token = gets(repl_2, i2);
i += 2;
} else if(~i1) {
out_token = gets(repl_1, i1);
i += 1;
if(llToUpper(src_1) == src_1 && strlen(out_token) == 1) {
ll = 1;
out_token = llToUpper(out_token);
}
} else {
out_token = src_1;
i += 1;
ll = 1;
}
if(ll || case == LOWER)
out_tokens += out_token;
else if(case == UPPER)
out_tokens += llToUpper(out_token);
else if(case == MIXED)
out_tokens += llToUpper(substr(out_token, 0, 0)) + substr(out_token, 1, -1);
}
return concat(out_tokens, "");
}
string f_caps(string message, string flags) {
return llToUpper(message);
}
string f_rot13(string message, string flags) {
string out;
integer fmax = strlen(message);
integer fi;
while(fi < fmax) {
integer c = llOrd(message, fi++);
if(c > 0x40 && c < 0x5b) { // after @ and before [
c -= 13;
if(c < 0x41)
c += 26;
} else if(c > 0x60 && c < 0x7b) { // after ` and before {
c -= 13;
if(c < 0x61)
c += 26;
}
out += llChar(c);
}
return out;
}
string f_slow(string message, string flags) {
// echo("FLAGS = " + flags);
return replace(message, " ", substr(" ", 0, llAbs((integer)flags)));
}
string f_bimbo(string message, string flags) {
string intake = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
string replacement = "αв¢∂єƒﻭнιנкℓмησρ۹яѕтυνωχуչαв¢∂єƒﻭнιנкℓмησρ۹яѕтυνωχуչ";
string output = "";
integer imax = strlen(message);
integer i;
while(i < imax) {
string c = substr(message, i, i);
integer j = strpos(intake, c);
if(~j) {
// echo(c + " is letter #" + (string)j + " and maps to " + substr(replacement, j, j));
output += substr(replacement, j, j);
} else {
// echo(c + " is not a letter");
output += c;
}
++i;
}
return output;
}
string f_superscript(string message, string flags) {
string intake = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-=~";
string replacement = "ᴬᴮᶜᴰᴱᶠᴳᴴᴵᴶᴷᴸᴹᴺᴼᴾ۹ᴿˢᵀᵁⱽᵂˣʸᶻᵃᵇᶜᵈᵉᶠᵍʰⁱʲᵏˡᵐⁿᵒᵖ۹ʳˢᵗᵘᵛʷˣʸᶻ⁰¹²³⁴⁵⁶⁷⁸⁹⁻⁼˜";
string output = "";
integer imax = strlen(message);
integer i;
while(i < imax) {
string c = substr(message, i, i);
integer j = strpos(intake, c);
if(~j) {
// echo(c + " is letter #" + (string)j + " and maps to " + substr(replacement, j, j));
output += substr(replacement, j, j);
} else {
// echo(c + " is not a letter");
output += c;
}
++i;
}
return output;
}
list ft_dropout_chars = ["░", "▒", "▓", "█"];
string f_corrupted(string message, string flags) {
float dropout_strength = 0.01 * (float)flags;
if(dropout_strength == 0)
return message;
integer imax = strlen(message);
string oo;
integer i = 0;
for(; i < imax; ++i) {
string c = substr(message, i, i);
if(llFrand(1) < dropout_strength && llToUpper(c) != llToLower(c)) {
oo += gets(ft_dropout_chars, (integer)llFrand(4));
} else
oo += substr(message, i, i);
}
return oo;
}
#define GLITCH_MULTIPLIER 1
string f_glitch(string message, string flags) {
if(flags == "") flags = "10";
float glitch_level = 0.01 * (float)flags;
list junkchars = ["̆","̎","̾","͋","̚","͠","͢","̨","̴","̶","̷","̡","̜","̼","̖","̞","̤","̰","͌","̂"];
if(glitch_level > 0.009) {
integer k = strlen(message);
integer j = strlen_byte(message);
integer i = (integer)((float)k * glitch_level * GLITCH_MULTIPLIER);
// llOwnerSay("Adding " + (string)i + " glitches to string of " + (string)strlen(message) + " characters.");
while(i > 0 && j < 1022) {
string a = gets(junkchars, (integer)llFrand(20));
string b = gets(junkchars, (integer)llFrand(20));
message = llInsertString(llInsertString(
message,
(integer)(llFrand((k) - 1)) + 1, a),
(integer)(llFrand((k += 2) - 3)) + 1, b);
j += strlen_byte(a + b);
// llOwnerSay(">>" + message);
--i;
}
}
return message;
}
list activated_filters = [];
key session;
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] signal " + (string)n + " from " + (string)src + ": " + m + " ("+(string)outs+":"+(string)ins+":"+(string)user +")");
#endif
list argv = split(m, " ");
integer argc = count(argv);
if(argc == 1) {
print(outs, user, "[" + PROGRAM_NAME + "] see 'help " + PROGRAM_NAME + "'");
} else {
string filter = gets(argv, 1);
string action = gets(argv, 2);
if(filter == "install" || filter == "remove") {
string fconf = getdbl("vox", ["filter"]);
integer fi = count(filters);
string msg;
if(filter == "install")
msg = "Installed " ;
else
msg = "Removed ";
if(fi > 2)
msg += (string)(fi / 2) + " filters";
else
msg += "1 filter";
while(fi >= 0) {
fi -= 2;
string fn = gets(filters, fi);
integer layer = geti(filters, fi + 1);
if(filter == "install")
fconf = setjs(fconf, [fn], jsarray([PROGRAM_NAME + " " + fn, layer]));
else if(getjs(fconf, [fn]) != JSON_INVALID)
fconf = setjs(fconf, [fn], JSON_DELETE);
}
setdbl("vox", ["filter"], fconf);
print(outs, user, "[" + PROGRAM_NAME + "] " + msg);
} else if(action == "activate" || action == "deactivate") {
integer new_status = (action == "activate");
integer fi = index(filters, filter);
string msg;
if(~fi) {
integer current_status = index(activated_filters, filter);
if(new_status && !~current_status) {
if(activated_filters == []) {
task_begin(session = llGenerateKey(), "");
}
activated_filters += filter;
} else if(~current_status && !new_status) {
activated_filters = delitem(activated_filters, current_status);
if(activated_filters == []) {
task_end(session);
}
if(filter == "translate" && translate_pipe != "") {
pipe_close(translate_pipe);
translate_pipe = "";
}
}
} else {
echo("[" + PROGRAM_NAME + "] No filter to " + action + ": " + filter);
}
} else {
// calling syntax: echo msg | filter <filter> <flags>
string flags = concat(delrange(argv, 0, 1), " ");
string message;
pipe_read(ins, message);
// echo(" -- filter in: " + message + " (from pipe " + (string)ins + ")");
if(filter == "translate") {
f_translate(message, flags, outs, ins, user, _resolved);
_resolved = 0;
// message = f_translate(message, flags);
} else {
if(filter == "censor") {
message = f_censor(message, flags);
} else if(filter == "replace") {
message = f_replace(message, flags);
} else if(filter == "nonverbal") {
message = f_nonverbal(message, flags);
} else if(filter == "rot13") {
message = f_rot13(message, flags);
} else if(filter == "glitch") {
message = f_glitch(message, flags);
} else if(filter == "corrupted") {
message = f_corrupted(message, flags);
} else if(filter == "stutter") {
message = f_stutter(message, flags);
} else if(filter == "serpentine") {
message = f_serpentine(message, flags);
} else if(filter == "lisp") {
message = f_lisp(message, flags);
} else if(filter == "mumble") {
message = f_mumble(message, flags);
} else if(filter == "caps") {
message = f_caps(message, flags);
} else if(filter == "bimbo") {
message = f_bimbo(message, flags);
} else if(filter == "superscript") {
message = f_superscript(message, flags);
} else if(filter == "slang") {
message = f_slang(message, flags);
} else if(filter == "slow") {
message = f_slow(message, flags);
} else {
echo("[" + PROGRAM_NAME + "] Filter '" + filter + "' unrecognized.");
#ifdef DEBUG
echo("argv 1 " + gets(argv, 1));
echo("message '" + m + "'");
echo("argc " + (string)count(argv));
#endif
}
// echo(" -- filter out: " + message + " (to pipe " + (string)outs + ")");
print(outs, user, message);
}
}
}
} else if(n == SIGNAL_NOTIFY) {
list argv = split(m, " ");
string action = gets(argv, 1);
// translation_queue: [0: message, 1: flags, 2: handle, 3: outs, 4: ins, 5: user, 6: rc];
if(action == "pipe" && ins == translate_pipe) {
string message = gets(translation_queue, 0);
string flags = gets(translation_queue, 1);
key handle = gets(translation_queue, 2);
#ifdef DEBUG
echo("got translation pipe; running " + TRANSLATION_URL);
#endif
http_get(TRANSLATION_URL, translate_pipe, handle);
} else if(action == "translation") {
string buffer;
pipe_read(ins, buffer);
integer hi = index(translation_queue, user);
if(~hi) {
integer mi = hi - 2;
// resolve_io(geti(translation_queue, mi + 6), gets(translation_queue, mi + 3), gets(translation_queue, mi + 4));
resolve_i(geti(translation_queue, mi + 6), gets(translation_queue, mi + 4));
buffer = getjs(buffer, ["responseData"]);
#ifdef DEBUG
echo("received translation server response data: " + buffer);
#endif
integer jj;
if(~(jj = strpos(buffer, "\\u"))) {
string remainder = substr(buffer, jj, -1);
integer o = strpos(remainder, "\"");
while(~jj) {
integer code = (integer)("0x" + substr(buffer, jj + 2, jj + 5));
buffer = llInsertString(delstring(buffer, jj, jj + 5), jj, llChar(code));
jj = strpos(buffer, "\\u");
}
}
string message = getjs(buffer, ["translatedText"]);
print(gets(translation_queue, mi + 3), gets(translation_queue, mi + 5), message);
translation_queue = delrange(translation_queue, mi, mi + 6);
}
#ifdef DEBUG
else
echo("got translation unexpectedly: " + buffer);
#endif
if(!count(translation_queue)) {
pipe_close(translate_pipe);
translate_pipe = "";
}
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
list active_filters = jskeys(getdbl("vox", ["active"]));
integer ai = count(active_filters);
while(ai--) {
string filter = gets(active_filters, ai);
integer fi = index(filters, filter);
if(~fi) {
if(activated_filters == []) {
task_begin(session = llGenerateKey(), "");
}
activated_filters += filter;
}
}
} 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 <ARES/program>

513
ARES/application/find.lsl Normal file
View File

@ -0,0 +1,513 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Find Application
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#define CLIENT_VERSION "0.0.3"
#define CLIENT_VERSION_TAGS "prealpha"
list argsplit(string m) {
// splits a string based on word boundaries, but groups terms inside double and single quotes
// tabs and newlines are converted to spaces
list results;
string TAB = llChar(9);
list atoms = llParseStringKeepNulls(m, [], [" ", "\\", "'", "\"", "\n", TAB]);
integer in_quotes; // 1 = single, 2 = double
integer ti = 0;
integer tmax = count(atoms);
string buffer;
while(ti < tmax) {
string t = gets(atoms, ti);
integer c = llOrd(t, 0);
if(c == 0x22) { // '"'
if(in_quotes == 0) {
in_quotes = 2;
} else if(in_quotes == 2) {
in_quotes = 0;
} else {
buffer += t;
}
} else if(c == 0x27) { // '\''
if(in_quotes == 0) {
in_quotes = 1;
} else if(in_quotes == 1) {
in_quotes = 0;
} else {
buffer += t;
}
} else if(c == 0x5c) { // '\\'
string t1 = gets(atoms, ti + 2);
if(t1 == "\"" || t1 == "'") {
buffer += t1;
ti += 2;
} else {
buffer += t;
}
} else if(c == 0x20 || c == 0x0a || c == 0x09) { // ' ', '\n', '\t'
if(in_quotes) {
buffer += " ";
} else {
results += buffer;
buffer = "";
}
} else {
buffer += t;
}
++ti;
}
if(tmax)
results += buffer;
return results;
}
// this is O(n^2), but the outer loop is usually sparse
// so, it might be faster than reversing the list and using a single normal search
/*
// no longer required; LL added llListFindListNext(haystack, needle, index) which can take -1 as index
integer nsListFindLast(list a, list b) {
if(a == [] || b == []) // either list is empty; bail
return NOWHERE;
integer i = llListFindList(a, b);
if(!~i) // doesn't exist; bail
return NOWHERE;
integer cb = count(b);
integer ca = count(a);
if(i == ca - cb) // first match is already at the end
return i;
if(!llListFindList(sublist(a, -cb, LAST), b)) // do we have a match at the end?
return ca - cb;
integer j = i;
list s;
while(~(i = llListFindList(s = llDeleteSubList(a, 0, j), b)))
j += i + 1;
return j;
} */
#define reverse_index(lis, item) llListFindListNext(lis, item, LAST)
integer first_is_skippable;
integer case_insensitive;
integer stop_after_first_match;
list states; // patterns matched at each offset
list exits; // next state of each state - stored as integer unless there are multiple hits
add_exit(integer where, integer to) {
if(!~where) {
#ifdef DEBUG
echo("can't patch -1 to " + (string)to);
#endif
return;
} else if(llGetListEntryType(exits, where) == TYPE_INTEGER) {
integer w = geti(exits, where);
if(~w) {
exits = alter(exits, [(string)to + "," + (string)w], where, where);
} else {
exits = alter(exits, [to], where, where);
}
} else {
exits = alter(exits, [(string)to + "," + gets(exits, where)], where, where);
}
}
construct_regex(string pattern) {
list paren_stack; // state indices corresponding to "(" symbols
list pipe_stack; // state indices corresponding to "|" symbols
first_is_skippable = 0; // set to TRUE if state 0 has ? or *
integer ci = 0;
integer cmax = strlen(pattern);
integer cc_open = NOWHERE; // character class open - position of '[' character
integer sc = 0; // number of states we've emitted so far
string buffer; // for character classes only
while(ci < cmax) {
string c = substr(pattern, ci, ci);
string d = substr(pattern, ci, ci + 1);
if(~cc_open) {
if(d == "\\-" || d == "\\\\") {
buffer += d;
ci += 1;
} else if(d == "\\^" /*|| d == "\\$" || d == "\\[" || d == "\\]" || d == "\\(" || d == "\\)" || d == "\\*" || d == "\\?" || d == "\\." || d == "\\\\"*/ || d == "\\]") {
buffer += substr(d, 1, 1);
ci += 1;
} else if(c == "]") {
string new_buffer = "\n" + substr(buffer, 0, 0);
integer bi = 1;
integer bmax = strlen(buffer);
integer found_hyphen = 0;
string last_e;
while(bi < bmax) {
string e = substr(buffer, bi, bi);
string f = substr(buffer, bi, bi + 1);
if(f == "\\$" || f == "\\[" || f == "\\(" || f == "\\)" || f == "\\*" || f == "\\?" || f == "\\." || f == "\\\\" || f == "\\-") {
e = substr(f, 1, 1);
++bi;
} else if(e == "-") {
found_hyphen = 2;
}
if(found_hyphen == 2) {
found_hyphen = 1;
} else if(found_hyphen == 1) {
integer ck = llOrd(last_e, 0) + 1;
integer end_char = llOrd(e, 0);
if(ck < end_char) {
while(ck < end_char)
new_buffer += llChar(ck++);
} else if(ck - 2 > end_char) {
ck -= 2;
while(ck > end_char)
new_buffer += llChar(ck--);
}
found_hyphen = 0;
new_buffer += e;
last_e = e;
} else {
new_buffer += e;
last_e = e;
}
++bi;
}
states += new_buffer;
exits += [NOWHERE];
add_exit(sc - 1, sc);
buffer = "";
++sc;
cc_open = NOWHERE;
} else {
buffer += c;
}
} else if(d == "[^" && !~cc_open) {
cc_open = ci;
buffer = "^";
ci += 1;
} else if(c == "[" && !~cc_open) {
cc_open = ci;
buffer = "[";
} else if(d == "\\^" || d == "\\$" || d == "\\[" || d == "\\]" || d == "\\(" || d == "\\)" || d == "\\*" || d == "\\?" || d == "\\." || d == "\\\\") {
states += d;
exits += [NOWHERE];
add_exit(sc - 1, sc);
++sc;
ci += 1;
} else if(c == "*") {
if(sc == 1)
first_is_skippable = TRUE;
else
add_exit(sc - 2, sc);
add_exit(sc - 1, sc - 1);
} else if(c == "+") {
add_exit(sc - 1, sc - 1);
} else if(c == "?") {
if(sc == 1)
first_is_skippable = TRUE;
else
add_exit(sc - 2, sc);
} else if(c == "(") {
paren_stack += [sc];
states += "";
exits += [NOWHERE];
add_exit(sc - 1, sc);
++sc;
} else if(d == ")?") {
// todo
ci += 1;
} else if(d == ")*") {
// todo
ci += 1;
} else if(c == ")") {
// todo
integer last_open = index(paren_stack, ")");
// find last "("
// find all subsequent "|"
// patch "(" to also point to "|"s
// patch symbols before "|" to also point to sc
// patch symbol before ")" to point to sc
} else if(c == "|") {
pipe_stack += [sc];
states += "";
exits += [NOWHERE];
add_exit(sc - 1, sc);
++sc;
} else if(d == "\\*" || d == "\\.") {
states += d;
//exits = alter(exits, [sc], sc - 1, sc - 1) + [NOWHERE];
add_exit(sc - 1, sc);
exits += [NOWHERE];
++sc;
} else {
states += c;
// exits = alter(exits, [sc], sc - 1, sc - 1) + [NOWHERE];
add_exit(sc - 1, sc);
exits += [NOWHERE];
++sc;
}
++ci;
}
// fix all jumps to nowhere
integer si = count(states);
while(si--) {
list next = split(gets(exits, si), ",");
integer affect = 0;
integer sin;
while(~(sin = index(next, (string)sc))) {
next = alter(next, ["-1"], sin, sin);
affect = 1;
}
if(affect) {
if(count(next) > 1)
exits = alter(exits, [concat(next, ",")], si, si);
else
exits = alter(exits, [(integer)gets(next, 0)], si, si);
#ifdef DEBUG
echo("fixed jump to nowhere in state " + (string)si);
#endif
}
}
list last_exits = split(gets(exits, LAST), ",");
integer nli = index(last_exits, (string)NOWHERE);
if(!~nli) {
exits = alter(exits, [concat(last_exits, ",") + ",-1"], LAST, LAST);
#ifdef DEBUG
echo("patched last exit");
#endif
}
}
integer match(string q, string t) {
if(q == ".")
return (t != "");
if(q == "\\^" || q == "\\$" || q == "\\[" || q == "\\]" || q == "\\(" || q == "\\)" || q == "\\*" || q == "\\?" || q == "\\." || q == "\\\\")
return (llOrd(t, 0) == llOrd(q, 1));
if(case_insensitive) {
q = llToLower(q);
t = llToLower(t);
}
if(llOrd(q, 0) == 0x0a) { // character ranges become '\n[' if positive and '\n^' if negative
if(llOrd(q, 1) == 0x5e) // '^'
return !~strpos(substr(q, 2, LAST), t);
else // implies '['
return TRUE && ~strpos(substr(q, 2, LAST), t);
}
return (q == t);
}
list find(string text) {
integer ti_start = NOWHERE;
integer ti;
integer tmax = strlen(text);
integer si;
string result;
list path;
integer smax = count(states);
integer last_exit = NOWHERE;
while(~si && ti < tmax) {
list next = split(gets(exits, si), ",");
string st = gets(states, si);
string tt = substr(text, ti, ti);
if(match(st, tt)) {
if(!count(path))
ti_start = ti;
result += tt;
#ifdef DEBUG
echo("matched \"" + (string)result + "\" ('" + tt + "' at char " + (string)ti + " vs. '" + st + "' from state " + (string)si + ")");
#endif
path += si;
si = geti(next, 0);
// todo: for backreferences this must be changed to the appropriate length
if(st != "")
++ti; // null symbols are legal but don't advance the character counter
if(!~si) {
return [ti_start, result];
}
} else if(si != last_exit) {
if(!count(path)) {
ti_start = ti = ti + 1;
si = 0;
next = split(gets(exits, 0), ",");
#ifdef DEBUG
echo("could not start matching with '" + tt + "' at char " + (string)ti);
#endif
} else {
integer prev = geti(path, LAST);
next = split(gets(exits, prev), ",");
#ifdef DEBUG
echo("Available 'next' options: " + concat(next, ","));
#endif
integer sj = index(next, (string)si);
next = delrange(next, 0, sj);
#ifdef DEBUG
echo("did not match ('" + tt + "' at char " + (string)ti + " vs. '" + st + "' from state " + (string)si + "); sj was " + (string)sj);
#endif
si = geti(next, 0);
}
} else {
integer dpi = count(path);
/* path = delitem(path, LAST);
result = delstring(result, LAST, LAST); */
integer dsi = si;
while(dpi--) {
integer dprev = geti(path, dpi);
list dnext = split(gets(exits, dprev), ",");
integer dlast_exit = geti(dnext, LAST);
if(dlast_exit != dsi) {
integer dsj = index(dnext, (string)dsi);
si = geti(dnext, dsj + 1);
#ifdef DEBUG
echo("backtracked to state " + (string)dprev + "; trying " + (string)si + "; dsj was " + (string)dsj);
#endif
jump got_it; // backtrack was successful
} else {
dsi = dprev;
string dst = gets(states, geti(path, LAST));
path = delitem(path, LAST);
// todo: for backreferences this must be changed to the appropriate length
if(dst != "")
result = delstring(result, LAST, LAST);
#ifdef DEBUG
echo("backtracked past " + (string)dprev + " = '" + gets(states, dprev) + "'");
#endif
}
}
// match failed - step forward
if(~ti_start) {
ti_start = ti = ti_start + 1;
} else {
ti += 1;
}
si = 0;
@got_it;
}
last_exit = geti(next, LAST);
}
return [NOWHERE, ""];
}
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = argsplit(m);
integer argc = count(argv);
string msg = "";
case_insensitive = 0;
stop_after_first_match = 0;
list options = ["-i", "-1"];
integer opt;
while(~(opt = index(options, gets(argv, 1)))) {
argv = delitem(argv, 1);
--argc;
if(opt == 0) {
case_insensitive = 1;
} else if(opt == 1) {
stop_after_first_match = 1;
}
}
if(argc == 1) {
msg = "Syntax: " + PROGRAM_NAME + " [-i -1] <pattern> [<filename> ...]";
} else {
states = exits = [];
string pattern = gets(argv, 1);
argv = delitem(argv, 1);
llResetTime();
construct_regex(pattern);
// echo("Compiled regular expression in " + (string)llGetTime() + " sec"); // usually returns zero!
echo("States: " + concat(states, "•"));
echo("Exits: " + concat(exits, "•"));
string query_string;
if(ins == NULL_KEY) {
pipe_read(ins, query_string);
}
if(query_string == "") {
query_string = concat(delitem(argv, 0), " ");
}
msg = concat(find(query_string), ": ");
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
} 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 <ARES/program>

View File

@ -0,0 +1,36 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* fortune Utility Headers
*
* 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.
*
* =========================================================================
*
*/
// This is a heavy-handed way of saying that the NS fortune server is not to be called outside of this ARES application, despite the rest of the code for the fortune utility being open-source.
#define URL "http://my.nanite-systems.com/fortune.star?params="

View File

@ -0,0 +1,122 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* fortune Utility
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#define CLIENT_VERSION "0.2.5"
#define CLIENT_VERSION_TAGS "alpha"
// LICENSING CAVEAT: Do not use the myNanite fortune server URL outside of ARES packages. It has been placed in a separate file for clarity. For your own applications using different websites, you can just replace this line with: #define URL "<whatever>"
#include "ARES/application/fortune.h.lsl"
list queue; // = [command, outs, ins, user, r, transport_pipe];
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
string action = gets(argv, 1);
if(action == "help" || action == "-?" || action == "--help" || action == "/?" || action == "-h" || action == "/h") {
msg = "Syntax: " + PROGRAM_NAME + " <parameters>\n\nDisplays fortunes from the myNanite GNU fortune server.\n\nSee https://linux.die.net/man/6/fortune for a list of available options, and 'help fortune' for the list of available fortune files.";
} else {
string command = "proc fetch " + URL + llEscapeURL(concat(delitem(argv, 0), " "));
key handle = llGenerateKey();
pipe_open(["p:" + (string)handle + " notify " + PROGRAM_NAME + " data"]);
queue += [command, outs, ins, user, _resolved, handle];
_resolved = 0;
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_NOTIFY) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
string action = gets(argv, 1);
if(action == "data") {
string buffer;
pipe_read(ins, buffer);
integer qi = index(queue, ins);
if(~qi) {
// some fortunes contain terminal control codes that SL just can't replicate:
list subs = [
llChar(0x09), " ", // tab
llChar(0x7f), "<-", // delete
llChar(0x08), "<-" // backspace
];
integer subi = count(subs);
while(subi > 0) {
subi -= 2;
buffer = replace(buffer, gets(subs, subi), gets(subs, subi + 1));
}
print(gets(queue, qi - 4), gets(queue, qi - 2), buffer);
// resolve_io((integer)gets(queue, qi - 1), gets(queue, qi - 4), gets(queue, qi - 3));
resolve_i((integer)gets(queue, qi - 1), gets(queue, qi - 3));
queue = delrange(queue, qi - 5, qi);
}
pipe_close([ins]);
} else if(action == "pipe") {
integer qi = index(queue, ins);
if(~qi) {
key user = gets(queue, qi - 2);
string command = gets(queue, qi - 5);
notify_program(command, ins, NULL_KEY, user);
} else {
pipe_close([ins]);
}
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
} 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 <ARES/program>

View File

@ -0,0 +1,309 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Help Utility
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#define CLIENT_VERSION "1.1.1"
#define CLIENT_VERSION_TAGS "release"
// files being indexed:
list files_to_index; // [filenames]
string cifile;
string cifile_short;
integer cii; // current line
integer cimax; // line limit
key ciq;
key index_user;
key index_outs;
do_index() {
if(count(files_to_index)) {
cifile = gets(files_to_index, 0);
cifile_short = delstring(cifile, -5, -1);
files_to_index = delitem(files_to_index, 0);
cii = 0;
cimax = NOWHERE;
ciq = llGenerateKey();
file_open(ciq, cifile);
} else {
task_end(index_outs);
}
}
// queue of requested pages to show:
list pages_to_show; // [page, outs, user]
string current_file;
key current_outs;
key current_user;
key file_pipe;
integer current_offset;
integer file_size;
integer storage_format;
do_read() {
if(count(pages_to_show)) {
string entry = gets(pages_to_show, 0);
list help_files = llList2ListStrided(js2list(llLinksetDataRead("help")), 0, LAST, 2);
integer hi = 0;
integer himax = count(help_files);
string clu;
while(hi < himax) {
current_file = gets(help_files, hi);
#ifdef DEBUG
echo("[help] Checking file " + current_file);
#endif
clu = getdbl("help", [current_file, entry]);
if(clu != JSON_INVALID)
jump got_it;
++hi;
}
@got_it;
current_outs = getk(pages_to_show, 1);
current_user = getk(pages_to_show, 2);
storage_format = (integer)getdbl("help", [current_file, "__format"]); // 1: line, 0: char
pages_to_show = delrange(pages_to_show, 0, 2);
if(clu == JSON_INVALID) {
print(current_outs, current_user, "No help entry: " + entry + ". Check http://support.nanite-systems.com/search or your spelling.");
task_end(current_outs);
do_read(); // oh no, recursion
} else {
file_size = NOWHERE;
current_offset = (integer)clu;
file_open(file_pipe = llGenerateKey(), current_file += ".info");
}
}
}
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_NOTIFY) {
if(m == "help file") {
string buffer;
if(ins == ciq || ins == file_pipe) {
pipe_read(ins, buffer);
if(ins == ciq) { // indexing files
if(cimax == NOWHERE) {
cimax = (integer)buffer;
if(cimax > 0) {
storage_format = (llOrd(buffer, strpos(buffer, " ") + 1) == 0x6C);
// 1: line, 0: other
setdbl("help", [cifile_short, "__format"], (string)storage_format);
// start reading from beginning of file
file_read(ins, cifile, cii = 0);
#ifdef DEBUG
echo("[help] Reading file: " + cifile);
#endif
} else {
file_close(ins);
print(index_outs, index_user,
"Help file missing: " + cifile + ". Run 'help reindex' to clean up.");
do_index();
}
} else {
integer new_cii = cii + FILE_PAGE_LENGTH;
list lines = splitnulls(buffer, "\n");
integer lmax = count(lines);
integer li = 0;
integer buffer_offset = 0;
while(li < lmax) {
string L = gets(lines, li);
integer line_len = strlen(L);
if(substr(L, 0, 5) == "TOPIC ") {
integer entry_offset;
if(storage_format == 1) {
entry_offset = li * FILE_LINE_WIDTH + cii;
} else {
entry_offset = buffer_offset + cii + line_len;
if(li == lmax - 1) {
// do not count this line as it may be fragmentary
new_cii = buffer_offset + cii;
jump ignore_topic;
}
}
setdbl(
"help",
[cifile_short, delstring(L, 0, 5)],
(string)(entry_offset)
);
#ifdef DEBUG
echo("[help] Found entry: " + L + " at " + (string)entry_offset);
#endif
}
@ignore_topic;
buffer_offset += line_len + 1;
++li;
}
if(new_cii > cimax) {
file_close(ciq);
do_index();
} else {
file_read(ciq, cifile, cii += FILE_PAGE_LENGTH);
}
}
} else if(ins == file_pipe) { // reading page
if(file_size == NOWHERE) {
file_size = (integer)buffer;
if(file_size > 0) {
// start reading from beginning of entry
file_read(ins, current_file, current_offset);
} else {
file_close(ins);
print(current_outs, current_user,
"Help file missing: " + current_file + ". Run 'help reindex' to clean up.");
task_end(current_outs);
do_read();
}
} else {
integer ti = strpos(buffer, "\nTOPIC ");
if(~ti) {
#ifdef DEBUG
echo("[help] stopping read; found TOPIC at " + (string)ti);
#endif
buffer = delstring(buffer, ti, LAST);
file_close(ins);
task_end(current_outs);
do_read();
} else if(substr(buffer, 0, 5) == "TOPIC ") {
#ifdef DEBUG
echo("[help] stopping read; found TOPIC at buffer start");
#endif
file_close(ins);
task_end(current_outs);
do_read();
return;
// TODO: ugly repetitive code above
} else if(current_offset + FILE_PAGE_LENGTH > file_size) {
#ifdef DEBUG
echo("[help] end of file");
#endif
file_close(ins);
task_end(current_outs);
do_read();
} else {
#ifdef DEBUG
echo("[help] pulling next page of " + current_file);
#endif
file_read(ins, current_file, current_offset += FILE_PAGE_LENGTH);
}
print(current_outs, current_user, buffer);
}
}
}
}
} else if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
if(argc == 1) {
argv += "main";
}
string action = gets(argv, 1);
if(action == "index") {
string f = gets(argv, 2);
if(substr(f, -5, -1) != ".info")
f += ".info";
print(
index_outs = outs,
index_user = user,
"Indexing info file: " + f
);
task_begin(outs, "index");
files_to_index += f;
do_index();
} else if(action == "reindex") {
print(
index_outs = outs,
index_user = user,
"Re-indexing all known info files. This may take a while."
);
llLinksetDataDelete("help");
task_begin(outs, "index");
/*integer c = llGetInventoryNumber(INVENTORY_NOTECARD);
while(c--) {
string f = llGetInventoryName(INVENTORY_NOTECARD, c);
if(substr(f, -5, -1) == ".info") {
files_to_index += f;
}
}*/
files_to_index = js2list(llLinksetDataRead("fs:info"));
do_index();
} else if(action == "forget") {
string f = gets(argv, 2);
if(substr(f, -5, -1) == ".info")
f = delstring(f, -5, -1);
if(getdbl("help", [f]) == JSON_INVALID) {
print(outs, user, "Info file not present: " + f + ".info");
} else {
deletedbl("help", [f]);
print(outs, user, "Removed info file " + f + ".info from help database.");
}
} else {
task_begin(outs, "read");
string p = concat(delitem(argv, 0), " ");
pages_to_show += [p, outs, user];
do_read();
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
} 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 <ARES/program>

700
ARES/application/id.lsl Normal file
View File

@ -0,0 +1,700 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Identity System Module
*
* 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 <ARES/a>
#include <ARES/api/auth.h.lsl>
#define CLIENT_VERSION ARES_VERSION
#define CLIENT_VERSION_TAGS ARES_VERSION_TAGS
string authority;
string unit_name;
string unit_serial;
string model;
string gender;
list colors = [ONES, <1, 0, 0>, <0, 1, 0>, <1, 1, 0>];
string format_color(vector c) {
string r = hex((integer)(c.x * 255));
string g = hex((integer)(c.y * 255));
string b = hex((integer)(c.z * 255));
if(strlen(r) == 1) r = "0" + r;
if(strlen(g) == 1) g = "0" + g;
if(strlen(b) == 1) b = "0" + b;
string hexcolor = "#" + r + g + b;
list preset_pair = js2list(llLinksetDataRead("swatch"));
integer hci = index(preset_pair, hexcolor);
if(~hci) {
return hexcolor + " (" + gets(preset_pair, hci - 1) + ")";
} else {
return hexcolor;
}
}
vector parse_color(list argv) {
// echo("parsing color " + concat(argv, " "));
integer argc = count(argv);
vector c;
if(argc == 1) {
string input = gets(argv, 0);
string cand = getdbl("swatch", [input]);
if(cand != JSON_INVALID)
input = cand;
if(substr(input, 0, 0) == "#" && strlen(input) == 7) {
c = <(integer)("0x" + substr(input, 1, 2)),
(integer)("0x" + substr(input, 3, 4)),
(integer)("0x" + substr(input, 5, 6))> / 255.0;
} else {
c = <-1, -1, -1>;
}
} else if(count(argv) == 3) {
c = <(float)gets(argv, 0),
(float)gets(argv, 1),
(float)gets(argv, 2)>;
if(c.x > 1 || c.y > 1 || c.z > 1)
c /= 255.0;
}
return c;
}
make_callsign(string prefix) {
string callsign;
integer lc = llOrd(prefix, LAST);
if(prefix == JSON_INVALID)
callsign = unit_name;
else if(lc == 0x2f || lc == 0x2e || lc == 0x2d || lc == 0x5f || prefix == "") // / . - _
callsign = prefix + unit_name;
else
callsign = prefix + " " + unit_name;
if(strlen_byte(callsign) != strlen(callsign))
echo("Warning: new callsign is not a valid object name (contains Unicode)");
setdbl("id", ["callsign"], callsign);
}
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg;
if(argc == 1) {
string vendor = getdbl("id", ["vendor"]);
if(vendor == JSON_INVALID || vendor == "")
vendor = "Nanite Systems Corporation";
model = getdbl("id", ["model"]);
if(model == JSON_INVALID || model == "")
model = "(unknown)";
string s_kernel = llLinksetDataRead("kernel");
// about:
msg = "About ARES\nARES (Psyche/CX) " + model + " System\n";
string kernel_version = getjs(s_kernel, ["version"]);
string kernel_version_tags = getjs(s_kernel, ["version-tags"]);
string package_version = getdbl("pkg", ["version", "ARES"]);
if(package_version != JSON_INVALID)
msg += "\nOS version: " + package_version;
if(kernel_version != package_version) {
if(kernel_version_tags != JSON_INVALID && kernel_version_tags != "")
kernel_version = kernel_version + " " + kernel_version_tags;
msg += "\nKernel version: " + kernel_version;
}
if(package_version != CLIENT_VERSION)
msg +=
"\nid version: " + CLIENT_VERSION
#ifdef CLIENT_VERSION_TAGS
+ " " + CLIENT_VERSION_TAGS + " (_id compiled " + __DATE__ + ")"
#else
+ "\nid compiled: " + __DATE__
#endif
;
msg += "\n\nUnit name: " + unit_name + "\nVendor: " + vendor;
if(authority != "(none)")
msg += "\nSupervising authority: " + authority;
integer ci = 4;
while(ci--)
colors = alter(colors, [str2vec(getdbl("id", ["color", ci]))], ci, ci);
msg +=
"\nSerial number: " + unit_serial +
"\nModel: " + model +
"\nColors:" +
"\n " + format_color(getv(colors, 0)) +
"\n " + format_color(getv(colors, 1)) +
"\n " + format_color(getv(colors, 2)) +
"\n " + format_color(getv(colors, 3)) +
"\n\nType 'security' for user information or 'device' for devices information."
;
} else {
integer check = sec_check(user, "identity", outs, m, m);
if(check == DENIED) {
print(outs, user, "You are not authorized to modify this unit's identity settings.");
return;
} else if(check != ALLOWED) {
return;
}
integer trigger_color_update = 0;
string action = gets(argv, 1);
if(action == "regen" || action == "regenerate") {
integer mantissa = (integer)("0x" + substr(llGetOwner(), 29, 35)) % 1000000;
string serial = getdbl("id", ["model"]);
if(~strpos(serial, "oXq")) {
#define letter_map "0123456789ABabcdefghijklmnopqrstuvwxyz."
integer part = 3;
string name;
while(part--) {
integer tangle;
if(part == 0) {
tangle = (integer)("0x" + substr(avatar, 2, 3))
^ (integer)("0x" + substr(avatar, 31, 33));
} else if(part == 2) {
tangle = (integer)("0x" + substr(avatar, 5, 7))
^ (integer)("0x" + substr(avatar, 25, 30));
} else if(part == 1) {
tangle = (integer)("0x" + substr(avatar, 9, 10) + substr(avatar, 14, 15))
^ (integer)("0x" + substr(avatar, 27, 31));
}
if(tangle < 0)
tangle = -tangle;
string frag = "";
while(tangle > 0) {
integer a;
if(part % 2 == 0) {
if(strlen(frag) % 2) {
a = tangle % 5;
frag += substr("aeiou", a, a);
tangle /= 6;
} else {
a = tangle % 21;
frag += substr("bcdfghklmnpqrstvwxyzj", a, a);
tangle /= 23;
}
} else {
a = tangle % 10;
tangle /= 16;
frag += substr(letter_map, a, a);
}
}
name += frag;
if(part)
name += ".";
}
serial = name;
} else {
string mantissa_s = substr((string)mantissa, 0, 1) + "-" + substr((string)mantissa, 2, 5);
if(~strpos(serial, "-")) {
serial += "-" + mantissa_s;
} else {
serial += " " + mantissa_s;
}
}
setdbl("id", ["serial"], unit_serial = serial);
make_callsign(getdbl("id", ["prefix"]));
} else if(action == "font") {
list font_names = jskeys(llLinksetDataRead("font"));
if(argc == 2) {
msg = "Current variatype font: " + getdbl("interface", ["font"]) +
"\nAvailable fonts: " + concat(font_names, ", ");
} else {
string font_name = gets(argv, 2);
if(contains(font_names, font_name)) {
setdbl("interface", ["font"], font_name);
msg = "Set variatype font to " + font_name + ".";
e_call(C_VARIATYPE, E_SIGNAL_CALL, (string)outs + " " + (string)user + " variatype reconfigure");
} else {
msg = "Unknown font '" + font_name + "'; available fonts: " + concat(font_names, ", ");
}
}
} else if(action == "interface") {
string setting = gets(argv, 2);
list keyname = split(setting, ".");
string current_value = getdbl("interface", keyname);
if(argc > 2) {
string new_value = gets(argv, 3);
if(new_value == "toggle") {
if(current_value == "0")
new_value = "1";
else
new_value = "0";
}
if(current_value != new_value) {
current_value = new_value;
setdbl("interface", keyname, current_value);
if(setting == "font") {
e_call(C_VARIATYPE, E_SIGNAL_CALL, (string)outs + " " + (string)user + " variatype reconfigure");
} else {
e_call(C_INTERFACE, E_SIGNAL_CALL, (string)outs + " " + (string)user + " interface reconfigure");
}
}
}
msg = "Interface setting " + setting + ": " + current_value;
} else if(action == "volume") {
float vol = (float)getdbl("interface", ["sound", "volume"]);
if(argc == 3) {
string new_vol = gets(argv, 2);
if(new_vol == "off" || new_vol == "mute") {
vol = 0;
} else if((string)((integer)new_vol) == new_vol) {
vol = 0.01 * (float)new_vol;
} else if(new_vol == "up") {
vol += 0.1;
} else if(new_vol == "down") {
vol -= 0.1;
} else if(new_vol == "cycle") {
vol += 0.1;
if(vol > 1.0)
vol = 0;
}
}
if(vol < 0.0)
vol = 0.0;
else if(vol > 1.0)
vol = 1.0;
string vf = (string)((integer)(100 * vol)) + "%";
setdbl("interface", ["sound", "volume"], (string)vol);
setdbl("interface", ["sound", "vf"], vf);
msg = "Interface volume: " + vf;
interface_sound("test");
} else if(action == "menu") {
list schemes = jskeys(getdbl("id", ["scheme"]));
list scheme_buttons = [];
integer sci = count(schemes);
while(sci--) {
string scheme = gets(schemes, sci);
string sb = jsarray([
scheme,
0,
"id color load " + scheme
]);
scheme_buttons = sb + scheme_buttons;
}
setdbl("m:color", ["d"], jsarray(scheme_buttons));
} else if(action == "name") {
if(argc == 2) {
msg = "Unit name: " + unit_name;
} else {
unit_name = concat(delrange(argv, 0, 1), " ");
if(unit_name == "none") {
deletedbl("id", ["name"]);
msg = "Name cleared.";
unit_name = "(none)";
setdbl("id", ["callsign"], unit_serial);
} else {
setdbl("id", ["name"], unit_name);
msg = "Name set.";
string prefix = getdbl("id", ["prefix"]);
make_callsign(prefix);
}
}
} else if(action == "authority") {
if(argc == 2) {
msg = "Supervising authority: " + authority;
} else {
authority = concat(delrange(argv, 0, 1), " ");
if(authority == "none") {
deletedbl("id", ["authority"]);
msg = "Authority cleared.";
authority = "(none)";
} else {
setdbl("id", ["authority"], authority);
msg = "Authority set.";
}
}
} else if(action == "chime") {
// id chime load <scheme>
// id chime save <scheme>
// id chime delete <scheme>
// id chime boot none|<uuid>
// id chime halt none|<uuid>
string verb = gets(argv, 2);
string value = gets(argv, 3);
if(verb == "load" && value != "") {
string scheme = getdbl("chime", [value]);
if(scheme != JSON_INVALID) {
string bc;
setdbl("id", ["chime", "boot"], bc = getjs(scheme, [0]));
setdbl("id", ["chime", "halt"], getjs(scheme, [1]));
msg = "Chime scheme '" + value + "' applied.";
play_sound(bc);
setdbl("m:chime", ["f"], value);
} else {
msg = "There is no chime scheme named '" + value + "'";
}
} else if(verb == "save" && value != "") {
string bc = getdbl("id", ["chime", "boot"]);
string hc = getdbl("id", ["chime", "halt"]);
setdbl("chime", [value], jsarray([bc, hc]));
} else if(verb == "delete" && value != "") {
string scheme = getdbl("chime", [value]);
if(scheme != JSON_INVALID) {
deletedbl("chime", [value]);
msg = "Deleted chime scheme '" + value + "'";
} else {
msg = "There is no chime scheme named '" + value + "'";
}
} else if((verb == "boot" || verb == "halt") && value != "") {
string old = getdbl("id", ["chime", verb]);
if(value == "clear") {
if(old != JSON_INVALID)
value = JSON_DELETE;
else
jump nah;
} else if(old == value)
jump nah;
setdbl("id", ["chime", verb], value);
msg = "Chime set.";
jump yah;
@nah;
msg = "No change.";
@yah;
} else {
string bc = getdbl("id", ["chime", "boot"]); if(bc == JSON_INVALID) bc = "(none)";
string hc = getdbl("id", ["chime", "halt"]); if(hc == JSON_INVALID) hc = "(none)";
string schemes = concat(jskeys(getdbl("chime", [])), ", ");
if(schemes == "") schemes = "(none; please reload default database)";
msg = "Current chime settings\n\nBoot: " + bc +
"\nHalt: " + hc +
"\nAvailable schemes: " + schemes;
}
} else if(action == "color") {
integer ci = 4;
while(ci--)
colors = alter(colors, [str2vec(getdbl("id", ["color", ci]))], ci, ci);
if(argc == 2) {
string schemes = getdbl("id", ["scheme"]);
if(schemes == JSON_INVALID)
schemes = "(none)";
else
schemes = concat(jskeys(schemes), ", ");
msg = "Current colors:\n " + format_color(getv(colors, 0)) +
"\n " + format_color(getv(colors, 1)) +
"\n " + format_color(getv(colors, 2)) +
"\n " + format_color(getv(colors, 3)) +
"\nAvailable swatches:" +
"\n " + concat(jskeys(llLinksetDataRead("swatch")), ", ") +
"\nAvailable schemes:" +
"\n " + schemes;
} else {
integer affect = -1; // affect all
string keyword = gets(argv, 2);
argv = delrange(argv, 0, 2);
list slot_names = ["a", "b", "c", "d", "primary", "secondary", "tertiary", "quartenary", "p", "s", "t", "q"];
integer si = index(slot_names, keyword);
if(~si) {
affect = si & 0x03; // % 4
keyword = gets(argv, 0);
} else if(keyword == "all" || keyword == "*") {
si = 0;
} else if(keyword == "swatch") {
affect = 5;
keyword = gets(argv, 0);
}
integer action; // 1: set, 2: save scheme, 3: load scheme, 4: delete scheme
list actions = [0, "set", "save", "load", "delete"];
integer ai = index(actions, keyword);
if(~ai) {
action = ai;
if(~si)
argv = delitem(argv, 0);
} else {
action = 1; // mode: set
}
vector c;
if(argc = count(argv)) { // update argc
if(affect == 5) {
if(keyword == "delete") {
string name = gets(argv, 1);
if(getdbl("swatch", [name]) != JSON_INVALID) {
deletedbl("swatch", [name]);
msg = "Deleted swatch " + name;
} else {
msg = "No swatch: " + name;
}
} else {
c = parse_color(delitem(argv, 0));
if(c == <-1, -1, -1>) {
msg = "Unknown color code: " + concat(argv, " ") + ". See 'help id' for more information.";
jump error;
}
string hex = substr(format_color(c), 0, 6);
setdbl("swatch", [keyword], hex);
}
} else if(action == 1) { // set
c = parse_color(argv);
if(c == <-1, -1, -1>) {
msg = "Unknown color code: " + concat(argv, " ") + ". See 'help id' for more information.";
jump error;
}
if(~affect) {
colors = alter(colors, [c], affect, affect);
} else {
colors = [c, c, c, c];
}
vector ca = getv(colors, 0);
vector cb = getv(colors, 1);
vector cc = getv(colors, 2);
vector cd = getv(colors, 3);
setdbl("id", ["color"], jsarray([
vec2str(ca),
vec2str(cb),
vec2str(cc),
vec2str(cd)
]));
trigger_color_update = 1;
msg = "Color updated.";
} else if(action == 2) { // save <scheme_name>
string scheme_name = concat(argv, " ");
vector ca = getv(colors, 0);
vector cb = getv(colors, 1);
vector cc = getv(colors, 2);
vector cd = getv(colors, 3);
setdbl("id", ["scheme", scheme_name], jsarray([
vec2str(ca),
vec2str(cb),
vec2str(cc),
vec2str(cd)
]));
msg = "Color scheme '" + scheme_name + "' saved.";
} else if(action == 3) { // load <scheme_name>
string scheme_name = concat(argv, " ");
string colorlist = getdbl("id", ["scheme", scheme_name]);
if(colorlist != JSON_INVALID) {
setdbl("id", ["color"], colorlist);
setdbl("m:color", ["f"], scheme_name);
integer ci = 4;
while(ci--) {
colors = alter(colors, [str2vec(getjs(colorlist, [ci]))], ci, ci);
}
trigger_color_update = 1;
msg = "Color scheme '" + scheme_name + "' loaded.";
} else {
msg = "No color scheme: '" + scheme_name + "'";
}
} else if(action == 4) { // delete <scheme_name>
string scheme_name = concat(argv, " ");
string colorlist = getdbl("id", ["scheme", scheme_name]);
if(colorlist != JSON_INVALID) {
setdbl("id", ["scheme", scheme_name], JSON_DELETE);
msg = "Color scheme '" + scheme_name + "' deleted.";
} else {
msg = "No color scheme: '" + scheme_name + "'";
}
}
@error;
} else {
msg = "Missing arguments: id color " + keyword;
}
}
} else if(action == "gender") {
string s_gender = getdbl("id", ["gender"]);
if(argc == 2) {
msg = "Gender status:\n\nMental gender (self-reported pronouns): " + getjs(s_gender, ["mental"]) +
"\nPhysical gender (pronouns used in descriptions): " + getjs(s_gender, ["physical"]) +
"\nVoice gender (chat tone/speech markers): " + getjs(s_gender, ["voice"]) +
"\n\nSee 'help gender' for instructions on how to configure.";
} else {
string tfield = gets(argv, 2);
string gfield = gets(argv, 3);
integer gender = NOWHERE;
integer topic = NOWHERE;
list pronoun_options = [
"neuter,they,them,their,theirs,themself",
"female,she,her,her,hers,herself",
"male,he,him,his,his,himself",
"inanimate,it,it,its,its,itself"
];
topic = llListFindList(["all", "mental", "physical", "voice"], [tfield]);
if(!~topic)
topic = strpos("*mpv", tfield);
gender = llListFindList(["neuter", "female", "male", "inanimate", "custom"], [gfield]);
if(!~gender)
gender = strpos("nfmic", gfield);
if(!~topic || !~gender) {
msg = "Unknown gender command: " + tfield + " " + gfield + ". Please check the manual and try again.";
} else {
if(topic != 3) { // anything but voice
string pronoun_source;
if(gender == 4) {
// custom
pronoun_source = gets(argv, 4);
} else {
pronoun_source = gets(pronoun_options, gender);
}
list pronouns = split(pronoun_source, ",");
string pronoun_obj = jsobject([
"adj", gets(pronouns, 0),
"sub", gets(pronouns, 1),
"obj", gets(pronouns, 2),
"gen", gets(pronouns, 3),
"pos", gets(pronouns, 4),
"refl", gets(pronouns, 5)
]);
if(topic == 0) {
if(gender > 2)
gender = 0;
setdbl("id", ["gender", "voice"], substr("nfm", gender, gender));
}
if(topic == 0 || topic == 1) {
setdbl("env", ["pm"], pronoun_obj);
setdbl("id", ["gender", "mental"], jsarray(pronouns));
}
if(topic == 0 || topic == 2) {
setdbl("env", ["pp"], pronoun_obj);
setdbl("id", ["gender", "physical"], jsarray(pronouns));
}
msg = "Gender set.";
} else { // voice
if(gender > 2)
gender = 0;
setdbl("id", ["gender", "voice"], substr("nfm", gender, gender));
msg = "Voice gender set.";
}
}
}
} else {
msg = PROGRAM_NAME + ": Unrecognized action '" + action + "'; see 'help id' for a list of valid actions";
}
if(trigger_color_update) {
e_call(C_INTERFACE, E_SIGNAL_CALL, (string)outs + " " + (string)user + " interface color");
e_call(C_VARIATYPE, E_SIGNAL_CALL, (string)outs + " " + (string)user + " variatype color");
e_call(C_HARDWARE, E_SIGNAL_CALL, (string)NULL_KEY + " " + (string)NULL_KEY + " hardware color");
// no keys = tell device to send update to all devices
e_call(C_REPAIR, E_SIGNAL_CALL, (string)outs + " " + (string)user + " repair color");
e_call(C_STATUS, E_SIGNAL_CALL, (string)outs + " " + (string)user + " status color");
}
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_INIT) {
string id_section = llLinksetDataRead("id");
unit_name = getjs(id_section, ["name"]);
unit_serial = getjs(id_section, ["serial"]);
if(unit_serial == JSON_INVALID || unit_serial == "")
unit_serial = "NS-100-00-0001";
if(unit_name == JSON_INVALID || unit_name == "")
unit_name = unit_serial;
if(strlen_byte(unit_name) != strlen(unit_name))
echo("Warning: unit name is invalid (contains Unicode)");
authority = getjs(id_section, ["authority"]);
if(authority == JSON_INVALID || authority == "")
authority = "(none)";
model = getjs(id_section, ["model"]);
if(model == JSON_INVALID || model == "")
model = "(unknown)";
gender = getjs(id_section, ["gender"]);
integer ci = 4;
while(ci--)
colors = alter(colors, [str2vec(getjs(id_section, ["color", ci]))], ci, ci);
/* // obsolete/niche:
} else if(n == SIGNAL_TERMINATE) {
echo("[" + PROGRAM_NAME + "] dying as ordered");
exit(); */
} else if(n == SIGNAL_UNKNOWN_SCRIPT) {
echo("[" + PROGRAM_NAME + "] failed to run '" + m + "' (kernel could not find the program specified)");
} else {
echo("[" + PROGRAM_NAME + "] unhandled: signal " + (string)n + ": " + m);
}
}
#include <ARES/program>

249
ARES/application/land.lsl Normal file
View File

@ -0,0 +1,249 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Land Management Utility
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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.
*
* =========================================================================
*
*/
/*
Wrapped LSL functions:
https://wiki.secondlife.com/wiki/LlGetEnv
https://wiki.secondlife.com/wiki/LlGetEnvironment - too complex for now
https://wiki.secondlife.com/wiki/LlParcelMediaCommandList
https://wiki.secondlife.com/wiki/LlParcelMediaQuery
https://wiki.secondlife.com/wiki/LlRequestSimulatorData
https://wiki.secondlife.com/wiki/LlSetEnvironment - too complex for now
https://wiki.secondlife.com/wiki/LlGetSimStats
https://wiki.secondlife.com/wiki/LlGetExperienceDetails
https://wiki.secondlife.com/wiki/LlGetParcelDetails
https://wiki.secondlife.com/wiki/LlGetParcelFlags
https://wiki.secondlife.com/wiki/LlGetParcelMaxPrims
https://wiki.secondlife.com/wiki/LlGetParcelPrimCount
https://wiki.secondlife.com/wiki/LlGetParcelPrimOwners
x https://wiki.secondlife.com/wiki/LlManageEstateAccess
x https://wiki.secondlife.com/wiki/LlGetRegionFlags
x https://wiki.secondlife.com/wiki/LlGetSimulatorHostname
x https://wiki.secondlife.com/wiki/LlGetDayLength
x https://wiki.secondlife.com/wiki/LlGetDayOffset
x https://wiki.secondlife.com/wiki/LlAddToLandBanList
x https://wiki.secondlife.com/wiki/LlAddToLandPassList
x https://wiki.secondlife.com/wiki/LlEjectFromLand
x https://wiki.secondlife.com/wiki/LlGetParcelMusicURL
x https://wiki.secondlife.com/wiki/LlGetRegionAgentCount
x https://wiki.secondlife.com/wiki/LlGetRegionAgentCount
x https://wiki.secondlife.com/wiki/LlGetRegionCorner
x https://wiki.secondlife.com/wiki/LlGetRegionDayLength
x https://wiki.secondlife.com/wiki/LlGetRegionDayOffset
x https://wiki.secondlife.com/wiki/LlGetRegionFPS
x https://wiki.secondlife.com/wiki/LlGetRegionName
x https://wiki.secondlife.com/wiki/LlGround
x https://wiki.secondlife.com/wiki/LlGroundSlope
x https://wiki.secondlife.com/wiki/LlMapDestination
x https://wiki.secondlife.com/wiki/LlRemoveFromLandBanList
x https://wiki.secondlife.com/wiki/LlRemoveFromLandPassList
x https://wiki.secondlife.com/wiki/LlResetLandBanList
x https://wiki.secondlife.com/wiki/LlResetLandPassList
x https://wiki.secondlife.com/wiki/LlReturnObjectsByID - still needs permission
x https://wiki.secondlife.com/wiki/LlReturnObjectsByOwner - still needs permission
x https://wiki.secondlife.com/wiki/LlSetParcelMusicURL
x https://wiki.secondlife.com/wiki/LlTeleportAgentHome
x https://wiki.secondlife.com/wiki/LlWater
x https://wiki.secondlife.com/wiki/LlWind
*/
#include <ARES/a>
#define CLIENT_VERSION "0.2.1"
#define CLIENT_VERSION_TAGS "alpha"
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
if(argc == 1) {
msg = "Syntax: " + PROGRAM_NAME + " <action>\n\nUtility for managing and inspecting land.\n\nSupported actions:\n\nParcel info: parcel [name], parcel capacity, parcel id, parcel desc, parcel prims, parcel owner, parcel group, area, flags \nSurveying: wind, water, ground, slope, day length, day offset, region day length, region day offset\nParcel media: music [<new URL>] (requires land ownership)\nMisc: map <url>\nAccess control: kick <uuid>, eject <uuid>, evict <uuid>, return <space-separated list of object UUIDs>, pass clear, pass revoke <uuid>, pass <uuid> [<hours>], ban clear, ban revoke <uuid>, unban <uuid>, unban all, ban <uuid> [<hours>], exile <uuid>\nRegion info: region [name], region flags, region host, region pop[ulation], region pos, region fps, uptime, region uptime, region version\nEstate: estate ban <uuid>, estate unban <uuid>, estate pass [group] <uuid>, estate revoke [group] <uuid>";
} else {
string action = gets(argv, 1);
string subject = gets(argv, 2);
string topic = gets(argv, 3);
string fourth = gets(argv, 4);
if(action == "parcel") {
if(argc == 2 || subject == "name")
msg = gets(llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_NAME]), 0);
else if(subject == "id")
msg = gets(llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_ID]), 0);
else if(subject == "desc")
msg = gets(llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_DESC]), 0);
else if(subject == "group")
msg = gets(llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_GROUP]), 0);
else if(subject == "owner")
msg = gets(llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_OWNER]), 0);
else if(subject == "capacity")
msg = (string)geti(llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_PRIM_CAPACITY]), 0);
else if(subject == "prims")
msg = (string)geti(llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_PRIM_USED]), 0);
} else if(action == "area")
msg = (string)geti(llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_AREA]), 0);
else if(action == "flags")
msg = (string)llGetParcelFlags(llGetPos());
else if(action == "wind")
msg = (string)llWind(ZV);
else if(action == "water")
msg = (string)llWater(ZV);
else if(action == "ground")
msg = (string)llGround(ZV);
else if(action == "slope")
msg = (string)llGroundSlope(ZV);
else if(action == "music") {
if(subject != "") {
llSetParcelMusicURL(subject);
} else {
msg = llGetParcelMusicURL();
}
} else if(action == "map") {
list url = split(subject, "/");
string region = gets(url, -4);
vector coords = <(float)gets(url, -3), (float)gets(url, -2), (float)gets(url, -1)>;
llMapDestination(region, coords, ZV);
} else if(action == "day") {
if(subject == "length")
msg = (string)llGetDayLength();
else if(subject == "offset")
msg = (string)llGetDayOffset();
} else if(action == "kick")
llTeleportAgentHome(subject);
else if(action == "eject")
llEjectFromLand(subject);
else if(action == "evict")
llReturnObjectsByOwner(subject, OBJECT_RETURN_PARCEL);
else if(action == "return")
llReturnObjectsByID(delrange(argv, 0, 1));
else if(action == "pass") {
if(subject == "clear")
llResetLandPassList();
else if(subject == "revoke")
llRemoveFromLandPassList(topic);
else
llAddToLandPassList(subject, (integer)fourth);
} else if(action == "ban") {
if(subject == "clear")
llResetLandBanList();
else if(subject == "revoke")
llRemoveFromLandBanList(topic);
else
llAddToLandBanList(subject, (integer)fourth);
} else if(action == "unban") {
if(subject == "all")
llResetLandBanList();
else
llRemoveFromLandBanList(subject);
} else if(action == "exile") {
llTeleportAgentHome(subject);
llAddToLandBanList(subject, 0);
llReturnObjectsByOwner(subject, OBJECT_RETURN_PARCEL_OWNER);
} else if(action == "uptime")
msg = format_time(llGetUnixTime() - (integer)llGetEnv("region_start_time"));
else if(action == "day") {
if(subject == "length")
msg = (string)llGetRegionDayLength();
else if(subject == "offset")
msg = (string)llGetRegionDayOffset();
} else if(action == "region") {
if(subject == "flags")
msg = (string)llGetRegionFlags();
else if(subject == "host")
msg = llGetEnv("simulator_hostname");
else if(subject == "version")
msg = llGetEnv("sim_channel") + " " + llGetEnv("sim_version");
else if(subject == "population" || subject == "pop")
msg = (string)llGetRegionAgentCount();
else if(subject == "pos")
msg = (string)llGetRegionCorner();
else if(subject == "fps")
msg = (string)llGetRegionFPS();
else if(subject == "uptime")
msg = (string)(llGetUnixTime() - (integer)llGetEnv("region_start_time"));
else if(subject == "day") {
if(topic == "length")
msg = (string)llGetRegionDayLength();
else if(topic == "offset")
msg = (string)llGetRegionDayOffset();
} else if(subject == "name" || subject == "")
msg = llGetRegionName();
} else if(action == "estate") {
if(subject == "ban") {
llManageEstateAccess(ESTATE_ACCESS_BANNED_AGENT_ADD, topic);
} else if(subject == "unban") {
llManageEstateAccess(ESTATE_ACCESS_BANNED_AGENT_REMOVE, topic);
} else if(subject == "pass") {
if(topic == "group")
llManageEstateAccess(ESTATE_ACCESS_ALLOWED_GROUP_ADD, fourth);
else
llManageEstateAccess(ESTATE_ACCESS_ALLOWED_AGENT_ADD, topic);
} else if(subject == "revoke") {
if(topic == "group")
llManageEstateAccess(ESTATE_ACCESS_ALLOWED_GROUP_REMOVE, fourth);
else
llManageEstateAccess(ESTATE_ACCESS_ALLOWED_AGENT_REMOVE, topic);
} else if(subject == "name" || subject == "") {
msg = llGetEnv("estate_name");
} else if(subject == "id") {
msg = llGetEnv("estate_id");
}
}
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
} 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 <ARES/program>

67
ARES/application/lore.lsl Normal file
View File

@ -0,0 +1,67 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Sim Lore Database Client Application
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#define CLIENT_VERSION "0.0.1"
#define CLIENT_VERSION_TAGS "placeholder"
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
string action = gets(argv, 1);
if(argc == 1 || action == "help") {
msg = "Syntax: " + PROGRAM_NAME + " <action>";
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
} 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 <ARES/program>

162
ARES/application/lslisp.lsl Normal file
View File

@ -0,0 +1,162 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* LSLisp System Module
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#include <lslisp.lsl>
#define CLIENT_VERSION "0.2.5"
#define CLIENT_VERSION_TAGS "lang " + INTERPRETER_VERSION
integer file_offset;
integer file_length;
key file_outs;
key file_user;
key file_pipe;
string file_name;
string file_unit;
#define FILE_STEP_SIZE 10
main(integer src, integer n, string m, key e_outs, key e_ins, key e_user) {
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
if(argc == 1) {
print(e_outs, e_user, "Syntax: @" + PROGRAM_NAME + " <filename>");
} else {
// set aside session info for when we have data to output:
file_outs = e_outs;
file_user = e_user;
parse_queue = v_names = v_values = interrupted_stack = [];
interrupt = "";
// always needed for file loading:
file_name = gets(argv, 1);
file_unit = "";
file_offset = file_length = NOWHERE;
file_open(file_pipe = llGenerateKey(), file_name);
// prevent job from ending while loading file:
task_begin(file_pipe, file_name);
}
} else if(n == SIGNAL_NOTIFY) {
if(m == PROGRAM_NAME + " file") {
if(e_ins == file_pipe) {
// copy contents of LSD file_pipe into local variable:
string file_buffer;
pipe_read(file_pipe, file_buffer);
integer read_length = FILE_STEP_SIZE;
if(file_unit == "b") read_length *= FILE_PAGE_LENGTH;
if(file_length == NOWHERE) {
// haven't loaded file length yet
list stat = split(file_buffer, " ");
file_length = (integer)gets(stat, 0);
file_unit = gets(stat, 1);
file_offset = 0;
// for notecards this is measured as 256 * (number of lines),
// NOT actual character count
} else {
file_offset += read_length;
}
if(read_length + file_offset > file_length)
read_length = file_length - file_offset;
if(file_offset == 0) {
if(file_length > 0) {
// start reading file
file_read(file_pipe, file_name, (string)file_offset + " " + (string)read_length);
} else {
// file not found or file empty
print(file_user, file_user, "No file: " + file_name);
file_close(file_pipe);
// job can now end:
task_end(file_pipe);
}
} else {
// got length earlier, so this must be file data
parse_queue += ["\n"] + tokenize(file_buffer);
// is there more file?
if(file_offset < file_length) {
// get next section
// move forward to next page:
file_read(file_pipe, file_name, (string)file_offset + " " + (string)read_length);
} else {
// reached end of file
file_close(file_pipe);
user = file_user;
outs = file_outs;
string prog = parse(parse_queue);
list output = execute([stack_frame(prog, [], NOWHERE, "{}", 0, NOWHERE)]);
string msg;
if(interrupt) {
msg = "-- Interrupted!";
} else {
msg = concat(output, " ");
}
print(file_user, file_outs, msg);
// job can now end:
// task_end(file_pipe);
}
}
} else {
echo("[" + PROGRAM_NAME + "] file data offered via unexpected pipe: " + (string)e_ins);
}
}
} else if(n == SIGNAL_INIT) {
// print(e_outs, e_user, "[" + PROGRAM_NAME + "] init event; nothing to do");
} 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 <ARES/program>

View File

@ -0,0 +1,896 @@
key E = "00000000-0000-0000-0000-000000000000";
key gC = "00000000-0000-0000-0000-000000000000";
key K = "00000000-0000-0000-0000-000000000000";
string LslUserScript = "program";
integer g_;
string H = "!!";
string A(key llJsonValueType)
{
string loc_spipe = "p:" + (string)llJsonValueType;
string loc_buffer = llLinksetDataRead(loc_spipe);
integer loc_eor = llSubStringIndex(loc_buffer, "");
if (~loc_eor)
{
if (loc_eor ^ ~-llStringLength(loc_buffer))
llLinksetDataWrite(loc_spipe, llDeleteSubString(loc_buffer, 0, loc_eor));
else
llLinksetDataDelete(loc_spipe);
loc_buffer = llDeleteSubString(loc_buffer, loc_eor, ((integer)-1));
}
else
{
llLinksetDataDelete(loc_spipe);
}
return loc_buffer;
}
string System = "{}";
list UThreadStackFrame;
list gB;
list gA;
key D;
key I;
key L;
list C(list llJsonValueType)
{
string loc_prog;
list loc_ix;
integer loc_parent;
string loc_local_vars;
integer loc_return_mode;
integer loc_last_symbol;
{
string loc_frame = llList2String(llJsonValueType, ((integer)-1));
llJsonValueType = llDeleteSubList(llJsonValueType, ((integer)-1), ((integer)-1));
loc_prog = llJsonGetValue(loc_frame, (list)"c");
loc_ix = llJson2List(llJsonGetValue(loc_frame, (list)"i"));
loc_parent = (integer)llJsonGetValue(loc_frame, (list)"p");
loc_local_vars = llJsonGetValue(loc_frame, (list)"v");
loc_return_mode = (integer)llJsonGetValue(loc_frame, (list)"r");
loc_last_symbol = (integer)llJsonGetValue(loc_frame, (list)"sp");
}
if (D)
{
D = "";
}
if (loc_prog == "")
return llJsonValueType;
integer loc_descending;
integer loc_backtrack;
if (loc_ix == [])
{
loc_ix = (list)((integer)-1);
loc_descending = 1;
}
while (1)
{
@J_autoGen00001;
loc_ix = llListReplaceList(loc_ix, (list)(-~llList2Integer(loc_ix, ((integer)-1))), ((integer)-1), ((integer)-1));
if (llJsonValueType(loc_prog, loc_ix) == "﷐")
{
if (!~-(loc_ix != []))
{
if (~loc_parent)
{
integer loc_previous_return_mode = loc_return_mode;
string loc_frame = llList2String(llJsonValueType, loc_parent);
if (!(llGetSubString(loc_frame, 0, 0) == "{"))
{
llOwnerSay(">> No frame at " + (string)loc_parent + " in: " + llList2Json("﷒", llJsonValueType));
return [];
}
llJsonValueType = llDeleteSubList(llJsonValueType, loc_parent, loc_parent);
loc_prog = llJsonGetValue(loc_frame, (list)"c");
loc_ix = llJson2List(llJsonGetValue(loc_frame, (list)"i"));
loc_parent = (integer)llJsonGetValue(loc_frame, (list)"p");
loc_local_vars = llJsonGetValue(loc_frame, (list)"v");
loc_return_mode = (integer)llJsonGetValue(loc_frame, (list)"r");
loc_last_symbol = (integer)llJsonGetValue(loc_frame, (list)"sp");
string loc_structure = llJsonGetValue(loc_prog, loc_ix);
list loc_step_again = loc_ix;
if (~-loc_previous_return_mode)
{
if (loc_previous_return_mode == 2)
{
llJsonValueType = llJsonValueType + loc_frame;
loc_prog = "[" + llJsonGetValue(loc_prog, llListReplaceList(loc_step_again, (list)1, ((integer)-1), ((integer)-1))) + "]";
loc_return_mode = 1;
loc_local_vars = "{}";
loc_parent = ~-(llJsonValueType != []);
loc_ix = (list)((integer)-1);
loc_last_symbol = ((integer)-1);
loc_descending = 1;
}
}
else
{
integer loc_retval = (integer)llList2String(llJsonValueType, ((integer)-1));
llJsonValueType = llDeleteSubList(llJsonValueType, ((integer)-1), ((integer)-1));
loc_ix = llDeleteSubList(loc_ix, ((integer)-1), ((integer)-1));
if (loc_structure == "if")
{
string loc_next_frame = llJsonSetValue(loc_frame, (list)"i", llList2Json("﷒", loc_ix));
list loc_subix = loc_ix;
if (loc_retval)
{
loc_subix = loc_subix + 2;
}
else if (llJsonValueType(loc_prog, loc_ix + 3) == "﷒")
{
loc_subix = loc_subix + 3;
}
else
{
jump J_autoGen00001;
}
loc_prog = "[" + llJsonGetValue(loc_prog, loc_subix) + "]";
llJsonValueType = llJsonValueType + loc_next_frame;
loc_return_mode = 0;
}
else if (loc_structure == "while")
{
if (loc_retval)
{
llJsonValueType = llJsonValueType + loc_frame;
loc_prog = "[" + llJsonGetValue(loc_prog, loc_ix + 2) + "]";
loc_descending = 1;
loc_return_mode = 2;
}
else
{
jump J_autoGen00001;
}
}
loc_local_vars = "{}";
loc_parent = ~-(llJsonValueType != []);
loc_last_symbol = ((integer)-1);
loc_ix = (list)((integer)-1);
}
jump J_autoGen00001;
}
else
{
return llJsonValueType;
}
}
loc_backtrack = 1;
loc_ix = llDeleteSubList(loc_ix, ((integer)-1), ((integer)-1));
}
else
while (llJsonValueType(loc_prog, loc_ix) == "﷒")
{
loc_ix = loc_ix + 0;
loc_descending = 1;
}
string loc_type = llJsonValueType(loc_prog, loc_ix);
if (loc_type == "﷓")
{
llJsonValueType = llJsonValueType + (float)llJsonGetValue(loc_prog, loc_ix);
}
else if (loc_descending)
{
string loc_verb = llJsonGetValue(loc_prog, loc_ix);
if (loc_verb == "if" | loc_verb == "while")
{
llJsonValueType = llJsonValueType + ("{\"c\":" + loc_prog + ",\"i\":" + llList2Json("﷒", loc_ix) + ",\"sp\":" + (string)loc_last_symbol + ",\"p\":" + (string)loc_parent + ",\"v\":" + loc_local_vars + ",\"r\":" + (string)loc_return_mode + ",\"t\":0}");
loc_prog = "[" + llJsonGetValue(loc_prog, llDeleteSubList(loc_ix, ((integer)-1), ((integer)-1)) + 1) + "]";
loc_return_mode = 1;
loc_local_vars = "{}";
loc_parent = ~-(llJsonValueType != []);
loc_ix = (list)((integer)-1);
loc_last_symbol = ((integer)-1);
loc_descending = 1;
jump J_autoGen00001;
}
else if (loc_verb == "lambda")
{
loc_ix = llDeleteSubList(loc_ix, ((integer)-1), ((integer)-1));
string loc_arglist = llDumpList2String(llJson2List(llJsonGetValue(loc_prog, loc_ix + 1)), " ");
string loc_code = llJsonGetValue(loc_prog, loc_ix + 2);
loc_descending = loc_backtrack = 0;
llJsonValueType = llJsonValueType + ("{\"c\":" + loc_code + ",\"t\":2,\"a\":\"" + loc_arglist + "\"}");
jump J_autoGen00001;
}
else
{
llJsonValueType = llJsonValueType + ("{\"s\":\"" + loc_verb + "\"" + ",\"t\":1,\"sp\":" + (string)loc_last_symbol + "}");
loc_last_symbol = ~-(llJsonValueType != []);
}
}
else if (loc_type == "﷔")
{
string loc_s = llJsonGetValue(loc_prog, loc_ix);
if (loc_s == "\\\"\\\"")
{
llJsonValueType = llJsonValueType + "";
}
else if (llGetSubString(loc_s, 0, 1) == "\\\"")
{
llJsonValueType = llJsonValueType + llGetSubString(loc_s, 2, ((integer)-3));
}
else if (llGetSubString(loc_s, 0, 0) == "$")
{
string loc_name = llDeleteSubString(loc_s, 0, 0);
string loc_local_type = llJsonValueType(loc_local_vars, (list)loc_name);
integer loc_temp_parent = loc_parent;
string loc_value;
if (loc_local_type == "﷐")
while (loc_local_type == "﷐")
{
if (~loc_temp_parent)
{
string loc_fr = llList2String(llJsonValueType, loc_temp_parent);
loc_local_type = llJsonValueType(loc_fr, (list)"v" + loc_name);
loc_value = llJsonGetValue(loc_fr, (list)"v" + loc_name);
loc_temp_parent = (integer)llJsonGetValue(loc_fr, (list)"p");
}
else
{
integer loc_si = llListFindList(UThreadStackFrame, (list)loc_name);
if (~loc_si)
{
integer loc_global_type = llGetListEntryType(gB, loc_si);
if (loc_global_type ^ 2)
if (~-loc_global_type)
llJsonValueType = llJsonValueType + llList2String(gB, loc_si);
else
llJsonValueType = llJsonValueType + llList2Integer(gB, loc_si);
else
llJsonValueType = llJsonValueType + llList2Float(gB, loc_si);
jump J_autoGen00001;
}
else
{
llOwnerSay(">> uninitialized variable: " + loc_s);
return [];
}
}
}
else
{
loc_value = llJsonGetValue(loc_local_vars, (list)loc_name);
}
if (loc_local_type == "﷓")
{
if (llFabs((float)loc_value + -(float)((integer)(0.5 + (float)loc_value))) < 7.6293945e-06)
llJsonValueType = llJsonValueType + (integer)loc_value;
else
llJsonValueType = llJsonValueType + (float)loc_value;
}
else
{
llJsonValueType = llJsonValueType + loc_value;
}
}
else
{
llJsonValueType = llJsonValueType + loc_s;
}
}
else if (loc_backtrack)
{
string loc_verbf = llList2String(llJsonValueType, loc_last_symbol);
string loc_verb = llJsonGetValue(loc_verbf, (list)"s");
if (loc_verb == "")
{
loc_last_symbol = (integer)llJsonGetValue(loc_verbf, (list)"sp");
loc_backtrack = loc_descending = 0;
jump J_autoGen00001;
}
integer loc_c;
list loc_params;
{
integer loc_scount = (llJsonValueType != []) + -loc_last_symbol;
loc_c = ~-loc_scount;
if (loc_c)
loc_params = llList2List(llJsonValueType, -~loc_last_symbol, ((integer)-1));
}
llJsonValueType = llDeleteSubList(llJsonValueType, loc_last_symbol, ((integer)-1));
loc_last_symbol = (integer)llJsonGetValue(loc_verbf, (list)"sp");
if (loc_verb == "list")
{
llJsonValueType = llJsonValueType + llList2Json("﷒", loc_params);
}
else if (loc_verb == "print")
{
{
llLinksetDataWrite("p:" + (string)L, llLinksetDataRead("p:" + (string)L) + (string)loc_params + "");
llRegionSayTo(gC, 2046828547, "%1" + H + (string)L + " " + (string)I);
}
}
else if (loc_verb == "global")
{
string loc_name = llList2String(loc_params, 0);
string loc_data;
if (2 < loc_c)
loc_data = llList2Json("﷒", llList2List(loc_params, 1, ((integer)-1)));
else if (loc_c == 2)
loc_data = llList2String(loc_params, 1);
integer loc_si = llListFindList(UThreadStackFrame, (list)loc_name);
if (~loc_si)
{
gB = llListReplaceList(gB, (list)loc_data, loc_si, loc_si);
}
else
{
UThreadStackFrame = UThreadStackFrame + loc_name;
gB = gB + loc_data;
}
}
else if (loc_verb == "set")
{
if (loc_c < 2)
{
llOwnerSay(">> unary set.");
return [];
}
string loc_name = llList2String(loc_params, 0);
list loc_data = llList2List(loc_params, 1, ((integer)-1));
string loc_local_type = llJsonValueType(loc_local_vars, (list)loc_name);
integer loc_old_parent = ((integer)-1);
integer loc_temp_parent = loc_parent;
string loc_fr;
while (loc_local_type == "﷐")
{
if (~loc_temp_parent)
{
loc_fr = llList2String(llJsonValueType, loc_temp_parent);
loc_local_type = llJsonValueType(loc_fr, (list)"v" + loc_name);
loc_old_parent = loc_temp_parent;
loc_temp_parent = (integer)llJsonGetValue(loc_fr, (list)"p");
}
else
{
integer loc_si = llListFindList(UThreadStackFrame, (list)loc_name);
if (~loc_si)
{
gB = llListReplaceList(gB, loc_data, loc_si, loc_si);
}
else
{
loc_local_vars = llJsonSetValue(loc_local_vars, (list)loc_name, (string)loc_data);
}
jump J_autoGen00002;
}
}
if (~loc_old_parent)
llJsonValueType = llListReplaceList(llJsonValueType, (list)llJsonSetValue(loc_fr, (list)"v" + loc_name, (string)loc_data), loc_old_parent, loc_old_parent);
else
loc_local_vars = llJsonSetValue(loc_local_vars, (list)loc_name, (string)loc_data);
@J_autoGen00002;
}
else if (loc_verb == "+")
{
float loc_out = 0;
while (loc_c--)
loc_out = loc_out + llList2Float(loc_params, loc_c);
llJsonValueType = llJsonValueType + loc_out;
}
else if (loc_verb == "*")
{
float loc_out = 1;
while (loc_c--)
loc_out = loc_out * llList2Float(loc_params, loc_c);
llJsonValueType = llJsonValueType + loc_out;
}
else if (loc_verb == "==")
{
if (llGetListEntryType(loc_params, 0) ^ 3)
if (~-llGetListEntryType(loc_params, 0))
llJsonValueType = llJsonValueType + (llList2Float(loc_params, 0) == llList2Float(loc_params, 1));
else
llJsonValueType = llJsonValueType + (llList2Integer(loc_params, 0) == llList2Integer(loc_params, 1));
else
llJsonValueType = llJsonValueType + (llList2String(loc_params, 0) == llList2String(loc_params, 1));
}
else if (loc_verb == "!=")
{
if (llGetListEntryType(loc_params, 0) ^ 3)
if (~-llGetListEntryType(loc_params, 0))
llJsonValueType = llJsonValueType + !(llList2Float(loc_params, 0) == llList2Float(loc_params, 1));
else
llJsonValueType = llJsonValueType + (llList2Integer(loc_params, 0) ^ llList2Integer(loc_params, 1));
else
llJsonValueType = llJsonValueType + !(llList2String(loc_params, 0) == llList2String(loc_params, 1));
}
else if (loc_verb == "<")
{
llJsonValueType = llJsonValueType + (llList2Float(loc_params, 0) < llList2Float(loc_params, 1));
}
else if (loc_verb == ">")
{
llJsonValueType = llJsonValueType + (llList2Float(loc_params, 1) < llList2Float(loc_params, 0));
}
else if (loc_verb == "<=")
{
llJsonValueType = llJsonValueType + !(llList2Float(loc_params, 1) < llList2Float(loc_params, 0));
}
else if (loc_verb == ">=")
{
llJsonValueType = llJsonValueType + !(llList2Float(loc_params, 0) < llList2Float(loc_params, 1));
}
else if (loc_verb == "and")
{
integer loc_out = 1;
while (loc_c--)
loc_out = loc_out & llList2Integer(loc_params, loc_c);
llJsonValueType = llJsonValueType + loc_out;
}
else if (loc_verb == "or")
{
integer loc_out;
while (loc_c--)
loc_out = loc_out | llList2Integer(loc_params, loc_c);
llJsonValueType = llJsonValueType + loc_out;
}
else if (loc_verb == "not" | loc_verb == "!")
{
llJsonValueType = llJsonValueType + !llList2Integer(loc_params, 0);
}
else if (loc_verb == "-")
{
if (1 < loc_c)
{
float loc_out = llList2Float(loc_params, 0);
--loc_c;
while (loc_c--)
{
loc_out = loc_out + -llList2Float(loc_params, -~loc_c);
}
llJsonValueType = llJsonValueType + loc_out;
}
else
{
llJsonValueType = llJsonValueType + -llList2Float(loc_params, 0);
}
}
else if (loc_verb == "/")
{
if (1 < loc_c)
{
float loc_out = llList2Float(loc_params, 0);
--loc_c;
while (loc_c--)
{
float loc_newv = llList2Float(loc_params, -~loc_c);
if (loc_newv == 0)
{
llOwnerSay(">> Division by zero.");
return [];
}
else
loc_out = loc_out / loc_newv;
}
llJsonValueType = llJsonValueType + loc_out;
}
else
{
float loc_out = llList2Float(loc_params, 0);
if (loc_out == 0)
{
llOwnerSay(">> Division by zero.");
return [];
}
else
llJsonValueType = llJsonValueType + ((float)1) / loc_out;
}
}
else if (loc_verb == "apply")
{
llJsonValueType = llJsonValueType + ("{\"c\":" + loc_prog + ",\"i\":" + llList2Json("﷒", loc_ix) + ",\"sp\":" + (string)loc_last_symbol + ",\"p\":" + (string)loc_parent + ",\"v\":" + loc_local_vars + ",\"r\":" + (string)loc_return_mode + ",\"t\":0}");
loc_local_vars = "{}";
loc_ix = (list)((integer)-1);
loc_parent = ~-(llJsonValueType != []);
{
string loc_func = llList2String(loc_params, 0);
list loc_arg_names = llParseString2List(llJsonGetValue(loc_func, (list)"a"), (list)" ", []);
integer loc_an = loc_arg_names != [];
while (loc_an--)
{
loc_local_vars = llJsonSetValue(loc_local_vars, (list)llList2String(loc_arg_names, loc_an), llList2String(loc_params, -~loc_an));
}
loc_prog = "[" + llJsonGetValue(loc_func, (list)"c") + "]";
}
loc_descending = loc_backtrack = 0;
jump J_autoGen00001;
}
else if (loc_verb == "concat")
{
llJsonValueType = llJsonValueType + llDumpList2String(llJson2List(llList2String(loc_params, 1)), llList2String(loc_params, 0));
}
else if (loc_verb == "split")
{
llJsonValueType = llJsonValueType + llList2Json("﷒", llParseString2List(llList2String(loc_params, 1), (list)llList2String(loc_params, 0), []));
}
else if (loc_verb == "get")
{
llJsonValueType = llJsonValueType + llJsonGetValue(llList2String(loc_params, 0), llJson2List(llList2String(loc_params, 1)));
}
else if (loc_verb == "index")
{
llJsonValueType = llJsonValueType + llListFindList(llJson2List(llList2String(loc_params, 0)), (list)llList2String(loc_params, 1));
}
else if (loc_verb == "count")
{
llJsonValueType = llJsonValueType + (llJson2List(llList2String(loc_params, 0)) != []);
}
else if (loc_verb == "put")
{
llJsonValueType = llJsonValueType + llJsonSetValue(llList2String(loc_params, 0), llJson2List(llList2String(loc_params, 1)), llList2String(loc_params, 2));
}
else if (loc_verb == "char")
{
integer loc_p = llList2Integer(loc_params, 1);
llJsonValueType = llJsonValueType + llGetSubString(llList2String(loc_params, 0), loc_p, loc_p);
}
else if (loc_verb == "substr")
{
llJsonValueType = llJsonValueType + llGetSubString(llList2String(loc_params, 0), llList2Integer(loc_params, 1), llList2Integer(loc_params, 2));
}
else if (loc_verb == "strpos")
{
llJsonValueType = llJsonValueType + llSubStringIndex(llList2String(loc_params, 0), llList2String(loc_params, 1));
}
else if (loc_verb == "rand")
{
llJsonValueType = llJsonValueType + llFrand(1);
}
else if (loc_verb == "int")
{
llJsonValueType = llJsonValueType + (integer)llList2String(loc_params, 0);
}
else if (loc_verb == "getd")
{
llJsonValueType = llJsonValueType + llJsonGetValue(llLinksetDataRead(llList2String(loc_params, 0)), llParseStringKeepNulls(llList2String(loc_params, 1), (list)".", []));
}
else if (loc_verb == "setd")
{
llLinksetDataWrite(llList2String(loc_params, 0), llJsonSetValue(llLinksetDataRead(llList2String(loc_params, 0)), llParseStringKeepNulls(llList2String(loc_params, 1), (list)".", []), llList2String(loc_params, 2)));
}
else if (loc_verb == "deleted")
{
llLinksetDataWrite(llList2String(loc_params, 0), llJsonSetValue(llLinksetDataRead(llList2String(loc_params, 0)), llParseStringKeepNulls(llList2String(loc_params, 1), (list)".", []), "﷘"));
}
else if (llGetSubString(loc_verb, 0, 0) == "@")
{
llMessageLinked(1, 0, "!!" + H + (string)L + " " + "00000000-0000-0000-0000-000000000000" + " " + (string)I + " " + llDeleteSubString(loc_verb, 0, 0) + " " + (string)loc_params, "");
}
else if (llGetSubString(loc_verb, 0, 0) == "#")
{
llMessageLinked(((integer)-4), (integer)llDeleteSubString(loc_verb, 0, 0), (string)loc_params, I);
}
else if (loc_verb == "﷐")
{
llJsonValueType = llJsonValueType + "[]";
}
else if (loc_verb == "﷕")
{
if (loc_c)
llJsonValueType = llJsonValueType + llList2List(loc_params, ((integer)-1), ((integer)-1));
}
else
{
llOwnerSay(">> unknown verb: " + loc_verb);
}
}
else
{
llOwnerSay(">> unknown data type: " + loc_type);
}
loc_descending = loc_backtrack = 0;
}
return llJsonValueType;
}
string _(list llJsonValueType)
{
list loc_opens;
integer loc_tcount = llJsonValueType != [];
integer loc_open_quote = ((integer)-1);
integer loc_comment_start = ((integer)-1);
integer loc_i;
for (; loc_i < loc_tcount; ++loc_i)
{
string loc_t = llList2String(llJsonValueType, loc_i);
if (~loc_comment_start)
{
if (loc_t == "\n")
{
llJsonValueType = llDeleteSubList(llJsonValueType, loc_comment_start, loc_i);
loc_tcount = loc_tcount + ~(loc_i + -loc_comment_start);
loc_i = loc_comment_start;
loc_comment_start = ((integer)-1);
}
}
else if (~loc_open_quote)
{
if (loc_t == "\"")
{
string loc_str;
if (-~loc_open_quote ^ loc_i)
loc_str = "\\\"" + llDumpList2String(llList2List(llJsonValueType, -~loc_open_quote, ~-loc_i), " ") + "\\\"";
else
loc_str = "";
llJsonValueType = llListReplaceList(llJsonValueType, (list)loc_str, loc_open_quote, loc_i);
loc_i = loc_open_quote;
loc_open_quote = ((integer)-1);
}
}
else
{
if (loc_t == "")
{
llJsonValueType = llDeleteSubList(llJsonValueType, loc_i, loc_i);
--loc_i;
--loc_tcount;
}
else if (loc_t == "\"")
{
loc_open_quote = loc_i;
}
else if (loc_t == "(")
{
loc_opens = loc_opens + loc_i;
}
else if (loc_t == ";")
{
loc_comment_start = loc_i;
}
else if (loc_t == "\n")
{
llJsonValueType = llDeleteSubList(llJsonValueType, loc_i, loc_i);
--loc_i;
--loc_tcount;
}
else if (loc_t == ")")
{
if (loc_opens == [])
{
llOwnerSay("++ Excess closing parens at " + (string)loc_i + ".");
return "";
}
integer loc_L = llList2Integer(loc_opens, ((integer)-1));
loc_opens = llDeleteSubList(loc_opens, ((integer)-1), ((integer)-1));
string loc_frag;
if (-~loc_L ^ loc_i)
loc_frag = llList2Json("﷒", llList2List(llJsonValueType, -~loc_L, ~-loc_i));
else
loc_frag = "[]";
if (llJsonValueType(loc_frag, (list)0) == "﷒")
loc_frag = llList2Json("﷒", "﷕" + llList2List(llJsonValueType, -~loc_L, ~-loc_i));
llJsonValueType = llListReplaceList(llJsonValueType, (list)loc_frag, loc_L, loc_i);
loc_tcount = llJsonValueType != [];
loc_i = loc_L;
}
}
}
if (~loc_open_quote)
{
llOwnerSay("++ unclosed quote following: " + llDumpList2String(llList2List(llJsonValueType, ~-~-loc_open_quote, 4 + loc_open_quote), " "));
return "";
}
else if (loc_opens != [])
{
llOwnerSay("++ imbalanced parens (" + (string)(loc_opens != []) + ").");
return "";
}
else
{
return llList2Json("﷒", llJsonValueType);
}
}
integer F;
integer J;
key Pop;
key edefaultchat;
key LslLibrary;
string edefaultstate_entry;
string ResumeVoid;
B(integer llJsonValueType, integer llGetListEntryType, string llFrand, key llList2String, key llSubStringIndex, key llListen)
{
if (llGetListEntryType)
if (llGetListEntryType ^ 275)
{
if (llGetListEntryType ^ 4093)
if (llGetListEntryType ^ 3850)
{
llOwnerSay("[" + LslUserScript + "] unimplemented signal " + (string)llGetListEntryType + ": " + llFrand);
}
else
{
llOwnerSay("[" + LslUserScript + "] failed to run '" + llFrand + "' (kernel could not find the program specified)");
}
}
else
{
if (llFrand == LslUserScript + " file")
{
if (llSubStringIndex == LslLibrary)
{
string loc_file_buffer;
{
loc_file_buffer = A(LslLibrary);
}
integer loc_read_length = 10;
if (ResumeVoid == "b")
loc_read_length = loc_read_length * 1024;
if (~J)
{
F = F + loc_read_length;
}
else
{
list loc_stat = llParseString2List(loc_file_buffer, (list)" ", []);
J = (integer)llList2String(loc_stat, 0);
ResumeVoid = llList2String(loc_stat, 1);
F = 0;
}
if (J < loc_read_length + F)
loc_read_length = J + -F;
if (F)
{
gA = gA + ("\n" + llParseStringKeepNulls(loc_file_buffer, (list)" ", (list)"(" + ")" + "\""));
if (F < J)
{
llRegionSayTo(gC, 2046828547, "%!" + H + (string)LslLibrary + " " + LslUserScript + " " + edefaultstate_entry + " " + ((string)F + " " + (string)loc_read_length));
}
else
{
llRegionSayTo(gC, 2046828547, "%3" + H + "[\"" + (string)LslLibrary + "\"]");
I = edefaultchat;
L = Pop;
string loc_prog = _(gA);
list loc_output = C((list)("{\"c\":" + loc_prog + ",\"i\":" + "[]" + ",\"sp\":" + "-1" + ",\"p\":" + "-1" + ",\"v\":" + "{}" + ",\"r\":" + "0" + ",\"t\":0}"));
string loc_msg;
if (D)
{
loc_msg = "-- Interrupted!";
}
else
{
loc_msg = llDumpList2String(loc_output, " ");
}
{
llLinksetDataWrite("p:" + (string)edefaultchat, llLinksetDataRead("p:" + (string)edefaultchat) + loc_msg + "");
llRegionSayTo(gC, 2046828547, "%1" + H + (string)edefaultchat + " " + (string)Pop);
}
}
}
else
{
if (0 < J)
{
llRegionSayTo(gC, 2046828547, "%!" + H + (string)LslLibrary + " " + LslUserScript + " " + edefaultstate_entry + " " + ((string)F + " " + (string)loc_read_length));
}
else
{
{
llLinksetDataWrite("p:" + (string)edefaultchat, llLinksetDataRead("p:" + (string)edefaultchat) + "No file: " + edefaultstate_entry + "");
llRegionSayTo(gC, 2046828547, "%1" + H + (string)edefaultchat + " " + (string)edefaultchat);
}
llRegionSayTo(gC, 2046828547, "%3" + H + "[\"" + (string)LslLibrary + "\"]");
System = llJsonSetValue(System, (list)LslLibrary, "﷘");
}
}
}
else
{
llOwnerSay("[" + LslUserScript + "] file data offered via unexpected pipe: " + (string)llSubStringIndex);
}
}
}
else
{
list loc_argv = llParseString2List(llFrand, (list)" ", []);
integer loc_argc = loc_argv != [];
if (~-loc_argc)
{
Pop = llList2String;
edefaultchat = llListen;
gA = UThreadStackFrame = gB = [];
D = "";
edefaultstate_entry = llList2String(loc_argv, 1);
ResumeVoid = "";
F = J = ((integer)-1);
llRegionSayTo(gC, 2046828547, "%!" + H + (string)(LslLibrary = llGenerateKey()) + " " + LslUserScript + " " + edefaultstate_entry + " " + "-1");
{
System = llJsonSetValue(System, (list)LslLibrary, edefaultstate_entry);
llMessageLinked(1, 3859, LslUserScript + "," + (string)(G = G | 1), "");
}
}
else
{
{
llLinksetDataWrite("p:" + (string)llList2String, llLinksetDataRead("p:" + (string)llList2String) + "Syntax: @" + LslUserScript + " <filename>" + "");
llRegionSayTo(gC, 2046828547, "%1" + H + (string)llList2String + " " + (string)llListen);
}
}
}
}
integer edefaultrez;
integer UThread;
key IsRestoring;
key Library;
key IsSaveDue;
integer G;
default
{
state_entry()
{
E = llGetLinkKey(1);
gC = llGetLinkKey(2);
K = llGetLinkKey(3);
LslUserScript = llGetScriptName();
edefaultrez = llListen(2046820352, "", E, "");
llMessageLinked(1, 3840, LslUserScript + "\n" + "0.2.5" + " " + "lang " + "0.2.3", "");
}
on_rez(integer llJsonValueType)
{
llResetScript();
}
listen(integer llListen, string llFrand, key llGetListEntryType, string llJsonValueType)
{
integer loc_n = (((integer)-33) + llOrd(llJsonValueType, 0)) * 64 + (((integer)-33) + llOrd(llJsonValueType, 1));
if (llListen ^ 2046820352)
{
IsRestoring = "00000000-0000-0000-0000-000000000000";
Library = "00000000-0000-0000-0000-000000000000";
string loc_remainder = llDeleteSubString(llJsonValueType, 0, 1);
if (loc_n & -!(loc_n == 275))
{
B(UThread = 0, loc_n, loc_remainder, "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000");
}
else
{
Library = llGetSubString(loc_remainder, 2, 37);
IsRestoring = llGetSubString(loc_remainder, 39, 74);
IsSaveDue = llGetSubString(loc_remainder, 76, 111);
B(UThread = (((integer)-33) + llOrd(loc_remainder, 0)) * 64 + (((integer)-33) + llOrd(loc_remainder, 1)), loc_n, llDeleteSubString(loc_remainder, 0, 112), Library, IsRestoring, IsSaveDue);
}
if (UThread & -(llJsonGetValue(System, (list)((string)IsRestoring)) == "﷐"))
llRegionSayTo(gC, 2046828547, "%3" + H + "V" + llList2Json("﷒", (list)IsRestoring));
if (llList2ListStrided(llJson2List(System), 0, ((integer)-1), 2) == [])
{
{
if (UThread)
llMessageLinked(1, 4, llChar(33 + ((UThread & 4032) >> 6)) + llChar(33 + (UThread & 63)) + (string)IsRestoring + " " + LslUserScript, "");
llMessageLinked(1, 3849, LslUserScript, "");
G = G & ((integer)-2);
llSetScriptState(LslUserScript, 0);
llSleep(0.022);
}
}
else
{
if (UThread)
llMessageLinked(1, 4, llChar(33 + ((UThread & 4032) >> 6)) + llChar(33 + (UThread & 63)) + (string)IsRestoring + " " + LslUserScript, "");
}
}
else
{
if (loc_n == 3841)
{
string loc_expected_program_name = llDeleteSubString(llJsonValueType, 0, 3);
if (loc_expected_program_name == LslUserScript)
{
llListenRemove(edefaultrez);
H = llGetSubString(llJsonValueType, 2, 3);
g_ = (((integer)-33) + llOrd(H, 0)) * 64 + (((integer)-33) + llOrd(H, 1));
integer loc_C = 2046832640 + g_;
edefaultrez = llListen(loc_C, "", E, "");
llMessageLinked(1, 3859, LslUserScript + "," + (string)G, "");
B(UThread = 0, 4093, "", "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000");
llMessageLinked(1, 3860, LslUserScript, "");
}
}
}
}
}

102
ARES/application/mail.lsl Normal file
View File

@ -0,0 +1,102 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Mail Utility
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#define CLIENT_VERSION "1.1.1"
#define CLIENT_VERSION_TAGS "release"
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
if(argc == 1 || argc == 2) {
msg = "Syntax: " + PROGRAM_NAME + " <address> <subject> [-b <body>]\n\nSends email to the targeted recipient. If -b is not specified, the body is read from the input stream.\n\nCaveats:\n - LSL enforces a 20-second sleep after each email is sent.\n - A given avatar may only generate 500 emails per hour.\n - Sending non-ASCII characters in the subject line may cause the email to fail entirely.\n - Only one recipient may be targeted.\n - The entire message (including all email headers) must fit in 4096 bytes, so keep it short.";
} else {
string recipient = gets(argv, 1);
string subject = "(no subject)";
string body;
integer bi = index(argv, "-b");
if(~bi) {
if(bi == 1) {
msg = "No recipient specified.";
jump fail;
} else if(bi > 1) {
if(bi == argc - 1) {
msg = "No body specified.";
jump fail;
}
body = concat(sublist(argv, bi + 1, LAST), " ");
if(bi > 2)
subject = concat(sublist(argv, 2, bi - 1), " ");
}
} else if(ins == NULL_KEY || ins == user) {
msg = "No body specified.";
jump fail;
} else {
subject = concat(delrange(argv, 0, 1), " ");
pipe_read(ins, body);
if(body == "") {
msg = "Empty body.";
jump fail;
}
}
print(outs, user, "Sending email to " + recipient + "...");
llEmail(recipient, subject, body);
msg = "Email sent to " + recipient + ".";
@fail;
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
} 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 <ARES/program>

308
ARES/application/mantra.lsl Normal file
View File

@ -0,0 +1,308 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Mantra Application
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#include <ARES/api/interface.consts.h.lsl>
#define CLIENT_VERSION "1.0.1"
#define CLIENT_VERSION_TAGS "release"
string fg_image = "947c7a17-4e88-2f96-b419-dc37b647127d";
string bg_image = "cf45ccd4-9bd0-1835-08f7-3883e234f3e1";
string loop_sound = "70f93e08-31c1-6076-4ea7-47a7b8a6ca3f";
float loop_vol = 0.5;
float start_speed = 0.125;
float stop_speed = 0.25;
// float screen_width = 1920;
// float screen_height = 1008;
// float diagonal_aspect_ratio = llSqrt(1 + llPow(screen_width / screen_height, 2.0));
// float diagonal_aspect_ratio = 2.16; // 2.1513060948717175859320949846553
// (ratio of screen height to diagonal width - minimum 1.0)
// for very wide screens:
// float diagonal_aspect_ratio = 3;
float diagonal_aspect_ratio = 2.16;
vector fg_color;
vector bg_color;
list mantras;
integer content_i;
integer mantra_interval;
end_meditation() {
task_end("mantra");
set_timer("mantra", 0);
llResetTime();
float t = 0;
while(t < 1) {
llSleep(0.01);
t += llGetAndResetTime() * stop_speed;
if(t > 1) t = 1;
setp(WIZARD_TEXT + 2, [PRIM_COLOR, ALL_SIDES, bg_color, 1.0 - t]);
}
llResetTime();
t = 0;
while(t < 1) {
llSleep(0.01);
t += llGetAndResetTime() * stop_speed;
if(t > 1) t = 1;
setp(WIZARD + 2, [PRIM_COLOR, ALL_SIDES, fg_color, 1.0 - t]);
llAdjustSoundVolume((1.0 - t) * loop_vol);
}
setp(WIZARD + 2, [
PRIM_SIZE, ZV,
PRIM_COLOR, ALL_SIDES, ZV, 0,
PRIM_ROT_LOCAL, INVISIBLE,
PRIM_POSITION, OFFSCREEN,
PRIM_OMEGA, ZV, 0, 0,
PRIM_LINK_TARGET, WIZARD_TEXT + 2,
PRIM_SIZE, ZV,
PRIM_COLOR, ALL_SIDES, ZV, 0,
PRIM_ROT_LOCAL, INVISIBLE,
PRIM_POSITION, OFFSCREEN,
PRIM_OMEGA, ZV, 0, 0,
PRIM_LINK_TARGET, WIZARD_TEXT,
PRIM_TEXT, "", ZV, 0,
PRIM_POSITION, OFFSCREEN
]);
llStopSound();
}
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
if(argc == 1) {
msg = "Syntax: " + PROGRAM_NAME + " start|stop";
} else {
string action = gets(argv, 1);
if(action == "sleep") {
float duration = (float)gets(argv, 2);
if(duration <= 0.1) duration = 0.1;
llSleep(duration);
} else if(action == "start") {
task_begin("mantra", user);
if(llLinksetDataRead("mantra") == JSON_INVALID) {
print(outs, user, "Database not configured. Please run @db load mantra.db");
return;
}
print(outs, user, "Mantra session beginning.");
content_i = 0;
loop_sound = getdbl("mantra", ["loop", "sound"]);
loop_vol = (float)getdbl("mantra", ["loop", "volume"]);
start_speed = (float)getdbl("mantra", ["speed", "start"]);
stop_speed = (float)getdbl("mantra", ["speed", "stop"]);
fg_image = getdbl("mantra", ["fg", "image"]);
bg_image = getdbl("mantra", ["bg", "image"]);
float fg_speed = (float)getdbl("mantra", ["fg", "speed"]);
float bg_speed = (float)getdbl("mantra", ["bg", "speed"]);
diagonal_aspect_ratio = (float)getdbl("mantra", ["scale"]);
integer fg_slot = (integer)getdbl("mantra", ["fg", "color"]);
integer bg_slot = (integer)getdbl("mantra", ["bg", "color"]);
if(fg_speed == 0) fg_speed = -1.0;
if(fg_slot == -1) {
fg_color = ONES;
} else {
string color_1 = getdbl("id", ["color", fg_slot]);
fg_color = str2vec(color_1);
if(fg_color == ZV)
fg_color = <1, 1, 1>;
}
if(bg_slot == -1) {
bg_color = ONES;
} else {
string color_2 = getdbl("id", ["color", bg_slot]);
bg_color = str2vec(color_2);
}
// this ensures the spiral covers the whole screen reliably
setp(WIZARD + 2, [
PRIM_TEXTURE, ALL_SIDES, fg_image, ONES, ZV, 0,
PRIM_COLOR, ALL_SIDES, fg_color, 0,
PRIM_SIZE, ONES * diagonal_aspect_ratio,
PRIM_POSITION, <-2, 0, 0>,
PRIM_ROT_LOCAL, VISIBLE,
PRIM_OMEGA, <1, 0, 0>, fg_speed, 1,
PRIM_LINK_TARGET, WIZARD_TEXT + 2,
PRIM_TEXTURE, ALL_SIDES, bg_image, ONES, ZV, 0,
PRIM_COLOR, ALL_SIDES, bg_color, 0,
PRIM_SIZE, ONES * diagonal_aspect_ratio,
PRIM_POSITION, <-1.9, 0, 0>,
PRIM_ROT_LOCAL, VISIBLE,
PRIM_OMEGA, <1, 0, 0>, bg_speed, 1,
PRIM_LINK_TARGET, WIZARD_TEXT,
PRIM_TEXT, "", ZV, 0,
PRIM_POSITION, ZV
]);
llPreloadSound(loop_sound);
llLoopSound(loop_sound, 0);
llSleep(0.4);
llResetTime();
float t = 0;
while(t < 1) {
llSleep(0.01);
t += llGetAndResetTime() * start_speed;
if(t > 1) t = 1;
setp(WIZARD + 2, [PRIM_COLOR, ALL_SIDES, fg_color, t]);
llAdjustSoundVolume(t * loop_vol);
}
llResetTime();
t = 0;
while(t < 1) {
llSleep(0.01);
t += llGetAndResetTime() * start_speed;
if(t > 1) t = 1;
setp(WIZARD_TEXT + 2, [PRIM_COLOR, ALL_SIDES, bg_color, t]);
}
mantra_interval = (integer)getdbl("mantra", ["interval"]);
mantras = js2list(getdbl("mantra", ["content"]));
set_timer("mantra", mantra_interval);
} else if(action == "stop") {
integer lock_in = (integer)getdbl("mantra", ["lock-in"]);
key initiator = getjs(tasks_queue, ["mantra"]);
if(user == initiator && user == avatar && lock_in) {
msg = "Nothing is more important than mental clarity.";
} else if(initiator == user || initiator == JSON_INVALID) {
print(outs, user, "Mantra session ending.");
end_meditation();
} else {
msg = "Only secondlife:///app/agent/" + (string)initiator + "/about may stop the session.";
}
}
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_TIMER) {
key initiator = getjs(tasks_queue, ["mantra"]);
if(initiator == JSON_INVALID) {
end_meditation();
return;
} else {
string msg = getdbl("mantra", ["content", content_i++]);
if(msg == JSON_INVALID) {
integer autowake = (integer)getdbl("mantra", ["autowake"]);
if(autowake) {
end_meditation();
return;
} else {
msg = getdbl("mantra", ["content", content_i=0]);
}
}
llResetTime();
float t = 0;
while(t < 1) {
llSleep(0.01);
t += llGetAndResetTime();
if(t >= 1)
t = 1;
setp(WIZARD_TEXT, [
PRIM_TEXT, msg, ONES, t
]);
}
llSleep(mantra_interval / 2);
llResetTime();
t = 0;
while(t < 1) {
llSleep(0.01);
t += llGetAndResetTime();
if(t >= 1)
t = 1;
setp(WIZARD_TEXT, [
PRIM_TEXT, msg, ONES, 1.0 - t
]);
}
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
setp(WIZARD + 2, [
PRIM_SIZE, ZV,
PRIM_COLOR, ALL_SIDES, ZV, 0,
PRIM_ROT_LOCAL, INVISIBLE,
PRIM_POSITION, OFFSCREEN,
PRIM_OMEGA, ZV, 0, 0,
PRIM_LINK_TARGET, WIZARD_TEXT + 2,
PRIM_SIZE, ZV,
PRIM_COLOR, ALL_SIDES, ZV, 0,
PRIM_ROT_LOCAL, INVISIBLE,
PRIM_POSITION, OFFSCREEN,
PRIM_OMEGA, ZV, 0, 0,
PRIM_LINK_TARGET, WIZARD_TEXT,
PRIM_TEXT, "", ZV, 0,
PRIM_POSITION, OFFSCREEN
]);
llStopSound();
set_timer("mantra", 0);
} 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 <ARES/program>

View File

@ -0,0 +1,59 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* media Utility - Event Handlers
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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.
*
* =========================================================================
*
*/
run_time_permissions(integer w) {
if(w & PERMISSION_TRIGGER_ANIMATION) {
integer sn = count(anim_start_queue);
while(sn--) {
llStartAnimation(gets(anim_start_queue, sn));
}
anim_start_queue = [];
sn = count(anim_stop_queue);
while(sn--) {
llStopAnimation(gets(anim_stop_queue, sn));
}
anim_stop_queue = [];
if(perms_thread != "") {
task_end(perms_thread);
}
}
}

234
ARES/application/media.lsl Normal file
View File

@ -0,0 +1,234 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* media Utility
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#define CLIENT_VERSION "0.3.0"
#define CLIENT_VERSION_TAGS "beta"
#define S_PLAY 0x01
#define S_STOP 0x02
#define S_LOOP 0x04
#define S_INT 0x08
#define S_EXT 0x10
#define S_PRELOAD 0x20
#define SILENCE "475b270a-8dc0-2b53-210d-32d4dc6ebc7c"
integer playing;
float volume = 1.0;
integer venue = 0;
list anim_start_queue;
list anim_stop_queue;
string perms_thread;
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
integer action = 0;
if(argc == 1) {
msg = gets(argv, 0) + " [-i|internal] [-x|external] [-v|vol|volume 0.0-1.0] [-n|link|prim <link #>] [-p|play|-l|loop|preload|trigger <sound>] [-p|play <animation>] [-d|duration|-s|sleep|-w|wait <seconds>] [stop] [stop <animation>] [freeze] [unfreeze] [-q 0|1]\n\nControls media playback.\n\ninternal/external: select audio channel\nvolume: set sound volume\nlink/prim: set prim number for sound playback (internal only, not compatible with -q)\nplay/loop/trigger: play sound name (loop only supported for internal mode)\nduration/sleep/wait: pause for <seconds>\nstop: end current sound playback\n-q: toggle sound queueing for internal audio (not compatible with -n)\n\nFor best results, set channel and volume before sound playback command, and add sleep afterward. Multiple commands are allowed.\n\nFuture versions will add a HUD widget for interacting with sound, images, and animations.";
} else {
integer C_LIGHT_BUS = 105 - (integer)("0x" + substr(avatar, 29, 35));
integer link_number = LINK_THIS;
integer argi = 1;
while(argi < argc) {
string arg = gets(argv, argi);
if(arg == "-q") {
++argi;
integer enable_queueing = (integer)gets(argv, argi);
llSetSoundQueueing(enable_queueing);
} else if(arg == "-n" || arg == "link" || arg == "prim") {
++argi;
link_number = (integer)gets(argv, argi);
} else if(arg == "play" || arg == "-p" || arg == "loop" || arg == "-l" || arg == "preload") {
++argi;
string fname = gets(argv, argi);
integer ftype = llGetInventoryType(fname);
integer do_sound = 0;
integer do_animation = 0;
integer loop = (arg == "loop" || arg == "-l");
integer preload = (arg == "preload");
string fuuid = llGetInventoryKey(fname);
if(ftype == INVENTORY_NONE) {
if(validate_key(fname)) {
do_sound = 1;
fuuid = fname;
} else {
msg = "no file '" + fname + "'";
}
} else if(ftype == INVENTORY_SOUND) {
do_sound = 1;
} else if(ftype == INVENTORY_ANIMATION) {
do_animation = 1;
} else {
msg = fname + ": wrong file type";
}
if(do_sound) {
if(preload) {
llPreloadSound(fname);
} else if(venue == S_INT) {
if(loop) {
// llLoopSound(fname, volume);
llLinkPlaySound(link_number, fname, volume, SOUND_LOOP);
} else {
// llPlaySound(fname, volume);
llLinkPlaySound(link_number, fname, volume, SOUND_PLAY);
}
} else if(venue == S_EXT) {
if(fuuid) {
if(loop) {
msg = "external sound playback does not support loop";
} else {
key controller = getdbl("device", ["controller", "id"]);
if(controller != JSON_INVALID) {
io_tell(controller, C_LIGHT_BUS, "fx s " + fuuid + " " + (string)volume);
} else {
msg = "no device available for external sound playback";
}
}
} else {
msg = "cannot get UUID of '" + fname + "' for external playback; insufficient inventory permissions";
}
}
} else if(do_animation) {
if(llGetPermissions() & PERMISSION_TRIGGER_ANIMATION) {
llStartAnimation(fname);
} else {
anim_start_queue += fname;
llRequestPermissions(avatar, PERMISSION_TRIGGER_ANIMATION);
if(perms_thread == "") {
task_begin(perms_thread = llGenerateKey(), "");
}
}
}
} else if(arg == "trigger") {
string fname = gets(argv, argi);
integer ftype = llGetInventoryType(fname);
integer do_sound = 0;
if(ftype == INVENTORY_NONE) {
if(validate_key(fname)) {
do_sound = 1;
} else {
msg = "no file '" + fname + "'";
}
} else if(ftype == INVENTORY_SOUND) {
do_sound = 1;
} else {
msg = fname + ": wrong file type";
}
if(do_sound)
// llTriggerSound(fname, volume);
llLinkPlaySound(link_number, fname, volume, SOUND_TRIGGER);
} else if(arg == "-i" || arg == "internal") {
venue = S_INT;
} else if(arg == "-x" || arg == "external") {
venue = S_EXT;
} else if(arg == "freeze") {
effector_restrict("mfreeze", "move=?");
} else if(arg == "unfreeze") {
effector_release("mfreeze");
} else if(arg == "-s" || arg == "sleep" || arg == "-w" || arg == "wait" || arg == "-d" || arg == "duration") {
++argi;
float length = (float)gets(argv, argi);
llSleep(argi);
} else if(arg == "stop") {
action = S_STOP;
string fname = gets(argv, argi + 1);
if(llGetInventoryType(fname) == INVENTORY_ANIMATION) {
++argi;
if(llGetPermissions() & PERMISSION_TRIGGER_ANIMATION) {
llStopAnimation(fname);
} else {
anim_stop_queue += fname;
llRequestPermissions(avatar, PERMISSION_TRIGGER_ANIMATION);
if(perms_thread == "") {
task_begin(perms_thread = llGenerateKey(), "");
}
}
} else if(venue == S_INT) {
// llStopSound();
llLinkStopSound(link_number);
} else if(venue == S_EXT) {
key controller = getdbl("device", ["controller", "id"]);
if(controller != JSON_INVALID) {
io_tell(controller, C_LIGHT_BUS, "fx s " + SILENCE + " 0");
} else {
msg = "no device available for external sound playback";
}
}
} else if(arg == "-v" || arg == "volume" || arg == "vol") {
++argi;
volume = (float)gets(argv, argi);
if(venue == S_INT)
llAdjustSoundVolume(volume);
} else {
msg = PROGRAM_NAME + ": unexpected parameter '" + arg + "'; see 'help media' for usage";
}
++argi;
}
}
if(msg != "") {
print(outs, user, PROGRAM_NAME + ": " + msg);
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
} 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);
}
}
#define EXT_EVENT_HANDLER "ARES/application/media.event.lsl"
#include <ARES/program>

311
ARES/application/news.lsl Normal file
View File

@ -0,0 +1,311 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* News RSS Aggregation Client
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#define CLIENT_VERSION "0.2.2"
#define CLIENT_VERSION_TAGS "alpha"
list feeds;
list headlines;
integer offset;
integer rate;
key news_pipe;
#define parcel_name() gets(llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_NAME]), 0)
#define parcel_traffic() count(llGetAgentList(AGENT_LIST_PARCEL, []))
string parcel;
check_parcel() {
string new_parcel = parcel_name();
if(new_parcel != parcel || parcel == "") {
alert("Welcome to " + new_parcel + ", " + llGetRegionName() + " (Population: " + (string)parcel_traffic() + ")",
ALERT_ICON_INFO, ALERT_COLOR_NORMAL, ALERT_BUTTONS_DISMISS, ["!clear"]);
parcel = new_parcel;
setdbl("news", ["parcel"], parcel);
}
}
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
if(argc == 1) {
msg = PROGRAM_NAME + " add <url>: adds a URL to the news feed list\n"
+ PROGRAM_NAME + " remove <url>: removes a URL from the news feed list\n"
+ PROGRAM_NAME + " feeds|headlines: shows current data\n"
+ PROGRAM_NAME + " rate <s>: seconds between each query (minimum 30, or 0 = off)\n"
+ "\nEach feed should return only the current headline. The program will cache the most recent headline from each feed and only display the headline when an update occurs. Queries are staggered by the number of feeds, so with N feeds, a new HTTP request is generated every N/s seconds.";
} else {
string action = gets(argv, 1);
string value = gets(argv, 2);
if(action == "feeds") {
msg = "Current feeds:\n" + concat(feeds, "\n");
} else if(action == "headlines") {
msg = "Latest headlines:\n" + concat(headlines, "\n");
} else if(action == "add") {
if(contains(feeds, value)) {
msg = "Already subscribed to news feed " + value;
} else {
msg = "Added news feed " + value;
feeds += value;
headlines += "?";
setdbl("news", ["feed"], jsarray(feeds));
integer fn = count(feeds);
set_timer("update", (float)rate / (float)fn);
}
} else if(action == "remove") {
integer fi = index(feeds, value);
if(~fi) {
msg = "Removed news feed " + value;
feeds = delitem(feeds, fi);
headlines = delitem(headlines, fi);
setdbl("news", ["feed"], jsarray(feeds));
setdbl("news", ["headline"], jsarray(headlines));
integer fn = count(feeds);
integer real_rate = llRound((float)rate / (float)fn);
integer minimum_rate = (integer)getdbl("interface", ["alert", "time"]);
if(real_rate <= minimum_rate)
real_rate = minimum_rate + 1;
if(fn)
set_timer("update", real_rate);
} else {
msg = "Not subscribed to news feed " + value;
}
} else if(action == "rate") {
integer rate = (integer)value;
if(rate > 0) {
if(rate < 30)
rate = 30;
integer fn = count(feeds);
integer real_rate = llRound((float)rate / (float)fn);
integer minimum_rate = (integer)getdbl("interface", ["alert", "time"]);
if(real_rate <= minimum_rate)
real_rate = minimum_rate + 1;
msg = "News feeds will now update every " + (string)(real_rate * fn) + " seconds.";
setdbl("news", ["rate"], (string)rate);
if(fn)
set_timer("update", real_rate);
} else {
rate = 0;
msg = "News feeds disabled.";
set_timer("update", 0);
}
setdbl("news", ["rate"], (string)rate);
} else if(action == "open") {
string name = llGetObjectName();
llSetObjectName("ARES News Ticker");
llLoadURL(user, "See full article?", value);
llSetObjectName(name);
} else {
msg = PROGRAM_NAME + ": unknown action '" + action + "'; see 'help news' for usage";
}
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_TIMER) {
// echo("news timer occurred: " + m);
if(count(feeds) == 0) {
set_timer("update", 0);
} else if((integer)getdbl("status", ["on"])) {
offset = (offset + 1) % count(feeds);
pipe_open(["notify " + PROGRAM_NAME + " headline"]);
}
} else if(n == SIGNAL_NOTIFY) {
list argv = split(m, " ");
string reason = gets(argv, 1);
if(reason == "pipe") {
news_pipe = ins;
string feed = gets(feeds, offset);
if(feed == "") {
offset = (offset + 1) % count(feeds);
feed = gets(feeds, offset);
}
http_get(feed, news_pipe, avatar);
} else if(reason == "headline") {
string url = gets(feeds, offset);
string headline;
pipe_read(ins, headline);
if(~strpos(headline, "<rss") || ~strpos(headline, "<feed")) {
string item;
integer item_start = strpos(headline, "<item");
if(~item_start) {
item = delstring(headline, 0, item_start);
integer item_end = strpos(item, "</item>");
if(~item_end) {
item = substr(item, 0, item_end);
}
} else {
integer entry_start = strpos(headline, "<entry");
if(~entry_start) {
item = delstring(headline, 0, entry_start);
integer entry_end = strpos(item, "</entry>");
if(~entry_end) {
item = substr(item, 0, entry_end);
}
}
}
// echo((string)strlen(headline));
headline = "(truncated Atom or RSS entry)";
integer title_start = strpos(item, "<title>");
integer title_end = strpos(item, "</title>");
if(~title_start && ~title_end) {
headline = llStringTrim(substr(item, title_start + 7, title_end - 1), STRING_TRIM);
headline = replace(headline, "\n", "");
if(substr(headline, 0, 8) == "<![CDATA[" && substr(headline, -3, -1) == "]]>")
headline = substr(headline, 9, -4);
string bad_punctuation = "‘’“”–—";
string good_punctuation = "''\"\"--";
integer sbs = strlen(bad_punctuation);
while(sbs--) {
headline = replace(headline, substr(bad_punctuation, sbs, sbs), substr(good_punctuation, sbs, sbs));
}
}
integer url_start = strpos(item, "<link>");
integer url_end = strpos(item, "</link>");
if(~url_start && ~url_end) {
url = llStringTrim(substr(item, url_start + 6, url_end - 1), STRING_TRIM);
url = replace(url, "\n", "");
} else if(~(url_start = strpos(item, "<link href=\""))) {
url = substr(item, url_start + 12, LAST);
url_end = strpos(url, "\"");
if(~url_end)
url = substr(url, 0, url_end - 1);
}
} else {
list ni = split(headline, "\n");
if(count(ni) > 1) {
url = gets(ni, 1);
headline = gets(ni, 0);
}
}
if(headline != gets(headlines, offset)) {
headlines = alter(headlines, [headline], offset, offset);
setdbl("news", ["headline"], jsarray(headlines));
alert(headline,
ALERT_ICON_INFO,
ALERT_COLOR_NORMAL,
ALERT_BUTTONS_DETAILS,
["!clear", "news open " + url]
);
}
pipe_close([ins]);
}
} else if(n == SIGNAL_EVENT) {
integer e = (integer)m;
if(e == EVENT_TELEPORT || e == EVENT_REGION_CHANGE) {
check_parcel();
} else {
echo("news event ?" + m);
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
string s_news = llLinksetDataRead("news");
if(s_news == JSON_INVALID) {
s_news = "{\"feed\":[],\"headline\":[],\"rate\":0}";
llLinksetDataWrite("news", s_news);
}
feeds = js2list(getjs(s_news, ["feed"]));
headlines = js2list(getjs(s_news, ["headline"]));
// blank entries can sometimes get created in a fresh database:
integer mutated;
integer blank_index;
while(~(blank_index = index(feeds, ""))) {
feeds = delitem(feeds, blank_index);
headlines = delitem(headlines, blank_index);
mutated = TRUE;
}
if(mutated) {
s_news = setjs(s_news, ["feed"], jsarray(feeds));
s_news = setjs(s_news, ["headline"], jsarray(headlines));
}
rate = (integer)getjs(s_news, ["rate"]);
parcel = getjs(s_news, ["parcel"]);
if(parcel == "" || parcel == JSON_INVALID) {
parcel = parcel_name();
s_news = setjs(s_news, ["parcel"], parcel);
mutated = 1;
} else {
check_parcel();
}
if(mutated) {
llLinksetDataWrite("news", s_news);
}
integer fn = count(feeds);
integer real_rate = llRound((float)rate / (float)fn);
integer minimum_rate = (integer)getdbl("interface", ["alert", "time"]);
if(real_rate <= minimum_rate)
real_rate = minimum_rate + 1;
if(fn)
set_timer("update", real_rate);
hook_events([EVENT_TELEPORT, EVENT_REGION_CHANGE]);
} 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 <ARES/program>

View File

@ -0,0 +1,158 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Personality System Module
*
* 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 <ARES/a>
#include <ARES/api/auth.h.lsl>
#define CLIENT_VERSION "1.0.2"
#define CLIENT_VERSION_TAGS "release"
string persona = "default";
key file_pipe;
integer file_offset;
integer file_length;
list available_personas;
// for activating a persona:
key a_user;
key a_outs;
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;
if(argc == 1) {
available_personas = js2list(llLinksetDataRead("fs:persona"));
msg = "active persona: " + persona + "\n\npersonas available: " + concat(available_personas, ", ");
} else {
string action = gets(argv, 1);
if(action == "set") {
integer permission = sec_check(user, "persona", outs, "", m);
if(permission == ALLOWED) {
list blocked_settings = js2list(getdbl("persona", ["block"]));
string setting = gets(argv, 2);
if(!contains(blocked_settings, setting) && setting != "block")
setdb("persona", setting, concat(delrange(argv, 0, 2), " "));
} else if(permission == DENIED) {
msg = "denied: special authorization is required to change this unit's persona";
}
} else if(llOrd(action, 0) == 0x2e) { // '.'
//echo("do persona . command: " + action);
string command = getdbl("persona", ["action", delstring(action, 0, 0)]);
if(command != JSON_INVALID) {
invoke("input say " + command, outs, ins, NULL_KEY);
} else if(action == ".info") {
string pslist = "." + concat(jskeys(getdbl("persona", ["action"])), ", .");
if(pslist == ".")
pslist = "(none)";
msg = "Available actions: " + pslist;
} else {
msg = "Not recognized: '" + action + "'. Type '.info' for a list of available persona actions.";
}
} else {
integer permission = sec_check(user, "persona", outs, "", m);
if(permission == ALLOWED) {
available_personas = js2list(llLinksetDataRead("fs:persona"));
if(contains(available_personas, action)
|| contains(available_personas, action + ".p")) {
if(substr(action, -2, LAST) == ".p")
action = delstring(action, -2, LAST);
setdbl("m:persona", ["f"], action);
string status_persona = action;
if(status_persona == "default")
status_persona = "";
e_call(C_STATUS, E_SIGNAL_CALL, NULL_KEY + " " + NULL_KEY + " status persona " + status_persona);
string rlv_path = getdbl("persona", ["path"]);
effector_rlv("detachall:" + rlv_path + "=force");
persona = action;
msg = "activating persona: " + persona;
if(user != avatar)
echo(msg);
setdbl("persona", ["persona"], persona);
a_outs = outs;
task_begin(a_user = user, "persona");
set_mode(_mode | MODE_ACCEPT_DONE);
invoke("exec do " + persona + ".p", outs, ins, user);
} else {
#ifdef DEBUG
msg = "bad command (no file): " + m;
#else
msg = "no matching persona file found: " + action;
#endif
}
} else if(permission == DENIED) {
msg = "denied: special authorization is required to change this unit's persona";
}
}
}
if(msg)
print(outs, user, msg);
} else if(n == SIGNAL_DONE) {
if(_mode & MODE_ACCEPT_DONE) {
set_mode(_mode & ~MODE_ACCEPT_DONE);
task_end(a_user);
print(a_outs, a_user, "[" + PROGRAM_NAME + "] activated " + persona);
if(persona != "default")
announce("persona-1");
else
announce("persona-0");
string rlv_path = getdbl("persona", ["path"]);
effector_rlv("attachallover:" + rlv_path + "=force");
}
} else if(n == SIGNAL_INIT) {
print(outs, user, "[" + PROGRAM_NAME + "] init event");
available_personas = js2list(llLinksetDataRead("fs:persona"));
} else if(n == SIGNAL_UNKNOWN_SCRIPT) {
echo("[" + PROGRAM_NAME + "] failed to run '" + m + "' (kernel could not find the program specified)");
} else {
print(outs, user, "[" + PROGRAM_NAME + "] unimplemented signal " + (string)n + ": " + m);
}
}
#include <ARES/program>

View File

@ -0,0 +1,137 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* saver Utility - Event Handlers
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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.
*
* =========================================================================
*
*/
timer() {
list acts;
if(current_saver == "stars") {
float cycle = 0;
float now = llGetTime() * 0.2;
float t = now - llFloor(now);
integer n = STARS_LAYERS;
while(n--) {
float i = (float)n / STARS_LAYERS;
float scale = t + i;
if(scale > 1.0) {
scale -= 1.0;
}
float o = (1 - scale * scale * scale) * (scale * scale - 0.05) * 4.25;
if(o > 1)
o = 1;
else if(o < 0)
o = 0;
acts += [
PRIM_LINK_TARGET, SS_FG + n,
//PRIM_SIZE, ONES * (2 + scale * 4.0),
PRIM_SIZE, ONES * (1.0 / (1.01 - scale)),
PRIM_POSITION, <-5, 0, 0>,
PRIM_COLOR, 0, ONES, o,
PRIM_ROTATION,
// llEuler2Rot(<0, 0, scale * TWO_PI>) *
VISIBLE,
PRIM_TEXTURE, 0, STARS_TEX, ONES, ZV, n * n
];
}
} else if(current_saver == "bounce") {
integer usable_width = screen_width - 256;
integer usable_height = screen_height - 128;
// float now = llGetAndResetTime();
bounce_pos += bounce_dir * BOUNCE_SPEED;
if(bounce_pos.y <= usable_width * -0.5
|| bounce_pos.y >= usable_width * 0.5) {
bounce_dir.y = -bounce_dir.y;
}
if(bounce_pos.z <= usable_height * -0.5
|| bounce_pos.z >= usable_height * 0.5) {
bounce_dir.z = -bounce_dir.z;
}
acts += [
PRIM_LINK_TARGET, SS_FG,
PRIM_SIZE, <256, 128, 0> * pixel_scale,
PRIM_POSITION, <-5, 0, 0> + bounce_pos * pixel_scale,
PRIM_TEXTURE, 0, model_badge, ONES, ZV, 0,
PRIM_ROTATION, VISIBLE,
PRIM_COLOR, 0, <1, 0, 0> * llEuler2Rot(<0, 0, llGetTime()> * PI) * 0.5 + ONES * 0.5, 1
];
} else if(current_saver == "toasters") {
integer n = TOASTERS;
while(n--) {
vector tp = getv(toaster_pos, n);
tp += <0, 2, -1> * (n + MIN_TOAST_SPEED);
integer visible = 1;
if(tp.y >= screen_width * 0.5 + 64
|| tp.z <= screen_height * -0.5 - 64) {
tp.y = screen_width * 0.25 - llFloor(llFrand(screen_width * 2));
tp.z = screen_height * 0.5 + 128;
visible = 0;
integer is_toast = llFrand(1.5) > 1;
if(!is_toast)
llSetLinkTextureAnim(SS_FG + n, ANIM_ON | LOOP | PING_PONG, ALL_SIDES, 4, 4, 0, 4, 16);
else
llSetLinkTextureAnim(SS_FG + n, ANIM_ON | LOOP | PING_PONG, ALL_SIDES, 4, 4, 4, 1, 16);
} else if(tp.y < (screen_width * -0.5 - 64)
|| tp.z > (screen_height * 0.5 + 64)) {
visible = 0;
}
acts += [
PRIM_LINK_TARGET, SS_FG + n,
PRIM_SIZE, <64, 64, 0> * pixel_scale,
PRIM_TEXTURE, 0, TOASTER_TEX, <0.25, 0.25, 0>, <-.375, .375, 0>, 0,
PRIM_POSITION, tp * pixel_scale - <5, 0, 0>,
PRIM_ROTATION, VISIBLE,
PRIM_COLOR, 0, ONES, visible
];
toaster_pos = alter(toaster_pos, [tp], n, n);
}
} else {
echo(current_saver);
}
setp(0, acts);
}

219
ARES/application/saver.lsl Normal file
View File

@ -0,0 +1,219 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Screen Saver Program
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#include <ARES/api/interface.consts.h.lsl>
#define CLIENT_VERSION "1.0.1"
#define CLIENT_VERSION_TAGS "release"
#define call_interface(_outs, _user, _msg) \
system(SIGNAL_CALL, E_INTERFACE + E_PROGRAM_NUMBER \
+ (string)_outs + " " + (string)_user + " interface " + (_msg));
list savers = ["blank", "toasters", "stars", "bounce"];
// #define STARS_TEX "e8105c7f-2595-6bd9-54fb-690764dcac3a"
#define STARS_TEX "e74b5970-f74f-9846-4707-eb9cf97af2f0"
// #define STARS_TEX "3c0d0224-7a9a-5d63-c218-ebd7721f1c08"
#define TOASTER_TEX "d56103e2-2ca9-3a18-40e8-5c3164f2b25c"
#define SS_BG 112
#define SS_FG 113
#define SS_LAYERS 15
#define STARS_LAYERS 8
#define BOUNCE_SPEED 32
#define TOASTERS 14
#define MIN_TOAST_SPEED 4
string model_badge = BADGE_DEFAULT;
integer screen_width = 1920;
integer screen_height = 1008;
float pixel_scale;
string current_saver;
vector bounce_dir = <0, 1, 1>;
vector bounce_pos = ZV;
float last_time;
list toaster_pos;
integer auto_enabled = FALSE;
#define AFK_CHECK_INTERVAL 30
main(integer src, integer n, string m, key outs, key ins, key user) {
@restart_main;
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
string action = gets(argv, 1);
if(action == "stop" || action == "off" || action == "cancel") {
if(current_saver != "") {
task_end("screensaver");
current_saver = "";
integer n = SS_LAYERS;
list acts;
while(n--) {
acts += [
PRIM_LINK_TARGET, SS_BG + n,
PRIM_SIZE, ZV,
PRIM_POSITION, OFFSCREEN,
PRIM_COLOR, 0, ZV, 1,
PRIM_ROTATION, INVISIBLE,
PRIM_TEXTURE, 0, TEXTURE_TRANSPARENT, ZV, ZV, 0
];
llSetLinkTextureAnim(SS_BG + n, 0, ALL_SIDES, 0, 0, 0, 0, 0);
}
setp(0, acts);
}
llSetTimerEvent(0);
call_interface(outs, user, "reconfigure");
unhook_events([EVENT_TOUCH]);
} else if(action == "auto") {
auto_enabled = !auto_enabled;
string saved_saver = getdbl("screensaver", ["name"]);
if(saved_saver == JSON_INVALID)
saved_saver = "random";
print(outs, user, "Screensaver (" + saved_saver + ") will " + gets(["not activate automatically.", "activate automatically when the unit goes Away."], auto_enabled));
set_timer("afk-check", AFK_CHECK_INTERVAL * auto_enabled);
setdbl("screensaver", ["auto"], (string)auto_enabled);
} else if(action == "random" || contains(savers, action)) {
if(action == "random")
current_saver = gets(shuffle(savers, 1), 0);
else
current_saver = action;
string saved_saver = getdbl("screensaver", ["name"]);
if(saved_saver != "random")
setdbl("screensaver", ["name"], action);
if(current_saver != "blank") {
if(current_saver == "toasters" || current_saver == "bounce") {
screen_width = (integer)getdbl("interface", ["width"]);
if(screen_width == 0) {
echo("Error: screen width not set. Please set manually with: '@db interface.width <amount>' using the window size information from your viewer's Help > About menu.");
return;
}
screen_height = (integer)getdbl("interface", ["height"]);
pixel_scale = 1.0 / (float)screen_height;
if(current_saver == "toasters") {
toaster_pos = [];
integer n = TOASTERS;
while(n--) {
toaster_pos += <0, screen_width, screen_height>;
}
} else {
model_badge = getjs("interface", ["badge"]);
if(model_badge == JSON_INVALID)
if((model_badge = getdbl("badge", [replace(getdbl("id", ["model"]), ".", "-")])) == JSON_INVALID)
model_badge = BADGE_DEFAULT;
}
}
llResetTime();
llSetTimerEvent(0.066);
}
hook_events([EVENT_TOUCH]);
task_begin("screensaver", user);
setp(SS_BG, [
PRIM_SIZE, <2, 1, 0>,
PRIM_POSITION, <-4, 0, 0>,
PRIM_COLOR, 0, ZV, 1,
PRIM_ROTATION, VISIBLE,
PRIM_TEXTURE, 0, TEXTURE_BLANK, ZV, ZV, 0
]);
} else {
msg =
PROGRAM_NAME + " stop|off|cancel: interrupts the screensaver\n" +
PROGRAM_NAME + " <saver>: starts a screensaver immediately\n" +
PROGRAM_NAME + " auto: toggles automatic triggering of screensaver once the unit has been Away for 0-30 seconds (uses last screensaver manually activated)\n\n" +
/* PROGRAM_NAME + " clear: removes scheduled screensaver task\n\n" + */
"Available screensavers: " + concat(savers, ", ") + ", random";
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_EVENT) {
if((integer)m == EVENT_TOUCH) {
if(current_saver != "") {
m = PROGRAM_NAME + " stop";
n = SIGNAL_INVOKE;
jump restart_main;
}
}
} else if(n == SIGNAL_TIMER) {
if(llGetAgentInfo(avatar) & AGENT_AWAY && current_saver == "") {
string saved_saver = getdbl("screensaver", ["name"]);
if(saved_saver == JSON_INVALID) {
m = PROGRAM_NAME + " random";
} else {
m = PROGRAM_NAME + " " + saved_saver;
}
n = SIGNAL_INVOKE;
jump restart_main;
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
call_interface(outs, user, "reconfigure");
llSleep(1.0);
auto_enabled = (integer)getdbl("screensaver", ["auto"]);
set_timer("afk-check", AFK_CHECK_INTERVAL * auto_enabled);
} 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);
}
}
#define EXT_EVENT_HANDLER "ARES/application/saver.event.lsl"
#include <ARES/program>

205
ARES/application/scidb.lsl Normal file
View File

@ -0,0 +1,205 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Scientific Database Lookup Utility
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#define CLIENT_VERSION "0.2.1"
#define CLIENT_VERSION_TAGS "alpha"
string DEFAULT_DOMAIN = "eutils.ncbi.nlm.nih.gov";
string DEFAULT_DB = "pubmed";
string DEFAULT_QUERY_KEY = "1";
#define SEARCH_STRING(DB, Q) "/entrez/eutils/esearch.fcgi?usehistory=y&db=" + llEscapeURL(DB) + "&term=" + llEscapeURL(Q)
#define POST_STRING(DB, Q) "/entrez/eutils/epost.fcgi?usehistory=y&db=" + llEscapeURL(DB) + "&id=" + llEscapeURL(Q)
#define SUMMARY_STRING(DB, Q, ENV) "/entrez/eutils/esummary.fcgi?usehistory=y&db=" + llEscapeURL(DB) + "&query_key=" + llEscapeURL(Q) + "&WebEnv=" + llEscapeURL(ENV)
#define ABSTRACT_STRING(DB, Q, ENV) "/entrez/eutils/efetch.fcgi?usehistory=y&db=" + llEscapeURL(DB) + "&query_key=" + llEscapeURL(Q) + "&WebEnv=" + llEscapeURL(ENV) + "&rettype=abstract&retmode=text"
#define FASTA_STRING(DB, Q, ENV) "/entrez/eutils/efetch.fcgi?usehistory=y&db=" + llEscapeURL(DB) + "&query_key=" + llEscapeURL(Q) + "&WebEnv=" + llEscapeURL(ENV) + "&rettype=fasta&retmode=text"
key scidb_receive_pipe;
string waiting_queries = "{}";
string queries_in_progress = "{}";
#define MODE_SEARCH 0
#define MODE_SUMMARY 1
#define MODE_ABSTRACT 2
#define MODE_FASTA 3
send_query(string query, key handle) {
string domain = getjs(query, [4]);
string database = getjs(query, [5]);
string question = getjs(query, [6]);
integer mode = (integer)getjs(query, [7]);
string query_key = getjs(query, [8]);
queries_in_progress = setjs(queries_in_progress, [(string)handle], query);
string url;
if(mode == MODE_SEARCH)
url = domain + SEARCH_STRING(database, question);
else if(mode == MODE_SEARCH)
url = domain + POST_STRING(database, question);
else if(mode == MODE_SUMMARY)
url = domain + SUMMARY_STRING(database, query_key, question);
else if(mode == MODE_ABSTRACT)
url = domain + ABSTRACT_STRING(database, query_key, question);
else if(mode == MODE_FASTA)
url = domain + FASTA_STRING(database, query_key, question);
http_get(url, scidb_receive_pipe, handle);
}
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
if(argc == 1) {
msg = "Syntax: " + PROGRAM_NAME + " [-d <database> -m <domain> -q <querykey> -s|-f|-a-p] <query>\n\nLooks up <query> on the specificed [https://www.ncbi.nlm.nih.gov/ NCBI Entrez] database at <domain> (default: " + DEFAULT_DB + " at " + DEFAULT_DOMAIN + "). -s will retrieve summaries from a matching WebEnv query. -q will set the query key (default: 1) -p will post queries to sequence databases. -a will fetch abstracts. -f will fetch FASTA sequences.";
} else {
string domain = DEFAULT_DOMAIN;
string database = DEFAULT_DB;
integer mode = MODE_SEARCH;
string query_key = DEFAULT_QUERY_KEY;
string a1;
integer a1i;
while(~(a1i = llListFindList(["-m", "-d", "-s", "-f", "-a", "-q"], [a1 = gets(argv, 1)]))) {
if(a1i == 0) { // -m
domain = gets(argv, 2);
argv = delrange(argv, 1, 2);
} else if(a1i == 1) { // -d
database = gets(argv, 2);
argv = delrange(argv, 1, 2);
} else if(a1i == 5) { // -q
query_key = gets(argv, 2);
argv = delrange(argv, 1, 2);
} else if(a1i == 2) { // -s
mode = MODE_SUMMARY;
argv = delrange(argv, 1, 1);
} else if(a1i == 3) { // -f
mode = MODE_FASTA;
argv = delrange(argv, 1, 1);
} else if(a1i == 4) { // -a
mode = MODE_ABSTRACT;
argv = delrange(argv, 1, 1);
}
}
string question = concat(delitem(argv, 0), " ");
if(!~strpos(domain, "://"))
domain = "https://" + domain;
key handle = llGenerateKey();
string query = jsarray([outs, ins, user, _resolved, domain, database, question, mode, query_key]);
_resolved = 0;
llSetMemoryLimit(0x10000);
if(scidb_receive_pipe) {
queries_in_progress = setjs(queries_in_progress, [handle], query);
send_query(query, handle);
} else {
scidb_receive_pipe = llGenerateKey();
waiting_queries = setjs(waiting_queries, [handle], query);
pipe_open(["p:"+(string)scidb_receive_pipe + " notify " + PROGRAM_NAME + " response"]);
}
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_NOTIFY) {
list argv = split(m, " ");
string cmd = gets(argv, 1);
if(cmd == "pipe") {
list queries = jskeys(waiting_queries);
integer qi = 0;
integer qmax = count(queries);
while(qi < qmax) {
key handle = gets(queries, qi);
string query = getjs(waiting_queries, [(string)handle]);
waiting_queries = setjs(waiting_queries, [(string)handle], JSON_DELETE);
send_query(query, handle);
++qi;
}
} else if(cmd == "response") {
string query = getjs(queries_in_progress, [(string)user]);
if(query != JSON_INVALID) {
key o_outs = getjs(query, [0]);
key o_ins = getjs(query, [1]);
key o_user = getjs(query, [2]);
integer o_rc = (integer)getjs(query, [3]);
string buffer;
pipe_read(ins, buffer);
/* string fieldset = getjs(buffer, ["query", "pages", 0]);
string field;
if(fieldset != JSON_INVALID) {
if(getjs(fieldset, ["missing"]) == JSON_TRUE) {
field = getjs(fieldset, ["title"]) + " does not exist (" + getjs(query, [0]) + "/)";
} else {
field = getjs(fieldset, ["extract"]);
}
} else {
field = buffer;
} */
print(o_outs, o_user, buffer);
// resolve_io(o_rc, o_outs, o_ins);
queries_in_progress = setjs(queries_in_progress, [(string)user], JSON_DELETE);
resolve_i(o_rc, o_ins);
}
if(queries_in_progress == "{}") {
pipe_close(scidb_receive_pipe);
scidb_receive_pipe = "";
llSetMemoryLimit(0x2000);
}
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
llSetMemoryLimit(0x2000);
} 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 <ARES/program>

91
ARES/application/tell.lsl Normal file
View File

@ -0,0 +1,91 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Tell Command
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#define CLIENT_VERSION "1.2.0"
#define CLIENT_VERSION_TAGS "release"
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = splitnulls(m, " "); // non-standard
integer argc = count(argv);
string msg = "";
if(argc == 1) {
msg = "Syntax: " + PROGRAM_NAME + " <key> <channel> <message>\n\nIf <key> is 00000000-0000-0000-0000-000000000000 or 'all', sends to everyone. If <channel> is 'lights', message is sent over light bus. If <channel> is 'instant', sends using llInstantMessage(). Cannot send to everyone if channel is 0 (SL restriction). Message will originate from ring 2 via io daemon (io_tell()) unless using 'instant'.";
} else {
key target = gets(argv, 1);
if(target == NULL_KEY)
return;
if(target == "all")
target = NULL_KEY;
integer channel;
string raw_channel = gets(argv, 2);
if(raw_channel == "instant") {
llInstantMessage(target, concat(delrange(argv, 0, 2), " "));
return;
} else if(raw_channel == "lights") {
if(llGetAttached())
channel = 105 - (integer)("0x" + substr(avatar, 29, 35));
else
channel = 105 - (integer)("0x" + substr(KERNEL, 29, 35));
} else {
channel = (integer)raw_channel;
}
if(channel == 0 && target == NULL_KEY) {
msg = PROGRAM_NAME + ": failed to execute '" + m + "' (may not broadcast on channel 0)";
} else {
io_tell(target, channel, concat(delrange(argv, 0, 2), " "));
}
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
} 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 <ARES/program>

View File

@ -0,0 +1,63 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Blank Program Template
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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 <ARES/a>
#define CLIENT_VERSION "0.0.1"
#define CLIENT_VERSION_TAGS "placeholder"
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
string action = gets(argv, 1);
if(argc == 1 || action == "help") {
msg = "Syntax: " + PROGRAM_NAME + " <action>";
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
} 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 <ARES/program>

87
ARES/application/type.lsl Normal file
View File

@ -0,0 +1,87 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* type Example Program
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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 <ARES/a>
#define CLIENT_VERSION "1.1.1"
#define CLIENT_VERSION_TAGS "release"
#define FILE_STEP_SIZE 4
#include <ARES/api/file.h.lsl>
key file_q;
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
if(argc == 1) {
print(outs, user, "Syntax: " + PROGRAM_NAME + " <filename>");
} else {
string file_name = gets(argv, 1);
file_q = fopen(outs, ins, user, file_name);
}
} else if(n == SIGNAL_NOTIFY) {
if(m == PROGRAM_NAME + " file") {
if(ins == file_q) {
user = getjs(tasks_queue, [file_q, FILE_USER]);
outs = getjs(tasks_queue, [file_q, FILE_OUTS]);
string fn = getjs(tasks_queue, [file_q, FILE_NAME]);
string unit = getjs(tasks_queue, [file_q, FILE_UNIT]);
string result = fread(file_q);
if(result == JSON_FALSE) {
print(user, user, "[" + PROGRAM_NAME + "] No file: " + fn);
} else if(result != JSON_TRUE) {
if(unit == "l")
result = llStringTrim(result, STRING_TRIM_TAIL);
print(outs, user, result);
// llSleep(0.1);
}
} else {
echo("[" + PROGRAM_NAME + "] file data offered via unexpected pipe: " + (string)ins);
}
}
} else if(n == SIGNAL_INIT) {
// print(outs, user, "[" + PROGRAM_NAME + "] init event; nothing to do");
} 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 <ARES/program>

223
ARES/application/xset.lsl Normal file
View File

@ -0,0 +1,223 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* XSET Command (Standalone)
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 2 (ASCL-ii). Although it appears in ARES as part of
* commercial software, it may be used as the basis of derivative,
* non-profit works that retain a compatible license. Derivative works of
* ASCL-ii software must retain proper attribution in documentation and
* source files as described in the terms of the ASCL. Furthermore, they
* must be distributed free of charge and provided with complete, legible
* source code included in the package.
*
* 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 <ARES/a>
#define CLIENT_VERSION "1.2.1"
#define CLIENT_VERSION_TAGS "release"
/*
replaces (string) number with (integer)number
and (string)\"number\" with (string)number
... from db.lsl
*/
list process_keyname(list keyname) {
integer ki = count(keyname);
while(ki--) {
string ks = gets(keyname, ki);
if((string)((integer)ks) == ks)
keyname = alter(keyname, [(integer)ks], ki, ki);
else if(llOrd(ks, 0) == 0x22 && llOrd(ks, LAST) == 0x22)
keyname = alter(keyname, [substr(ks, 1, -2)], ki, ki);
}
return keyname;
}
list pipes;
list tags;
finish(key tag) {
integer ti = index(tags, tag);
if(~ti) {
integer r = (integer)getjs(tasks_queue, [tag, "r"]);
key ins = getjs(tasks_queue, [tag, "ins"]);
key outs = getjs(tasks_queue, [tag, "outs"]);
task_end(tag);
pipe_close([getk(pipes, ti)]);
pipes = delitem(pipes, ti);
tags = delitem(tags, ti);
// resolve_io(r, outs, ins);
resolve_i(r, ins);
#ifdef DEBUG
echo("xset: resolved");
#endif
} else {
echo("xset: spurious receipt: " + (string)tag);
}
}
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = splitnulls(m, " ");
integer quick_mode;
if(gets(argv, 1) == "-q") {
quick_mode = 1;
argv = delitem(argv, 1);
}
integer argc = count(argv);
string msg = "";
if(argc < 2) {
msg = "Syntax: " + PROGRAM_NAME + " [-q] <variable> <command>\n\nxset WILL hang if a job returns no output at all. If this is a risk, specify -q.\n\nNew in 1.2: The command 'db json' will be detected and parsed; try also 'xset <var> = <db.value>' as a nicer alternative.";
} else if(argc >= 3) {
string varname = gets(argv, 1);
string command = concat(delrange(argv, 0, 1), " ");
if(gets(argv, 2) == "=" || (gets(argv, 2) == "db" && gets(argv, 3) == "json")) { // short-circuit tedious 'db json' usage
if(gets(argv, 2) == "db")
argv = delitem(argv, 3);
string keyname = gets(argv, 3);
list keyparts = process_keyname(split(keyname, "."));
string value = getdbl(gets(keyparts, 0), delitem(keyparts, 0));
if(value != JSON_INVALID) {
setdb("env", varname, value); // not dbl
} else {
if(getdb("env", varname) != JSON_INVALID) // not dbl
deletedb("env", varname); // still not dbl
}
} else {
key tag = llGenerateKey();
key pipe = llGenerateKey();
pipes += pipe; // where we'll receive data from the subtask
tags += tag; // the receipt we'll get when the subtask resolves
task_begin(tag, jsobject([
"r", _resolved, // the PID we'll report to when we're done
"ins", ins, // input stream of initial calling environment (re-used as subtask's output stream)
"outs", outs, // output stream of initial calling environment (not seen by subtask)
"var", varname, // env variable we're updating
"command", command, // the command we're executing
"user", user, // who we're doing this for
"done", 0, // turns to 1 once we receive DONE
"data", quick_mode // turns to 1 once we receive data
]));
// xset ends a task when both 'done' and 'data' conditions are met
pipe_open(["p:" + (string)pipe + " notify " + PROGRAM_NAME + " data"]);
_resolved = 0;
}
} else {
msg = PROGRAM_NAME + ": insufficient arguments: " + m + "; see 'help xset' for usage";
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_DONE) {
key tag = substr(m, 0, 35);
integer got_data = (integer)getjs(tasks_queue, [tag, "data"]);
if(got_data) {
finish(tag);
} else {
tasks_queue = setjs(tasks_queue, [tag, "done"], "1");
}
} else if(n == SIGNAL_NOTIFY) {
list argv = splitnulls(m, " ");
string reason = gets(argv, 1);
if(reason == "pipe") {
integer pi = index(pipes, ins);
if(!~pi) {
echo("xset: spurious pipe: " + (string)ins);
} else {
#ifdef DEBUG
echo("xset: created pipe " + (string)ins);
#endif
key tag = getk(tags, pi);
string varname = getjs(tasks_queue, [tag, "var"]);
setdbl("env", [varname], "");
string command = getjs(tasks_queue, [tag, "command"]);
invoke(command, ins, tag, getjs(tasks_queue, [tag, "user"]));
#ifdef DEBUG
echo("xset: invoked " + command);
#endif
}
} else if(reason == "data") {
integer pi = index(pipes, ins);
if(!~pi) {
echo("xset: data from non-open pipe: " + (string)ins);
} else {
key tag = getk(tags, pi);
string buffer;
pipe_read(ins, buffer);
string varname = getjs(tasks_queue, [tag, "var"]);
#ifdef DEBUG
echo("xset: data received for " + varname);
#endif
string current_value = getdbl("env", [varname]);
if(current_value != "")
current_value += "\n";
setdb("env", varname, current_value + buffer);
integer got_done = (integer)getjs(tasks_queue, [tag, "done"]);
if(got_done) {
finish(tag);
} else {
tasks_queue = setjs(tasks_queue, [tag, "data"], "1");
}
}
} else {
echo("xset: unexpected notify: " + m);
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
set_mode(_mode | MODE_ACCEPT_DONE);
} 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 <ARES/program>

View File

@ -0,0 +1,48 @@
ARES Command Cheat Sheet
By default, ARES commands start with '@' when typed in local chat.
However, '@' is not used when writing shell scripts. In this case, an '@' in an ARES script file indicates a label.
Linux bash Windows cmd.exe ARES exec ARES aliases
------------------------------------------------------------------------------
cat <file> type <file> type <file>
ls <pattern> dir <pattern> fs match <pattern> ls <pattern>
ls dir fs
shutdown -h now shutdown /s /t 0 power system off off
echo <string> echo <string> echo <string>
<var>=<string> set <var>=<string> set <var> <string>
rm <file> del <file> fs remove <file>
env set db env env
exit goto :EOF exit
--- goto <label> jump <label>
--- :<label> @<label>:
File existence checks
bash: if -e <file>; then <command>; fi
cmd.exe: if exist <file> <command>
ARES: if exist <file> then <command>
String comparison
bash: if [ <string1>==<string2> ]; then <command>; fi
cmd.exe: if <string1>==<string2> <command>
ARES: if <string1> is <string2> then <command>
Storing a numeric expression in an environment variable
bash: let <var>=<math>
cmd.exe: set /a <var>=<math>
ARES: set <var>=<math>
Notes
- bash is an ALGOL dialect; for ideological reasons it does not support goto
- The 'fs match <pattern>' facility in ARES is designed for creating file associations, not data science; it only supports a single "*" wildcard

View File

@ -0,0 +1,374 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* NS-476 Aegis Hardware Driver
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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 <utils.lsl>
#include <objects.lsl>
vector base_c = <0, 0.5, 1>;
vector c4 = <0, 0.75, 1>;
#define SCREEN 7
// #define SCREEN_2 8
// #define SCREEN_3 9
#define SCREEN_CONE 8
#define TEXT_START 9
#define TEXT_COUNT 66
integer screen_open = FALSE;
#define X_STEP (1.0/15.0)
#define Y_STEP (1.0/52.0)
#define X_BIAS -2.5
#define Y_BIAS -5.0
#define projection_magnitude 0.5
key system;
integer CL;
screen_control(integer open) {
list acts;
if(open) {
if(power_on && !door_open)
operate_door();
vector scale = <0, 5 * X_STEP, 3 * X_STEP>;
integer pn = TEXT_START;
vector screen_origin = <0, 0.625, 0.0>;
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, <projection_magnitude * 0.5, projection_magnitude, projection_magnitude> * 1.05,
PRIM_POS_LOCAL, screen_origin,
/*PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, <0.24, 0.06, 0.06>,
PRIM_POS_LOCAL, screen_origin + <-0.05, 0, 0.18>,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, <0.24, 0.06, 0.06>,
PRIM_POS_LOCAL, screen_origin + <-0.05, 0, -0.18>, */
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_SIZE, <projection_magnitude * 0.5, projection_magnitude, projection_magnitude> * 1.05,
PRIM_POS_LOCAL, screen_origin + <0, 0, -0.001>,
PRIM_COLOR, ALL_SIDES, base_c, 0.05,
PRIM_LINK_TARGET, 1,
PRIM_COLOR, 1, base_c, 1,
PRIM_FULLBRIGHT, 1, TRUE,
PRIM_GLOW, 1, 0.2
];
rotation post_R = llEuler2Rot(<PI_BY_TWO, PI, 0>);
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx + X_BIAS;
float dy = 10 - ty;
float secondary_scale = 1.0;
if(dy == 9) {
dy = 9.25;
secondary_scale = 1;
} else if(dy == 0) {
dy = -0.25;
secondary_scale = 1;
} else if(dy == 10) {
dy = 10.5;
secondary_scale = 1;
}
float y = (float)(dy) + Y_BIAS;
rotation R = llEuler2Rot(<0, 0.17075 * -x * PI_BY_TWO, 0>);
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, scale * secondary_scale,
PRIM_POS_LOCAL, (<0, y * Y_STEP, 0.025 * secondary_scale * secondary_scale> * secondary_scale - <0, 0, projection_magnitude * 0.55>) * R * post_R + screen_origin,
PRIM_ALPHA_MODE, ALL_SIDES, PRIM_ALPHA_MODE_MASK, 128,
// PRIM_ALPHA_MODE, ALL_SIDES, PRIM_ALPHA_MODE_BLEND, 128,
PRIM_ROT_LOCAL, llEuler2Rot(<-PI_BY_TWO, 0, PI_BY_TWO>) * R * post_R
//,
//PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
} else {
integer pn = TEXT_START;
vector screen_origin = <0, 0, 0.25>;
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,
/*PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,*/
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,
PRIM_LINK_TARGET, 1,
PRIM_COLOR, 1, ZV, 1,
PRIM_FULLBRIGHT, 1, FALSE,
PRIM_GLOW, 1, 0
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx - 2.5;
float dy = 10 - ty;
if(dy == 9)
dy = 9.25;
else if(dy == 0)
dy = -0.25;
else if(dy == 10)
dy = 10.5;
float y = (float)(dy) - 5.5;
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV
// PRIM_ROT_LOCAL, ZR,
// PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
if(door_open)
operate_door();
}
screen_open = open;
// echo("screen now " + (string)open);
}
integer power_on;
#define DOOR_MOVE_SOUND "c4f9e160-641f-b61d-7b9e-0bc9d4ac0ea1"
integer door_open;
operate_door() {
llLinkPlaySound(6, DOOR_MOVE_SOUND, 1, SOUND_PLAY);
if(door_open && !power_on)
tell(system, CL, "door 0");
// door_open = 1;
door_open = !door_open;
float sign = (float)(door_open << 1) - 1.0;
float hi = 0;
list lid_props = getp(6, [PRIM_SIZE, PRIM_POS_LOCAL, PRIM_ROT_LOCAL]);
list lid2_props = getp(3, [PRIM_SIZE]);
vector lids = getv(lid_props, 0);
vector lidp = getv(lid_props, 1);
rotation lidr = getr(lid_props, 2);
vector lido = <0, lids.y * -0.5, lids.z * -0.5>;
vector lid2s = getv(lid2_props, 0);
vector lid2o = <0, -0.01, -0.01125>;
float lid2_scale_factor = lid2s.y / 0.0246600;
rotation postR_logo = llEuler2Rot(<0, 0, PI>);
while(hi <= 1) {
rotation lidnr = llEuler2Rot(<120 * DEG_TO_RAD * hi * sign, 0, 0>);
rotation lid2nr = llEuler2Rot(<120 * DEG_TO_RAD * hi * sign, 0, 0>);
setp(6, [
PRIM_POS_LOCAL, lidp + (lido - lido * lidnr) * lidr,
PRIM_ROT_LOCAL, lidnr * lidr,
PRIM_LINK_TARGET, 3,
PRIM_POS_LOCAL, lidp + (lido - (lid2o * lid2_scale_factor + lido) * lidnr) * lidr,
PRIM_ROT_LOCAL, postR_logo * lidnr * lidr
]);
hi += 0.1;
llSleep(0.022);
}
if(door_open && !power_on)
tell(system, CL, "door 1");
}
integer timer_close_hatch;
default {
state_entry() {
// llLinkStopSound(LINK_SET);
llSetLinkTextureAnim(SCREEN_CONE, ANIM_ON | SMOOTH | LOOP | PING_PONG, ALL_SIDES, 0, 0, 0, 1, 100);
llSetLinkTextureAnim(SCREEN, ANIM_ON | SMOOTH | LOOP | PING_PONG, 1, 0, 0, 0, 1, 100);
llSetMemoryLimit(0x8000);
#ifdef TEST_SCREEN
power_on = 1;
screen_control(TRUE);
linked(LINK_THIS, 0, "menu-start", "");
#else
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
#endif
// damp(0);
/* echo(llGetLinkName(HOSE_L));
echo(llGetLinkName(HOSE_R)); */
// damp(0.0);
/*integer pn = llGetNumberOfPrims();
while(pn--) {
echo((string)pn + " = " + llGetLinkName(pn));
}*/
}
touch_start(integer n) {
while(n--) {
key toucher = llDetectedKey(n);
integer pi = llDetectedLinkNumber(n);
string part = llGetLinkName(pi);
if(part == "lid" && !power_on) {
if(door_open) {
timer_close_hatch = 1;
// tell(llGetOwner(), CL, "hatch-blocked-q");
linked(LINK_THIS, 0, "touch-hatch", toucher);
llSetTimerEvent(0.75);
} else {
operate_door();
}
//operate_door();
} else if(part == "text" && power_on) {
linked(LINK_THIS, pi, "touch-screen", toucher);
} else if(part == "lid" && power_on) {
if(door_open && screen_open) {
screen_control(FALSE);
} else {
linked(LINK_THIS, 0, "menu-start", toucher);
screen_control(TRUE);
}
} else if(part == "screen") {
} else {
// if "Object" || ("lid" && power_on)
if((power_on && screen_open) || !power_on) {
linked(LINK_THIS, 0, "menu-request", toucher);
} else {
linked(LINK_THIS, 0, "menu-request", toucher);
/*
linked(LINK_THIS, 0, "menu-start", toucher);
screen_control(TRUE);
*/
}
}
}
}
timer() { // hatch open
llSetTimerEvent(0);
if(door_open && timer_close_hatch && !power_on)
operate_door();
timer_close_hatch = 0;
}
link_message(integer s, integer n, string m, key id) {
// echo("supervisor screen: " + m);
if(m == "hatch-blocked") {
llSetTimerEvent(0);
timer_close_hatch = 0;
} else if(m == "on") {
timer_close_hatch = 0;
llSetTimerEvent(0);
power_on = 1;
system = id;
CL = 105 - (integer)("0x" + substr(llGetOwner(), 29, 35));
} else if(m == "off") {
power_on = 0;
system = id;
CL = 105 - (integer)("0x" + substr(llGetOwner(), 29, 35));
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
// damp(0);
} else if(m == "menu-open") {
if(!screen_open)
screen_control(TRUE);
} else if(m == "menu-close") {
system = id;
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
} else {
list argv = split(m, " ");
string cmd = gets(argv, 0);
/*if(cmd == "fan") {
fan = (float)gets(argv, 1);
damp(rate * 0.001 + fan * 0.5);
} else*/
if(cmd == "color") {
base_c = (vector)concat(delitem(argv, 0), " ");
/* if(screen_open)
screen_control(TRUE); */
} else if(cmd == "color-4") {
c4 = (vector)concat(delitem(argv, 0), " ");
if(screen_open)
screen_control(TRUE);
}
/*else if(cmd == "rate") {
rate = (float)gets(argv, 1);
if(rate > 0)
power_on = 1;
// damp(rate * 0.001 + fan * 0.5);
}*/
}
}
}

View File

@ -0,0 +1,355 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* YS-712 XSU Hardware Driver
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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 <utils.lsl>
#include <objects.lsl>
vector base_c = <0, 0.5, 1>;
vector c4 = <0, 0.75, 1>;
#define SCREEN 35
#define SCREEN_2 36
#define SCREEN_3 37
#define SCREEN_CONE 34
#define TEXT_START 38
#define TEXT_COUNT 66
#define PROJECTOR 5
integer screen_open = FALSE;
#define X_STEP (1.0/15.0)
#define Y_STEP (1.0/5.0)
screen_control(integer open) {
list acts;
if(open) {
vector scale = <0, 5 * X_STEP, 3 * X_STEP>;
integer pn = TEXT_START;
vector screen_origin = <-0.0915, 0, -0.0340>;
#define conex 0.0945
setp(SCREEN_CONE, [
PRIM_POS_LOCAL, screen_origin + <conex * 0.5, 0, 0>
]);
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, <0.4900000, 0.4900000, 0.2450000>,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, <0.24, 0.06, 0.06>,
PRIM_POS_LOCAL, screen_origin + <-0.05, 0, 0.18>,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, <0.24, 0.06, 0.06>,
PRIM_POS_LOCAL, screen_origin + <-0.05, 0, -0.18>,
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_SIZE, <0.4900000, 0.4900000, conex>,
PRIM_COLOR, ALL_SIDES, c4, 0.0625,
PRIM_LINK_TARGET, PROJECTOR,
PRIM_COLOR, ALL_SIDES, c4, 1,
PRIM_FULLBRIGHT, ALL_SIDES, TRUE,
PRIM_GLOW, ALL_SIDES, 1.0
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx - 2.5;
float dy = 10 - ty;
float secondary_scale = 1.0;
if(dy == 9) {
dy = 8.5;
secondary_scale = 1.3;
} else if(dy == 0) {
dy = -0.25;
secondary_scale = 1.1;
} else if(dy == 10) {
dy = 10.5;
secondary_scale = 1.1;
}
float y = (float)(dy) - 5.5;
// rotation R = llEuler2Rot(<0, 0.17075 * x * PI_BY_TWO, 0>);
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, scale * secondary_scale,
PRIM_POS_LOCAL, (<-0.025 * secondary_scale * secondary_scale, -x * X_STEP, y * X_STEP / 3.0> * secondary_scale + screen_origin),
PRIM_ALPHA_MODE, ALL_SIDES, PRIM_ALPHA_MODE_MASK, 128
// PRIM_ROT_LOCAL, llEuler2Rot(<-PI_BY_TWO, 0, PI_BY_TWO>) * R,
//PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
} else {
integer pn = TEXT_START;
vector screen_origin = <0.03125, 0, -0.0340>;
setp(SCREEN_CONE, [
PRIM_SIZE, ZV,
PRIM_COLOR, ALL_SIDES, c4, 0
]);
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, PROJECTOR,
PRIM_COLOR, ALL_SIDES, base_c * power_on, 1,
PRIM_FULLBRIGHT, ALL_SIDES, power_on,
PRIM_GLOW, ALL_SIDES, 0.05 * power_on
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx - 2.5;
float dy = 10 - ty;
if(dy == 9)
dy = 9.25;
else if(dy == 0)
dy = -0.25;
else if(dy == 10)
dy = 10.5;
float y = (float)(dy) - 5.5;
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin
// PRIM_ROT_LOCAL, ZR,
// PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
}
screen_open = open;
// echo("screen now " + (string)open);
}
integer power_on;
#define original_size 0.026540
#define STEP 0.1
#define movement_range 2
operate_door(integer new_state) {
integer door_open = !new_state;
float polarity = 1;
if(door_open) {
polarity = -1;
llTriggerSound("3f075de9-738c-1b1e-ae67-8149a84647ee", 1);
} else {
llTriggerSound("0c205a19-ebd7-7195-7e8e-426b1bda48d4", 1);
}
llTriggerSound("10722bf0-fdac-2497-3b2c-f6d9e6625f7c", 1);
vector s = llGetScale();
float scale = s.x / original_size;
vector so = ZERO_VECTOR; getv(getp(1, [PRIM_POS_LOCAL]), 0);
float initial = movement_range * door_open;
float target = (movement_range * (!door_open)) - initial;
float i;
float r;
#define ioff 0.0073
#define doff 0.003125
vector vo = so + <doff, 0, 0>;
for(i = 0; i <= 1 + STEP; i += STEP) {
r = i * target + initial;
list f = [];
integer p = 3;
while(p--) {
rotation r0 = llEuler2Rot(<0, 270, p * 120 + 180> * DEG_TO_RAD);
rotation r1 = llEuler2Rot(<0, -r, 0>);
vector v = <0.020, 0, -0.002>;
f += [
PRIM_LINK_TARGET, p + 6,
PRIM_POS_LOCAL, (v * r1 - v) * scale * r0 + vo,
PRIM_ROT_LOCAL, r1 * r0
];
}
setp(0, f);
}
if(door_open)
llTriggerSound("d24539cd-0dd0-c0c3-99c2-3421fdbf5656", 1);
else
llTriggerSound("2227d6ea-fe24-dd2f-fb1e-a31e1531b402", 1);
door_open = !door_open;
}
default {
state_entry() {
llSetLinkTextureAnim(SCREEN_CONE, ANIM_ON | LOOP | SMOOTH, ALL_SIDES, 0, 0, 0, 1, 101);
llSetMemoryLimit(0x8000);
//screen_control(FALSE);
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
// damp(0);
/* echo(llGetLinkName(HOSE_L));
echo(llGetLinkName(HOSE_R)); */
// damp(0.0);
/*integer pn = llGetNumberOfPrims();
while(pn--) {
echo((string)pn + " = " + llGetLinkName(pn));
}*/
}
/*
touch_start(integer n) {
while(n--) {
key toucher = llDetectedKey(n);
integer pi = llDetectedLinkNumber(n);
string part = llGetLinkName(pi);
if((part == "lidl" || part == "lidr") && !power_on) {
linked(LINK_THIS, 0, "touch-hatch", toucher);
} else if(part == "text" && power_on) {
linked(LINK_THIS, pi, "touch-screen", toucher);
} else if(part == "screen") {
} else {
// if "Object" || ("lid" && power_on)
if((power_on && screen_open) || !power_on) {
linked(LINK_THIS, 0, "menu-request", toucher);
} else {
linked(LINK_THIS, 0, "menu-start", toucher);
screen_control(TRUE);
}
}
}
}
*/
timer() { // menu expiry
// echo("(supervisor screen menu expiry)");
if(screen_open) {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
}
llSetTimerEvent(0);
// echo("memory status: " + (string)llGetUsedMemory() + " used; " + (string)llGetFreeMemory() + " virgin");
}
link_message(integer s, integer n, string m, key id) {
if(n == 1) {
// non-light-bus messages
if(m == "door 1") {
operate_door(TRUE);
} else if(m == "door 0") {
operate_door(FALSE);
}
} else {
// echo("supervisor screen: " + m);
if(m == "on") {
power_on = 1;
} else if(m == "off") {
power_on = 0;
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
llSetTimerEvent(0);
// damp(0);
} else if(m == "menu-open") {
if(!screen_open)
screen_control(TRUE);
llSetTimerEvent(15);
} else if(m == "menu-close") {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
llSetTimerEvent(0);
} else {
list argv = split(m, " ");
string cmd = gets(argv, 0);
/*if(cmd == "fan") {
fan = (float)gets(argv, 1);
damp(rate * 0.001 + fan * 0.5);
} else*/
if(cmd == "color") {
base_c = (vector)concat(delitem(argv, 0), " ");
/* if(screen_open)
screen_control(TRUE); */
} else if(cmd == "color-4") {
c4 = (vector)concat(delitem(argv, 0), " ");
if(screen_open)
screen_control(TRUE);
}
/*else if(cmd == "rate") {
rate = (float)gets(argv, 1);
if(rate > 0)
power_on = 1;
// damp(rate * 0.001 + fan * 0.5);
}*/
}
}
}
}

View File

@ -0,0 +1,360 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* NS-119 Arecibo Hardware Driver
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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 <utils.lsl>
#include <objects.lsl>
vector base_c = <0, 0.5, 1>;
vector c4 = <0, 0.75, 1>;
#define DORSAL ZERO_ROTATION
#define VENTRAL llEuler2Rot(<PI, PI, 0>)
rotation MASTER_ROT;
vector MASTER_OFFSET;
vector custom_offset;
#define VENTRAL_OFFSET <0.25, 0, 0.2>
#define SCREEN 17
#define SCREEN_2 20
#define SCREEN_3 21
#define SCREEN_4 3
#define SCREEN_5 19
#define SCREEN_CONE 18
#define TEXT_START 22
#define TEXT_COUNT 66
integer screen_open = FALSE;
#define X_STEP (1.0/15.0)
#define Y_STEP (1.0/52.0)
#define X_BIAS -2.5
#define Y_BIAS -5.0
#define projection_magnitude 0.5
screen_control(integer open) {
list acts;
if(open) {
vector scale = <0, 5 * X_STEP, 3 * X_STEP>;
integer pn = TEXT_START;
vector screen_origin = <0, 0, 0.0>;
#define POST_ROT llEuler2Rot(<PI_BY_TWO, PI_BY_TWO, 0>)
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, ONES * 0.75 * projection_magnitude,
PRIM_POS_LOCAL, (screen_origin + MASTER_OFFSET) * MASTER_ROT,
PRIM_ROT_LOCAL, POST_ROT * MASTER_ROT,
PRIM_COLOR, ALL_SIDES, base_c, 0.1,
PRIM_OMEGA, <1, 0, 0>, 1, 1,
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_SIZE, ONES * 0.5 * projection_magnitude,
PRIM_POS_LOCAL, (screen_origin + <0.03125, 0, 0> + MASTER_OFFSET) * MASTER_ROT,
PRIM_ROT_LOCAL, POST_ROT * MASTER_ROT,
PRIM_COLOR, ALL_SIDES, base_c, 0.05,
PRIM_OMEGA, <1, 0, 0>, -2, 1,
PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, ONES * 0.25 * projection_magnitude,
PRIM_POS_LOCAL, (screen_origin + <0.0225, 0, 0> + MASTER_OFFSET) * MASTER_ROT,
PRIM_ROT_LOCAL, POST_ROT * MASTER_ROT,
PRIM_COLOR, ALL_SIDES, base_c, 0.1,
PRIM_OMEGA, <1, 0, 0>, -0.25, 1,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, ONES * 0.75 * projection_magnitude,
PRIM_POS_LOCAL, (screen_origin + <0.1, 0, 0> + MASTER_OFFSET) * MASTER_ROT,
PRIM_ROT_LOCAL, POST_ROT * MASTER_ROT,
PRIM_COLOR, ALL_SIDES, base_c, 0.05,
PRIM_OMEGA, <1, 0, 0>, 0.5, 1,
PRIM_LINK_TARGET, SCREEN_4,
PRIM_SIZE, ONES * 0.5 * projection_magnitude,
PRIM_POS_LOCAL, (screen_origin - <0.065, 0, 0> + MASTER_OFFSET) * MASTER_ROT,
PRIM_ROT_LOCAL, POST_ROT * MASTER_ROT,
PRIM_COLOR, ALL_SIDES, base_c, 0.05,
PRIM_OMEGA, <1, 0, 0>, 1.125, 1,
PRIM_LINK_TARGET, SCREEN_5,
PRIM_SIZE, ONES * 0.875 * projection_magnitude,
PRIM_POS_LOCAL, (screen_origin + <0.13, 0, 0> + MASTER_OFFSET) * MASTER_ROT,
PRIM_ROT_LOCAL, POST_ROT * MASTER_ROT,
PRIM_COLOR, ALL_SIDES, base_c, 0.05,
PRIM_OMEGA, <1, 0, 0>, -0.375, 1,
PRIM_LINK_TARGET, 12,
PRIM_COLOR, 1, base_c, 1,
PRIM_FULLBRIGHT, 1, TRUE,
PRIM_GLOW, 1, 0.2
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx + X_BIAS;
float dy = 10 - ty;
float secondary_scale = 1.0;
if(dy == 9) {
dy = 9.25;
secondary_scale = 1;
} else if(dy == 0) {
dy = -0.25;
secondary_scale = 1;
} else if(dy == 10) {
dy = 10.5;
secondary_scale = 1;
}
#define final_text_offset <-0.25, 0, 0>
float y = (float)(dy) + Y_BIAS;
rotation R = llEuler2Rot(<0, 0.085375 * x * PI_BY_TWO, 0>);
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, scale * secondary_scale,
PRIM_POS_LOCAL, ((<0, y * Y_STEP, 0.025 * secondary_scale * secondary_scale> * secondary_scale + <0, 0, projection_magnitude * 0.95>) * R * POST_ROT + final_text_offset + screen_origin + MASTER_OFFSET) * MASTER_ROT,
PRIM_ALPHA_MODE, ALL_SIDES, PRIM_ALPHA_MODE_MASK, 128,
// PRIM_ALPHA_MODE, ALL_SIDES, PRIM_ALPHA_MODE_BLEND, 128,
PRIM_ROT_LOCAL, llEuler2Rot(<-PI_BY_TWO, 0, PI_BY_TWO>) * R * POST_ROT * MASTER_ROT
//,
//PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
} else {
integer pn = TEXT_START;
vector screen_origin = <-0.0625, 0, 0>;
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_4,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_5,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, 12,
PRIM_COLOR, 1, c4 * power_on, 1,
PRIM_FULLBRIGHT, 1, power_on,
PRIM_GLOW, 1, 0.2 * (float)power_on
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx - 2.5;
float dy = 10 - ty;
if(dy == 9)
dy = 9.25;
else if(dy == 0)
dy = -0.25;
else if(dy == 10)
dy = 10.5;
float y = (float)(dy) - 5.5;
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin
// PRIM_ROT_LOCAL, ZR,
// PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
}
screen_open = open;
// echo("screen now " + (string)open);
}
integer power_on = 1;
default {
state_entry() {
// llLinkStopSound(LINK_SET);
llSetLinkTextureAnim(SCREEN_CONE, ANIM_ON | SMOOTH | LOOP | PING_PONG, ALL_SIDES, 0, 0, 0, 1, 100);
llSetLinkTextureAnim(SCREEN, ANIM_ON | SMOOTH | LOOP | PING_PONG, ALL_SIDES, 0, 0, 0, 1, 100);
llSetLinkTextureAnim(SCREEN_4, ANIM_ON | SMOOTH | LOOP | PING_PONG, ALL_SIDES, 0, 0, 0, 1, 100);
//llSetMemoryLimit(0x8000);
#ifdef TEST_SCREEN
screen_control(TRUE);
linked(LINK_THIS, 0, "menu-start", "");
#else
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
#endif
// damp(0);
/* echo(llGetLinkName(HOSE_L));
echo(llGetLinkName(HOSE_R)); */
// damp(0.0);
/*integer pn = llGetNumberOfPrims();
while(pn--) {
echo((string)pn + " = " + llGetLinkName(pn));
}*/
}
/*
touch_start(integer n) {
while(n--) {
key toucher = llDetectedKey(n);
integer pi = llDetectedLinkNumber(n);
string part = llGetLinkName(pi);
if((part == "lidl" || part == "lidr") && !power_on) {
linked(LINK_THIS, 0, "touch-hatch", toucher);
} else if(part == "text" && power_on) {
linked(LINK_THIS, pi, "touch-screen", toucher);
} else if(part == "screen") {
} else {
// if "Object" || ("lid" && power_on)
if((power_on && screen_open) || !power_on) {
linked(LINK_THIS, 0, "menu-request", toucher);
} else {
linked(LINK_THIS, 0, "menu-start", toucher);
screen_control(TRUE);
}
}
}
}
*/
timer() { // menu expiry
// echo("(supervisor screen menu expiry)");
if(screen_open) {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
}
llSetTimerEvent(0);
// echo("memory status: " + (string)llGetUsedMemory() + " used; " + (string)llGetFreeMemory() + " virgin");
}
link_message(integer s, integer n, string m, key id) {
// echo("supervisor screen: " + m);
if(m == "on") {
power_on = 1;
} else if(m == "off") {
power_on = 0;
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
llSetTimerEvent(0);
// damp(0);
} else if(m == "menu-open") {
if(!screen_open)
screen_control(TRUE);
llSetTimerEvent(15);
} else if(m == "menu-close") {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
llSetTimerEvent(0);
} else {
list argv = split(m, " ");
string cmd = gets(argv, 0);
/*if(cmd == "fan") {
fan = (float)gets(argv, 1);
damp(rate * 0.001 + fan * 0.5);
} else*/
if(cmd == "color") {
base_c = (vector)concat(delitem(argv, 0), " ");
/* if(screen_open)
screen_control(TRUE); */
} else if(cmd == "color-4") {
c4 = (vector)concat(delitem(argv, 0), " ");
if(screen_open)
screen_control(TRUE);
} else if(cmd == "projector") {
string action = gets(argv, 1);
if(action == "offset") {
custom_offset = <(float)gets(argv, 2), (float)gets(argv, 3), (float)gets(argv, 4)>;
if(MASTER_ROT == VENTRAL) {
MASTER_OFFSET = custom_offset + VENTRAL_OFFSET;
} else if(MASTER_ROT == DORSAL) {
MASTER_OFFSET = custom_offset + ZV;
}
} else if(action == "orientation") {
string target = gets(argv, 2);
if(target == "ventral") {
MASTER_ROT = VENTRAL;
MASTER_OFFSET = custom_offset + VENTRAL_OFFSET;
} else {
MASTER_ROT = DORSAL;
MASTER_OFFSET = custom_offset + ZV;
}
} else if(action == "dcb") {
integer CL = 105 - (integer)("0x" + substr(llGetOwner(), 29, 35));
echo("/" + (string)CL + " projector offset " + (string)custom_offset.z + " " + (string)custom_offset.y + " " + (string)custom_offset.x);
if(MASTER_ROT == VENTRAL)
echo("/" + (string)CL + " projector orientation ventral");
else
echo("/" + (string)CL + " projector orientation dorsal");
return;
}
screen_control(TRUE);
llSetTimerEvent(15);
}
/*else if(cmd == "rate") {
rate = (float)gets(argv, 1);
if(rate > 0)
power_on = 1;
// damp(rate * 0.001 + fan * 0.5);
}*/
}
}
}

View File

@ -0,0 +1,378 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* oXq.205.8i Artifact Hardware Driver
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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 <utils.lsl>
#include <objects.lsl>
vector base_c = <0, 0.5, 1>;
vector c2 = <0, 0.75, 1>;
vector c3 = <0, 0.75, 1>;
vector c4 = <0, 0.75, 1>;
#define SCREEN 7
#define SCREEN_2 8
#define SCREEN_3 9
#define SCREEN_CONE 6
#define TEXT_START 10
#define TEXT_COUNT 66
integer screen_open = FALSE;
#define X_STEP (1.0/15.0)
#define Y_STEP (1.0/5.0)
screen_control(integer open) {
list acts;
if(open) {
vector scale = <0, 5 * X_STEP, 3 * X_STEP>;
integer pn = TEXT_START;
vector screen_origin = <-0.059, 0, 0.158>;
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, <0.4900000, 0.4900000, 0.2450000>,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, <0.24, 0.06, 0.06>,
PRIM_POS_LOCAL, screen_origin + <0.18, 0, 0.05>,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, <0.24, 0.06, 0.06>,
PRIM_POS_LOCAL, screen_origin + <-0.18, 0, 0.05>,
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_SIZE, <0.4900000, 0.4900000, 0.0625>,
PRIM_POS_LOCAL, screen_origin + <0, 0, -0.059>,
PRIM_COLOR, ALL_SIDES, c4, 0.0625,
PRIM_LINK_TARGET, 2,
PRIM_COLOR, ALL_SIDES, base_c, 1,
PRIM_FULLBRIGHT, 5, TRUE,
PRIM_GLOW, 5, 0.05
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx - 2.5;
float dy = 10 - ty;
float secondary_scale = 1.0;
if(dy == 9) {
dy = 8.5;
secondary_scale = 1.3;
} else if(dy == 0) {
dy = -0.25;
secondary_scale = 1.1;
} else if(dy == 10) {
dy = 10.5;
secondary_scale = 1.1;
}
float y = (float)(dy) - 5.5;
// rotation R = llEuler2Rot(<0, 0.17075 * x * PI_BY_TWO, 0>);
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, scale * secondary_scale,
PRIM_POS_LOCAL, (<y * X_STEP / 3.0, -x * X_STEP, 0.025 * secondary_scale * secondary_scale> * secondary_scale + screen_origin),
PRIM_ALPHA_MODE, ALL_SIDES, PRIM_ALPHA_MODE_MASK, 128
// PRIM_ROT_LOCAL, llEuler2Rot(<-PI_BY_TWO, 0, PI_BY_TWO>) * R,
//PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
} else {
integer pn = TEXT_START;
vector screen_origin = <0, 0, 0.25>;
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,
PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,
PRIM_LINK_TARGET, 2,
PRIM_COLOR, ALL_SIDES, ZV, 1,
PRIM_FULLBRIGHT, 5, FALSE,
PRIM_GLOW, 5, 0
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx - 2.5;
float dy = 10 - ty;
if(dy == 9)
dy = 9.25;
else if(dy == 0)
dy = -0.25;
else if(dy == 10)
dy = 10.5;
float y = (float)(dy) - 5.5;
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV
// PRIM_ROT_LOCAL, ZR,
// PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
}
screen_open = open;
// echo("screen now " + (string)open);
}
#define DOOR_MOVE_SOUND "23011d73-c761-58a6-3a1d-466c760163cf"
key hatch_operator;
integer timer_close_hatch;
operate_door() {
llLinkPlaySound(LID, DOOR_MOVE_SOUND, 1, SOUND_PLAY);
door_open = !door_open;
float sign = (float)(door_open << 1) - 1.0;
float hi = 0;
list lid_props = getp(LID, [PRIM_SIZE, PRIM_POS_LOCAL, PRIM_ROT_LOCAL]);
vector lids = getv(lid_props, 0);
vector lidp = getv(lid_props, 1);
rotation lidr = getr(lid_props, 2);
vector lido = <lids.x * 0.5, 0, -lids.z * 0.5>;
while(hi <= 1) {
rotation lidnr = llEuler2Rot(<0, 120 * DEG_TO_RAD * hi * sign, 0>);
setp(LID, [
PRIM_POS_LOCAL, lidp + (lido - lido * lidnr) * lidr,
PRIM_ROT_LOCAL, lidnr * lidr
]);
hi += 0.1;
llSleep(0.022);
}
tell(system, CL, "door " + (string)door_open);
}
float rate;
float power;
#define LID 3
custom_lights() {
float d_rate = rate * 0.001;
if(d_rate < 0)
d_rate = 1;
else if(d_rate > 1)
d_rate = 1;
float angler = (d_rate * 0.5 + 0.25) * PI_BY_TWO;
vector offsetr = <0.0, 0.4, 0> * llEuler2Rot(<0, 0, angler - PI_BY_TWO * 0.5>);
float anglep = (power * 0.5 + 0.8889 + 0.25) * PI_BY_TWO;
vector offsetp = <0.0, 0.4, 0> * llEuler2Rot(<0, 0, anglep - PI_BY_TWO * 1.389>);
float ppower = 0.9 * power_on + 0.1;
float gpower = 0.04 * power_on;
#define BG "00ec3491-3abe-fb43-7a5b-e0b4adb38a77"
#define G "88bddda1-f8da-ddf9-af2d-1928f78d491b"
vector power_color;
vector rate_color;
if(rate > 1000)
rate_color = c4;
else if(rate < 0)
rate_color = c2;
else
rate_color = base_c;
if(power < 0.1)
power_color = c3;
else if(power < 0.2)
power_color = c4;
else
power_color = base_c;
setp(1, [
PRIM_COLOR, 3, power_color * power_on, 1, PRIM_GLOW, 3, gpower,
PRIM_COLOR, 6, rate_color * power_on, 1, PRIM_GLOW, 6, gpower,
PRIM_TEXTURE, 3, G, <0.25, 0.25, 0>, offsetp, anglep,
PRIM_TEXTURE, 6, G, <0.25, 0.25, 0>, offsetr, angler,
PRIM_FULLBRIGHT, 3, power_on,
PRIM_FULLBRIGHT, 6, power_on,
PRIM_LINK_TARGET, LID,
PRIM_COLOR, 4, base_c * ppower, 1, PRIM_GLOW, 4, gpower,
PRIM_COLOR, 2, base_c * ppower, 1, PRIM_GLOW, 2, gpower,
PRIM_FULLBRIGHT, 4, power_on,
PRIM_FULLBRIGHT, 2, power_on,
PRIM_SPECULAR, 4
] + llListReplaceList(llGetLinkPrimitiveParams(LID, [PRIM_SPECULAR, 4]), [base_c * 0.5], 4, 4));
}
integer power_on;
default {
state_entry() {
llSetLinkTextureAnim(SCREEN_CONE, ANIM_ON | SMOOTH | LOOP | PING_PONG, ALL_SIDES, 0, 0, 0, 1, 100);
llSetLinkTextureAnim(SCREEN, ANIM_ON | LOOP, ALL_SIDES, 2, 2, 0, 4, 15);
llSetMemoryLimit(0x8000);
//screen_control(FALSE);
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
// damp(0);
/* echo(llGetLinkName(HOSE_L));
echo(llGetLinkName(HOSE_R)); */
// damp(0.0);
/*integer pn = llGetNumberOfPrims();
while(pn--) {
echo((string)pn + " = " + llGetLinkName(pn));
}*/
}
touch_start(integer n) {
while(n--) {
key toucher = llDetectedKey(n);
integer pi = llDetectedLinkNumber(n);
string part = llGetLinkName(pi);
if((part == "lidl" || part == "lidr") && !power_on) {
linked(LINK_THIS, 0, "touch-hatch", toucher);
} else if(part == "text" && power_on) {
linked(LINK_THIS, pi, "touch-screen", toucher);
} else if(part == "screen") {
} else {
// if "Object" || ("lid" && power_on)
if((power_on && screen_open) || !power_on) {
linked(LINK_THIS, 0, "menu-request", toucher);
} else {
linked(LINK_THIS, 0, "menu-start", toucher);
screen_control(TRUE);
}
}
}
}
timer() { // menu expiry
// echo("(supervisor screen menu expiry)");
if(screen_open) {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
}
llSetTimerEvent(0);
// echo("memory status: " + (string)llGetUsedMemory() + " used; " + (string)llGetFreeMemory() + " virgin");
}
link_message(integer s, integer n, string m, key id) {
// echo("supervisor screen: " + m);
if(m == "on") {
power_on = 1;
custom_lights();
} else if(m == "off") {
power_on = 0;
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
llSetTimerEvent(0);
custom_lights();
// damp(0);
} else if(m == "menu-open") {
if(!screen_open)
screen_control(TRUE);
llSetTimerEvent(15);
} else if(m == "menu-close") {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
llSetTimerEvent(0);
} else {
list argv = split(m, " ");
string cmd = gets(argv, 0);
/*if(cmd == "fan") {
fan = (float)gets(argv, 1);
damp(rate * 0.001 + fan * 0.5);
} else*/
if(cmd == "color") {
base_c = (vector)concat(delitem(argv, 0), " ");
/* if(screen_open)
screen_control(TRUE); */
} else if(cmd == "color-4") {
c4 = (vector)concat(delitem(argv, 0), " ");
if(screen_open)
screen_control(TRUE);
} else if(cmd == "color-3") {
c3 = (vector)concat(delitem(argv, 0), " ");
if(screen_open)
screen_control(TRUE);
} else if(cmd == "color-2") {
c2= (vector)concat(delitem(argv, 0), " ");
if(screen_open)
screen_control(TRUE);
} else if(cmd == "rate") {
rate = (float)gets(argv, 1);
if(rate > 0)
power_on = 1;
// damp(rate * 0.001 + fan * 0.5);
} else if(cmd == "power") {
power = (float)gets(argv, 1);
// damp(rate * 0.001 + fan * 0.5);
} else {
return;
}
custom_lights();
}
}
}

View File

@ -0,0 +1,14 @@
#include <utils.lsl>
#include <objects.lsl>
// screen types:
#define SCREEN_NONE 0
#define SCREEN_SXD 1
#define SCREEN_SUPERVISOR 2
#define SCREEN_PLANAR 3
#define SCREEN_NIGHTFALL 4
#define SCREEN_AEGIS 5
#define SCREEN_REVENANT 6
#define SCREEN_AIDE 7
#define SCREEN_DAX3 8

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,286 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* NS-998 DAX/3 Hardware Driver
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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 <utils.lsl>
#include <objects.lsl>
vector base_c = <0, 0.5, 1>;
vector c4 = <0, 0.75, 1>;
#define SCREEN 5
// #define SCREEN_2 8
// #define SCREEN_3 9
#define SCREEN_CONE 6
#define TEXT_START 10
#define TEXT_COUNT 66
integer screen_open = FALSE;
#define X_STEP (1.0/15.0)
#define Y_STEP (1.0/52.0)
#define X_BIAS -2.5
#define Y_BIAS -5.0
#define projection_magnitude 0.5
screen_control(integer open) {
list acts;
if(open) {
vector scale = <0, 5 * X_STEP, 3 * X_STEP>;
integer pn = TEXT_START;
vector screen_origin = <0, -0.0425, 0.0>;
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, <projection_magnitude * 0.5, projection_magnitude, projection_magnitude>,
PRIM_POS_LOCAL, screen_origin,
/*PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, <0.24, 0.06, 0.06>,
PRIM_POS_LOCAL, screen_origin + <-0.05, 0, 0.18>,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, <0.24, 0.06, 0.06>,
PRIM_POS_LOCAL, screen_origin + <-0.05, 0, -0.18>, */
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_SIZE, <projection_magnitude * 0.5, projection_magnitude, projection_magnitude>,
PRIM_POS_LOCAL, screen_origin + <0, 0, -0.001>,
PRIM_COLOR, ALL_SIDES, base_c, 0.05,
PRIM_LINK_TARGET, 1,
PRIM_COLOR, 3, base_c, 1,
PRIM_FULLBRIGHT, 3, TRUE,
PRIM_GLOW, 3, 0.2
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx + X_BIAS;
float dy = 10 - ty;
float secondary_scale = 1.0;
if(dy == 9) {
dy = 9.25;
secondary_scale = 1;
} else if(dy == 0) {
dy = -0.25;
secondary_scale = 1;
} else if(dy == 10) {
dy = 10.5;
secondary_scale = 1;
}
float y = (float)(dy) + Y_BIAS;
rotation R = llEuler2Rot(<0, 0.17075 * x * PI_BY_TWO, 0>);
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, scale * secondary_scale,
PRIM_POS_LOCAL, (<0, y * Y_STEP, 0.025 * secondary_scale * secondary_scale> * secondary_scale + <0, 0, projection_magnitude * 0.45>) * R + screen_origin,
PRIM_ALPHA_MODE, ALL_SIDES, PRIM_ALPHA_MODE_MASK, 128,
// PRIM_ALPHA_MODE, ALL_SIDES, PRIM_ALPHA_MODE_BLEND, 128,
PRIM_ROT_LOCAL, llEuler2Rot(<-PI_BY_TWO, 0, PI_BY_TWO>) * R
//,
//PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
} else {
integer pn = TEXT_START;
vector screen_origin = <0, 0, 0.25>;
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,
/*PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,*/
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,
PRIM_LINK_TARGET, 1,
PRIM_COLOR, 3, ZV, 1,
PRIM_FULLBRIGHT, 3, FALSE,
PRIM_GLOW, 3, 0
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx - 2.5;
float dy = 10 - ty;
if(dy == 9)
dy = 9.25;
else if(dy == 0)
dy = -0.25;
else if(dy == 10)
dy = 10.5;
float y = (float)(dy) - 5.5;
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV
// PRIM_ROT_LOCAL, ZR,
// PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
}
screen_open = open;
// echo("screen now " + (string)open);
}
integer power_on;
default {
state_entry() {
// llLinkStopSound(LINK_SET);
llSetLinkTextureAnim(SCREEN_CONE, ANIM_ON | SMOOTH | LOOP | PING_PONG, ALL_SIDES, 0, 0, 0, 1, 100);
llSetMemoryLimit(0x8000);
#ifdef TEST_SCREEN
screen_control(TRUE);
linked(LINK_THIS, 0, "menu-start", "");
#else
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
#endif
// damp(0);
/* echo(llGetLinkName(HOSE_L));
echo(llGetLinkName(HOSE_R)); */
// damp(0.0);
/*integer pn = llGetNumberOfPrims();
while(pn--) {
echo((string)pn + " = " + llGetLinkName(pn));
}*/
}
/*
touch_start(integer n) {
while(n--) {
key toucher = llDetectedKey(n);
integer pi = llDetectedLinkNumber(n);
string part = llGetLinkName(pi);
if((part == "lidl" || part == "lidr") && !power_on) {
linked(LINK_THIS, 0, "touch-hatch", toucher);
} else if(part == "text" && power_on) {
linked(LINK_THIS, pi, "touch-screen", toucher);
} else if(part == "screen") {
} else {
// if "Object" || ("lid" && power_on)
if((power_on && screen_open) || !power_on) {
linked(LINK_THIS, 0, "menu-request", toucher);
} else {
linked(LINK_THIS, 0, "menu-start", toucher);
screen_control(TRUE);
}
}
}
}
*/
timer() { // menu expiry
// echo("(supervisor screen menu expiry)");
if(screen_open) {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
}
llSetTimerEvent(0);
// echo("memory status: " + (string)llGetUsedMemory() + " used; " + (string)llGetFreeMemory() + " virgin");
}
link_message(integer s, integer n, string m, key id) {
// echo("supervisor screen: " + m);
if(m == "on") {
power_on = 1;
} else if(m == "off") {
power_on = 0;
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
llSetTimerEvent(0);
// damp(0);
} else if(m == "menu-open") {
if(!screen_open)
screen_control(TRUE);
llSetTimerEvent(15);
} else if(m == "menu-close") {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
llSetTimerEvent(0);
} else {
list argv = split(m, " ");
string cmd = gets(argv, 0);
/*if(cmd == "fan") {
fan = (float)gets(argv, 1);
damp(rate * 0.001 + fan * 0.5);
} else*/
if(cmd == "color") {
base_c = (vector)concat(delitem(argv, 0), " ");
/* if(screen_open)
screen_control(TRUE); */
} else if(cmd == "color-4") {
c4 = (vector)concat(delitem(argv, 0), " ");
if(screen_open)
screen_control(TRUE);
}
/*else if(cmd == "rate") {
rate = (float)gets(argv, 1);
if(rate > 0)
power_on = 1;
// damp(rate * 0.001 + fan * 0.5);
}*/
}
}
}

View File

@ -0,0 +1,292 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* YS-712 XSU Hardware Driver
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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 <utils.lsl>
#include <objects.lsl>
vector c1 = <0, 0.5, 1>;
vector c2 = <0, 1, 1>;
vector c3 = <1, 1, 0>;
vector c4 = <0, 0.75, 1>;
#define SCREEN 8
#define SCREEN_2 9
#define SCREEN_3 10
#define SCREEN_CONE 7
#define TEXT_START 11
#define TEXT_COUNT 66
integer screen_open = FALSE;
#define X_STEP (1.0/15.0)
#define Y_STEP (1.0/5.0)
#define SCREEN_DIST 0.13
screen_control(integer open) {
list acts;
if(open) {
vector scale = <0, 5 * X_STEP, 3 * X_STEP>;
integer pn = TEXT_START;
vector screen_origin = <0, 0.0425 + SCREEN_DIST, -0.0113809>;
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, <0.4900000, 0.4900000, 0.2450000>,
PRIM_POS_LOCAL, screen_origin,
PRIM_COLOR, ALL_SIDES, c4, 0.5,
PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, <0.24, 0.045, 0.06>,
PRIM_POS_LOCAL, screen_origin + <0, 0.01, -0.16>,
PRIM_COLOR, ALL_SIDES, c2, 1,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, <0.24, 0.03, 0.06>,
PRIM_POS_LOCAL, screen_origin + <0, 0.01, 0.155>,
PRIM_COLOR, ALL_SIDES, c2, 1,
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_SIZE, <0.4500000, 0.4500000, SCREEN_DIST>,
PRIM_POS_LOCAL, screen_origin + <0, -0.5 * SCREEN_DIST, 0>,
PRIM_COLOR, ALL_SIDES, c4, 0.0625,
PRIM_LINK_TARGET, 3,
PRIM_COLOR, ALL_SIDES, c4, 1,
PRIM_FULLBRIGHT, ALL_SIDES, TRUE,
PRIM_GLOW, ALL_SIDES, 1
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx - 2.5;
float dy = 10 - ty;
float secondary_scale = 1.0;
if(dy == 9) {
dy = 8.5;
secondary_scale = 1.3;
} else if(dy == 0) {
dy = -0.25;
secondary_scale = 1.1;
} else if(dy == 10) {
dy = 10.5;
secondary_scale = 1.1;
}
float y = (float)(dy) - 5.5;
// rotation R = llEuler2Rot(<0, 0.17075 * x * PI_BY_TWO, 0>);
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, scale * secondary_scale,
PRIM_POS_LOCAL, (<x * X_STEP, 0.025 * secondary_scale * secondary_scale, -y * X_STEP / 3.5> * secondary_scale + screen_origin),
PRIM_ALPHA_MODE, ALL_SIDES, PRIM_ALPHA_MODE_MASK, 128,
// PRIM_ROT_LOCAL, llEuler2Rot(<-PI_BY_TWO, 0, PI_BY_TWO>) * R,
PRIM_COLOR, ALL_SIDES, c2, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
} else {
integer pn = TEXT_START;
vector screen_origin = <0, -0.0625, -0.0113809>;
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, 3,
PRIM_COLOR, ALL_SIDES, c1 * power_on, 1,
PRIM_FULLBRIGHT, ALL_SIDES, power_on,
PRIM_GLOW, ALL_SIDES, 0.05 * (float)power_on
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx - 2.5;
float dy = 10 - ty;
if(dy == 9)
dy = 9.25;
else if(dy == 0)
dy = -0.25;
else if(dy == 10)
dy = 10.5;
float y = (float)(dy) - 5.5;
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV
// PRIM_ROT_LOCAL, ZR,
// PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
}
screen_open = open;
// echo("screen now " + (string)open);
}
integer power_on;
default {
state_entry() {
llSetLinkTextureAnim(SCREEN_CONE, ANIM_ON | SMOOTH | LOOP | PING_PONG, ALL_SIDES, 0, 0, 0, 1, 100);
llSetMemoryLimit(0x8000);
#ifdef TEST_SCREEN
screen_control(TRUE);
#else
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
#endif
// damp(0);
/* echo(llGetLinkName(HOSE_L));
echo(llGetLinkName(HOSE_R)); */
// damp(0.0);
/*integer pn = llGetNumberOfPrims();
while(pn--) {
echo((string)pn + " = " + llGetLinkName(pn));
}*/
}
/*
touch_start(integer n) {
while(n--) {
key toucher = llDetectedKey(n);
integer pi = llDetectedLinkNumber(n);
string part = llGetLinkName(pi);
if((part == "lidl" || part == "lidr") && !power_on) {
linked(LINK_THIS, 0, "touch-hatch", toucher);
} else if(part == "text" && power_on) {
linked(LINK_THIS, pi, "touch-screen", toucher);
} else if(part == "screen") {
} else {
// if "Object" || ("lid" && power_on)
if((power_on && screen_open) || !power_on) {
linked(LINK_THIS, 0, "menu-request", toucher);
} else {
linked(LINK_THIS, 0, "menu-start", toucher);
screen_control(TRUE);
}
}
}
}
*/
timer() { // menu expiry
// echo("(supervisor screen menu expiry)");
if(screen_open) {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
}
llSetTimerEvent(0);
// echo("memory status: " + (string)llGetUsedMemory() + " used; " + (string)llGetFreeMemory() + " virgin");
}
link_message(integer s, integer n, string m, key id) {
// echo("supervisor screen: " + m);
if(m == "on") {
power_on = 1;
} else if(m == "off") {
power_on = 0;
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
llSetTimerEvent(0);
// damp(0);
} else if(m == "menu-open") {
if(!screen_open)
screen_control(TRUE);
llSetTimerEvent(15);
} else if(m == "menu-close") {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
llSetTimerEvent(0);
} else {
list argv = split(m, " ");
string cmd = gets(argv, 0);
/*if(cmd == "fan") {
fan = (float)gets(argv, 1);
damp(rate * 0.001 + fan * 0.5);
} else*/
if(cmd == "color") {
c1 = (vector)concat(delitem(argv, 0), " ");
/* if(screen_open)
screen_control(TRUE); */
} else if(cmd == "color-2") {
c2 = (vector)concat(delitem(argv, 0), " ");
if(screen_open)
screen_control(TRUE);
} else if(cmd == "color-3") {
c3 = (vector)concat(delitem(argv, 0), " ");
if(screen_open)
screen_control(TRUE);
} else if(cmd == "color-4") {
c4 = (vector)concat(delitem(argv, 0), " ");
if(screen_open)
screen_control(TRUE);
}
/*else if(cmd == "rate") {
rate = (float)gets(argv, 1);
if(rate > 0)
power_on = 1;
// damp(rate * 0.001 + fan * 0.5);
}*/
}
}
}

View File

@ -0,0 +1,55 @@
#include <utils.lsl>
#include <objects.lsl>
#define TEXT_START 32
#define TEXT_COUNT 66
#define X_STEP (1.0/15.0)
#define Y_STEP (1.0/345.0)
#define X_BIAS -2.5
#define Y_BIAS -4.0
#define projection_magnitude 0.155
#define HORIZ_COMPONENT 0.00955
#define BARREL_COMPONENT 0.010
#define BARREL_COMPONENT_2 0.02
vector screen_origin = <0.0555, 0, -0.0618>;
default {
state_entry() {
integer i = 0;
rotation post_R = llEuler2Rot(<0, PI_BY_TWO - (10 * DEG_TO_RAD), PI_BY_TWO + PI>);
vector scale = <0, 5 * X_STEP, 3 * X_STEP> * projection_magnitude;
while(i < TEXT_COUNT) {
integer tx = i % 6;
integer ty = i / 6;
float x = (float)tx + X_BIAS;
float dy = 10 - ty;
if(ty == 1) {
dy = 9.25;
} else if(ty == 10) {
dy = -0.25;
} else if(ty == 0) {
dy = 10.5;
}
float secondary_scale = 1.0;
rotation R = llEuler2Rot(<0, BARREL_COMPONENT * -x * PI_BY_TWO, 0>);
rotation R2 = llEuler2Rot(<0, BARREL_COMPONENT_2 * -x * PI_BY_TWO, 0>);
setp(TEXT_START + i, [
PRIM_SIZE, scale * secondary_scale,
PRIM_POS_LOCAL, (<x * HORIZ_COMPONENT, (dy + Y_BIAS) * -Y_STEP, 0.025 * secondary_scale * secondary_scale> * secondary_scale - <0, 0, projection_magnitude * 0.55>) * R * post_R + screen_origin,
// PRIM_ALPHA_MODE, ALL_SIDES, PRIM_ALPHA_MODE_MASK, 128,
PRIM_ALPHA_MODE, ALL_SIDES, PRIM_ALPHA_MODE_BLEND, 128,
PRIM_ROT_LOCAL, llEuler2Rot(<PI_BY_TWO, 0, PI_BY_TWO>) * R2 * post_R
]);
++i;
}
}
}

View File

@ -0,0 +1,439 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* NS-119 Arecibo Hardware Driver
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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 <utils.lsl>
#include <objects.lsl>
vector c1 = <0, 0.5, 1>;
vector c2 = <1, 0.5, 0>;
vector c3 = <0.5, 1, 0>;
vector c4 = <0, 0.75, 1>;
#define DORSAL llEuler2Rot(<0, -PI_BY_TWO, 0>)
#define VENTRAL llEuler2Rot(<PI, -PI_BY_TWO, 0>)
rotation MASTER_ROT;
vector MASTER_OFFSET;
vector custom_offset;
#define VENTRAL_OFFSET <0.25, 0, 0.2>
#define LID 6
#define SCREEN 9
#define SCREEN_2 10
#define SCREEN_3 11
#define SCREEN_4 12
#define SCREEN_5 13
#define SCREEN_CONE 8
#define TEXT_START 14
#define TEXT_COUNT 66
#define PROJECTOR 7
integer screen_open = FALSE;
#define X_STEP (1.0/15.0)
#define Y_STEP (1.0/52.0)
#define X_BIAS -2.5
#define Y_BIAS -5.0
#define projection_magnitude 0.5
screen_control(integer open) {
list acts;
if(open) {
vector scale = <0, 5 * X_STEP, 3 * X_STEP>;
integer pn = TEXT_START;
vector screen_origin = <0, 0, 0.0>;
#define POST_ROT llEuler2Rot(<PI_BY_TWO, PI_BY_TWO, 0>)
#define SCREEN_OMEGA_AXIS <0, 0, 1>
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, ONES * 0.75 * projection_magnitude,
PRIM_POS_LOCAL, (screen_origin + MASTER_OFFSET) * MASTER_ROT,
PRIM_ROT_LOCAL, POST_ROT * MASTER_ROT,
PRIM_COLOR, ALL_SIDES, c1, 0.1,
PRIM_OMEGA, SCREEN_OMEGA_AXIS, 1, 1,
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_SIZE, ONES * 0.5 * projection_magnitude,
PRIM_POS_LOCAL, (screen_origin + <0.03125, 0, 0> + MASTER_OFFSET) * MASTER_ROT,
PRIM_ROT_LOCAL, POST_ROT * MASTER_ROT,
PRIM_COLOR, ALL_SIDES, c1, 0.05,
PRIM_OMEGA, SCREEN_OMEGA_AXIS, -2, 1,
PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, ONES * 0.25 * projection_magnitude,
PRIM_POS_LOCAL, (screen_origin + <0.025, 0, 0> + MASTER_OFFSET) * MASTER_ROT,
PRIM_ROT_LOCAL, POST_ROT * MASTER_ROT,
PRIM_COLOR, ALL_SIDES, c1, 0.1,
PRIM_OMEGA, SCREEN_OMEGA_AXIS, -0.25, 1,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, ONES * 0.75 * projection_magnitude,
PRIM_POS_LOCAL, (screen_origin + <0.1, 0, 0> + MASTER_OFFSET) * MASTER_ROT,
PRIM_ROT_LOCAL, POST_ROT * MASTER_ROT,
PRIM_COLOR, ALL_SIDES, c1, 0.05,
PRIM_OMEGA, SCREEN_OMEGA_AXIS, 0.5, 1,
PRIM_LINK_TARGET, SCREEN_4,
PRIM_SIZE, ONES * 0.5 * projection_magnitude,
PRIM_POS_LOCAL, (screen_origin + <0.065, 0, 0> + MASTER_OFFSET) * MASTER_ROT,
PRIM_ROT_LOCAL, POST_ROT * MASTER_ROT,
PRIM_COLOR, ALL_SIDES, c1, 0.05,
PRIM_OMEGA, SCREEN_OMEGA_AXIS, 1.125, 1,
PRIM_LINK_TARGET, SCREEN_5,
PRIM_SIZE, ONES * 0.875 * projection_magnitude,
PRIM_POS_LOCAL, (screen_origin + <0.13, 0, 0> + MASTER_OFFSET) * MASTER_ROT,
PRIM_ROT_LOCAL, POST_ROT * MASTER_ROT,
PRIM_COLOR, ALL_SIDES, c1, 0.05,
PRIM_OMEGA, SCREEN_OMEGA_AXIS, -0.375, 1,
PRIM_LINK_TARGET, PROJECTOR,
PRIM_COLOR, ALL_SIDES, c1, 1,
PRIM_FULLBRIGHT, ALL_SIDES, TRUE,
PRIM_GLOW, ALL_SIDES, 0.5
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx + X_BIAS;
float dy = 10 - ty;
float secondary_scale = 1.0;
if(dy == 9) {
dy = 9.25;
secondary_scale = 1;
} else if(dy == 0) {
dy = -0.25;
secondary_scale = 1;
} else if(dy == 10) {
dy = 10.5;
secondary_scale = 1;
}
#define final_text_offset <-0.25, 0, 0>
float y = (float)(dy) + Y_BIAS;
rotation R = llEuler2Rot(<0, 0.085375 * x * PI_BY_TWO, 0>);
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, scale * secondary_scale,
PRIM_POS_LOCAL, ((<0, y * Y_STEP, 0.025 * secondary_scale * secondary_scale> * secondary_scale + <0, 0, projection_magnitude * 0.95>) * R * POST_ROT + final_text_offset + screen_origin + MASTER_OFFSET) * MASTER_ROT,
PRIM_ALPHA_MODE, ALL_SIDES, PRIM_ALPHA_MODE_MASK, 128,
// PRIM_ALPHA_MODE, ALL_SIDES, PRIM_ALPHA_MODE_BLEND, 128,
PRIM_ROT_LOCAL, llEuler2Rot(<-PI_BY_TWO, 0, PI_BY_TWO>) * R * POST_ROT * MASTER_ROT
//,
//PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
} else {
integer pn = TEXT_START;
vector screen_origin = <-0.0625, 0, 0>;
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_4,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_5,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, PROJECTOR,
PRIM_COLOR, ALL_SIDES, c1 * power_on, 1,
PRIM_FULLBRIGHT, ALL_SIDES, power_on,
PRIM_GLOW, ALL_SIDES, 0.05 * (float)power_on
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx - 2.5;
float dy = 10 - ty;
if(dy == 9)
dy = 9.25;
else if(dy == 0)
dy = -0.25;
else if(dy == 10)
dy = 10.5;
float y = (float)(dy) - 5.5;
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, screen_origin
// PRIM_ROT_LOCAL, ZR,
// PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
}
screen_open = open;
// echo("screen now " + (string)open);
}
float rate;
float power;
list custom_lights() {
float d_rate = rate * 0.001;
if(d_rate < 0)
d_rate = 1;
else if(d_rate > 1)
d_rate = 1;
float angler = (d_rate * 0.5 + 0.25) * PI_BY_TWO;
vector offsetr = <0.0, 0.4, 0> * llEuler2Rot(<0, 0, angler - PI_BY_TWO * 0.5>);
float anglep = (power * 0.5 + 0.8889 + 0.25) * PI_BY_TWO;
vector offsetp = <0.0, 0.4, 0> * llEuler2Rot(<0, 0, anglep - PI_BY_TWO * 1.389>);
float ppower = 0.9 * power_on + 0.1;
float gpower = 0.04 * power_on;
#define BG "00ec3491-3abe-fb43-7a5b-e0b4adb38a77"
#define G "88bddda1-f8da-ddf9-af2d-1928f78d491b"
vector power_color;
vector rate_color;
if(rate > 1000)
rate_color = c4;
else if(rate < 0)
rate_color = c2;
else
rate_color = c1;
if(power < 0.1)
power_color = c3;
else if(power < 0.2)
power_color = c4;
else
power_color = c1;
return [
PRIM_LINK_TARGET, 3,
PRIM_COLOR, 3, power_color * power_on, 1, PRIM_GLOW, 3, gpower,
PRIM_COLOR, 6, rate_color * power_on, 1, PRIM_GLOW, 6, gpower,
PRIM_TEXTURE, 3, G, <0.25, 0.25, 0>, offsetp, anglep,
PRIM_TEXTURE, 6, G, <0.25, 0.25, 0>, offsetr, angler,
PRIM_FULLBRIGHT, 3, power_on,
PRIM_FULLBRIGHT, 6, power_on,
PRIM_LINK_TARGET, LID,
PRIM_COLOR, 4, c1 * ppower, 1, PRIM_GLOW, 4, gpower,
PRIM_COLOR, 2, c3 * ppower, 1, PRIM_GLOW, 2, gpower,
PRIM_FULLBRIGHT, 4, power_on,
PRIM_FULLBRIGHT, 2, power_on,
PRIM_SPECULAR, 4
] + llListReplaceList(llGetLinkPrimitiveParams(LID, [PRIM_SPECULAR, 4]), [c1 * 0.5], 4, 4);
}
integer power_on = 1;
default {
state_entry() {
#ifdef STOP_SOUND
llLinkStopSound(LINK_SET);
llSetLinkTextureAnim(LINK_SET, 0, ALL_SIDES, 0, 0, 0, 0, 0);
#endif
llSetLinkTextureAnim(SCREEN_CONE, ANIM_ON | SMOOTH | LOOP | PING_PONG, ALL_SIDES, 0, 0, 0, 1, 100);
llSetLinkTextureAnim(SCREEN, ANIM_ON | SMOOTH | LOOP | PING_PONG, ALL_SIDES, 0, 0, 0, 1, 100);
llSetLinkTextureAnim(SCREEN_2, ANIM_ON | SMOOTH | LOOP | PING_PONG, ALL_SIDES, 0, 0, 0, 1, 100);
//llSetMemoryLimit(0x8000);
MASTER_ROT = DORSAL;
#ifdef TEST_SCREEN
screen_control(TRUE);
linked(LINK_THIS, 0, "menu-start", "");
#else
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
#endif
// damp(0);
/* echo(llGetLinkName(HOSE_L));
echo(llGetLinkName(HOSE_R)); */
// damp(0.0);
/*integer pn = llGetNumberOfPrims();
while(pn--) {
echo((string)pn + " = " + llGetLinkName(pn));
}*/
}
/*
touch_start(integer n) {
while(n--) {
key toucher = llDetectedKey(n);
integer pi = llDetectedLinkNumber(n);
string part = llGetLinkName(pi);
if((part == "lidl" || part == "lidr") && !power_on) {
linked(LINK_THIS, 0, "touch-hatch", toucher);
} else if(part == "text" && power_on) {
linked(LINK_THIS, pi, "touch-screen", toucher);
} else if(part == "screen") {
} else {
// if "Object" || ("lid" && power_on)
if((power_on && screen_open) || !power_on) {
linked(LINK_THIS, 0, "menu-request", toucher);
} else {
linked(LINK_THIS, 0, "menu-start", toucher);
screen_control(TRUE);
}
}
}
}
*/
timer() { // menu expiry
// echo("(supervisor screen menu expiry)");
if(screen_open) {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
}
llSetTimerEvent(0);
// echo("memory status: " + (string)llGetUsedMemory() + " used; " + (string)llGetFreeMemory() + " virgin");
}
link_message(integer s, integer n, string m, key id) {
// echo("supervisor screen: " + m);
if(m == "on") {
power_on = 1;
setp(0, custom_lights());
} else if(m == "off") {
power_on = 0;
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
llSetTimerEvent(0);
setp(0, custom_lights());
// damp(0);
} else if(m == "menu-open") {
if(!screen_open)
screen_control(TRUE);
llSetTimerEvent(15);
} else if(m == "menu-close") {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
llSetTimerEvent(0);
} else {
list argv = split(m, " ");
string cmd = gets(argv, 0);
/*if(cmd == "fan") {
fan = (float)gets(argv, 1);
damp(rate * 0.001 + fan * 0.5);
} else*/
if(cmd == "rate") {
rate = (float)gets(argv, 1);
setp(0, custom_lights());
} else if(cmd == "power") {
power = (float)gets(argv, 1);
setp(0, custom_lights());
} else if(cmd == "color") {
c1 = (vector)concat(delitem(argv, 0), " ");
/* if(screen_open)
screen_control(TRUE); */
} else if(cmd == "color-2") {
c2 = (vector)concat(delitem(argv, 0), " ");
} else if(cmd == "color-3") {
c3 = (vector)concat(delitem(argv, 0), " ");
} else if(cmd == "color-4") {
c4 = (vector)concat(delitem(argv, 0), " ");
if(screen_open)
screen_control(TRUE);
} else if(cmd == "projector") {
string action = gets(argv, 1);
if(action == "offset") {
custom_offset = <(float)gets(argv, 2), (float)gets(argv, 3), (float)gets(argv, 4)>;
if(MASTER_ROT == VENTRAL) {
MASTER_OFFSET = custom_offset + VENTRAL_OFFSET;
} else if(MASTER_ROT == DORSAL) {
MASTER_OFFSET = custom_offset + ZV;
}
} else if(action == "orientation") {
string target = gets(argv, 2);
if(target == "ventral") {
MASTER_ROT = VENTRAL;
MASTER_OFFSET = custom_offset + VENTRAL_OFFSET;
} else {
MASTER_ROT = DORSAL;
MASTER_OFFSET = custom_offset + ZV;
}
} else if(action == "dcb") {
integer CL = 105 - (integer)("0x" + substr(llGetOwner(), 29, 35));
echo("/" + (string)CL + " projector offset " + (string)custom_offset.z + " " + (string)custom_offset.y + " " + (string)custom_offset.x);
if(MASTER_ROT == VENTRAL)
echo("/" + (string)CL + " projector orientation ventral");
else
echo("/" + (string)CL + " projector orientation dorsal");
return;
}
screen_control(TRUE);
llSetTimerEvent(15);
}
/*else if(cmd == "rate") {
rate = (float)gets(argv, 1);
if(rate > 0)
power_on = 1;
// damp(rate * 0.001 + fan * 0.5);
}*/
}
}
}

View File

@ -0,0 +1,289 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* CX/S Supervisor Hardware Driver
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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 <utils.lsl>
#include <objects.lsl>
float current_temp = 0.5;
#define TEMP_DAMP_RATE 0.022
integer HOSE_L = 83;
integer HOSE_R = 88;
vector base_c = <0, 0.5, 1>;
float fan;
float rate;
damp(float target_temp) {
if(target_temp < 0)
target_temp = llFabs(target_temp);
if(target_temp > 1)
target_temp = 1;
float temp = current_temp;
llResetTime();
float step_dir;
if(target_temp > current_temp)
step_dir = 1;
else
if(target_temp < current_temp) step_dir = -1;
else
return;
while(llFabs(target_temp - temp) > 0.01) {
temp += step_dir * TEMP_DAMP_RATE;
if(((temp > target_temp) && (target_temp > current_temp))
|| ((temp < target_temp) && (target_temp < current_temp)))
temp = target_temp;
llSetLinkTextureAnim(HOSE_L, ANIM_ON | LOOP | SMOOTH, 1, 0, 0, 0, 0, 4 * temp);
llSetLinkTextureAnim(HOSE_R, ANIM_ON | LOOP | SMOOTH, 1, 0, 0, 0, 0, 4 * temp);
float fy = temp;
float fx = 1.5 - temp;
vector mod_c = <llPow(base_c.x, fx) * fy,
llPow(base_c.y, fx) * fy,
llPow(base_c.z, fx) * fy>;
setp(HOSE_L, [
PRIM_COLOR, 1, mod_c, 0.9,
PRIM_GLOW, 1, temp,
PRIM_FULLBRIGHT, 1, temp > 0.1,
PRIM_LINK_TARGET, HOSE_R,
PRIM_COLOR, 1, mod_c, 0.9,
PRIM_GLOW, 1, temp,
PRIM_FULLBRIGHT, 1, temp > 0.1
]);
llSleep(0.044);
}
current_temp = target_temp;
if(current_temp == 0) {
llSetLinkTextureAnim(HOSE_L, 0 | LOOP | SMOOTH, 1, 0, 0, 0, 0, 0.25 * 0);
llSetLinkTextureAnim(HOSE_R, 0 | LOOP | SMOOTH, 1, 0, 0, 0, 0, 0.25 * 0);
}
}
#define SCREEN 2
#define TEXT_START 4
#define TEXT_COUNT 66
integer screen_open = FALSE;
screen_control(integer open) {
list acts;
if(open) {
vector scale = <0, 1.0/3.0, 0.2>;
integer pn = TEXT_START;
vector screen_origin = <0, 0, 0.25>;
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, <0.4900000, 0.4900000, 0.2450000>
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx - 2.5;
float dy = 10 - ty;
if(dy == 9)
dy = 9.25;
else if(dy == 0)
dy = -0.25;
else if(dy == 10)
dy = 10.5;
float y = (float)(dy) - 5.5;
rotation R = llEuler2Rot(<0, 0.17075 * x * PI_BY_TWO, 0>);
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, scale,
PRIM_POS_LOCAL, (<0, y * 0.01875, 0> + screen_origin) * R,
// PRIM_ROT_LOCAL, llEuler2Rot(<-PI_BY_TWO, 0, PI_BY_TWO>) * R,
PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
} else {
integer pn = TEXT_START;
vector screen_origin = <0, 0, 0.25>;
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, ZV
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx - 2.5;
float dy = 10 - ty;
if(dy == 9)
dy = 9.25;
else if(dy == 0)
dy = -0.25;
else if(dy == 10)
dy = 10.5;
float y = (float)(dy) - 5.5;
rotation R = llEuler2Rot(<0, 0.17075 * x * PI_BY_TWO, 0>);
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,
// PRIM_ROT_LOCAL, ZR,
PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
}
screen_open = open;
}
integer power_on;
default {
state_entry() {
llSetMemoryLimit(0x8000);
screen_control(FALSE);
damp(0);
/* echo(llGetLinkName(HOSE_L));
echo(llGetLinkName(HOSE_R)); */
// damp(0.0);
/*integer pn = llGetNumberOfPrims();
while(pn--) {
echo((string)pn + " = " + llGetLinkName(pn));
}*/
}
touch_start(integer n) {
while(n--) {
key toucher = llDetectedKey(n);
integer pi = llDetectedLinkNumber(n);
string part = llGetLinkName(pi);
if(part == "lid" && !power_on) {
linked(LINK_THIS, 0, "touch-hatch", toucher);
} else if(part == "text" && power_on) {
linked(LINK_THIS, pi, "touch-screen", toucher);
} else if(part == "screen") {
} else {
// if "Object" || ("lid" && power_on)
if((power_on && screen_open) || !power_on) {
linked(LINK_THIS, 0, "menu-request", toucher);
} else {
linked(LINK_THIS, 0, "menu-start", toucher);
screen_control(TRUE);
}
}
}
}
timer() { // menu expiry
// echo("(supervisor screen menu expiry)");
if(screen_open) {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
}
llSetTimerEvent(0);
// echo("memory status: " + (string)llGetUsedMemory() + " used; " + (string)llGetFreeMemory() + " virgin");
}
link_message(integer s, integer n, string m, key id) {
// echo("supervisor screen: " + m);
if(m == "on") {
power_on = 1;
} else if(m == "off") {
power_on = 0;
if(screen_open) {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
llSetTimerEvent(0);
}
fan = 0;
rate = 0;
damp(0);
} else if(m == "menu-open") {
if(!screen_open)
screen_control(TRUE);
llSetTimerEvent(15);
} else if(m == "menu-close") {
screen_control(FALSE);
llSetTimerEvent(0);
} else {
list argv = split(m, " ");
string cmd = gets(argv, 0);
if(cmd == "fan") {
fan = (float)gets(argv, 1);
damp(rate * 0.001 + fan * 0.5);
} else if(cmd == "color") {
base_c = (vector)concat(delitem(argv, 0), " ");
if(power_on) {
float jiggle = current_temp - 0.01 + llFrand(0.02);
if(jiggle < 0)
jiggle = 0;
damp(jiggle);
}
} else if(cmd == "rate") {
rate = (float)gets(argv, 1);
if(rate > 0)
power_on = 1;
damp(rate * 0.001 + fan * 0.5);
}
}
}
}

View File

@ -0,0 +1,274 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* YS-712 XSU Hardware Driver
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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 <utils.lsl>
#include <objects.lsl>
vector base_c = <0, 0.5, 1>;
vector c4 = <0, 0.75, 1>;
#define SCREEN 10
#define SCREEN_2 11
#define SCREEN_3 12
#define SCREEN_CONE 6
#define TEXT_START 13
#define TEXT_COUNT 66
integer screen_open = FALSE;
#define X_STEP (1.0/15.0)
#define Y_STEP (1.0/5.0)
screen_control(integer open) {
list acts;
if(open) {
vector scale = <0, 5 * X_STEP, 3 * X_STEP>;
integer pn = TEXT_START;
vector screen_origin = <-0.125, 0, -0.0113809>;
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, <0.4900000, 0.4900000, 0.2450000>,
PRIM_POS_LOCAL, screen_origin,
PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, <0.24, 0.06, 0.06>,
PRIM_POS_LOCAL, screen_origin + <-0.05, 0, 0.18>,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, <0.24, 0.06, 0.06>,
PRIM_POS_LOCAL, screen_origin + <-0.05, 0, -0.18>,
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_SIZE, <0.4900000, 0.4900000, 0.0625>,
PRIM_POS_LOCAL, screen_origin + <0.059, 0, 0>,
PRIM_COLOR, ALL_SIDES, c4, 0.0625,
PRIM_LINK_TARGET, 1,
PRIM_COLOR, 5, base_c, 1,
PRIM_FULLBRIGHT, 5, TRUE,
PRIM_GLOW, 5, 0.05
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx - 2.5;
float dy = 10 - ty;
float secondary_scale = 1.0;
if(dy == 9) {
dy = 8.5;
secondary_scale = 1.3;
} else if(dy == 0) {
dy = -0.25;
secondary_scale = 1.1;
} else if(dy == 10) {
dy = 10.5;
secondary_scale = 1.1;
}
float y = (float)(dy) - 5.5;
// rotation R = llEuler2Rot(<0, 0.17075 * x * PI_BY_TWO, 0>);
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, scale * secondary_scale,
PRIM_POS_LOCAL, (<-0.025 * secondary_scale * secondary_scale, -x * X_STEP, y * X_STEP / 3.0> * secondary_scale + screen_origin),
PRIM_ALPHA_MODE, ALL_SIDES, PRIM_ALPHA_MODE_MASK, 128
// PRIM_ROT_LOCAL, llEuler2Rot(<-PI_BY_TWO, 0, PI_BY_TWO>) * R,
//PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
} else {
integer pn = TEXT_START;
vector screen_origin = <0, 0, 0.25>;
acts += [
PRIM_LINK_TARGET, SCREEN,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,
PRIM_LINK_TARGET, SCREEN_2,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,
PRIM_LINK_TARGET, SCREEN_3,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,
PRIM_LINK_TARGET, SCREEN_CONE,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV,
PRIM_LINK_TARGET, 1,
PRIM_COLOR, 5, ZV, 1,
PRIM_FULLBRIGHT, 5, FALSE,
PRIM_GLOW, 5, 0
];
while(pn < TEXT_START + TEXT_COUNT) {
integer tx = (pn - TEXT_START) % 6;
integer ty = (pn - TEXT_START) / 6;
float x = (float)tx - 2.5;
float dy = 10 - ty;
if(dy == 9)
dy = 9.25;
else if(dy == 0)
dy = -0.25;
else if(dy == 10)
dy = 10.5;
float y = (float)(dy) - 5.5;
acts += [
PRIM_LINK_TARGET, pn,
// PRIM_DESC, "T" + (string)ty + "," + (string)tx,
PRIM_SIZE, ZV,
PRIM_POS_LOCAL, ZV
// PRIM_ROT_LOCAL, ZR,
// PRIM_COLOR, ALL_SIDES, ONES, 1
];
++pn;
if(llGetFreeMemory() < 256) {
setp(0, acts);
acts = [];
}
}
setp(0, acts);
}
screen_open = open;
// echo("screen now " + (string)open);
}
integer power_on;
default {
state_entry() {
llSetLinkTextureAnim(SCREEN_CONE, ANIM_ON | SMOOTH | LOOP | PING_PONG, ALL_SIDES, 0, 0, 0, 1, 100);
llSetMemoryLimit(0x8000);
//screen_control(FALSE);
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
// damp(0);
/* echo(llGetLinkName(HOSE_L));
echo(llGetLinkName(HOSE_R)); */
// damp(0.0);
/*integer pn = llGetNumberOfPrims();
while(pn--) {
echo((string)pn + " = " + llGetLinkName(pn));
}*/
}
/*
touch_start(integer n) {
while(n--) {
key toucher = llDetectedKey(n);
integer pi = llDetectedLinkNumber(n);
string part = llGetLinkName(pi);
if((part == "lidl" || part == "lidr") && !power_on) {
linked(LINK_THIS, 0, "touch-hatch", toucher);
} else if(part == "text" && power_on) {
linked(LINK_THIS, pi, "touch-screen", toucher);
} else if(part == "screen") {
} else {
// if "Object" || ("lid" && power_on)
if((power_on && screen_open) || !power_on) {
linked(LINK_THIS, 0, "menu-request", toucher);
} else {
linked(LINK_THIS, 0, "menu-start", toucher);
screen_control(TRUE);
}
}
}
}
*/
timer() { // menu expiry
// echo("(supervisor screen menu expiry)");
if(screen_open) {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
}
llSetTimerEvent(0);
// echo("memory status: " + (string)llGetUsedMemory() + " used; " + (string)llGetFreeMemory() + " virgin");
}
link_message(integer s, integer n, string m, key id) {
// echo("supervisor screen: " + m);
if(m == "on") {
power_on = 1;
} else if(m == "off") {
power_on = 0;
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
llSetTimerEvent(0);
// damp(0);
} else if(m == "menu-open") {
if(!screen_open)
screen_control(TRUE);
llSetTimerEvent(15);
} else if(m == "menu-close") {
screen_control(FALSE);
linked(LINK_THIS, 0, "menu-end", "");
llSetTimerEvent(0);
} else {
list argv = split(m, " ");
string cmd = gets(argv, 0);
/*if(cmd == "fan") {
fan = (float)gets(argv, 1);
damp(rate * 0.001 + fan * 0.5);
} else*/
if(cmd == "color") {
base_c = (vector)concat(delitem(argv, 0), " ");
/* if(screen_open)
screen_control(TRUE); */
} else if(cmd == "color-4") {
c4 = (vector)concat(delitem(argv, 0), " ");
if(screen_open)
screen_control(TRUE);
}
/*else if(cmd == "rate") {
rate = (float)gets(argv, 1);
if(rate > 0)
power_on = 1;
// damp(rate * 0.001 + fan * 0.5);
}*/
}
}
}

52
ARES/license.txt Normal file
View File

@ -0,0 +1,52 @@
In the text below, <YEAR> is 2022-2024. Substitute this value when including attribution in any distributions.
This version of ARES is copyright <YEAR> by Nanite Systems Corporation (hereafter, the Corporation). It is proprietary software. Its use is subject to the terms of the Nanite Systems EULA ( http://support.nanite-systems.com/?id=216 ).
Preamble. To facilitate community involvement in the ARES ecosystem, we have included the source code for some ARES utilities and system components. These individual files are provided under various terms, described below.
Section 1. Files marked 'ASCL-i' are to be considered proprietary. They are offered to you on a limited basis to facilitate modification and customization.
You may distribute modified ASCL-i files to other ARES users provided they remain full-permissions, are offered free of charge, and retain proper copyright markings. You may not create other derivative works of ASCL-i programs.
The Corporation reserves the right to collect, alter, and distribute such modifications covered by this section, including by adding them to the standard distribution.
We may also take action (as outlined in the EULA) against abusive, harmful, or misleading modifications, as we police other third-party products that represent potential or real harms to the community or the Corporation.
By releasing a modification to code covered under this section, you are donating your work to the Corporation and to its customers.
The Corporation may elect to withdraw ASCL-i licensing for new versions of a file (see 'Unforeseeable futures', below), but this does not affect previously-released versions.
Section 2. Files marked 'ASCL-ii' are provided under a modified copyleft license. Although they appear here in ARES as part of commercial software, they may be used as the basis of derivative, non-profit works that retain a compatible license.
Derivative works of ASCL-ii software must retain proper attribution in documentation and source code, such as: Based on <FILENAME> from Nanite Systems ARES. Copyright <YEAR> Nanite Systems Corporation. Licensed under ASCL-ii.
Additionally, ASCL-ii derivative products must be distributed free of charge and with legible source code. (LSL scripts need not be full permissions; a copy/transfer notecard is sufficient.) A copy of this copyright license must be included.
ASCL-ii code may be used in GNU General Public License (GPL) projects, provided the above attribution is preserved.
Section 3. Files marked 'ASCL-iii' are provided under a 'BSD-style' license. They may be redistributed or used as the basis of commercial, closed-source products so long as the following terms are abided. The ARES SDK uses this license.
Based on code from Nanite Systems ARES. Copyright <YEAR> Nanite Systems Corporation.
i. Redistributions of source code must retain a copy of these conditions, the above copyright notice, and the disclaimer below.
ii. Redistributions in binary form must include the copyright notice in the documentation and/or other materials provided with the distribution as defined above.
iii. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
The ASCL-iii license is intended to be compatible with a three-clause BSD license.
Section 4. Files marked 'ASCL-iv' are closed-source, proprietary, commercial code. Aside from the following disclaimer and the copyright text, you may not distribute them under any circumstance for the foreseeable future.
Section 5. Unforeseeable futures. The Corporation retains full control over ASCL-i and -iv code. It may, at any time and at its sole discretion, elect to release this code under a different license.
For example, ASCL-i coverage may be withdrawn to protect trade secrets in new versions of relevant code, or to convert ARES into a copyleft project in the event that the Corporation cannot continue to operate on a commercial basis.
Section 6. Disclaimer. The following must be included with all ASCL-licensed code:
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.
This is an abridged version of the BSD disclaimer. The text is interchangeable with that disclaimer.
Section 7. Further Information. Additional insight regarding Nanite Systems copyrights can be obtained by e-mailing support@nanite-systems.com. The latest version of this license can be obtained by visiting http://nanite-systems.com/ASCL.

286
ARES/program Normal file
View File

@ -0,0 +1,286 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222023 Nanite Systems Corporation
*
* =========================================================================
*
* PROGRAM.H.LSL Footer Component
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
// #define DEBUG
integer L_KERNEL;
#ifdef DAEMON_LISTEN
// used by fs
integer L_DAEMON;
#endif
/*
* CHEAP JOB RECEIPTS
*
* set '_resolved = src;' whenever a task is complete
* set '_resolved = 0;' whenever a task is created
* make sure you store the src so you can use it later
*/
integer _resolved;
key _input_s;
key _output_s;
key _user;
/*
* CURRENT JOB MODE
*
* (_mode & 1) if engaged in a thread (waiting for an event)
* (_mode & 2) if NON_VOLATILE (never quits)
* (_mode & 4) if DONE messages should be accepted
* (_mode & 8) if invokes should trigger stream messages (FLOW)
* -- the FLOW API is not yet implemented --
*/
integer _mode
#ifdef NON_VOLATILE
#ifdef FLOW
= 10
#else
= MODE_NON_VOLATILE
#endif
#elif defined FLOW
= MODE_FLOW
#endif
;
/*
* AUTOMATIC MEMORY MANAGEMENT
*
* For small, single-purpose utilities, 64 kb may be way too much memory.
* Adding #define AUTO_MEMORY_MANAGEMENT to your program will cause the script
* to attempt to minimize this impact.
*
* The additional constant AMM_GENEROUS will add 8 kb to the limit instead of
* 2 kb. This is quite a lot of memory to add, but makes sense for small
* programs that work with pipes.
*
*/
#ifdef DISHONEST_MEMORY_MANAGEMENT
#define AUTO_MEMORY_MANAGEMENT
#endif
#ifdef AUTO_MEMORY_MANAGEMENT
integer amm_profiling;
#endif
default {
// should NOT be used in user applications
// (required for _proc and _fs)
#ifdef EXT_EVENT_HANDLER
#include EXT_EVENT_HANDLER
#endif
#ifndef OVERRIDE_STATE_ENTRY
state_entry() {
#ifdef AUTO_MEMORY_MANAGEMENT
llScriptProfiler(amm_profiling = PROFILE_SCRIPT_MEMORY);
#endif
KERNEL = llGetLinkKey(R_KERNEL);
DAEMON = llGetLinkKey(R_DAEMON);
PROGRAM = llGetLinkKey(R_PROGRAM);
PROGRAM_NAME = llGetScriptName();
avatar = llGetOwner();
L_KERNEL = llListen(C_UNASSIGNED, "", KERNEL, "");
linked(R_KERNEL, SIGNAL_SOLICIT_ADDRESS, PROGRAM_NAME + "\n" + CLIENT_VERSION + " " + CLIENT_VERSION_TAGS, "");
#ifdef DEBUG
echo(PROGRAM_NAME + " compiled " + __DATE__ + " in debug mode; " + (string)llGetUsedMemory() + " bytes used.");
#endif
}
#endif
#ifndef OVERRIDE_ON_REZ
#ifdef NON_VOLATILE
on_rez(integer n) {
KERNEL = llGetLinkKey(R_KERNEL);
DAEMON = llGetLinkKey(R_DAEMON);
PROGRAM = llGetLinkKey(R_PROGRAM);
PROGRAM_NAME = llGetScriptName();
avatar = llGetOwner();
#ifdef DAEMON_LISTEN
llListenRemove(L_DAEMON);
#endif
llListenRemove(L_KERNEL);
L_KERNEL = llListen(C_UNASSIGNED, "", KERNEL, "");
PROGRAM_NUMBER = 0;
E_PROGRAM_NUMBER = "";
linked(R_KERNEL, SIGNAL_SOLICIT_ADDRESS, PROGRAM_NAME + "\n" + ARES_VERSION + " " + ARES_VERSION_TAGS, "");
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] waiting for PID");
#endif
}
#else
on_rez(integer n) {
llResetScript();
}
#endif
#endif
#ifndef OVERRIDE_LISTEN
listen(integer c, string source_name, key id, string m) {
integer n = ares_decode(m);
integer s = (
((id == KERNEL) * R_KERNEL)
| ((id == DAEMON) * R_DAEMON)
| ((id == PROGRAM) * R_PROGRAM)
);
#ifndef NEVER_UNASSIGNED
if(c == C_UNASSIGNED) {
if(n == SIGNAL_ASSIGN_ADDRESS) {
string expected_program_name = strdelete(m, 0, 3);
if(expected_program_name == PROGRAM_NAME) {
#ifdef DAEMON_LISTEN
llListenRemove(L_DAEMON);
#endif
llListenRemove(L_KERNEL);
E_PROGRAM_NUMBER = substr(m, 2, 3);
PROGRAM_NUMBER = ares_decode(E_PROGRAM_NUMBER);
integer C = C_PROGRAM_BASE + PROGRAM_NUMBER;
#ifdef DAEMON_LISTEN
L_DAEMON = llListen(C, "", DAEMON, "");
#endif
L_KERNEL = llListen(C, "", KERNEL, "");
#ifdef DEBUG
echo(PROGRAM_NAME + " registered with kernel as PID " + (string)PROGRAM_NUMBER);
#endif
set_mode(_mode);
main(_resolved = 0, SIGNAL_INIT, "", NULL_KEY, NULL_KEY, NULL_KEY);
#ifndef OVERRIDE_READY
system(SIGNAL_READY, PROGRAM_NAME);
#endif
}
}
} else
#endif // NEVER_UNASSIGNED
#ifdef EXT_COM_HANDLER
#include EXT_COM_HANDLER
else
#endif
{
#ifdef DEBUG
// echo(llGetScriptName() + " incoming message: " + m);
#endif
#ifdef DISHONEST_MEMORY_MANAGEMENT
llSetMemoryLimit(0x10000);
llScriptProfiler(amm_profiling = PROFILE_SCRIPT_MEMORY);
#endif
_input_s = NULL_KEY;
_output_s = NULL_KEY;
string remainder = strdelete(m, 0, 1);
if(n == SIGNAL_INVOKE || n == SIGNAL_NOTIFY) {
_output_s = substr(remainder, 2, 37);
_input_s = substr(remainder, 39, 74);
_user = substr(remainder, 76, 111);
main(_resolved = ares_decode(remainder), n, strdelete(remainder, 0, 112), _output_s, _input_s, _user);
#ifdef AUTO_MEMORY_MANAGEMENT
if(amm_profiling) {
llScriptProfiler(amm_profiling = PROFILE_NONE);
integer estimate = llGetSPMaxMemory();
#ifndef AMM_GENEROUS
if(estimate < 0xe000) {
estimate += 0x7ff;
llSetMemoryLimit(estimate);
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] memory needs estimated at " + (string)estimate + " bytes");
#endif
}
#else
if(estimate < 0xd000) {
estimate += 0x1fff;
llSetMemoryLimit(estimate);
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] memory needs estimated at " + (string)estimate + " bytes");
#endif
}
#endif
#ifdef DEBUG
else {
echo(PROGRAM_NAME + " memory usage is very high; automatic memory management should be disabled");
}
#endif
}
#endif
}
#ifdef EXT_MSG_HANDLER
#include EXT_MSG_HANDLER
#endif
else {
main(_resolved = 0, n, remainder, NULL_KEY, NULL_KEY, NULL_KEY);
}
#ifndef NON_VOLATILE
/*
PROBLEM: unlike linked messages, listeners can accumulate requests
while the program is hibernating, causing it to sleep while there
are still messages in the queue. The kernel solves this by keeping
a queue of all tasks yet to be executed, and by replaying the last
task if the program has apparently restarted (i.e., if it solicits
an address.)
*/
if(_resolved && getjs(tasks_queue, [(string)_input_s]) == JSON_INVALID)
pipe_close_volatile([_input_s]);
if(!task_count()) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] finished " + (string)n + ": " + remainder);
#endif
exitc(_input_s); // also performs resolve(_resolved)
} else {
resolvec(_resolved, _input_s);
}
#endif
}
}
#endif
}

146
ARES/reference.txt Normal file
View File

@ -0,0 +1,146 @@
================================
ARES API REFERENCE
================================
(work in progress)
To send a message to a program:
linked(R_KERNEL, SIGNAL_INVOKE, "!!" + ares_encode(PROGRAM_NUMBER) + params, "")
Commands to the kernel itself have a similar format; see next paragraph.
The "!!" is the destination PID field and will be auto-populated by the kernel. This part is absent in kernel commands.
Note that SIGNAL_INVOKE has a particularly structured params format, discussed later.
The program will receive this message through its listen() event on the channel (C_PROGRAM_BASE + PROGRAM_NUMBER), after being awoken by _delegate if necessary. The destination PID field will have been removed and replaced with an encoded signal message (e.g. "!#" for SIGNAL_EVENT).
The program's main() will be called with main(SIGNAL_INVOKE, params, PROGRAM_NUMBER). The target pipe and program name must be extracted manually.
To send a message to a daemon, call it directly:
tell(DAEMON, C_IO, ares_encode(SIGNAL_DATA_REQUEST) + ares_encode(PROGRAM_NUMBER) + request)
The Daemon will receive this as main(SIGNAL_DATA_REQUEST, request, PROGRAM_NUMBER)
---------------------
SPECIFIC MESSAGES
---------------------
SIGNAL_INVOKE (program to program)
params: (string)target_pipe + " " + command_line
In general, the first word of the command_line must match the filename of the source program. System programs are an exception (the leading underscore is removed.)
The target_pipe is a UUID to which output messages should be sent through the IO daemon. In most cases the target_pipe is a user's key.
SIGNAL_EVENT (kernel to program)
params: <event> <params>
Sent when an event occurs that has been hooked by the program.
SIGNAL_SUMMON (daemon to program)
params:
SIGNAL_DATA_REQUEST (program to daemon)
params:
SIGNAL_DATA_UNAVAILABLE (program to daemon)
params:
SIGNAL_DATA_VALUE (program to daemon)
params:
SIGNAL_DATA_LIST (program to daemon)
params:
SIGNAL_DATA_SET (program to daemon)
params:
SIGNAL_DATA_DELETE (program to daemon)
params:
SIGNAL_CALL (program to daemon)
params: <callback> <program> <parameters>
SIGNAL_CREATE_RULE (program to daemon)
params:
SIGNAL_DELETE_RULE (program to daemon)
params:
SIGNAL_NOTIFY (program to daemon, program to program)
params: <callback> <program> <parameters>
Informs another process that a configuration change has been made, the substance of which is identified by a unique tag, usually the relevant LSD section and JSON key. The parameters are comparable to those used for an INVOKE or CALL message.
This message can be routed through the kernel, in which case it will attempt to wake up any user program required. Used e.g. by the power system in this way.
// 0xf00-0xfff: kernel interactions
SIGNAL_SOLICIT_ADDRESS (program to kernel)
params: program name
SIGNAL_ASSIGN_ADDRESS (kernel to program)
params: address + program name
SIGNAL_TERMINATE (kernel to program or delegate)
params: program name
SIGNAL_WAKE (kernel to program or delegate)
params: program name
SIGNAL_WOKE (any to kernel)
params: program name
SIGNAL_OVERVIEW (kernel to delegate or status daemon)
params: none
SIGNAL_OVERVIEW_REPORT (delegate or status daemon to kernel)
params: [[idle,programs],[running,programs]]
SIGNAL_HOOK_EVENT (any to kernel)
params: <PID><event>
SIGNAL_UNHOOK_EVENT (any to kernel)
params: <PID><event>
SIGNAL_TERMINATED (any to kernel)
params: program name
SIGNAL_UNKNOWN_SCRIPT (various)
params: program name
SIGNAL_QUERY_MODULES (any to program)
params: none
SIGNAL_QUERY_HOOKS (any to program)
params: none
SIGNAL_QUERY_DAEMONS (any to program)
params: none
SIGNAL_MODULES_REPORT (kernel to any)
params: kernel modules table
SIGNAL_HOOKS_REPORT (kernel to any)
params: kernel hooks table
SIGNAL_DAEMONS_REPORT (kernel to any)
params: kernel daemons table
SIGNAL_EVENT_TRIGGER (any to kernel)
params: <event> <params>
SIGNAL_INIT (program to self)
params: none
SIGNAL_DAEMON_RESET (daemon to kernel)
params: program name
SIGNAL_KERNEL_RESET (kernel to delegate or status daemon)
params: none
Triggers reset of all ring 3 programs and re-identification of all daemons. Only message normally passed to daemons via linked message rather than usual listener-based isolation.

589
ARES/system/display.lsl Normal file
View File

@ -0,0 +1,589 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Display System Module
*
* 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 <ARES/a>
#include <ARES/api/interface.consts.h.lsl>
#define call_interface(_outs, _user, _msg) \
system(SIGNAL_CALL, E_INTERFACE + E_PROGRAM_NUMBER \
+ (string)_outs + " " + (string)_user + " interface " + (_msg));
#define CLIENT_VERSION ARES_VERSION
#define CLIENT_VERSION_TAGS ARES_VERSION_TAGS
integer power_on = TRUE;
string fixed_warning_texture;
integer fixed_warning_mode = 1;
string devices_texture;
float device_scale;
integer device_mode = 1;
integer device_wires = 1;
vector devices_offset = <0, -860, 0>;
string sound_heat_alarm = "5db9d9d6-6eda-485e-6436-e97dfdc1983c";
string sound_shield_alarm = "348fcd3d-7993-54fb-720c-c5ebc42de505";
integer heat_alarm;
integer shield_alarm;
integer color_slot;
vector color = <1, 1, 1>;
vector color_bad = <1, 0.25, 0>;
integer screen_height = 1008;
integer screen_height_mlook = 1027;
float sound_volume = 1;
float pixel_scale;
vector fixed_warning_offset = <0, 768, 384>;
list active_warnings = [0, 0, 0, 0, 0, 0, 0];
integer in_mlook = FALSE;
integer old_in_mlook;
integer devices_in_mlook = NOWHERE;
integer old_devices_in_mlook;
integer config_visible;
integer config_target;
string config_parameter = "(uninitialized)";
vector config_value;
vector config_backup;
position_all(integer only_devices) {
in_mlook = TRUE && (llGetAgentInfo(avatar) & AGENT_MOUSELOOK);
if(in_mlook) {
pixel_scale = 1.0 / screen_height_mlook;
} else {
pixel_scale = 1.0 / screen_height;
}
/* DEVICES */
#define MAX_DEVICES 16
// string devices = llLinksetDataRead("device");
string device_icons = getdbl("display", ["devices", "icon"]);
list device_names = jskeys(llLinksetDataRead("device"));
integer di;
list device_names_local;
list device_names_remote;
integer dmax = count(device_names);
while(di < dmax) {
string device = gets(device_names, di);
integer remote = (integer)getdbl("device", [device, "r"]);
if(remote)
device_names_remote += device;
else
device_names_local += device;
++di;
}
device_names = device_names_local + device_names_remote;
di = 0;
list updates;
/*string s_interface = llLinksetDataRead("interface");
float screen_height = (float)getjs(s_interface, ["height"]);
if(screen_height == 0) screen_height = 1008;
if(llGetAgentInfo(avatar) & AGENT_MOUSELOOK) {
screen_height = (float)getjs(s_interface, ["height-mlook"]);
if(screen_height == 0) screen_height = 1027;
} */
vector scaled_devices_offset = devices_offset * pixel_scale;
float scale_module = 16.0 * pixel_scale * device_scale;
old_devices_in_mlook = devices_in_mlook;
devices_in_mlook = (llGetAgentInfo(avatar) & AGENT_MOUSELOOK) && TRUE;
float line_texture_rotation = 0;
vector device_main_axis = <0, 0, 1>;
vector device_cross_axis = <0, -1, 0>;
vector line_size = <8, 1, 0>;
if(device_mode == 2) {
device_main_axis = <0, -1, 0>;
device_cross_axis = <0, 0, -1>;
line_size = <1, 8, 0>;
line_texture_rotation = -PI_BY_TWO;
}
if(old_devices_in_mlook != devices_in_mlook) {
di = 0;
while(di < MAX_DEVICES) {
updates += [
PRIM_LINK_TARGET, DEVICE_ICON_BASE + di + MAX_DEVICES,
PRIM_POS_LOCAL, scaled_devices_offset
+ device_cross_axis * 0.25
+ device_main_axis * (di * scale_module),
PRIM_ROTATION, INVISIBLE,
PRIM_COLOR, ALL_SIDES, color, 0,
PRIM_LINK_TARGET, DEVICE_ICON_BASE + di,
PRIM_POS_LOCAL, scaled_devices_offset
+ device_cross_axis * 0.125
+ device_main_axis * (di * scale_module),
PRIM_ROTATION, INVISIBLE,
PRIM_COLOR, ALL_SIDES, color, 0
];
++di;
}
setp(0, updates);
updates = [];
llSleep(0.25);
}
di = 0;
string texk = devices_texture;
while(di < MAX_DEVICES) {
string device = gets(device_names, di);
string device_info = getdbl("device", [device]);
if(device != "") {
integer remote = (integer)getjs(device_info, ["r"]);
if(device_wires)
updates += [
PRIM_LINK_TARGET, DEVICE_ICON_BASE + di + MAX_DEVICES,
// PRIM_TEXTURE, 0, texk, <1.0, 0.125, 0>, <0, -0.4375 + 0.125 * remote, 0>, 0,
PRIM_TEXTURE, 0, texk, <1.0, 0.125, 0>, <0, -0.3125 - 0.125 * remote, 0>, line_texture_rotation,
PRIM_SIZE, line_size * scale_module,
PRIM_ROTATION, VISIBLE,
PRIM_POS_LOCAL, scaled_devices_offset
+ device_cross_axis * 4.5 * scale_module
+ device_main_axis * (di * scale_module),
PRIM_COLOR, ALL_SIDES, color, 1
];
updates += [
PRIM_LINK_TARGET, DEVICE_ICON_BASE + di,
PRIM_SIZE, <scale_module, scale_module, 0>,
PRIM_ROTATION, VISIBLE,
PRIM_POS_LOCAL, scaled_devices_offset
+ device_cross_axis * 0
+ device_main_axis * (di * scale_module),
PRIM_DESC, device,
PRIM_COLOR, ALL_SIDES, color, 1
];
string icon = getjs(device_info, ["i"]);
if(icon != JSON_INVALID) {
updates += [
PRIM_TEXTURE, 0, icon, <1, 1, 0>, ZV, 0
];
} else {
icon = getjs(device_icons, [device]);
if(strlen(icon) == 36) {
updates += [
PRIM_TEXTURE, 0, icon, <1, 1, 0>, ZV, 0
];
} else {
if(icon == JSON_INVALID)
icon = "40";
float iconx = -0.46875 + (float)(llOrd(icon, 0) - 48) * 0.0625;
float icony = 0.46875 - (float)(llOrd(icon, 1) - 48) * 0.0625;
updates += [
PRIM_TEXTURE, 0, texk, <0.0625, 0.0625, 0>, <iconx, icony, 0>, 0
];
}
}
} else {
updates += [
PRIM_LINK_TARGET, DEVICE_ICON_BASE + di,
PRIM_TEXTURE, 0, TEXTURE_TRANSPARENT, ZV, ZV, 0,
PRIM_POS_LOCAL, scaled_devices_offset
+ device_cross_axis * 0.125
+ device_main_axis * (di * scale_module),
PRIM_ROTATION, INVISIBLE,
PRIM_DESC, "",
PRIM_LINK_TARGET, DEVICE_ICON_BASE + di + MAX_DEVICES,
PRIM_TEXTURE, 0, TEXTURE_TRANSPARENT, ZV, ZV, 0,
PRIM_POS_LOCAL, scaled_devices_offset
+ device_cross_axis * 0.25
+ device_main_axis * (di * scale_module),
PRIM_ROTATION, INVISIBLE
];
}
setp(0, updates);
updates = [];
++di;
}
setp(0, updates);
if(only_devices) // allow device updates without refreshing whole UI
return;
if(fixed_warning_mode == 1) {
setp(FIXED_WARNING_LIST, [
// PRIM_TEXTURE, ALL_SIDES, FIXED_MSGS_TEX, <1, 0.03125, 0>, <0, 0.015625 + (float)(1) * 0.03125, 0>, PI_BY_TWO,
//PRIM_TEXTURE, ALL_SIDES, TEXTURE_TRANSPARENT, ONES, ZV, 0,
//PRIM_ROTATION, llEuler2Rot(<0, 0, -PI_BY_TWO>) * VISIBLE,
//PRIM_ROTATION, INVISIBLE_FWL,
PRIM_POS_LOCAL, (fixed_warning_offset - <0, 0, 64>) * pixel_scale,
PRIM_SIZE, <128, 128, 0> * pixel_scale
]);
if(sum(active_warnings) > 0 && power_on)
setp(FIXED_WARNING_LIST, [
PRIM_ROTATION, VISIBLE_FWL
]);
else
setp(FIXED_WARNING_LIST, [
PRIM_TEXTURE, ALL_SIDES, TEXTURE_TRANSPARENT, ZV, ZV, 0,
PRIM_ROTATION, INVISIBLE_FWL
]);
} else if(fixed_warning_mode == 2) {
setp(FIXED_WARNING_LIST, [
// PRIM_TEXTURE, ALL_SIDES, FIXED_MSGS_TEX, <1, 0.03125, 0>, <0, 0.015625 + (float)(1) * 0.03125, 0>, PI_BY_TWO,
//PRIM_TEXTURE, ALL_SIDES, TEXTURE_TRANSPARENT, ONES, ZV, 0,
//PRIM_ROTATION, llEuler2Rot(<0, 0, -PI_BY_TWO>) * VISIBLE,
//PRIM_ROTATION, INVISIBLE_FWL,
PRIM_POS_LOCAL, fixed_warning_offset * pixel_scale,
PRIM_SIZE, <128, 16, 0> * pixel_scale
]);
if(sum(active_warnings) > 0 && power_on)
setp(FIXED_WARNING_LIST, [
PRIM_ROTATION, VISIBLE
]);
else
setp(FIXED_WARNING_LIST, [
PRIM_TEXTURE, ALL_SIDES, TEXTURE_TRANSPARENT, ZV, ZV, 0,
PRIM_ROTATION, INVISIBLE
]);
}
if(config_visible) {
setp(CONFIG_CONTROLS, [
PRIM_ROTATION, VISIBLE,
PRIM_COLOR, ALL_SIDES, color, 1,
PRIM_TEXT, "configuring: " + config_parameter + "\n" + (string)config_value + "\n ", color, 1,
PRIM_SIZE, <256, 32, 0> * pixel_scale,
PRIM_POSITION, <-5, 0, 0>,
PRIM_TEXTURE, 0, MOVER_TEX, <0.125, 1, 0>, <-0.4375, 0, 0>, 0,
PRIM_TEXTURE, 1, MOVER_TEX, <0.125, 1, 0>, <-0.3125, 0, 0>, 0,
PRIM_TEXTURE, 2, MOVER_TEX, <0.125, 1, 0>, <-0.1875, 0, 0>, 0,
PRIM_TEXTURE, 3, MOVER_TEX, <0.125, 1, 0>, <-0.0625, 0, 0>, 0,
PRIM_TEXTURE, 4, MOVER_TEX, <0.125, 1, 0>, < 0.0625, 0, 0>, 0,
PRIM_TEXTURE, 5, MOVER_TEX, <0.125, 1, 0>, < 0.1875, 0, 0>, 0,
PRIM_TEXTURE, 6, MOVER_TEX, <0.125, 1, 0>, < 0.3125, 0, 0>, 0,
PRIM_TEXTURE, 7, MOVER_TEX, <0.125, 1, 0>, < 0.4375, 0, 0>, 0
]);
}
}
main(integer src, integer n, string m, key outs, key ins, key user) {
@restart_main;
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
string action = gets(argv, 1);
if(argc == 1 || action == "help") {
msg = "Usage: " + PROGRAM_NAME + " <action> ...\nConfigures and assists the interface daemon.\n\n show: Turns on all UI elements\n reconfigure: Refreshes the UI\n devices: Refreshes just the display of devices\n c[onfig] on: Turns on the UI mover.\n c[onfig] off: Turns off the UI mover.\n c[onfig] <database entry>: Causes the UI mover to operate on the specified vector.\n\nUnrecognized actions will be forwarded to the interface daemon.";
} else if(action == "show") {
setp(LINK_ALL_CHILDREN, [
PRIM_ROTATION, VISIBLE,
PRIM_LINK_TARGET, FIXED_WARNING_LIST,
PRIM_ROTATION, VISIBLE_FWL
]);
} else if(action == "reconfigure") {
call_interface(outs, user, concat(delitem(argv, 0), " "));
jump reconfigure;
} else if(action == "devices") {
position_all(TRUE);
} else if(action == "config" || action == "c") {
string arg2 = gets(argv, 2);
if(arg2 == "none" || arg2 == "off" || arg2 == "done") {
config_visible = FALSE;
unhook_events([EVENT_TOUCH]);
setp(CONFIG_CONTROLS, [
PRIM_TEXT, "", ZV, 0,
PRIM_POSITION, OFFSCREEN,
PRIM_ROTATION, INVISIBLE,
PRIM_SIZE, ZV,
PRIM_TEXTURE, ALL_SIDES, TEXTURE_BLANK, ZV, ZV, 0
]);
} else {
if(config_parameter != "(uninitialized)") {
list keyname = split(config_parameter, ".");
string hive = gets(keyname, 0);
keyname = delitem(keyname, 0);
setdbl(hive, keyname, (string)config_value);
}
config_parameter = arg2;
if(config_parameter != "(uninitialized)") {
list keyname = split(config_parameter, ".");
string hive = gets(keyname, 0);
keyname = delitem(keyname, 0);
config_backup = config_value = (vector)getdbl(hive, keyname);
}
if(!config_visible) {
hook_events([EVENT_TOUCH]);
config_visible = TRUE;
echo("UI mover active. Press the ⭯ button to see changes, or type '@display config done' to close.");
}
position_all(FALSE);
}
} else {
call_interface(outs, user, concat(delitem(argv, 0), " "));
}
if(msg != "") {
print(outs, user, msg);
}
} else if(n == SIGNAL_EVENT) {
// echo(m);
// cannot access warning texture - pls fix
integer e = (integer)m;
if(e == EVENT_TOUCH) {
if(!config_visible) {
unhook_events([EVENT_TOUCH]);
} else {
list args = split(m, " ");
if((integer)gets(args, 2) == CONFIG_CONTROLS) {
integer button = (integer)gets(args, 3);
vector current_value = config_value;
integer SPEED = 4;
if(button == 0) {
invoke("_menu start mover", DAEMON, NULL_KEY, avatar);
return;
} else if(button == 1) { // Left = +Y
config_value.y += SPEED;
} else if(button == 2) { // Up = +Z
config_value.z += SPEED;
} else if(button == 3) { // Down = -Z
config_value.z -= SPEED;
} else if(button == 4) { // Right = -Y
config_value.y -= SPEED;
} else if(button == 5) { // Away = +X
config_value.x += SPEED * 128;
} else if(button == 6) { // Back = -X
config_value.x -= SPEED * 128;
} else if(button == 7) {
interface_sound("go");
m = PROGRAM_NAME + " reconfigure";
n = SIGNAL_INVOKE;
jump restart_main;
}
interface_sound("act");
if(config_parameter != "(uninitialized)" && config_value != current_value) {
list keyname = split(config_parameter, ".");
string hive = gets(keyname, 0);
keyname = delitem(keyname, 0);
setdbl(hive, keyname, (string)config_value);
setp(CONFIG_CONTROLS, [
PRIM_TEXT, "configuring: " + config_parameter + "\n" + (string)config_value + "\n ", color, 1
]);
}
}
}
} else if(e == EVENT_INTERFACE) {
power_on = (integer)getdbl("status", ["on"]);
position_all(FALSE);
} else if(e == EVENT_WARNING) {
list argv = split(m, " ");
integer slot = (integer)gets(argv, 1);
integer msg = (integer)gets(argv, 2);
// 0x00b <slot> <number> (0 = clear)
// echo(m);
if(slot < 0 || slot > 7 || msg < 0 || msg > 32) return;
integer old_warning = geti(active_warnings, slot);
active_warnings = alter(active_warnings, [msg], slot, slot);
string announcements = "{"
+ "\"1\":\"error-malfunction\","
+ "\"3\":\"repair-1\","
+ "\"4\":\"dqd-1\","
+ "\"5\":\"dqd-1\","
+ "\"7\":\"heat-1\","
+ "\"8\":\"heat-0\","
+ "\"9\":\"heat-1\","
+ "\"10\":\"pressure-1\","
+ "\"11\":\"pressure-0\","
+ "\"13\":\"cryolube-025\","
+ "\"15\":\"nav-1\","
+ "\"16\":\"follow-1\","
+ "\"25\":\"battery-020\","
+ "\"26\":\"charging-1\"}";
string deactivate_announcements = "{"
+ "\"3\":\"repair-0\","
+ "\"4\":\"dqd-0\","
+ "\"5\":\"dqd-0\","
+ "\"15\":\"nav-0\","
+ "\"16\":\"follow-0\","
+ "\"26\":\"charging-0\"}";
string announcement = getjs(announcements, [(string)msg]);
if(announcement == JSON_INVALID)
announcement = getjs(deactivate_announcements, [(string)old_warning]);
if(announcement != JSON_INVALID)
system(SIGNAL_CALL, E_EFFECTOR + E_PROGRAM_NUMBER + NULL_KEY + " " + NULL_KEY + " effector announce " + announcement);
if(slot == 2 && msg == 9) { // DUMP HEAT!
heat_alarm = 1;
sound_volume = (float)getdbl("interface", ["sound", "volume"]);
llLinkPlaySound(HEAT_GAUGE, sound_heat_alarm, sound_volume, SOUND_LOOP);
setp(FIXED_WARNING_LIST, [
PRIM_COLOR, slot, color_bad, 1
]);
} else if(slot == 2 && heat_alarm) {
heat_alarm = 0;
setp(FIXED_WARNING_LIST, [
PRIM_COLOR, slot, color, 1
]);
}
if(msg == 0) {
setp(FIXED_WARNING_LIST, [
PRIM_TEXTURE, slot, TEXTURE_TRANSPARENT, ONES, ZV, 0
]);
} else if(fixed_warning_mode == 2) {
setp(FIXED_WARNING_LIST, [
PRIM_TEXTURE, slot, fixed_warning_texture, <1, 0.03125, 0>, <0, 0.515625 - (float)msg * 0.03125, 0>, 0
]);
} else if(fixed_warning_mode == 1) {
setp(FIXED_WARNING_LIST, [
PRIM_TEXTURE, slot, fixed_warning_texture, <1, 0.03125, 0>, <0, 0.515625 - (float)msg * 0.03125, 0>, PI_BY_TWO
]);
}
if(fixed_warning_mode == 2) {
if(sum(active_warnings))
setp(FIXED_WARNING_LIST, [
PRIM_ROTATION, VISIBLE
]);
else
setp(FIXED_WARNING_LIST, [
PRIM_ROTATION, INVISIBLE
]);
} else if(fixed_warning_mode == 1) {
if(sum(active_warnings))
setp(FIXED_WARNING_LIST, [
PRIM_ROTATION, VISIBLE_FWL
]);
else
setp(FIXED_WARNING_LIST, [
PRIM_ROTATION, INVISIBLE_FWL
]);
}
if(!heat_alarm)
llLinkStopSound(HEAT_GAUGE);
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
hook_events([EVENT_INTERFACE, EVENT_WARNING]);
unhook_events([EVENT_TOUCH]);
llLinkStopSound(HEAT_GAUGE);
jump reconfigure;
} 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);
}
jump end;
@reconfigure;
{
string s_interface = llLinksetDataRead("interface");
screen_height = (integer)getjs(s_interface, ["height"]);
if(!screen_height) screen_height = 1008;
screen_height_mlook = (integer)getjs(s_interface, ["height-mlook"]);
if(!screen_height_mlook) screen_height_mlook = 1027;
color_slot = (integer)getjs(s_interface, ["color"]);
}
if(color_slot == 0)
color = ONES;
else
color = str2vec(getdbl("id", ["color", color_slot - 1]));
integer cbi = 2;
if(color_slot - 1== 2)
cbi = 3;
color_bad = str2vec(getdbl("id", ["color", cbi]));
{
string s_display = llLinksetDataRead("display");
fixed_warning_offset = (vector)getjs(s_display, ["warning", "offset"]);
fixed_warning_texture = getjs(s_display, ["warning", "texture"]);
fixed_warning_mode = (integer)getjs(s_display, ["warning", "mode"]);
devices_texture = getjs(s_display, ["devices", "texture"]);
devices_offset = (vector)getjs(s_display, ["devices", "offset"]);
device_scale = (float)getjs(s_display, ["devices", "scale"]);
if(device_scale <= 0)
device_scale = 1;
device_mode = (integer)getjs(s_display, ["devices", "mode"]);
device_wires = (integer)getjs(s_display, ["devices", "wires"]);
}
position_all(FALSE);
@end;
}
#include <ARES/program>

View File

@ -0,0 +1,42 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Exec Event Handlers
*
* 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.
*
* =========================================================================
*
*/
// This timer is here to avoid the stack overhead from repeated recursion between process_input() and continue_script(). It is a hack, not a role model.
timer() {
llSetTimerEvent(0);
continue_script();
}

752
ARES/system/exec.lsl Normal file
View File

@ -0,0 +1,752 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Exec System Module
*
* 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 <ARES/a>
#define CLIENT_VERSION ARES_VERSION
#define CLIENT_VERSION_TAGS ARES_VERSION_TAGS
// it's good enough for real computers:
#define NON_VOLATILE
#define DAEMON_LISTEN
#ifdef TRACE
integer trace;
#endif
string file_name;
integer file_offset;
integer file_length;
string file_unit;
key file_pipe;
key file_outs;
key file_user;
string script;
list script_lines;
integer script_li;
key script_handle = NULL_KEY; // don't continue executing a script until we get a suitable DONE back
string expression(list tokens) {
if((string)tokens == "undefined")
return JSON_INVALID;
float value = (float)gets(tokens, 0);
integer ki = 1;
integer kmax = count(tokens);
while(ki < kmax) {
string op = gets(tokens, ki);
float r = (float)gets(tokens, ki + 1);
if(op == "!=") {
value = (float)(llFabs(value - r) >= 0.001);
} else if(op == "==") {
value = (float)(llFabs(value - r) < 0.001);
} else if(op == ">") {
value = (float)((value - r) > 0.001);
} else if(op == "<") {
value = (float)((r - value) > 0.001);
} else if(op == ">=") {
value = (float)((value - r) > -0.001);
} else if(op == "<=") {
value = (float)((r - value) > -0.001);
} else if(op == "+") {
value += r;
} else if(op == "-") {
value -= r;
} else if(op == "*") {
value *= r;
} else if(op == "%") {
value = (float)((integer)value % (integer)r);
} else if(op == "**") {
value = llPow(value, r);
} else if(op == "/" || op == "\\") {
if(r != 0) {
value /= r;
} else {
echo("[_exec] division by 0 in expression: " + concat(tokens, " "));
return "0";
}
if(op == "\\")
value = (integer)value;
} else {
echo("[_exec] unknown operator '" + op + "' in expression: " + concat(tokens, " "));
return "0";
}
ki += 2;
}
// return integer if within a thousandth:
if(llFabs((float)((integer)value) - value) < 0.001)
return (string)((integer)value);
else
return (string)value;
}
string pipe_queue = "{}"; // {handle:[[commands], outs, ins, user, rc, [pipe-keys]]} - will replace script handle
integer process_input(key outs, key handle, key user, string cml, integer in_script) {
@internal_restart;
if(in_script)
script_handle = handle = llGenerateKey();
#ifdef TRACE
if(trace)
echo(" >> " + cml);
#endif
string first = substr(cml, 0, 0);
string second = substr(cml, 0, 1);
if(~strpos(cml, "\\n"))
cml = replace(cml, "\\n", "\n");
// =======================================
// shell control commands
// =======================================
integer varpos = strpos(cml, "$");
if(~varpos) {
integer vi = varpos;
integer vL = strlen(cml);
string varacc;
integer vaL = 0;
integer vaStart;
integer vp_state = 0;
while(vi < vL) {
integer vc = llOrd(cml, vi);
++vi;
if(vc == 0x24 && vp_state == 0) { // '$'
if(llOrd(cml, vi - 2) == 0x5c) { // '\'
cml = delstring(cml, vi - 2, vi - 2);
--vi;
--vL;
} else {
varacc = "";
vaL = 0;
vaStart = vi - 1;
vp_state = 1;
}
} else if(vp_state == 1) {
integer vd = llOrd(cml, vi);
//if(vc == 0x2e) echo((string)vd);
if((vc >= 0x41 && vc <= 0x5a) // uppercase
|| (vc >= 0x61 && vc <= 0x7a) // lowercase
|| vc == 0x5f || vc == 0x2d // _ and -
|| (vaL > 0 && vc == 0x2e && (vd >= 0x61 && vd <= 0x7a)) // . followed by lowercase letter
|| (vaL > 0 && vc == 0x2e && (vd >= 0x30 && vd <= 0x39)) // . followed by digit
|| (vaL > 0 && (vc >= 0x30 && vc <= 0x39))) { // digits if non-first
varacc += llChar(vc);
// echo("added " + (string)vc);
++vaL;
} else {
vp_state = 0;
}
if(vi == vL && vp_state) {
vp_state = 0; // end automatically at string end
++vi;
}
}
if(vaL && vp_state == 0) {
// echo("subbing in " + varacc);
string sub;
if(varacc == "name") {
sub = llGetDisplayName(user);
} else if(varacc == "user") {
sub = (string)user;
} else if(varacc == "self") {
sub = (string)avatar;
} else if(varacc == "me") {
sub = getdbl("id", ["callsign"]);
} else {
sub = getdbl("env", split(varacc, "."));
}
cml = delstring(cml, vaStart, vi - 2);
cml = llInsertString(cml, vaStart, sub);
vi -= vaL;
vi += strlen(sub) - 2;
vL = strlen(cml);
vaStart = 0;
vaL = 0;
varacc = "";
}
}
}
list cargv = splitnulls(cml, " ");
string prog = gets(cargv, 0);
string aliascmd;
if((aliascmd = getdbl("input", ["alias", prog])) != JSON_INVALID) {
cml = aliascmd + " " + concat(delitem(cargv, 0), " ");
cargv = splitnulls(cml, " ");
prog = gets(cargv, 0);
}
// string pipe_queue; // {handle:[[commands], outs, ins, user, rc]} - will replace script handle
cml = replace(cml, "\\|", NAK);
integer pipe_pos = strpos(cml, "|");
if(~pipe_pos) {
list commands = split(replace(cml, "|", "|exec "), "|");
list pipe_keys;
integer cimax = count(commands);
integer ci = cimax;
while(ci--) {
pipe_keys += [llGenerateKey()];
commands = alter(commands, [replace(gets(commands, ci), NAK, "|")], ci, ci);
}
key new_handle = getk(pipe_keys, cimax - 1);
key feed_handle = getk(pipe_keys, 1);
pipe_keys += [outs];
if(in_script)
script_handle = new_handle;
ci = cimax;
while(ci--)
// patch last two pipes so we can resolve them properly:
if(ci > 0)
commands = alter(commands, [
"p:" + gets(pipe_keys, ci) +
" n:" + gets(pipe_keys, ci + 1) +
" " + gets(commands, ci)
], ci, ci);
//if(count(commands) > 2) {
//commands = alter(commands, ["n:" + (string)new_handle + " " + gets(commands, -2)], -2, -2);
// commands = alter(commands, ["p:" + (string)feed_handle + " " + gets(commands, 1)], 1, 1);
if(cimax == 2)
feed_handle = new_handle;
pipe_queue = setjs(pipe_queue, [new_handle], jsarray([
jsarray(commands), outs, handle, user, _resolved, feed_handle, jsarray(pipe_keys), in_script
]));
#ifdef DEBUG
echo("pipes formed to: " + pipe_queue);
#endif
pipe_open(delitem(commands, 0));
return TRUE;
} else if(~strpos(cml, NAK)) {
cml = replace(cml, NAK, "|");
cargv = splitnulls(cml, " ");
prog = gets(cargv, 0);
}
string filetype;
integer invtype;
string error_msg;
if(prog == "runaway") {
if(llGetInventoryType("_security") == INVENTORY_SCRIPT) {
invoke("_security runaway", outs, handle, user);
} else {
error_msg = "Can't run away: _security is not installed (yikes)";
}
} else if(prog == "safeword") {
if(llGetInventoryType("restraint") == INVENTORY_SCRIPT) {
invoke("restraint safeword", outs, handle, user);
} else {
error_msg = "Can't safeword: restraint is not installed";
}
} else if(prog == "alias") {
string word = gets(cargv, 1);
string command = concat(delrange(cargv, 0, 1), " ");
if(word == "delete" && command != "") {
string alias = getdbl("input", ["alias", command]);
if(alias != JSON_INVALID)
setdbl("input", ["alias", command], JSON_DELETE);
error_msg = "OK.";
} else if(command != "") {
setdbl("input", ["alias", word], command);
} else if(word != "") {
error_msg = getdbl("input", ["alias", word]);
} else {
list aliases = js2list(getdbl("input", ["alias"]));
integer ai = count(aliases) >> 1;
if(!ai)
error_msg = "No aliases defined.";
else while(ai--) {
error_msg += "\n - " + gets(aliases, ai << 1) + ": " + gets(aliases, (ai << 1) + 1);
}
}
#ifdef TRACE
} else if(prog == "trace") {
if(count(cargv) > 1)
trace = llListFindList(["off", "on"], [gets(cargv, 1)]);
else
error_msg = "Trace is " + gets(["off.", "on."], trace);
if(!~trace)
trace = 0;
#endif
} else if(prog == "to") {
string statement = concat(delrange(cargv, 0, 1), " ");
outs = gets(cargv, 1);
cml = statement;
jump internal_restart;
// process_input(gets(cargv, 1), user, statement, in_script);
} else if(prog == "from") {
handle = gets(cargv, 1);
if(in_script)
script_handle = handle;
invoke(concat(delrange(cargv, 0, 1), " "), outs, handle, user);
} else if(prog == "do") {
setdbl("env", ["arg"], jsarray(delrange(cargv, 0, 0)));
setdbl("env", ["args"], (string)(count(cargv) - 2));
file_offset = file_length = NOWHERE;
file_outs = outs;
file_user = user;
file_open(file_pipe = llGenerateKey(), file_name = gets(cargv, 1));
// task_begin(file_pipe, file_name);
// not checking in_script; allow manual interruption
if(script_lines != []) {
llSetTimerEvent(0);
script_lines = [];
script_li = 0;
script_handle = NULL_KEY;
set_mode(_mode & ~MODE_ACCEPT_DONE);
// resolve_io(script_trigger_resolve, script_trigger_outs, script_trigger_ins);
resolve_i(script_trigger_resolve, script_trigger_ins);
echo("[_exec] interrupted by new script");
}
script_trigger_ins = handle;
script_trigger_outs = outs;
script_trigger_resolve = _resolved;
return TRUE; // do not continue_script();
} else if(prog == "exit") {
// not checking in_script; allow manual interruption
if(script_lines != []) {
script_lines = [];
script_li = 0;
script_handle = NULL_KEY;
set_mode(_mode & ~MODE_ACCEPT_DONE);
// resolve_io(script_trigger_resolve, script_trigger_outs, script_trigger_ins);
resolve_i(script_trigger_resolve, script_trigger_ins);
llSetTimerEvent(0);
}
setdbl("env", ["arg"], "[]");
setdbl("env", ["args"], "0");
return FALSE; // do not continue_script();
} else if(prog == "say") {
/*cml = concat(delitem(cargv, 0), " ");
say_restart = TRUE;
jump internal_restart;*/
if((user == avatar) && !(integer)getdbl("input", ["mind"]) && !in_script) {
print(outs, user, "You cannot think.");
} else {
invoke("input " + cml, outs, handle, user);
}
return FALSE;
// process_input(outs, user, concat(delitem(cargv, 0), " "), in_script);
} else if(prog == "jump") {
integer new_script_li = index(script_lines, "@" + gets(cargv, 1) + ":");
if(~new_script_li) {
script_li = new_script_li + 1;
} else {
error_msg = "[_exec] script error: no label " + gets(cargv, 1);
}
} else if(prog == "echo") {
string a1 = gets(cargv, 1);
if(a1 == "-c") {
echo(replace(concat(delrange(cargv, 0, 1), " "), "\\n", "\n"));
} else if(a1 == "-d") {
llWhisper(DEBUG_CHANNEL, replace(concat(delrange(cargv, 0, 1), " "), "\\n", "\n"));
} else {
print(outs, user, replace(concat(delitem(cargv, 0), " "), "\\n", "\n"));
}
} else if(prog == "set") {
if(count(cargv) > 2) {
string value;
if(gets(cargv, 2) == "=")
value = expression(delrange(cargv, 0, 2));
else
value = concat(delrange(cargv, 0, 1), " ");
string sv = gets(cargv, 2);
if(value == "%key") {
value = llGenerateKey();
} else if(value == "%undefined") {
value = JSON_DELETE;
} else if(value == "%empty") {
value = "";
} else if(sv == "%keys") {
value = concat(delrange(cargv, 0, 2), " ");
value = concat(jskeys(value), " ");
} else if(sv == "%count") {
string mode = gets(cargv, 3);
value = concat(delrange(cargv, 0, 3), " ");
if(mode == "words") {
value = (string)count(llParseString2List(value, [" ", "\n"], []));
} else if(mode == "lines") {
value = (string)count(splitnulls(value, "\n"));
} else if(mode == "chars") {
value = (string)strlen(value);
} else if(mode == "keys") {
value = (string)count(jskeys(value));
}
} else if((gets(cargv, 4) == "of") && (sv == "%index")) {
// set <var> %index $a of $json
// set <var> %index $a.$b of $json
list indices = split(gets(cargv, 3), ".");
// echo("getting indices " + concat(indices, ", "));
value = concat(delrange(cargv, 0, 4), " ");
value = getjs(value, indices);
} else if((gets(cargv, 4) == "in") && (sv == "%word" || sv == "%line" || sv == "%char")) {
// set <var> %word $i in $text
integer i = (integer)gets(cargv, 3);
value = concat(delrange(cargv, 0, 4), " ");
// echo("command: " + cml);
// echo("extracting " + sv + " #" + (string)i + " from " + value);
if(sv == "%word") {
value = gets(llParseString2List(value, [" ", "\n"], []), i);
} else if(sv == "%line") {
value = gets(splitnulls(value, "\n"), i);
} else if(sv == "%char") {
value = substr(value, i, i);
}
// echo("got " + value);
}
setdb("env", gets(cargv, 1), value);
} else {
error_msg = "[_exec] not enough arguments: set";
}
} else if(prog == "if") {
integer negate = 0;
if(gets(cargv, 1) == "not") {
cargv = delitem(cargv, 1);
negate = 1;
}
integer endpoint = index(cargv, "then");
if(~endpoint) {
string statement = concat(delrange(cargv, 0, endpoint), " ");
integer success = 0;
if(gets(cargv, 1) == "exists") {
string filename = concat(sublist(cargv, 2, endpoint - 1), " ");
success = llGetInventoryType(filename) != INVENTORY_NONE;
} else {
integer mid_pos = index(cargv, "is");
if(mid_pos > 1 && mid_pos < endpoint) {
string LHS = concat(sublist(cargv, 1, mid_pos - 1), " ");
string RHS = concat(sublist(cargv, mid_pos + 1, endpoint - 1), " ");
// echo("cargv: " + concat(cargv, " ") + "\nLHS: " + LHS + "\nRHS: " + RHS);
if(LHS == "%undefined")
LHS = JSON_INVALID;
else if(LHS == "%empty" || LHS == "\"\"")
LHS = "";
if(RHS == "%undefined")
RHS = JSON_INVALID;
else if(RHS == "%empty" || RHS == "\"\"")
RHS = "";
success = (LHS == RHS);
// echo("success? " + (string)success);
} else {
mid_pos = index(cargv, "in");
if(mid_pos > 1 && mid_pos < endpoint) {
string LHS = concat(sublist(cargv, 1, mid_pos - 1), " ");
string RHS = concat(sublist(cargv, mid_pos + 1, endpoint - 1), " ");
success = (getjs(RHS, [LHS]) != JSON_INVALID);
} else {
string value = expression(sublist(cargv, 1, endpoint - 1));
success = (llFabs((float)value) >= 0.001);
}
}
}
if(negate)
success = !success;
if(success) {
cml = statement;
jump internal_restart;
/*process_input(outs, user, statement, in_script);
return; // do not continue_script(); - the true branch will handle that
*/
}
} else {
error_msg = "[_exec] script error: incomplete statement " + cml;
}
} else if(prog == "service") {
system(SIGNAL_CALL, E_UNKNOWN + E_PROGRAM_NUMBER + (string)outs + " " + (string)user + " " + concat(delitem(cargv, 0), " "));
} else if(~index(js2list(getdbl("input", ["device-command"])), prog)) {
system(SIGNAL_CALL, E_HARDWARE + E_PROGRAM_NUMBER + (string)outs + " " + (string)user + " hardware command " + cml);
} else if((invtype = llGetInventoryType("_" + prog)) == INVENTORY_SCRIPT) {
invoke("_" + cml, outs, handle, user);
return FALSE;
} else if((invtype = llGetInventoryType(prog)) == INVENTORY_SCRIPT) {
invoke(cml, outs, handle, user);
return FALSE;
} else {
integer exists = (invtype != INVENTORY_NONE);
if(!exists) {
exists = ((filetype = getdbl("fs:root", [prog])) != JSON_INVALID);
}
if(exists) {
string ext = gets(splitnulls(prog, "."), LAST);
string assoc = getdbl("fs", ["open", ext]);
if(~strpos(prog, ".")) {
if(assoc != JSON_INVALID) {
invoke(assoc + " " + cml, outs, handle, user);
return FALSE;
} else {
error_msg = "No association for file type: ." + ext;
}
}
} else {
error_msg = prog + ": not found";
}
}
if(error_msg)
print(outs, user, error_msg);
// reduces interference from user acting during script execution
if(in_script) {
llSetTimerEvent(0.1); // continue_script()
return TRUE;
} else
return FALSE;
}
integer script_trigger_resolve;
key script_trigger_ins = NULL_KEY;
key script_trigger_outs = NULL_KEY;
continue_script() {
integer script_length = count(script_lines);
if(script_li < script_length) {
string line;
integer fc;
while(script_li < script_length && (line == "" || fc == 0x40 || fc == 0x23)) {
// skip blank lines and lines starting with '@' or '#'
// echo((string)script_li + "/" + (string)script_length + " skipped");
line = llStringTrim(gets(script_lines, script_li++), STRING_TRIM);
fc = llOrd(line, 0);
}
// echo((string)script_li + "/" + (string)script_length + ": " + line);
if(script_li <= script_length) {
process_input(file_outs, NULL_KEY, file_user, line, TRUE);
return;
}
}
#ifdef DEBUG
echo("[_exec] script done; resolving " + (string)script_trigger_ins);
#endif
setdbl("env", ["arg"], "[]");
setdbl("env", ["args"], "0");
script_lines = [];
script_li = 0;
script_handle = NULL_KEY;
llSetTimerEvent(0);
set_mode(_mode & ~MODE_ACCEPT_DONE);
// resolve_io(script_trigger_resolve, script_trigger_outs, script_trigger_ins);
resolve_i(script_trigger_resolve, script_trigger_ins);
script_trigger_resolve = 0;
}
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_DONE) {
// echo("[_exec] receipt: " + m);
ins = substr(m, 0, 35);
string pipestruct = getjs(pipe_queue, [ins]);
if(pipestruct != JSON_INVALID) {
echo("[_exec] finished pipe procedure " + (string)ins);
pipe_queue = setjs(pipe_queue, [ins], JSON_DELETE);
}
if(_mode & MODE_ACCEPT_DONE && ins == script_handle) {
continue_script();
} else {
echo("[_exec] script '" + file_name + "' still running; use 'exit' to abort");
// echo("(got " + (string)ins + " instead of " + (string)script_handle);
}
} else if(n == SIGNAL_INVOKE) {
list argv = splitnulls(llStringTrim(m, STRING_TRIM), " ");
integer argc = count(argv);
integer still_awake; // started a script
if(argc > 1) {
key handle = ins;
if(handle == NULL_KEY)
handle = llGenerateKey();
still_awake = process_input(outs, handle, user, llStringTrim(concat(delitem(argv, 0), " "), STRING_TRIM), FALSE);
/*
string action = gets(argv, 1);
if(action == "reset") {
main(0, SIGNAL_INIT, "", "", "", "");
resolve_io(src, outs, ins);
} else if(action == "do") {
process_input(outs, NULL_KEY, user, "@do " + concat(delrange(argv, 0, 1), " "), FALSE, FALSE);
script_trigger_resolve = src;
script_trigger_outs = ins;
script_trigger_ins = ins;
} else if(action == "say") {
process_input(outs, ins, user, concat(delrange(argv, 0, 1), " "), FALSE, FALSE);
} else {
print(outs, user, "usage: " + gets(argv, 0) + " reset|do <filename>|say <message>");
resolve_io(src, outs, ins);
}*/
}
// must include this here since exec never sleeps:
if(!still_awake)
resolve_i(src, ins);
} else if(n == SIGNAL_NOTIFY) {
// echo("EXEC notify: " + m);
list argv = splitnulls(m, " ");
string action = gets(argv, 1);
if(action == "pipe") {
#ifdef DEBUG
echo("exec: pipe created (" + (string)ins + "): " + m);
#endif
string pipestruct = getjs(pipe_queue, [ins]);
if(pipestruct != JSON_INVALID) {
#ifdef DEBUG
echo("exec: pipe struct " + pipestruct);
#endif
key feed_handle = getjs(pipestruct, [5]);
string first_command = getjs(pipestruct, [0, 0]);
key user = getjs(pipestruct, [3]);
// invoke(first_command, feed_handle, NULL_KEY, user);
// process_input(key outs, key handle, key user, string cml, integer in_script) {
process_input(feed_handle, llGenerateKey(), user, first_command, (integer)getjs(pipestruct, [7]));
} else {
echo("exec: unknown pipe (struct not found): " + m);
}
} else if(action == "file") { // file data available
if(ins == file_pipe) {
string file_buffer;
pipe_read(file_pipe, file_buffer);
integer read_length = 10;
if(file_length != NOWHERE) {
if(file_unit == "b")
read_length *= FILE_PAGE_LENGTH;
file_offset += read_length;
if(file_offset + read_length > file_length)
read_length = file_length - file_offset;
// print(file_outs, file_user, file_buffer);
script += file_buffer;
if(file_offset < file_length)
file_read(file_pipe, file_name, (string)file_offset + " " + (string)read_length);
else {
file_close(file_pipe);
// print(avatar, avatar, script);
set_mode(MODE_MASK_BATCH);
// start run script:
script_lines = split(script, "\n");
script = "";
script_li = 0;
continue_script();
// task_end(file_pipe);
}
} else {
list bfs = splitnulls(file_buffer, " ");
file_length = (integer)gets(bfs, 0);
file_unit = gets(bfs, 1);
if(file_unit == "b")
read_length *= FILE_PAGE_LENGTH;
// echo("File " + file_name + " stat: " + (string)file_buffer);
if(file_length > 0) {
file_offset = 0;
if(file_offset + read_length > file_length)
read_length = file_length - file_offset;
file_read(file_pipe, file_name, (string)file_offset + " " + (string)read_length);
} else { // file not found or file empty
print(file_user, file_user, "No file: " + file_name);
file_close(file_pipe);
// task_end(file_pipe);
}
}
} /*else {
echo("File data offered via unexpected pipe: " + (string)ins);
}*/
} else {
echo("Unknown notify: " + m);
}
resolve_i(src, ins);
} else if(n == SIGNAL_INIT) {
// NOP
} 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);
}
}
#define EXT_EVENT_HANDLER "ARES/system/exec.event.lsl"
#include <ARES/program>

317
ARES/system/libmenu.lsl Normal file
View File

@ -0,0 +1,317 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Menu Library
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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 <ARES/a>
#include <ARES/api/auth.h.lsl>
#define CLIENT_VERSION ARES_VERSION
#define CLIENT_VERSION_TAGS ARES_VERSION_TAGS
string software_server_topic;
main(integer src, integer n, string m, key outs, key ins, key user) {
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
string action = gets(argv, 1);
if(argc == 1 || action == "help") {
msg = "Syntax: \n"
+ PROGRAM_NAME + " cf <database entry>: step database entry by +0.1, wrapping around in the range [0.0, 1.0]\n"
+ PROGRAM_NAME + " build <menu> [<topic>]: repopulate dynamic menu\n"
+ PROGRAM_NAME + " field <menu> <value>: update the 'last used' legend on the specified menu\n"
+ PROGRAM_NAME + " prompt <command>: display a generic text input prompt to provide parameters for a system command"
;
} else if(action == "cf") {
string item = gets(argv, 2);
integer authorization = sec_check(user, "database", outs, "", m);
if(authorization == ALLOWED) {
list parts = split(item, ".");
string section = gets(parts, 0);
parts = delitem(parts, 0);
float f = (float)getdbl(section, parts);
f += 0.1;
if(f > 1.05)
f = 0;
else if(f > 0.95)
f = 1;
setdbl(section, parts, (string)f);
} else if(authorization == DENIED) {
msg = "You do not have permission to adjust this setting.";
}
} else if(action == "field") {
string menu = gets(argv, 2);
string value = concat(delrange(argv, 0, 2), " ");
string current = getdbl("m:" + menu, ["f"]);
if(current != JSON_INVALID) {
setdbl("m:" + menu, ["f"], value);
} else {
msg = "Menu m:" + menu + " does not exist or has no appropriate field.";
}
} else if(action == "build") {
// old: menu build <category>; topic inferred from context of session
// new: libmenu build <category> $topic
// string s_fs = llLinksetDataRead("fs");
string category = gets(argv, 2);
string topic = gets(argv, 3); // getjs(sessions, [(string)outs, SESSION_TOPIC]);
string menu_name;
string buttons;
if(category == "chime") {
string raw = getdbl("chime", []);
list items;
if(raw != JSON_INVALID) {
items = jskeys(raw);
integer i = count(items);
while(i--) {
string name = getk(items, i);
// string sinfo = getjs(raw, [server]);
items = alter(items, [jsarray([
name, 0, "id chime load " + name
])], i, i);
}
}
setdbl("m:chime", ["d"], jsarray(items));
} else if(category == "security") {
integer add = 0;
string otopic = topic;
if(substr(topic, 0, 3) == "add-") {
topic = delstring(topic, 0, 3);
add = 1;
}
if(topic == "user" || topic == "ban" || topic == "guest") {
list entries;
if(add)
entries = llGetAgentList(AGENT_LIST_PARCEL_OWNER, []);
else
entries = jskeys(getdbl("security", [topic]));
list buttons = [];
integer ei = count(entries);
while(ei--) {
string entry = gets(entries, ei);
string name;
string b;
if(add) {
name = llGetUsername(entry);
b = jsarray([
name, 0, "security " + topic + " " + entry
]);
} else {
name = getdbl("security", ["name", entry]);
b = jsarray([
name, 1, "m:" + topic + "-info", entry
]);
}
buttons = b + buttons;
}
setdbl("m:" + otopic, ["d"], jsarray(buttons));
}
} else if(category == "ax-source") {
string raw = getdbl("ax", ["source"]);
list items;
if(raw != JSON_INVALID) {
items = jskeys(raw);
integer i = count(items);
while(i--) {
key server = getk(items, i);
// string sinfo = getjs(raw, [server]);
if(object_pos(server) == ZV) {
items = delitem(items, i);
} else {
items = alter(items, [jsarray([
llKey2Name(server), 1, "m:ax-server", server
])], i, i);
}
}
}
if(!count(items))
msg = "No trusted sources in region. See http://support.nanite-systems.com/ax_servers for a list of package servers and where to find them.";
setdbl("m:ax-source", ["d"], jsarray(items));
} else if(category == "ax-server") {
if(topic != "" && topic != JSON_INVALID)
software_server_topic = topic;
string raw = llLinksetDataRead("pkg:" + (string)software_server_topic);
list items;
if(raw != JSON_INVALID) {
items = jskeys(raw);
integer i = count(items);
while(i--) {
string name = gets(items, i);
string data = getjs(raw, [name]);
items = alter(items, [jsarray([
name + " " + data, 1, "m:ax-sd", name
])], i, i);
}
}
if(!count(items))
msg = "Catalog empty for this server. Try 'update regional catalog' (@ax update) first.";
setdbl("m:ax-server", ["d"], jsarray(items));
} else if(category == "ax-local") {
list pkgs = js2list(getdbl("pkg", ["version"])); // object
integer pi = count(pkgs);
while(pi > 0) {
pi -= 2;
string name = gets(pkgs, pi);
string version = gets(pkgs, pi + 1);
pkgs = alter(pkgs, [
jsarray([name + " " + version, 1, "m:ax-ld", name])
], pi, pi + 1);
}
setdbl("m:ax-local", ["d"], jsarray(pkgs));
} else if(category == "ax-cache") {
list pkgs = js2list(llLinksetDataRead("fs:package")); // array
integer pi = count(pkgs);
while(pi--) {
string fn = gets(pkgs, pi);
string nameversion = concat(delitem(splitnulls(fn, "."), LAST), ".");
pkgs = alter(pkgs, [jsarray([nameversion, 1, "m:ax-cd", nameversion])], pi, pi);
}
setdbl("m:ax-cache", ["d"], jsarray(pkgs));
} else if(category == "fs") {
list sources = jskeys(getdbl("fs", ["source"]));
list views = jskeys(getdbl("fs", ["view"]));
integer pi;
if(~(pi = index(views, "parc")))
views = delitem(views, pi);
integer vmax = count(views);
integer vi;
while(vi < vmax) {
string view = gets(views, vi);
buttons = setjs(buttons, [JSON_APPEND], jsarray([
view + " files...",
1,
"m:fs-view",
view
]));
++vi;
}
vmax = count(sources);
vi = 0;
while(vi < vmax) {
string source = gets(sources, vi);
buttons = setjs(buttons, [JSON_APPEND], jsarray([
source + ":>",
1,
"m:fs-source",
source
]));
++vi;
}
menu_name = "m:fs";
} else { // including 'source' and 'view'
integer is_source = (category == "source");
if(category == "view" || category == "source") {
menu_name = "m:fs-" + category;
category = topic;
} else {
menu_name = getdbl("fs", ["view", category, "menu"]);
}
if(menu_name != JSON_INVALID) {
setdbl(menu_name, ["d"], "[]");
list files;
if(is_source)
files = split(llLinksetDataRead("fs:" + category), "\n");
else
files = js2list(llLinksetDataRead("fs:" + category));
files = llListSort(files, 1, TRUE);
integer fmax = count(files);
integer fi = 0;
while(fi < fmax) {
string fn = gets(files, fi);
if(is_source)
fn = gets(split(fn, " "), 0);
buttons = setjs(buttons, [JSON_APPEND], jsarray([
fn,
4
]));
++fi;
}
} else {
echo("[_menu] "+"no .menu definition for filesystem view '" + category + "'");
return;
}
}
// s_fs = "";
setdbl(menu_name, ["d"], buttons);
} else if(action == "prompt") {
string cmd = concat(delrange(argv, 0, 1), " ");
e_call(C_BASEBAND, E_SIGNAL_CALL,
(string)outs + " " + (string)user + " baseband prompt " + jsobject([
"message", "Specify parameters for the following command:\n\n\t" + cmd
+ "\n\nhelp: http://support.nanite-systems.com/?id=2832#" + gets(argv, 2),
"ins", ins,
"command", cmd
])
);
} else {
msg = "Unknown action: " + action + ". See '" + gets(argv, 0) + " help' for more information.";
}
if(msg != "")
print(outs, user, msg);
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
} 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 <ARES/program>

357
ARES/system/policy.lsl Normal file
View File

@ -0,0 +1,357 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Policy System Module
*
* 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 <ARES/a>
#include <ARES/api/auth.h.lsl>
#define CLIENT_VERSION ARES_VERSION
#define CLIENT_VERSION_TAGS ARES_VERSION_TAGS
list opt_de = ["disabled", "enabled"];
list opt_af = ["allowed", "forbidden"];
list opt_standard = ["off", "on", "toggle"];
integer C_LIGHT_BUS = NOWHERE;
main(integer src, integer n, string m, key outs, key ins, key user) {
@restart_main;
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
string action = gets(argv, 1);
string target = gets(argv, 2);
integer result;
if(argc == 1) {
string policies = llLinksetDataRead("policy");
msg = "Current policy status\n"
+ "\nChat release: "
+ gets(opt_af, (integer)getjs(policies, ["release"]))
+ "\nOutfit (apparel) restriction: "
+ gets(opt_de, (integer)getjs(policies, ["outfit"]))
+ "\nCurfew enforcement: "
+ gets(opt_de, (integer)getjs(policies, ["curfew", "enabled"]))
+ "\nPrivate radio communications: "
+ getjs(policies, ["radio"])
/*+ "\nDistress beacon: "
+ gets(opt_de, (integer)getjs(policies, ["beacon", "enabled"]))*/
+ "\nAuto-Lock: "
+ gets(opt_de, (integer)getjs(policies, ["autolock", "enabled"]))
+ "\nSafety bolts: "
+ gets(opt_de, (integer)getdbl("status", ["bolts"]));
} else if(action == "help") {
string selfname = gets(argv, 0);
msg = "Syntax:\n";
selfname + " help: This message\n" +
selfname + " bolts [on|off|auto|cycle]: control anti-theft safety bolts\n" +
selfname + " outfit|apparel [on|off|toggle]: control apparel (outfit) enforcement\n" +
selfname + " curfew [on|off|toggle]: control curfew enforcement\n" +
selfname + " curfew time <hhnn>: set curfew time to hh:nn SLT (24-hour clock); 0 = midnight\n" +
selfname + " curfew home <SLURL|here>: set curfew home\n";
print(outs, user, msg);
llSleep(0.5);
msg =
selfname + " radio [open|closed|receive|transmit|cycle]: control radio policy\n" +
selfname + " release [on|off|toggle]: block chat release\n" +
selfname + " autolock [on|off|toggle]: control Auto-Lock\n" +
selfname + " autolock time <secs>: automatically lock after <secs> time\n" +
selfname + " lock: lock local commands and menu, preventing access\n" +
selfname + " unlock <password>: unlock\n" +
selfname + " password <password>: change lock password\n";
} else if(action == "beacon") {
msg = "The distress beacon is not yet implemented.";
} else if(action == "lock") {
string password = getdbl("policy", ["password"]);
if(password == "" || password == JSON_INVALID) {
m = "* unlock";
jump restart_main;
} else {
setdbl("policy", ["lock"], "1");
msg = "Unit locked.";
announce("lock-1");
io_tell(NULL_KEY, C_LIGHT_BUS, "locked");
notify_program("security power", outs, NULL_KEY, user);
}
} else if(action == "unlock") {
string password = getdbl("policy", ["password"]);
string attempt = concat(delrange(argv, 0, 1), " ");
if(password == "" || password == JSON_INVALID) {
msg = "Cannot lock console: no password set.";
setdbl("policy", ["lock"], "0");
announce("lock-0");
io_tell(NULL_KEY, C_LIGHT_BUS, "unlocked");
notify_program("security power", outs, NULL_KEY, user);
} else if(attempt != password) {
msg = "Incorrect password.";
announce("denied");
io_tell(NULL_KEY, C_LIGHT_BUS, "locked");
} else {
msg = "Unlocked.";
setdbl("policy", ["lock"], "0");
announce("lock-0");
io_tell(NULL_KEY, C_LIGHT_BUS, "unlocked");
notify_program("security power", outs, NULL_KEY, user);
}
} else {
integer check = sec_check(user, "manage", outs, m, m);
if(check == DENIED) {
msg = "Authorization denied.";
} else if(check == ALLOWED) {
if(action == "bolts") {
integer bolts = (integer)getdbl("status", ["bolts"]);
if(target == "on") {
bolts = 1;
} else if(target == "off") {
bolts = 0;
} else if(target == "auto") {
bolts = 2;
} else if(target == "cycle") {
bolts = (bolts + 1) % 3;
}
setdbl("status", ["bolts"], (string)bolts);
msg = "Safety bolts are now " + gets(["disengaged", "always engaged", "engaged only while the unit is powered on"], bolts) + ".";
notify_program("security power", outs, NULL_KEY, user);
} else if(action == "outfit" || action == "apparel") {
result = (integer)getdbl("policy", ["outfit"]);
if(contains(opt_standard, target)) {
setdbl("policy", ["outfit"],
(string)(result = geti([0, 1, !result],
index(opt_standard, target)))
);
msg = "Changing outfits is now " + gets(opt_af, result) + ".";
if(result)
effector_restrict("policy_apparel", "unsharedwear=?,unsharedunwear=?");
else
effector_release("policy_apparel");
} else {
msg = "Changing outfits is " + gets(opt_af, result) + ".";
}
} else if(action == "release") {
result = (integer)getdbl("policy", ["release"]);
if(contains(opt_standard, target)) {
setdbl("policy", ["release"],
(string)(result = geti([0, 1, !result],
index(opt_standard, target)))
);
msg = "Releasing chat is now " + gets(opt_af, result) + ".";
notify_program("input release " + gets(["y", "n"], result), outs, NULL_KEY, user);
} else {
msg = "Releasing chat is " + gets(opt_af, result) + ".";
}
} else if(action == "curfew") {
result = (integer)getdbl("policy", ["curfew", "enabled"]);
if(contains(opt_standard, target)) {
setdbl("policy", ["curfew", "enabled"],
(string)(result = geti([0, 1, !result],
index(opt_standard, target)))
);
msg = "Curfew enforcement is now " + gets(opt_de, result) + ".";
} else {
msg = "Curfew enforcement is " + gets(opt_de, result) + ".";
if(result)
msg += " The unit must be near " + getdbl("policy", ["curfew", "home"])
+ " at " + getdbl("policy", ["curfew", "time"]) + " SLT, or it will be teleported there.";
}
// TODO: actual effects
} else if(action == "radio") {
list opt_radio = ["open", "closed", "receive", "transmit"];
/* list radio_explanation = [
"The unit may IM anyone, provided it has power and is not otherwise restricted.",
"The unit may not IM anyone except registered users.",
"The unit may only send IMs to registered users. Anyone may IM it.",
"The unit may only receive IMs from registered users. It may IM anyone."
]; */
string current_radio = getdbl("policy", ["radio"]);
integer ri = index(opt_radio, current_radio);
if(target == "cycle" || contains(opt_radio, target)) {
if(target == "cycle") {
current_radio = gets(opt_radio, ri = ((ri + 1) % 4));
} else {
current_radio = target;
}
setdbl("policy", ["radio"], current_radio);
msg = "Private radio communications are now in '" + current_radio + "' mode."; // + gets(radio_explanation, ri);
// TODO: actual effects
} else {
msg = "Private radio communications are in '" + current_radio + "' mode."; // + gets(radio_explanation, ri);
}
} else if(action == "password") {
string password = concat(delrange(argv, 0, 1), " ");
if(password == "NONE" || password == "none")
password = "";
setdbl("policy", ["password"], password);
if(password == "") {
msg = "Lock password removed.";
setdbl("policy", ["autolock", "enabled"], "0");
set_timer("autolock", 0); // shouldn't be possible; disable
} else {
msg = "Lock password is now: " + password;
}
} else if(action == "autolock") {
string password = getdbl("policy", ["password"]);
result = (integer)getdbl("policy", ["autolock", "enabled"]);
if(contains(opt_standard, target)) {
if(password == "" || password == JSON_INVALID) {
msg = "You must set a password to enable Auto-Lock.";
setdbl("policy", ["autolock", "enabled"], "0");
set_timer("autolock", 0);
} else {
setdbl("policy", ["autolock", "enabled"],
(string)(result = geti([0, 1, !result],
index(opt_standard, target)))
);
msg = "Auto-Lock is now " + gets(opt_de, result) + ".";
if(result) {
integer time = (integer)getdbl("policy", ["autolock", "time"]);
set_timer("autolock", time);
} else {
set_timer("autolock", 0);
}
}
} else if(target == "time") {
integer time = (integer)getdbl("policy", ["autolock", "time"]);
target = gets(argv, 3);
if(target != "") {
if((integer)target < 15)
target = "15";
setdbl("policy", ["autolock", "time"],
(string)(time = (integer)target));
integer result = (integer)getdbl("policy", ["autolock", "enabled"]);
if(result) {
set_timer("autolock", time);
}
}
msg = "Auto-Lock will now activate after " + (string)time + " seconds of inactivity.";
} else {
msg = "Auto-Lock is " + gets(opt_de, result) + ".";
}
} else {
msg = "Unknown action: " + action;
}
} // check allowed
} // argc > 1
if(msg != "") {
print(outs, user, msg);
}
} else if(n == SIGNAL_NOTIFY) {
if(gets(split(m, " "), 1) == "delay") {
integer time = (integer)getdbl("policy", ["autolock", "time"]);
integer result = (integer)getdbl("policy", ["autolock", "enabled"]);
if(result) {
set_timer("autolock", time); // push back time
// echo("(delaying autolock)");
} else {
set_timer("autolock", 0); // clear any errant messages
}
} else {
echo("[" + PROGRAM_NAME + "] " + m + "?");
}
} else if(n == SIGNAL_TIMER) {
if(m == "autolock") {
integer time = (integer)getdbl("policy", ["autolock", "time"]);
integer result = (integer)getdbl("policy", ["autolock", "enabled"]);
string password = getdbl("policy", ["password"]);
if(result) {
if(password == "" || password == JSON_INVALID) {
setdbl("policy", ["autolock", "enabled"], "0");
set_timer("autolock", 0); // shouldn't be possible; disable
} else {
integer locked = (integer)getdbl("policy", ["lock"]);
if(!locked) {
setdbl("policy", ["lock"], "1");
announce("lock-1");
io_tell(NULL_KEY, C_LIGHT_BUS, "locked");
notify_program("security power", outs, NULL_KEY, user);
}
set_timer("autolock", time); // set next time
}
} else {
set_timer("autolock", 0); // clear any errant messages
}
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
if(llGetAttached())
C_LIGHT_BUS = 105 - (integer)("0x" + substr(avatar, 29, 35));
else
C_LIGHT_BUS = 105 - (integer)("0x" + substr(KERNEL, 29, 35));
if((integer)getdbl("policy", ["outfit"]))
effector_restrict("policy_apparel", "unsharedwear=?,unsharedunwear=?");
else
effector_release("policy_apparel");
integer time = (integer)getdbl("policy", ["autolock", "time"]);
if((integer)getdbl("policy", ["autolock", "enabled"]))
set_timer("autolock", time); // set next time
} 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 <ARES/program>

783
ARES/system/power.lsl Normal file
View File

@ -0,0 +1,783 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Power Management 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 <ARES/a>
#define CLIENT_VERSION ARES_VERSION
#define CLIENT_VERSION_TAGS ARES_VERSION_TAGS
#define C_PUBLIC 0xff676981
integer EPS_active = 0;
integer power_on = 1;
integer max_state;
integer power_state;
string power_systems;
string power_system_names = "{}";
float power_draw = 0;
string notify_cmds = "{}";
key cmd_outs;
key cmd_user;
apply_state(integer update, integer report_state) {
// key ek = llGenerateKey();
// task_begin(ek, "apply");
string s_status = llLinksetDataRead("status");
power_draw = 0;
integer psi = 0;
string ps;
list enabled_systems;
list masked_systems;
list disabled_systems;
notify_cmds = "{}";
while((ps = getjs(power_systems, [(string)psi])) != JSON_INVALID) {
integer system_enabled = ((power_state & (1 << psi)) != FALSE) && power_on;
integer raw_se = system_enabled;
string psname = getjs(ps, ["name"]);
if(system_enabled) {
list psreqs = js2list(getjs(ps, ["req"]));
integer psri = count(psreqs);
while(psri--) {
integer psreq = geti(psreqs, psri);
system_enabled = (system_enabled && (power_state & (1 << psreq)));
}
}
if(report_state) {
if(system_enabled)
enabled_systems += psname;
else if(raw_se)
masked_systems += psname;
else
disabled_systems += psname;
}
string sys_enabled_char = gets(["n", "y"], system_enabled);
if(update) {
string sys_notify = getjs(ps, ["notify"]);
if(sys_notify != JSON_INVALID) {
sys_notify = replace(sys_notify, "?", sys_enabled_char);
list argv = splitnulls(sys_notify, " ");
string cmd = gets(argv, 0);
string cmdl = getjs(notify_cmds, [cmd]);
if(cmdl == JSON_INVALID)
notify_cmds = setjs(notify_cmds, [cmd], "[]");
notify_cmds = setjs(notify_cmds, [cmd, JSON_APPEND], concat(delitem(argv, 0), " "));
}
string sys_rlv = getjs(ps, ["rlv"]);
if(sys_rlv != JSON_INVALID) {
// sys_rlv = replace(sys_rlv, "?", sys_enabled_char);
if(sys_enabled_char == "y")
effector_release("power_" + psname);
else
effector_restrict("power_" + psname, sys_rlv);
//echo("@" + sys_rlv);
}
}
float sys_draw = (float)getjs(ps, ["draw"]);
if(sys_draw != 0 && system_enabled) {
power_draw += sys_draw;
}
++psi;
}
if(update) {
list notify_acts = js2list(notify_cmds);
integer ni = count(notify_acts);
while(ni) {
ni -= 2;
string nc = gets(notify_acts, ni);
list np = js2list(gets(notify_acts, ni + 1));
string cmdline = concat([nc] + np, " ");
if(nc == PROGRAM_NAME)
main(0, SIGNAL_NOTIFY, cmdline, avatar, NULL_KEY, avatar);
else
notify_program(cmdline, avatar, NULL_KEY, avatar);
}
}
if(report_state) {
string s = concat(jskeys(power_system_names), ", ");
string e = concat(enabled_systems, ", ");
string m = concat(masked_systems, ", ");
string d = concat(disabled_systems, ", ");
integer sl = strlen(s);
if(s == "") {
s = "None detected. Database maintenance is required.";
} else {
if(e == "") e = "(none)"; else if(sl == strlen(e)) e = "(all)";
if(m == "") m = "(none)"; else if(sl == strlen(m)) m = "(all)";
if(d == "") d = "(none)"; else if(sl == strlen(d)) d = "(all)";
}
print(cmd_outs, cmd_user, "[" + PROGRAM_NAME + "] system status\n"
+ "\nsupported subsystems: " + s
+ "\nfunctioning subsystems: " + e
+ "\nblocked subsystems: " + m
+ "\ndisabled subsystems: " + d
+ "\n\nsubsystem draw: " + (string)((integer)power_draw) + " W");
}
if(update) {
s_status = setjs(setjs(setjs(s_status,
["state"], (string)power_state),
["draw"], (string)power_draw),
["on"], (string)power_on);
llLinksetDataWrite("status", s_status);
e_call(C_STATUS, E_SIGNAL_CALL, (string)avatar + " " + (string)avatar + " status update");
}
// task_end(ek);
}
apply_EPS(integer EPS_on) {
string change_char = gets(["n", "y"], EPS_on);
if(!power_on) {
effector_rlv(replace(getdbl("power", ["EPS", "rlv"]), "?", change_char));
list EPS_notify = js2list(getdbl("power", ["EPS", "notify"]));
integer En = count(EPS_notify);
while(En--) {
notify_program(replace(gets(EPS_notify, En), "?", change_char), avatar, NULL_KEY, avatar);
}
}
llSleep(0.5);
}
integer wifi_state;
integer locomotion_state;
integer lidar_state;
integer base_state;
integer hud_state;
integer video_state;
integer optics_state;
integer motors_state;
integer has_motors = TRUE;
integer can_move = TRUE;
integer can_unsit = TRUE;
integer video_in_lidar_mode;
integer no_signal_mode;
integer no_video_mode;
integer EPS_mode;
integer overlay_active;
#define ZAP_CHANNEL 0x5a415021
key zap_user;
key zap_outs;
integer zap_spend_total;
key zap_pipe = "00005a41-5020-4520-414c-4c2031393839";
integer zap_amount;
integer rebooting;
main(integer src, integer n, string m, key outs, key ins, key user) {
@restart_main;
key instance = llGenerateKey();
// task_begin(instance, (string)n);
if(n == SIGNAL_TIMER) {
if(m == "zap-finish") {
pipe_close(zap_pipe);
set_timer("zap-finish", 0);
string msg;
if(zap_spend_total == 0)
msg = PROGRAM_NAME + " zap: No targets in range.";
else if(zap_spend_total != zap_amount)
msg = PROGRAM_NAME + " zap: Disbursed " + (string)zap_spend_total + " kJ total.";
if(msg != "")
print(zap_outs, zap_user, msg);
task_end("zap");
} else {
echo("[_power] Performing scheduled power " + m + " task...");
set_timer(m, 0);
n = SIGNAL_INVOKE;
m = PROGRAM_NAME + " " + m;
user = outs = avatar;
ins = NULL_KEY;
jump restart_main;
}
} else if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
string msg;
integer argc = count(argv);
// echo("[_power] executing: " + m);
cmd_outs = outs;
cmd_user = user;
// echo((string)argc);
if(argc == 1) {
apply_state(0, 1);
e_call(C_STATUS, E_SIGNAL_CALL, (string)outs + " " + (string)user + " status power");
} else {
string sys = gets(argv, 1);
string act = gets(argv, 2);
string pss;
string ann;
if(sys == "drainprotect") {
list opt_de = ["disabled", "enabled"];
list opt_standard = ["off", "on", "toggle"];
integer result = (integer)getdbl("status", ["drainprotect"]);
if(contains(opt_standard, act)) {
setdbl("status", ["drainprotect"],
(string)(result = geti([0, 1, !result],
index(opt_standard, act)))
);
msg = "DrainProtect™ is now " + gets(opt_de, result) + ".";
e_call(C_STATUS, E_SIGNAL_CALL, NULL_KEY + " " + NULL_KEY + " status update");
} else {
msg = "DrainProtect™ is " + gets(opt_de, result) + ".";
}
if(result)
msg += " The unit will be protected from unauthorized attempts to extract power.";
} else if(sys == "menu") {
list schemes = jskeys(getdbl("power", ["profile"]));
list scheme_buttons = [];
integer sci = count(schemes);
while(sci--) {
string scheme = gets(schemes, sci);
string sb = jsarray([
scheme,
0,
"power load " + scheme
]);
scheme_buttons = sb + scheme_buttons;
}
setdbl("m:profile", ["d"], jsarray(scheme_buttons));
} else if(sys == "zap") {
task_begin("zap", "");
if(act == "reply") {
string buffer;
pipe_read(ins, buffer);
if(llVecDist(llGetPos(), object_pos(user)) < 20) {
zap_spend_total += zap_amount;
io_tell(user, C_PUBLIC, "charge " + (string)zap_amount);
e_call(C_STATUS, E_SIGNAL_CALL, NULL_KEY + " " + NULL_KEY + " charge " + (string)(1000 * -zap_amount));
print(zap_outs, zap_user, PROGRAM_NAME + " zap: Sent " + (string)zap_amount + " kJ to " + llKey2Name(user));
}
set_timer("zap-finish", 2);
} else {
zap_user = user;
zap_outs = outs;
zap_amount = llAbs((integer)act);
if(zap_amount == 0)
zap_amount = 240;
else if(zap_amount > 720) {
zap_amount = 720;
print(outs, user, "Notice: zapping is capped at 720 kJ");
}
zap_spend_total = 0;
pipe_open("p:" + (string)zap_pipe + " from " + (string)ZAP_CHANNEL + " " + NULL_KEY + " power zap reply");
set_timer("zap-finish", 2);
}
} else if(sys == "cancel") {
set_timer("on", 0);
set_timer("off", 0);
set_timer("cycle", 0);
set_timer("reboot", 0);
msg = "Scheduled tasks cleared.";
} else if(sys == "on" || sys == "off" || sys == "cycle" || sys == "reboot") {
integer delay = (integer)act;
if(delay > 0) {
if(delay < 10)
delay = 10;
msg = "[_power] Scheduled power " + llToUpper(sys) + " to occur in " + format_time(delay) + "; abort with 'power cancel'";
if(user != avatar) {
echo(msg);
}
print(outs, user, msg);
set_timer(sys, delay);
return;
}
integer viable = FALSE;
integer new_state = (sys == "on");
if(!new_state && sys != "off") {
if(!power_on) {
new_state = 1;
sys = "on";
} else {
rebooting = 1;
sys = "off";
}
}
float integrity = (float)getdbl("repair", ["integrity"]);
if(integrity == 0) {
viable = 0;
msg = "Repairs are required to restore power control.";
rebooting = 0;
} else if(new_state == power_on) {
viable = 0;
msg = "Cannot power " + sys + ": already " + sys + ".";
} else if(new_state == 1) {
float capacity = (float)getdbl("status", ["capacity"]);
float charge = (float)getdbl("status", ["charge"]);
if(capacity == 0) {
msg = "Cannot power on: no battery is installed.";
viable = 0;
} else if(charge == 0) {
msg = "Cannot power on: battery is empty.";
viable = 0;
} else {
viable = 1;
}
} else {
viable = 1;
}
if(viable) {
setdbl("status", ["on"], (string)new_state);
// power_on = new_state;
n = SIGNAL_NOTIFY;
m = "power system " + sys;
jump restart_main;
// main(src, SIGNAL_NOTIFY, "power system " + sys, outs, ins, user);
// apply_state(1, 0);
// the SIGNAL_NOTIFY should take care of applying state and status update
}
/*power_on = (sys == "on");
apply_state(TRUE, FALSE);*/
} else if(sys == "load") {
if(act == "") {
string pplist = concat(jskeys(getdbl("power", ["profile"])), ", ");
if(pplist == "")
pplist = "(none)";
msg = "Available power profiles: " + pplist;
} else {
string power_profile = getdbl("power", ["profile", act]);
if(power_profile == JSON_INVALID) {
msg = "No power profile: " + act;
} else {
setdbl("m:profile", ["f"], act);
power_state = (integer)power_profile;
ann = "subsystem-profile";
msg = "Power profile '" + act + "' loaded.";
apply_state(1, 0);
}
}
} else if(sys == "delete") {
if(act != "") {
string power_profile = getdbl("power", ["profile", act]);
if(power_profile == JSON_INVALID) {
msg = "No power profile: " + act;
} else {
deletedbl("power", ["profile", act]);
msg = "Deleted power profile '" + act + "'";
}
} else {
msg = "To delete a power profile, please specify a name.";
}
} else if(sys == "save") {
if(act != "") {
setdbl("power", ["profile", act], (string)power_state);
msg = "Power profile '" + act + "' saved.";
} else {
msg = "To save a power profile, please specify a name.";
}
} else if(sys == "all") {
if(act == "toggle" || act == "") {
integer old_power_state = power_state;
power_state = ~power_state & max_state;
if(old_power_state < power_state)
ann = "subsystem-1";
else
ann = "subsystem-0";
} else if(act == "off") {
power_state = 0;
ann = "subsystem-0";
} else if(act == "on") {
power_state = max_state;
ann = "subsystem-1";
}
apply_state(1, 0);
} else if((pss = getjs(power_system_names, [sys])) != JSON_INVALID) {
integer mask = 1 << (integer)pss;
// echo("Power state starts at " + (string)power_state);
if(act == "toggle" || act == "") {
integer new_power_state = power_state ^ mask;
if(new_power_state > power_state)
ann = "subsystem-1";
else
ann = "subsystem-0";
power_state = new_power_state;
} else if(act == "on") {
power_state = power_state | mask;
ann = "subsystem-1";
} else if(act == "off") {
power_state = power_state & ~(mask);
ann = "subsystem-0";
}
// echo("Changing " + pss + " #?# " + (string)mask + " -> " + (string)power_state);
apply_state(1, 0);
} else {
msg = "no subsystem: " + sys;
}
if(ann)
announce(ann);
}
if(msg != "") {
print(outs, user, msg);
}
} else if(n == SIGNAL_NOTIFY) {
float integrity = (float)getdbl("repair", ["integrity"]);
list argv = delitem(splitnulls(m, " "), 0);
// echo("power notify: " + (string)ins + " " + (string)user + " | " + concat(argv, ".."));
string cmd1 = gets(argv, 0);
integer ci = count(argv);
if(cmd1 == "pipe" && ins == zap_pipe) {
io_tell(NULL_KEY, C_PUBLIC, "ping " + (string)ZAP_CHANNEL);
} else while(ci > 0) {
ci -= 2;
string cmd = gets(argv, ci);
string status = gets(argv, ci + 1);
// echo("POWER notify: " + cmd + " " + status);
if(cmd == "wifi") {
// block non-attached devices
wifi_state = (status == "y");
setdbl("status", ["remote-device"], (string)wifi_state);
e_call(C_HARDWARE, E_SIGNAL_CALL, (string)avatar + " " + (string)avatar + " hardware reconfigure");
} else if(cmd == "locomotion") {
// block unsit if already sitting, tell effector daemon to take controls
locomotion_state = (status == "y");
} else if(cmd == "lidar") {
lidar_state = (status == "y");
} else if(cmd == "base") {
base_state = (status == "y");
} else if(cmd == "hud") {
hud_state = (status == "y");
} else if(cmd == "motors") {
motors_state = (status == "y");
} else if(cmd == "video") {
video_state = (status == "y");
} else if(cmd == "optics") {
optics_state = (status == "y");
} else if(cmd == "notify") {
// redeliver cached notify messages:
string nc = getjs(notify_cmds, [status]);
if(nc != JSON_INVALID) {
list np = js2list(nc);
string cmdline = concat([status] + np, " ");
if(status == PROGRAM_NAME)
main(src, SIGNAL_NOTIFY, cmdline, avatar, NULL_KEY, avatar);
else
notify_program(cmdline, avatar, NULL_KEY, avatar);
}
} else if(cmd == "system") {
integer new_power_on = (status == "on");
if(new_power_on != power_on) {
power_on = new_power_on;
if(EPS_active && power_on)
apply_EPS(FALSE);
apply_state(TRUE, FALSE);
if(power_on) {
effector_release("a:shutdown");
echo("[_power] System initialized.");
announce("power-on");
string boot_chime = getdbl("id", ["chime", "boot"]);
if(boot_chime != JSON_INVALID)
llTriggerSound(boot_chime, 1);
} else {
string anim = "s_shutdown";
if(integrity > 0) {
echo("[_power] System shutdown complete. Type '@on' to boot.");
announce("power-off");
string halt_chime = getdbl("id", ["chime", "halt"]);
if(halt_chime != JSON_INVALID)
llTriggerSound(halt_chime, 1);
} else {
anim = "s_dead";
echo("[_power] System offline due to damage.");
}
effector_restrict("a:shutdown", anim);
}
notify_program("security power", outs, NULL_KEY, user);
llSleep(0.5);
e_call(C_INTERFACE, E_SIGNAL_CALL, (string)avatar + " " + (string)avatar + " interface reconfigure");
if(rebooting) {
llSleep(1);
rebooting = 0;
echo("[_power] Rebooting...");
/*m = PROGRAM_NAME + " on";
n = SIGNAL_INVOKE;
jump restart_main;*/
// invoke("_power on", outs, NULL_KEY, user);
// must finish shutdown first
set_timer("on", 1);
}
}
} else if(cmd == "aux") {
if(rebooting) {
echo("[_power] Not entering EPS; reboot in progress");
} else {
integer new_EPS_on = (status == "on");
if(new_EPS_on != EPS_active) {
if(power_on && new_EPS_on) {
EPS_active = FALSE;
echo("[_power] Not entering EPS; power is on.");
} else {
EPS_active = new_EPS_on;
apply_EPS(EPS_active);
/* if(!power_on)
echo("[_power] Auxiliary power now " + status + "."); */
}
}
}
}
}
if(!motors_state && has_motors) {
has_motors = FALSE;
effector_restrict("a:motors", "s_frozen");
} else if(motors_state && !has_motors) {
has_motors = TRUE;
effector_release("a:motors");
}
if(lidar_state && !optics_state && video_state && !video_in_lidar_mode) {
effector_restrict(
"power_lidar", "setsphere=?||setsphere_mode:0=force,setsphere_param:0/0/0/0=force,setsphere_distmin:0=force,setsphere_distmax:10=force,setsphere_distextend:3=force,setsphere_valuemin:0.0=force,setsphere_valuemax:1=force"
);
effector_restrict(
"power_lidar-2", "setenv_daytime:-1=force,setenv=?,setenv_daytime:-1=force||setenv_ambient:2/2/2=force,setenv_bluedensity:1/1/1=force,setenv_bluehorizon:1/1/1=force,setenv_scenegamma:2=force,setenv_hazedensity:1=force,setenv_hazehorizon:0=force,setenv_maxaltitude:4000=force,setenv_densitymultiplier:2=force,setenv_distancemultiplier:2000=force,setenv_starbrightness:0=force"
);
video_in_lidar_mode = TRUE;
} else if(video_in_lidar_mode) {
effector_release("power_lidar");
effector_release("power_lidar-2");
llSleep(0.2);
echo("@setenv=n,setenv_reset=force,setenv=y");
video_in_lidar_mode = FALSE;
}
/*
"No Video" and "No Signal" modes interact in a tricky manner because they both use setoverlay
Overlay priorities:
dead, no rez
dead, will rez
EPS active
no battery
empty battery
no EPS (bootable)
no video
no signal
*/
#define AUX_POWER_OVERLAY "27262ded-a1fa-a5ed-3fb9-5870e67794b1"
#define REALLY_DEAD_OVERLAY "cc377084-0058-f385-e19e-bd23f33df085"
#define WILL_RECLAIM_OVERLAY "b54ada0c-342d-3851-a4e9-e85661a96d76"
#define NO_BATTERY_OVERLAY "fc6c3a3a-f9ee-5301-7563-07d6b023770d"
#define EMPTY_BATTERY_OVERLAY "b35df830-9cab-d5f8-4007-e21223db886a"
#define BOOTABLE_OVERLAY "d5a932db-2526-e949-e23f-30c9aeea0811"
// #define NO_VIDEO_OVERLAY "c15ea705-570d-ee99-10ba-ee2d978033aa"
#define NO_VIDEO_OVERLAY "e8a40fce-1f34-9cfd-953e-9e31b2b4dc7c"
#define NO_SIGNAL_OVERLAY "e9c6ad2b-879c-4472-58a9-03ae64ad5b7d"
string overlay;
if(EPS_active && !power_on) {
overlay = AUX_POWER_OVERLAY;
} else if(!power_on) {
integer can_reclaim = (integer)getdbl("repair", ["reclamation"]);
float battery_charge = (float)getdbl("status", ["charge"]);
float battery_capacity = (float)getdbl("status", ["capacity"]);
if(integrity == 0) {
if(can_reclaim)
overlay = WILL_RECLAIM_OVERLAY;
else
overlay = REALLY_DEAD_OVERLAY;
} else if(battery_capacity > 0) {
if(battery_charge > 0) {
overlay = BOOTABLE_OVERLAY;
} else {
overlay = EMPTY_BATTERY_OVERLAY;
}
} else {
overlay = NO_BATTERY_OVERLAY;
}
} else if(!video_state && !no_video_mode) {
overlay = NO_VIDEO_OVERLAY;
no_video_mode = TRUE;
no_signal_mode = FALSE;
} else if(video_state && !(lidar_state || optics_state) && !no_signal_mode) {
// overlay = "97614d75-2bcb-e2e5-6df8-8fe5cf0b1988";
overlay = NO_SIGNAL_OVERLAY;
no_video_mode = FALSE;
no_signal_mode = TRUE;
} else if(no_video_mode && video_state) {
no_signal_mode = FALSE;
no_video_mode = FALSE;
}
if(no_signal_mode && (optics_state || lidar_state)) {
no_signal_mode = FALSE;
}
if(overlay_active) { // is active
if(power_on && !(no_video_mode || no_signal_mode)) { // but we don't need it
overlay_active = FALSE;
effector_release("power_overlay");
} else if(overlay != "") { // we changed it
// echo("[_power] RLV overlay " + overlay + " applied during notify " + m);
// echo("subsystem states: " + (string)power_state + ", " + getdbl("status", ["state"]));
effector_restrict("power_overlay", "setoverlay=?,setoverlay_alpha:1=force,setoverlay_tint:1/1/1=force,setoverlay_texture:" + overlay + "=force");
}
} else if(overlay != "") { // isn't active but we need to apply it
overlay_active = TRUE;
effector_restrict("power_overlay", "setoverlay=?,setoverlay_alpha:1=force,setoverlay_tint:1/1/1=force,setoverlay_texture:" + overlay + "=force");
} // else isn't active and we don't need it
if(locomotion_state && !can_move) {
effector_release("power_movement");
can_move = TRUE;
if(!can_unsit) {
effector_release("power_unsit");
can_unsit = TRUE;
}
} else if(!locomotion_state && can_move) {
effector_restrict("power_movement", "move=?"); // special pseudo-RLV command
can_move = FALSE;
if(llGetAgentInfo(avatar) & AGENT_SITTING) {
can_unsit = FALSE;
effector_restrict("power_unsit", "unsit=?");
// TODO: when implementing handles, they need a way to re-enable can_unsit
}
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
// hook_events([EVENT_TELEPORT, EVENT_ON_REZ, EVENT_REGION_CHANGE]);
hook_events([EVENT_ON_REZ]);
string s_status = llLinksetDataRead("status");
string s_power = llLinksetDataRead("power");
power_on = (integer)getjs(s_status, ["on"]);
power_state = (integer)getjs(s_status, ["state"]);
power_draw = (float)getjs(s_status, ["draw"]);
power_systems = getjs(s_power, ["system"]);
integer psi = 0;
string ps = "";
while((ps = getjs(power_systems, [(string)psi])) != JSON_INVALID) {
string psname = getjs(ps, ["name"]);
power_system_names = setjs(power_system_names, [psname], (string)psi);
++psi;
}
max_state = (1 << psi) - 1;
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] (DEBUG) available subsystems: " + (string)psi);
#endif
apply_state(1, 0);
} else if(n == SIGNAL_EVENT) {
integer e = (integer)m;
if(e == EVENT_ON_REZ) {
notify_program("security power", avatar, NULL_KEY, avatar);
}
/*} else if(n == SIGNAL_EVENT) {
// handles events EVENT_ON_REZ, EVENT_TELEPORT
// both cause FTL resets
// tell status daemon about it
integer e = (integer)m;
if(e == EVENT_TELEPORT || e == EVENT_ON_REZ || e == EVENT_REGION_CHANGE) {
e_call(C_STATUS, E_SIGNAL_CALL, (string)avatar + " " + (string)avatar + " status teleport");
}
// echo("[" + PROGRAM_NAME + "] unimplemented event " + m);
// apply_state();
*/
// the above is just too slow
} 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);
}
// task_end(instance);
}
#include <ARES/program>

720
ARES/system/security.lsl Normal file
View File

@ -0,0 +1,720 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* Security System Module
*
* 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 <ARES/a>
#include <ARES/api/auth.h.lsl>
#define CLIENT_VERSION ARES_VERSION
#define CLIENT_VERSION_TAGS ARES_VERSION_TAGS
integer recognition_inhibited;
integer allow_time = 30;
key name_lookup_pipe;
list name_lookup_queue;
list RANK_LIST = [0, 0, 0, "user", "manager", "owner", "self", "?"];
list RULE_TERMINOLOGY = ["nobody","all","consent","user","manager","owner","self","cycle","toggle"];
main(integer src, integer n, string m, key outs, key ins, key user) {
@restart_main;
if(n == SIGNAL_INVOKE) {
list argv = split(m, " ");
integer argc = count(argv);
string msg = "";
string target;
key identified_owner = getdbl("id", ["owner"]);
integer identified_owner_affected;
if(argc == 1) {
msg = "ARES security status\n\nType '" + PROGRAM_NAME + " help' for a quick syntax guide or 'help security' for the full manual page.\n";
print(outs, user, msg);
list topics = ["ban", "guest", "user"];
integer ti = 3;
while(ti--) {
string topic = gets(topics, ti);
list people = jskeys(getdbl("security", [topic]));
if(count(people)) {
msg = llChar(llOrd(topic, 0) & 0x5f) + substr(topic, 1, LAST) + "s:\n";
integer pi = count(people);
while(pi--) {
string person = gets(people, pi);
integer pvalue = (integer)getdbl("security", [topic, person]);
string spvalue = "indefinite";
if(topic == "user") {
spvalue = gets(RANK_LIST, pvalue);
} else if(pvalue > 1600000000) {
if(pvalue < llGetUnixTime()) {
spvalue = "expired";
deletedbl("security", [topic, person]);
} else {
spvalue = "expires in " + format_time(pvalue - llGetUnixTime());
}
}
msg += " - secondlife:///app/agent/" + person + "/about (" + spvalue + ")\n";
}
} else {
msg = "No " + topic + "s configured.\n";
}
llSleep(0.0625);
print(outs, user, msg);
}
msg = "";
} else {
string topic;
target = gets(argv, 2);
string action = gets(argv, 1);
if(action == "yes" || action == "no" || action == "trust" || action == "block") {
key h = (key)target;
string request = getjs(tasks_queue, [h]);
if(user != avatar) {
tell(user, 0, "Only the unit may respond to consent prompts.");
} else if(request != JSON_INVALID) {
key subject = getjs(request, [2]);
key s_outs = getjs(request, [3]);
string callsign = getdbl("id", ["callsign"]);
string s_msg;
if(action == "yes" && !recognition_inhibited) {
setdbl("security", ["guest", (string)subject], (string)(llGetUnixTime() + allow_time));
s_msg = "You have been granted access to " + callsign + " for " + (string)allow_time + " seconds.";
} else if(action == "no") {
setdbl("security", ["ban", (string)subject], (string)(llGetUnixTime() + allow_time));
s_msg = "You have been denied access to " + callsign + " for " + (string)allow_time + " seconds.";
} else if(action == "trust" && !recognition_inhibited) {
setdbl("security", ["guest", (string)subject], "1");
s_msg = "You have been granted access to " + callsign + " indefinitely.";
} else if(action == "block") {
setdbl("security", ["ban", (string)subject], "1");
s_msg = "You have been denied access to " + callsign + " indefinitely.";
}
print(s_outs, subject, s_msg);
if(recognition_inhibited && (action == "yes" || action == "trust"))
echo("Consent failed due to radiation interference. Instruct operator to try again.");
else
echo("[" + PROGRAM_NAME + "] implementing consent decision (" + action + ")");
string outcome;
if(action == "no" || action == "block" || recognition_inhibited)
outcome = getjs(request, [1]);
else
outcome = getjs(request, [0]);
if(outcome != "")
invoke(outcome, s_outs, NULL_KEY, subject);
task_end(h);
jump add_name;
} else {
echo("No pending consent request: " + (string)h);
}
} else if(action == "help") {
msg = "Syntax: \n\n"
+ " " + PROGRAM_NAME + " yes|no|trust|block <key>: Respond to a consent prompt.\n"
+ " " + PROGRAM_NAME + " user|manager|owner <key>: Add or update a user.\n"
+ " " + PROGRAM_NAME + " guest|ban <key> [<time>]: Add a ban or guest, either permanently or for <time> seconds.\n"
+ " " + PROGRAM_NAME + " reset|runaway: Clear the unit's user list. Existing owners will be notified.\n"
+ " " + PROGRAM_NAME + " forget <key>: Remove a user, guest, or ban.\n"
+ " " + PROGRAM_NAME + " guest|ban <key> [<minutes>]: Whitelist or blacklist an avatar.\n";
print(outs, user, msg);
llSleep(0.5);
msg = " " + PROGRAM_NAME + " rules: List all security rules.\n"
+ " " + PROGRAM_NAME + " bolts on|off|auto|cycle: Control safety bolts (detach prevention)\n"
+ " " + PROGRAM_NAME + " audit: Check for missing name entries and assign self-ownership if the unit has no owner.\n"
+ " " + PROGRAM_NAME + " <rule> cycle|toggle|0-6: Modify a security rule.\n\n"
+ "The new value for a security rule can be specified with a mnemonic instead of a number. ('toggle' alternates between 0 and 6.) The rule values are:\n\n"
+ "0 (nobody): no one may do this\n"
+ "1 (all): consent is not required to do this\n"
+ "2 (consent): guests and all users may do this\n"
+ "3 (user): all users may do this\n"
+ "4 (manager): managers and owners may do this\n"
+ "5 (owner): only owners may do this\n"
+ "6 (self): only the unit may do this (no rank required)\n";
} else if(action == "name") {
request_name(target, outs, avatar);
} else if(action == "uuid") {
request_uuid(concat(delrange(argv, 0, 1), " "), outs, avatar);
} else if(action == "rules") {
string rules = getdbl("security", ["rule"]);
msg = "Defined rules:\n\n" + rules;
} else if(action == "user" || action == "manager" || action == "owner") {
if(strlen(target) != 36) {
msg = "Invalid UUID.";
jump no_affect;
}
msg = "Give " + target + " the rank of " + action;
integer current_rank = (integer)getdbl("security", ["user", target]);
string current_rank_name = gets(RANK_LIST, current_rank);
integer check = sec_check(user, "add-" + action, outs, "", m);
if(check == ALLOWED) {
if(target == user)
current_rank = 6; // prevent self-promotion
integer new_rank = index(RANK_LIST, action);
if(current_rank == new_rank) {
msg = target + " already has " + action + " authorization.";
} else if(new_rank > current_rank) {
setdbl("security", ["user", target], (string)new_rank);
msg += ": success.";
if(getdbl("security", ["guest", target]) != JSON_INVALID) {
deletedbl("security", ["guest", target]);
msg += " Guest entry removed.";
}
if(getdbl("security", ["ban", target]) != JSON_INVALID) {
deletedbl("security", ["ban", target]);
msg += " Ban removed.";
}
if(current_rank == 0)
jump add_name;
} else if(sec_check(user, "demote-" + current_rank_name, outs, "", m) == ALLOWED) {
setdbl("security", ["user", target], (string)new_rank);
msg += ": success.";
if(target == identified_owner)
identified_owner_affected = 1;
} else {
current_rank_name += "s";
if(current_rank_name == "selfs")
current_rank_name = "yourself";
msg = "You do not have the authority to demote " + current_rank_name + ".";
}
} else {
msg = "You do not have the authority to assign " + action + "s.";
}
} else if(action == "forget") {
integer check = sec_check(user, "manage", outs, "", m);
if(check == ALLOWED) {
msg = "Deleting all knowledge of " + target + "...";
list phases = ["ban", "guest", "user", "name"];
integer pi = 4;
while(pi--) {
string phase = gets(phases, pi);
string status = getdbl("security", [phase, target]);
if(status != JSON_INVALID) {
if(phase == "user") {
string current_rank_name = gets(RANK_LIST, (integer)status);
integer check = sec_check(user, "demote-" + current_rank_name, outs, "", m);
if(check == ALLOWED) {
msg += "\nRemoving " + current_rank_name + ".";
} else if(target == user) {
check = sec_check(user, "demote-self", outs, "", m);
if(check == ALLOWED) {
msg += "\nRemoving user's own user entry.";
}
}
if(check == ALLOWED) {
deletedbl("security", ["user", target]);
if(target == identified_owner)
identified_owner_affected = 1;
} else {
msg = "\nInsufficient authorization to remove " + phase + " entry.";
}
} else {
deletedbl("security", [phase, target]);
msg += "\nRemoved " + phase + " entry.";
}
}
}
} else {
msg = "You do not have the authority to manage this unit.";
}
msg += "\nFinished.";
// TODO: automatically kick RLV and remote devices
jump remove_name;
} else if(action == "guest") {
if(user == avatar) {
integer end_time = 1;
msg = "secondlife:///app/agent/" + target + "/about is now a guest";
if(argc == 4) {
end_time = (integer)gets(argv, 3) + llGetUnixTime();
msg += "; this will expire in " + format_time(end_time - llGetUnixTime());
}
setdbl("security", ["guest", target], (string)end_time);
msg += "\nYou can cancel this at any time with: @" + PROGRAM_NAME + " forget " + target;
jump add_name;
} else {
msg = "Only the unit may add guests.";
}
} else if(action == "ban") {
if(sec_check(user, "manage", outs, "", m) == ALLOWED) {
integer end_time = 1;
msg = "secondlife:///app/agent/" + target + "/about is now banned";
if(argc == 4) {
end_time = (integer)gets(argv, 3) + llGetUnixTime();
msg += "; this will expire in " + format_time(end_time - llGetUnixTime());
}
integer user_record = (integer)getdbl("security", ["user", target]);
if(user_record) {
deletedbl("security", ["user", target]);
msg += "\nRevoked user access.";
}
integer guest_record = (integer)getdbl("security", ["guest", target]);
if(guest_record) {
deletedbl("security", ["guest", target]);
msg += "\nRevoked guest access.";
}
setdbl("security", ["ban", target], (string)end_time);
if(target != user)
msg += "\nYou can cancel this at any time with: @" + PROGRAM_NAME + " forget " + target;
// TODO: automatically kick RLV and remote devices
jump add_name;
} else {
msg = "You do not have authorization to manage this unit.";
}
} else if(action == "reset" || action == "runaway") {
if(sec_check(user, "run-away", outs, "", m) == ALLOWED) {
list users = jskeys(getdbl("security", ["user"]));
integer ui = count(users);
string callsign = getdbl("id", ["callsign"]);
while(ui--) {
target = gets(users, ui);
integer target_rank = (integer)getdbl("security", ["user", target]);
llSetObjectName(callsign);
if(target_rank == SEC_OWNER) {
echo("Notifying secondlife:///app/agent/" + target + "/about...");
llInstantMessage(target, llGetUsername(user) + " has initiated a user reset. You will no longer be recognized as an owner of " + llGetUsername(avatar) + ".");
}
llSetObjectName(":");
}
setdbl("security", ["user"], "{}");
setdbl("security", ["guest"], "{}");
setdbl("security", ["name"], "{}");
msg = "Security reset complete. All users and guests have been cleared.";
setdbl("security", ["user", avatar], "5");
identified_owner_affected = 1;
main(src, n, "security audit", outs, NULL_KEY, user);
} else {
msg = "You may not run away.";
}
} else if(action == "audit") {
list names_missing = [];
integer owners_found = 0;
list phases = ["ban", "guest", "user"];
integer pi = 3;
while(pi--) {
string phase = gets(phases, pi);
list keys = jskeys(getdbl("security", [phase]));
integer ki = count(keys);
while(ki--) {
target = gets(keys, ki);
integer deleted = 0;
if(phase == "user") {
integer target_rank = (integer)getdbl("security", ["user", target]);
if(target_rank == 5)
++owners_found;
} else {
string user_entry = getdbl("security", ["user", target]);
if(user_entry != JSON_INVALID) {
msg += "\nRemoving " + phase + " entry for user secondlife:///app/agent/" + target + "/about";
deletedbl("security", [phase, target]);
deleted = 2;
} else {
integer now = llGetUnixTime();
integer entry = (integer)getdbl("security", [phase, target]);
if(entry != 1 && entry < now) {
deletedbl("security", [phase, target]);
msg += "\nRemoving expired " + phase + " entry for secondlife:///app/agent/" + target + "/about";
deleted = 1;
}
}
}
string target_name = getdbl("security", ["name", target]);
if(deleted != 1 && target_name == JSON_INVALID && !contains(names_missing, target)) {
names_missing += target;
// msg += "\nRequesting name of " + target;
} else if(deleted == 1 && target_name != JSON_INVALID) {
deletedbl("security", ["name", target]);
// msg += "\nRemoved obsolete name entry for secondlife:///app/agent/" + target + "/about";
}
}
// do this early so self-guest and self-ban can be removed:
if(phase == "user") {
if(!owners_found) {
msg += "\nNo owners found. Granting self-ownership.";
setdbl("security", ["user", (string)avatar], (string)SEC_OWNER);
string target_name = getdbl("security", ["name", (string)avatar]);
if(target_name == JSON_INVALID && !contains(names_missing, (string)avatar)) {
names_missing += (string)avatar;
// msg += "\nRequesting name of " + (string)avatar;
}
setdbl("id", ["owner"], avatar);
}
}
}
list name_entries = jskeys(getdbl("security", ["name"]));
integer nei = count(name_entries);
while(nei--) {
target = gets(name_entries, nei);
pi = 3;
integer needed = 0;
while(pi--) {
string e = getdbl("security", [gets(phases, pi), target]);
if(e != JSON_INVALID)
++needed;
}
if(!needed) {
deletedbl("security", ["name", target]);
// msg += "\nRemoved spurious name entry for secondlife:///app/agent/" + target + "/about";
}
}
if(names_missing != []) {
name_lookup_queue += names_missing;
if(name_lookup_pipe) {
request_name(gets(name_lookup_queue, 0), name_lookup_pipe, avatar);
} else {
pipe_open(["notify " + PROGRAM_NAME + " name"]);
}
}
if(msg == "")
msg = "Audit completed without any issue.";
else
msg = "ARES security self-audit\n" + msg;
} else if(action == "debug") {
msg = "Waiting tasks: " + tasks_queue + "\n\nWaiting names: " + concat(name_lookup_queue, ", ");
} else if((topic = getdbl("security", ["rule", action])) != JSON_INVALID) {
// @security <rule> 0-6|cycle|toggle|nobody|all|consent|user|manager|owner|self
msg = "The permission '" + action + "' is currently ";
if(argc > 2) {
// echo(msg);
integer my_rank = (integer)getdbl("security", ["user", user]);
if(my_rank != SEC_OWNER) {
msg = "Only owners may adjust security rule settings. " + msg;
} else {
integer nact;
// echo("target is " + target);
if(target != (string)((integer)target)) {
nact = index(RULE_TERMINOLOGY, target);
} else {
nact = (integer)target;
if(nact > 6) {
nact = NOWHERE;
}
}
// echo("nact is " + (string)nact);
if(~nact) {
if(nact == 7) {
nact = (1 + (integer)topic) % 6;
} else if(nact == 8) {
nact = 6 * !(integer)topic;
}
setdbl("security", ["rule", action], (string)nact);
msg = "The permission '" + action + "' will be ";
topic = (string)nact;
} else {
msg = "Invalid action: " + target;
jump no_affect;
}
}
}
list responses = [
"forbidden completely.",
"allowed for anyone who has not been banned, without the unit's consent.",
"allowed for guests and users. The unit will be asked to grant guest access to strangers.",
"allowed for all registered users except guests.",
"allowed only for managers and owners.",
"allowed only for owners.",
"allowed only for the unit itself, even if the unit is banned."
];
msg += gets(responses, (integer)topic);
} else {
msg = "Unknown action: " + action + ". See 'help security' for a list, or 'security rules' for the list of defined security rules.";
}
}
jump no_name;
@add_name;
name_lookup_queue += target;
if(name_lookup_pipe) {
request_name(gets(name_lookup_queue, 0), name_lookup_pipe, avatar);
} else {
pipe_open(["notify " + PROGRAM_NAME + " name"]);
}
jump no_name;
@remove_name;
string target_name = getdbl("security", ["name", target]);
if(target_name != JSON_INVALID)
deletedbl("security", ["name", target]);
@no_name;
if(!identified_owner_affected && identified_owner != JSON_INVALID) {
// echo("identified owner " + (string)identified_owner + " not affected");
jump no_affect;
}
list users = js2list(getdbl("security", ["user"]));
integer ui = count(users);
key old_identified_owner = identified_owner;
identified_owner = avatar;
while(ui) {
ui -= 2;
integer user_rank = (integer)gets(users, ui + 1);
if(user_rank == SEC_OWNER) {
identified_owner = getk(users, ui);
if(identified_owner != old_identified_owner)
echo("[_security] Your primary owner is now secondlife:///app/agent/" + (string)identified_owner + "/about");
jump new_owner_identified;
}
}
identified_owner = JSON_INVALID;
// echo("no identifiable owner found");
@new_owner_identified;
// echo("identified owner " + (string)identified_owner + " set");
setdbl("id", ["owner"], identified_owner);
@no_affect;
if(msg != "") {
print(outs, user, msg);
}
} else if(n == SIGNAL_NOTIFY) {
list argv = splitnulls(m, " ");
string action = gets(argv, 1);
if(action == "power") {
integer power_on = (integer)getdbl("status", ["on"]);
// integer locked = (integer)getdbl("policy", ["lock"]);
integer C_LIGHT_BUS;
if(llGetAttached())
C_LIGHT_BUS = 105 - (integer)("0x" + substr(avatar, 29, 35));
else
C_LIGHT_BUS = 105 - (integer)("0x" + substr(KERNEL, 29, 35));
integer bolts = (integer)getdbl("status", ["bolts"]);
if(bolts == 2)
bolts = power_on;
io_tell(NULL_KEY, C_LIGHT_BUS, "bolts " + gets(["off", "on"], bolts));
if(bolts) {
effector_restrict("bolts", "detach=?");
} else {
effector_release("bolts");
}
} else if(action == "consent") {
list props = js2list(concat(delrange(argv, 0, 1), " "));
props += [user, outs, llGetTime()];
integer current_ban = (integer)getdbl("security", ["ban", user]);
integer current_guest = (integer)getdbl("security", ["guest", user]);
if(current_ban > 1 && current_ban < llGetUnixTime()) {
deletedbl("security", ["ban", user]);
} else if(current_ban) {
// no need to consent - reject
string deny_cmd = gets(props, 1);
if(deny_cmd != "")
invoke(deny_cmd, outs, NULL_KEY, user);
return;
}
if(current_guest > 1 && current_guest < llGetUnixTime()) {
deletedbl("security", ["guest", user]);
} else if(current_guest) {
// no need to consent - accept
string retry_cmd = gets(props, 0);
if(retry_cmd != "")
invoke(retry_cmd, outs, NULL_KEY, user);
return;
}
key h = llGenerateKey();
task_begin(h, jsarray(props));
/*echo("Your consent is required.\nGuest secondlife:///app/agent/" + (string)user + "/about wishes to:\n\n "
+ gets(props, 0)
+ "\n\nIf you will allow this, type: security trust " + (string)h
+ "\nOtherwise, type: security ban " + (string)h); */
string command = llStringTrim(gets(props, 0), STRING_TRIM);
string alert_msg;
string name;
if(object_pos(user) != ZV) {
// definitely in sim
if(llGetAgentSize(user) != ZV) {
// definitely a person in the sim
name = llGetUsername(user);
echo("Your consent is required. "+"secondlife:///app/agent/" + (string)user + "/about"+" wants to access your system. See HUD for details.");
} else {
// definitely an object in the sim
name = llKey2Name(user);
echo("Your consent is required. '" + name + "' owned by "+"secondlife:///app/agent/" + (string)user + "/about"+" wants to access your system. See HUD for details.");
if(name == "")
name = "(unnamed object)";
}
} else {
name = getdbl("security", ["name", user]);
if(name == JSON_INVALID)
name = "(UUID " + substr(user, 0, 7) + "...)";
// avatar not in sim
echo("Your consent is required. "+"secondlife:///app/agent/" + (string)user + "/about"+" wants to access your system. See HUD for details.");
}
if(strlen(name) > 32)
name = substr(name, 0, 28) + "...";
if(substr(command, 0, 5) == "_menu "
|| substr(command, 0, 4) == "menu "
|| command == "_menu"
|| command == "menu") {
alert_msg = name + " wants to access your menus";
} else if(substr(command, 0, 12) == "_device auth ") {
string device_name = gets(splitnulls(command, " "), 2);
alert_msg = name + " wants to access your " + device_name;
} else
alert_msg = name + " wants to run a command: " + command;
alert(alert_msg, 1, 0, 0, [
"security yes " + (string)h,
"security trust " + (string)h,
"security no " + (string)h,
"security block " + (string)h
]);
} else if(action == "memory") {
// high radiation levels are preventing user recognition
recognition_inhibited = (gets(argv, 2) == "n");
} else if(action == "name") {
if(ins == name_lookup_pipe) {
string buffer;
pipe_read(name_lookup_pipe, buffer);
list parts = split(buffer, " = ");
string uuid = gets(parts, 0);
string username = gets(parts, 1);
if(username != "") {
// echo("[_security] assigned name " + username + " to agent " + uuid);
setdbl("security", ["name", uuid], username);
}
}
if(!count(name_lookup_queue)) {
// echo("[_security] finished fetching names; retiring pipe");
pipe_close([name_lookup_pipe]);
name_lookup_pipe = "";
} else {
request_name(gets(name_lookup_queue, 0), name_lookup_pipe, avatar);
name_lookup_queue = delitem(name_lookup_queue, 0);
}
} else if(action == "pipe") {
if(count(name_lookup_queue)) {
// echo("[_security] got pipe " + (string)ins + ": " + m);
name_lookup_pipe = ins;
request_name(gets(name_lookup_queue, 0), name_lookup_pipe, avatar);
name_lookup_queue = delitem(name_lookup_queue, 0);
} else {
// echo("[_security] got unnecessary pipe; destroying");
pipe_close([ins]);
}
}
} else if(n == SIGNAL_INIT) {
#ifdef DEBUG
echo("[" + PROGRAM_NAME + "] init event");
#endif
} 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 <ARES/program>

61
README.md Normal file
View File

@ -0,0 +1,61 @@
# ARES Software Development Kit
ARES is a small nanokernel operating system written in the [Second Life](https://secondlife.com/) [Linden Scripting Language](https://wiki.secondlife.com/wiki/LSL_Portal). It is normally used to power immersive HUD attachments for roleplaying as a robot, but in principle can be adapted as a general-purpose platform through the addition and removal of system components.
ARES provides a robust API which is quite dissimilar to normal LSL programming. Here are the highlights:
- Many common "`ll`"-prefixed LSL functions are renamed for brevity and convenience. This was optional in Companion but highly encouraged in ARES. These definitions come from the `utils.lsl` file in the SDK root.
- No `default {}` state—the program's entry point for command-line use is the `SIGNAL_INVOKE` block inside a `main()` function. Timers and events like `on_rez` are emulated through kernel signals, but can still be implemented for programs that need high performance.
- A basic stream API is used for text input and output, similar to Unix pipes. `llSay(0, "Hello World!");` is now `print(outs, user, "Hello World!");` — pipes can be chained together to automatically pass messages from one program to another, or even in and out of normal chat.
## ARES SDK
ARES software development requires the Firestorm viewer, which augments the LSL compilation process with the [Boost::Wave](https://www.boost.org/doc/libs/1_40_0/libs/wave/index.html) preprocessor. The SDK includes UDL 2.1 syntax highlighting definitions for Notepad++ (provided you like dark mode.)
To install the SDK, download this Git repo's contents into a new directory (ideally one without any spaces in the path), and configure Firestorm's preprocessor (Preferences > Firestorm > Build 1) to point at it. The LSL preprocessor must be enabled, along with the 'Script optimizer' and '#includes from local disk' checkboxes.
**Not all of the files in the ARES SDK may be reused freely**. Your use of the SDK is subject to the terms of the [ARES Software Copyright License](http://nanite-systems.com/ASCL). Please make sure you are familiar with the terms of the ASCL's different sublicenses before downloading.
Once the SDK is installed, look for the file `ARES/application/template.lsl` as a basis for writing your own programs.
### Hardware Development
Device development for ARES is similar to development for Companion, and most of the familiar processes still apply. Most development is done using the <a href="/light_bus">light bus</a>, albeit with minor differences. For the time being, starting materials can be found from the Companion 8 SDK, available for free from our store in Eisa. ARES also uses many of the <a href="/public_bus">public bus</a> commands, and compatibility with ACS devices and interference is similar to that of Companion.
### Packaging and Distribution
The server axtest:0 in Eisa contains the `sample` package, which is a self-documenting template for creating your own packages.
ARES package servers are not yet available for purchase. In the interim, note that any inventory items (with the exception of scripts) ctrl-dragged onto the ARES HUD will be automatically relocated to user memory, where you may act upon them.
### File Licenses
Most of the files in the SDK are ASCL-iii, which is a "BSD-like" permissive open source license that allows for closed-source derivatives. However, many (especially those in the `ARES/application/` directory) fall under more restrictive licenses, as summarized below.
|file|description|terms|
|--- |--- |--- |
|ARES/application/calc.lsl|calculator|ASCL-ii (copyleft/share-alike)|
|ARES/application/db.lsl|database utility|ASCL-ii (copyleft/share-alike)|
|ARES/application/define.lsl|wiki lookup|ASCL-ii (copyleft/share-alike)|
|ARES/application/filter.lsl|vox filters|ASCL-i (modding only)|
|ARES/application/find.lsl|grep clone (WIP)|ASCL-ii (copyleft/share-alike)|
|ARES/application/fortune.lsl|GNU fortune frontend|ASCL-ii (copyleft/share-alike)|
|ARES/application/fortune.h.lsl|GNU fortune frontend (dependency)|ASCL-i (modding only)|
|ARES/application/help.lsl|manual interface|ASCL-ii (copyleft/share-alike)|
|ARES/application/id.lsl|system configuration tool|ASCL-i (modding only)|
|ARES/application/land.lsl|parcel and region utilities|ASCL-ii (copyleft/share-alike)|
|ARES/application/lslisp.lsl|LSLisp programming language|ASCL-ii (copyleft/share-alike)|
|ARES/application/mantra.lsl|self-hypnosis tool|ASCL-ii (copyleft/share-alike)|
|ARES/application/mail.lsl|email utility|ASCL-ii (copyleft/share-alike)|
|ARES/application/media.lsl|sound & animation playback widget|ASCL-ii (copyleft/share-alike)|
|ARES/application/media.event.lsl|sound & animation playback widget (dependency)|ASCL-ii (copyleft/share-alike)|
|ARES/application/news.lsl|RSS aggregator|ASCL-ii (copyleft/share-alike)|
|ARES/application/persona.lsl|personality configuration tool|ASCL-ii (copyleft/share-alike)|
|ARES/application/scidb.lsl|scientific database query tool|ASCL-ii (copyleft/share-alike)|
|ARES/application/tell.lsl|sends chat messages|ASCL-ii (copyleft/share-alike)|
|ARES/application/xset.lsl|captures standard output|ASCL-ii (copyleft/share-alike)|
|ARES/system/exec.lsl|command shell|ASCL-i (modding only)|
|ARES/system/exec.event.lsl|command shell (dependency)|ASCL-i (modding only)|
|ARES/system/policy.lsl|security policies|ASCL-i (modding only)|
|ARES/system/power.lsl|subsystem manager|ASCL-i (modding only)|
|ARES/system/security.lsl|user access control|ASCL-i (modding only)|
|glob.lsl|Linux filename pattern matching|Dual GPL 2.0 and MIT|
|lslisp.lsl|LSLisp programming language|ASCL-ii (copyleft/share-alike)|
|lslisp.h.lsl|LSLisp programming language|ASCL-ii (copyleft/share-alike)|
In case of disagreements or omissions between actual files and the entries above, license declarations inside the files take precedence.

64
UDL/ARES Shell.xml Normal file
View File

@ -0,0 +1,64 @@
<NotepadPlus>
<UserLang name="ARES Exec Script" ext="as p pkg spkg" udlVersion="2.1">
<Settings>
<Global caseIgnored="no" allowFoldOfComments="no" foldCompact="no" forcePureLC="2" decimalSeparator="0" />
<Prefix Keywords1="no" Keywords2="no" Keywords3="no" Keywords4="no" Keywords5="no" Keywords6="no" Keywords7="no" Keywords8="no" />
</Settings>
<KeywordLists>
<Keywords name="Comments">00#AXP 01 02 03# 04((EOL))</Keywords>
<Keywords name="Numbers, prefix1"></Keywords>
<Keywords name="Numbers, prefix2">0x</Keywords>
<Keywords name="Numbers, extras1">A B C D E F a b c d e f</Keywords>
<Keywords name="Numbers, extras2"></Keywords>
<Keywords name="Numbers, suffix1"></Keywords>
<Keywords name="Numbers, suffix2"></Keywords>
<Keywords name="Numbers, range"></Keywords>
<Keywords name="Operators1">&lt; = &gt; ! ^ &amp; | + - * / $</Keywords>
<Keywords name="Operators2">is</Keywords>
<Keywords name="Folders in code1, open"></Keywords>
<Keywords name="Folders in code1, middle"></Keywords>
<Keywords name="Folders in code1, close"></Keywords>
<Keywords name="Folders in code2, open"></Keywords>
<Keywords name="Folders in code2, middle"></Keywords>
<Keywords name="Folders in code2, close"></Keywords>
<Keywords name="Folders in comment, open"></Keywords>
<Keywords name="Folders in comment, middle"></Keywords>
<Keywords name="Folders in comment, close"></Keywords>
<Keywords name="Keywords1">if then set do exit in</Keywords>
<Keywords name="Keywords2">ai calc define domain fortune hookup land lore lslisp mail mantra news pds restraint scidb spy tell toot tweet type wander web con pipet net ax</Keywords>
<Keywords name="Keywords3">from say set service to</Keywords>
<Keywords name="Keywords4">%undefined %word %char %line</Keywords>
<Keywords name="Keywords5">db filter fs help id input menu nav persona pkg power proc security vox wiz xset device display libmenu</Keywords>
<Keywords name="Keywords6">drop delete</Keywords>
<Keywords name="Keywords7">baseband effector hardware interface io repair status variatype sexuality warrior</Keywords>
<Keywords name="Keywords8"></Keywords>
<Keywords name="Delimiters">00%count 01 02((words lines chars EOL)) 03 04 05 06 07&#x000D;&#x000A; 08 09 10 11 12jump 13 14((EOL)) 15@ 16 17((: ; EOL)) 18 19 20 21echo 22 23((EOL))</Keywords>
</KeywordLists>
<Styles>
<WordsStyle name="DEFAULT" fgColor="AAAAC6" bgColor="171717" fontStyle="0" nesting="0" />
<WordsStyle name="COMMENTS" fgColor="474767" bgColor="171717" fontStyle="0" nesting="768" />
<WordsStyle name="LINE COMMENTS" fgColor="FF0080" bgColor="FFFFFF" colorStyle="1" fontStyle="1" nesting="768" />
<WordsStyle name="NUMBERS" fgColor="C0C0D4" bgColor="171717" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS1" fgColor="F72488" bgColor="171717" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS2" fgColor="F72488" bgColor="171717" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS3" fgColor="8080A8" bgColor="171717" fontStyle="2" nesting="0" />
<WordsStyle name="KEYWORDS4" fgColor="8080A8" bgColor="171717" fontStyle="2" nesting="0" />
<WordsStyle name="KEYWORDS5" fgColor="D9D9E6" bgColor="171717" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS6" fgColor="8383A9" bgColor="171717" fontStyle="1" nesting="0" />
<WordsStyle name="KEYWORDS7" fgColor="8080A8" bgColor="171717" fontStyle="2" nesting="0" />
<WordsStyle name="KEYWORDS8" fgColor="AAAAC6" bgColor="171717" fontStyle="1" nesting="0" />
<WordsStyle name="OPERATORS" fgColor="5F5F8B" bgColor="171717" fontStyle="0" nesting="0" />
<WordsStyle name="FOLDER IN CODE1" fgColor="5F5F8B" bgColor="171717" fontStyle="0" nesting="0" />
<WordsStyle name="FOLDER IN CODE2" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="FOLDER IN COMMENT" fgColor="33334A" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS1" fgColor="8080A8" bgColor="171717" fontStyle="2" nesting="83886080" />
<WordsStyle name="DELIMITERS2" fgColor="FF80FF" bgColor="171717" fontStyle="1" nesting="0" />
<WordsStyle name="DELIMITERS3" fgColor="E10F5E" bgColor="171717" fontStyle="1" nesting="0" />
<WordsStyle name="DELIMITERS4" fgColor="474767" bgColor="171717" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS5" fgColor="F72488" bgColor="171717" fontStyle="2" nesting="0" />
<WordsStyle name="DELIMITERS6" fgColor="F72488" bgColor="171717" fontStyle="1" nesting="0" />
<WordsStyle name="DELIMITERS7" fgColor="FF80C0" bgColor="171717" fontStyle="2" nesting="0" />
<WordsStyle name="DELIMITERS8" fgColor="FFFFFF" bgColor="171717" fontStyle="0" nesting="16777216" />
</Styles>
</UserLang>
</NotepadPlus>

64
UDL/LSL (ARES).xml Normal file

File diff suppressed because one or more lines are too long

64
UDL/LSL (Companion).xml Normal file

File diff suppressed because one or more lines are too long

64
UDL/NS Manual Format.xml Normal file
View File

@ -0,0 +1,64 @@
<NotepadPlus>
<UserLang name="NS Manual" ext="info" udlVersion="2.1">
<Settings>
<Global caseIgnored="no" allowFoldOfComments="no" foldCompact="no" forcePureLC="0" decimalSeparator="0" />
<Prefix Keywords1="no" Keywords2="no" Keywords3="yes" Keywords4="no" Keywords5="no" Keywords6="no" Keywords7="no" Keywords8="no" />
</Settings>
<KeywordLists>
<Keywords name="Comments">00 01 02 03 04</Keywords>
<Keywords name="Numbers, prefix1"></Keywords>
<Keywords name="Numbers, prefix2"></Keywords>
<Keywords name="Numbers, extras1"></Keywords>
<Keywords name="Numbers, extras2"></Keywords>
<Keywords name="Numbers, suffix1"></Keywords>
<Keywords name="Numbers, suffix2"></Keywords>
<Keywords name="Numbers, range"></Keywords>
<Keywords name="Operators1">|</Keywords>
<Keywords name="Operators2"></Keywords>
<Keywords name="Folders in code1, open"></Keywords>
<Keywords name="Folders in code1, middle"></Keywords>
<Keywords name="Folders in code1, close"></Keywords>
<Keywords name="Folders in code2, open"></Keywords>
<Keywords name="Folders in code2, middle"></Keywords>
<Keywords name="Folders in code2, close"></Keywords>
<Keywords name="Folders in comment, open"></Keywords>
<Keywords name="Folders in comment, middle"></Keywords>
<Keywords name="Folders in comment, close"></Keywords>
<Keywords name="Keywords1">see See</Keywords>
<Keywords name="Keywords2"></Keywords>
<Keywords name="Keywords3">@</Keywords>
<Keywords name="Keywords4"></Keywords>
<Keywords name="Keywords5"></Keywords>
<Keywords name="Keywords6"></Keywords>
<Keywords name="Keywords7"></Keywords>
<Keywords name="Keywords8"></Keywords>
<Keywords name="Delimiters">00TOPIC 00&lt;?php 01 02((EOL)) 02((EOL)) 03&lt; 04 05&gt; 06[ 07 08] 09&quot; 10 11&quot; 12(see 12(See 13 14) 14) 15` 16 17` 18 19 20 21 22 23</Keywords>
</KeywordLists>
<Styles>
<WordsStyle name="DEFAULT" fgColor="808080" bgColor="FFFFFF" colorStyle="1" fontStyle="0" nesting="0" />
<WordsStyle name="COMMENTS" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="LINE COMMENTS" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontStyle="0" nesting="0" />
<WordsStyle name="NUMBERS" fgColor="C0C0C0" bgColor="181818" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS1" fgColor="CDA2DD" bgColor="181818" colorStyle="1" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS2" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS3" fgColor="67E086" bgColor="FFFFFF" colorStyle="1" fontStyle="1" nesting="0" />
<WordsStyle name="KEYWORDS4" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS5" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS6" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS7" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS8" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="OPERATORS" fgColor="3A5841" bgColor="181818" fontStyle="0" nesting="0" />
<WordsStyle name="FOLDER IN CODE1" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="FOLDER IN CODE2" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="FOLDER IN COMMENT" fgColor="408080" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS1" fgColor="00FFFF" bgColor="000000" colorStyle="1" fontName="OCR A Extended" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS2" fgColor="A2D7AF" bgColor="FFFFFF" colorStyle="1" fontStyle="2" nesting="50331652" />
<WordsStyle name="DELIMITERS3" fgColor="84D8E1" bgColor="FFFFFF" colorStyle="1" fontStyle="0" nesting="6" />
<WordsStyle name="DELIMITERS4" fgColor="00FF80" bgColor="FFFFFF" colorStyle="1" fontStyle="0" nesting="2" />
<WordsStyle name="DELIMITERS5" fgColor="CDA2DD" bgColor="FFFFFF" colorStyle="1" fontStyle="0" nesting="8" />
<WordsStyle name="DELIMITERS6" fgColor="67E086" bgColor="FFFFFF" colorStyle="1" fontStyle="0" nesting="50593787" />
<WordsStyle name="DELIMITERS7" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS8" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontStyle="0" nesting="0" />
</Styles>
</UserLang>
</NotepadPlus>

196
glob.lsl Normal file
View File

@ -0,0 +1,196 @@
#ifndef GLOB_MATCH
#define GLOB_MATCH
// ported by rhetorica 2024-06-25
// from https://github.com/torvalds/linux/blob/master/lib/glob.c
// License: Dual MIT/GPL
/**
* glob_match - Shell-style pattern matching, like !fnmatch(pat, str, 0)
* @pat: Shell-style pattern to match, e.g. "*.[ch]".
* @str: String to match. The pattern must match the entire string.
*
* Perform shell-style glob matching, returning true (1) if the match
* succeeds, or false (0) if it fails. Equivalent to !fnmatch(@pat, @str, 0).
*
* Pattern metacharacters are ?, *, [ and \.
* (And, inside character classes, !, - and ].)
*
* This is small and simple implementation intended for device blacklists
* where a string is matched against a number of patterns. Thus, it
* does not preprocess the patterns. It is non-recursive, and run-time
* is at most quadratic: strlen(@str)*strlen(@pat).
*
* An example of the worst case is glob_match("*aaaaa", "aaaaaaaaaa");
* it takes 6 passes over the pattern before matching the string.
*
* Like !fnmatch(@pat, @str, 0) and unlike the shell, this does NOT
* treat / or leading . specially; it isn't actually used for pathnames.
*
* Note that according to glob(7) (and unlike bash), character classes
* are complemented by a leading !; this does not support the regex-style
* [^a-z] syntax.
*
* An opening bracket without a matching close is matched literally.
*/
#ifdef DEBUG_GLOB
integer last_c;
integer last_d;
integer last_str_i;
integer last_pat_i;
#endif
// bool __pure glob_match(char const *pat, char const *str)
integer glob_match(string pat, string str)
{
/*
* Backtrack to previous * on mismatch and retry starting one
* character later in the string. Because * matches all characters
* (no exception for /), it can be easily proved that there's
* never a need to backtrack multiple levels.
*/
// char const *back_pat = NULL, *back_str;
integer back_pat = NOWHERE;
integer back_str;
/*
* Loop over each token (character or class) in pat, matching
* it against the remaining unmatched tail of str. Return false
* on mismatch, or true after matching the trailing nul bytes.
*/
//for (;;) {
integer str_i;
integer pat_i;
while (TRUE) {
// unsigned char c = *str++;
// unsigned char d = *pat++;
integer c = llOrd(str, str_i++);
integer d = llOrd(pat, pat_i++);
#ifdef DEBUG_GLOB
echo((string)c + ", " + (string)d + ", " + (string)str_i + ", " + (string)pat_i);
if(c == last_c && d == last_d && str_i == last_str_i && pat_i == last_pat_i)
return 2 / 0;
#endif
// switch (d) {
// case '?': /* Wildcard: anything but nul */
if(d == 0x3f) { // '?'
if (c == 0x00) // EOL
// return false;
return FALSE;
//break;
jump skip_default;
// case '*': /* Any-length wildcard */
} else if(d == 0x2a) { // '*'
// if (*pat == '\0') /* Optimize trailing * case */
if(llOrd(pat, pat_i) == 0x00)
// return true;
return TRUE;
//back_pat = pat;
back_pat = pat_i;
//back_str = --str; /* Allow zero-length match */
back_str = --str_i;
// break;
jump skip_default;
// case '[': { /* Character class */
} else if(d == 0x5b) { // '['
//bool match = false, inverted = (*pat == '!');
integer match = FALSE;
integer inverted = (llOrd(pat, pat_i) == 0x21); // '!'
//char const *class = pat + inverted;
integer class_i = pat_i + inverted;
//unsigned char a = *class++;
integer a = llOrd(pat, class_i++);
/*
* Iterate over each span in the character class.
* A span is either a single character a, or a
* range a-b. The first span may begin with ']'.
*/
do {
// unsigned char b = a;
integer b = a;
//if (a == '\0') /* Malformed */
if(a == 0x00)
// goto literal;
jump literal; // FIXME
//if (class[0] == '-' && class[1] != ']') {
if(llOrd(pat, class_i + 0) == 0x2d && llOrd(pat, class_i + 1) != 0x5d) {
// b = class[1];
b = llOrd(pat, class_i + 1);
//if (b == '\0')
if(b == 0x00)
// goto literal;
jump literal;
// class += 2;
class_i += 2;
/* Any special action if a > b? */
}
// match |= (a <= c && c <= b);
match = match | (a <= c && c <= b);
// } while ((a = *class++) != ']');
} while((a = llOrd(pat, class_i++)) != 0x5d);
if (match == inverted)
jump backtrack;
// goto backtrack;
//pat = class;
pat_i = class_i;
// } // <-- unnecessary
// break;
jump skip_default;
// case '\\':
} else if(d == 0x5c) { // '\\'
// d = *pat++;
d = llOrd(pat, pat_i++);
// fallthrough;
}
//default: /* Literal character */
//literal:
@literal;
if (c == d) {
//if (d == '\0')
if(d == 0x00)
//return true;
return TRUE;
//break;
jump skip_default;
}
//backtrack:
@backtrack;
//if (c == '\0' || !back_pat)
if(c == 0x00 || !~back_pat)
//return false; /* No point continuing */
return FALSE;
/* Try again from last *, one character later in str. */
//pat = back_pat;
pat_i = back_pat;
//str = ++back_str;
str_i = ++back_str;
//break;
jump skip_default;
//}
@skip_default;
#ifdef DEBUG_GLOB
last_d = d;
last_c = c;
last_str_i = str_i;
last_pat_i = pat_i;
#endif
}
return FALSE; // unreachable
}
#endif // GLOB_MATCH

25
lslisp.h.lsl Normal file
View File

@ -0,0 +1,25 @@
// LSLisp Meta
// Implementation for finding number of occurrences of a substring
// (Note that the inline version can take a string or a list)
#ifdef INLINE_OCCUR
#define occur(haystack, needle) (count(splitnulls((string)(haystack), (needle))) - 1)
#else
integer occur(list haystack, string needle) {
return (count(splitnulls((string)(haystack), (needle))) - 1);
}
#endif
#define tokenize(str) llParseStringKeepNulls(str, [" "], ["(", ")", "\""])
// LSLisp Constants
#ifndef LISP_EXECUTE_FILE
#define LISP_EXECUTE_FILE 860
#endif
#ifndef LISP_EXECUTE_FUNC
#define LISP_EXECUTE_FUNC 861
#endif

954
lslisp.lsl Normal file
View File

@ -0,0 +1,954 @@
/*
LSLisp 0.2
Implementation copyright 2018, Nanite Systems Research
*/
#define INTERPRETER_VERSION "0.2.3"
#define INLINE_OCCUR
// #define COMPANION
#include <utils.lsl>
#include <lslisp.h.lsl>
#ifdef COMPANION
#include <system.lsl>
#include <System/pipes.lsl>
#include <System/tasks.lsl>
#endif
// floats with a decimal place smaller than this value
// will be rounded to the nearest integer:
#define ROUNDING_ERROR 0.00000762939453125
// (~6 bits of mantissa error in an IEEE 32-bit float)
// #define GENERAL_DEBUG
list v_names;
list v_values;
string filename;
key parse_q; // retrieval key
integer parse_n; // line number
list parse_queue; // tokens waiting to be analyzed
string interrupted_frame;
list interrupted_stack;
key interrupt; // increment PC and resume execution when matching value arrives
key user;
#ifdef ARES
key outs;
#endif
/* structure for program frames
[
'c':json - parsed symbol tree (SF)
'i':json array - program counter (S)
'p':integer - stack location of previous stack frame (S)
'sp':integer - stack location of previous verb symbol (SV)
'v':json {v_names:v_values} - variables in this scope (S)
'r':integer - one of:
0: return value and backtrack (S)
1: conditional (used in (if) and (while)) (S)
't':integer - one of:
0: stack frame (S)
1: verb frame (V)
2: function frame (F)
's':string - symbol for verb name (V)
'a':string - space-separated arglist (F)
]
*/
#define stack_frame(code_json, ixx, parent_stack_index, vars, retmode, last_symbol) ( \
"{\"c\":" + code_json \
+ ",\"i\":" + list2js(JSON_ARRAY, ixx) \
+ ",\"sp\":" + (string)last_symbol \
+ ",\"p\":" + (string)parent_stack_index \
+ ",\"v\":" + vars \
+ ",\"r\":" + (string)retmode \
+ ",\"t\":0}" \
)
#define verb_frame(symbol, last_symbol) ( \
"{\"s\":\"" + symbol + "\"" \
+ ",\"t\":1,\"sp\":" + (string)last_symbol \
+ "}")
#define func_frame(arg_list, code_json) ( \
"{\"c\":" + code_json + "" \
+ ",\"t\":2,\"a\":\"" + (string)arg_list + "\"}" \
)
list execute(list stack) {
string prog;
list ix;
integer parent;
string local_vars;
integer return_mode;
integer last_symbol;
{
string frame = gets(stack, LAST);
stack = delitem(stack, LAST);
prog = getjs(frame, (list)"c");
ix = js2list(getjs(frame, (list)"i"));
parent = (integer)getjs(frame, (list)"p");
local_vars = getjs(frame, (list)"v");
return_mode = (integer)getjs(frame, (list)"r");
last_symbol = (integer)getjs(frame, (list)"sp");
}
// resuming interrupted code:
if(interrupt) {
interrupted_frame = "";
interrupted_stack = [];
interrupt = "";
}
// no program:
if(prog == "")
return stack;
// the next token will be the head of a list:
integer descending = FALSE;
// we just ended a parenthesized expression:
integer backtrack = FALSE;
if(ix == []) {
ix = (list)NOWHERE; // start at -1 since we begin with incrementing this
descending = TRUE;
}
while(TRUE) {
@next;
// llOwnerSay("Stack: " + concat(stack, "; "));
// step forward:
ix = alter(ix, [1 + geti(ix, LAST)], LAST, LAST);
// integer pcount = 0;
if(jstype(prog, ix) == JSON_INVALID) { // no successor node
if(count(ix) == 1) { // EOF
if(parent == NOWHERE) {
//echo(">> Return to nowhere. Done.");
return stack;
} else {
//echo(">> Return to " + (string)parent);
integer previous_return_mode = return_mode;
string frame = gets(stack, parent);
if(substr(frame, 0, 0) != "{") {
echo(">> No frame at " + (string)parent + " in: " + list2js(JSON_ARRAY, stack));
return [];
}
stack = delitem(stack, parent);
prog = getjs(frame, (list)"c");
ix = js2list(getjs(frame, (list)"i"));
parent = (integer)getjs(frame, (list)"p");
local_vars = getjs(frame, (list)"v");
return_mode = (integer)getjs(frame, (list)"r");
last_symbol = (integer)getjs(frame, (list)"sp");
#ifdef GENERAL_DEBUG
if(~last_symbol)
echo(">> Return from subframe; last symbol was " + gets(stack, last_symbol));
else
echo(">> Return from subframe; no last symbol");
#endif
string structure = getjs(prog, ix);
list step_again = ix;
if(previous_return_mode == 1) { // leaving a conditional expression
integer retval = (integer)gets(stack, LAST);
// echo(">> " + structure + " conditional results: rv=" + (string)retval + "; ix=" + concat(ix, "."));
stack = delitem(stack, LAST);
//frame = stack_frame(prog, ix, parent, local_vars, return_mode);
ix = delitem(ix, LAST);
//list step_forward = ix; // alter(ix, [1 + geti(ix, LAST)], LAST, LAST);
if(structure == "if") { // entering 'if' or 'else' body
string next_frame = setjs(frame, (list)"i", list2js(JSON_ARRAY, ix));
list subix = ix;
if(retval) {
subix += 2;
} else if(jstype(prog, ix + 3) == JSON_ARRAY) { // else branch exists; activate
subix += 3;
} else { // else 'else' branch doesn't exist; carry on
// ix = step_forward;
// proceed to increment
jump next;
}
prog = "[" + getjs(prog, subix) + "]";
stack += next_frame;
return_mode = 0;
} else if(structure == "while") { // entering 'while' body
if(retval) {
// echo(">> ENTERING A WHILE BODY; ix IS " + concat(ix, "."));
stack += frame;
prog = "[" + getjs(prog, ix + 2) + "]";
descending = TRUE;
return_mode = 2;
//echo(">> stack: " + list2js(JSON_ARRAY, stack));
} else {
// echo(">> NOT ENTERING A WHILE BODY; TEST FAILED");
// ix = step_forward;
// proceed to increment
jump next;
}
} /*else {
echo(">>#12: " + structure);
}*/
// init new stack frame for descent
local_vars = "{}";
parent = count(stack) - 1;
last_symbol = NOWHERE;
ix = [NOWHERE];
// echo(">> Initialized new stack frame; " + (string)parent + " is now address of parent");
} else if(previous_return_mode == 2) { // leaving a 'while' body; evaluate conditional again
// echo(">> LEAVING A WHILE BODY; ix IS " + concat(ix, "."));
stack += frame; //stack_frame(prog, ix, parent, local_vars, return_mode);
prog = "[" + getjs(prog, alter(step_again, (list)1, LAST, LAST)) + "]";
// echo(">> RE-CHECKING CONDITION " + (string)prog);
// echo(">> stack after re-check frame is added: " + list2js(JSON_ARRAY, stack));
return_mode = 1;
local_vars = "{}";
parent = count(stack) - 1;
ix = [NOWHERE];
last_symbol = NOWHERE;
descending = TRUE;
} /*else {
echo(">>#11:" + structure);
}*/
jump next;
}
}
backtrack = TRUE;
// pcount = geti(ix, LAST);
/*if(jstype(prog, ix) == JSON_ARRAY) {
echo(">> Empty backtrack warning.");
// ++pcount;
}*/
ix = delitem(ix, LAST);
// ix = alter(ix, (list)(1 + geti(ix, -2)), LAST, LAST); // back out
} else while(jstype(prog, ix) == JSON_ARRAY) { // at a list, so go in
// echo(">> Engage descent");
ix += 0;
/*if(jstype(prog, ix) == JSON_ARRAY) { // THIS is the implicit progn
echo(">> Empty descent!");
// stack += JSON_NULL;
}*/
descending = TRUE;
}
string type = jstype(prog, ix);
// we have an entry point; continue
// parse should be in depth-first *exit*, so (9 (8 (5 (3 1 2) 4) 6 7)) (13 (12 10 11))
if(type == JSON_NUMBER) {
// echo(">> Number");
stack += (float)getjs(prog, ix);
} else if(descending) {
string verb = getjs(prog, ix);
#ifdef GENERAL_DEBUG
echo(">> verb: " + verb);
#endif
if(verb == "if" || verb == "while") {
/*
ix = delitem(ix, LAST);
string condition = "[" + getjs(prog, ix + [1]) + "]";
string body = "[" + getjs(prog, ix + [2]) + "]";
string alternative = "[" + getjs(prog, ix + [3]) + "]";
// construct frame, put on stack and replace state with conditional
list retval = execute([stack_frame(condition, [], TODO:parent, "{}", 1)]);
// move to retmode evaluation within EOF backtracking case
if(geti(retval, 0)) {
stack += execute(stack_frame(body, [], TODO:parent, "{}", 0));
} else if(alternative != JSON_INVALID) {
stack += execute(stack_frame(alternative, [], TODO:parent, "{}", 0));
}
descending = FALSE;
*/
stack += stack_frame(prog, ix, parent, local_vars, return_mode, last_symbol); // push old state
// set up conditional frame:
prog = "[" + getjs(prog, delitem(ix, LAST) + 1) + "]";
return_mode = 1;
local_vars = "{}";
parent = count(stack) - 1;
ix = (list)NOWHERE;
last_symbol = NOWHERE;
// entering conditional structure:
descending = TRUE;
// do not backtrack:
jump next;
} else if(verb == "lambda") {
ix = delitem(ix, LAST);
string arglist = concat(js2list(getjs(prog, ix + 1)), " ");
string code = getjs(prog, ix + 2);
descending = backtrack = FALSE;
stack += func_frame(arglist, code);
jump next;
} else {
stack += verb_frame(verb, last_symbol);
last_symbol = count(stack) - 1;
// echo(">> (descend verb " + verb + " is non-structural)");
}
} else if(type == JSON_STRING) {
// variable name or other literal:
string s = getjs(prog, ix);
if(s == "\\\"\\\"") {
stack += "";
} else if(substr(s, 0, 1) == "\\\"") { // list2js eats quotes
stack += substr(s, 2, (integer)-3);
// stack += s;
} else if(substr(s, 0, 0) == "$") {
string name = delstring(s, 0, 0);
string local_type = jstype(local_vars, (list)name);
integer temp_parent = parent;
#ifdef GENERAL_DEBUG
echo(">> searching for var " + name + " in stack (p=" + (string)parent + "):");
integer sti = count(stack);
while(sti--)
echo("/me .. [" + (string)sti + "] " + gets(stack, sti));
#endif
string value;
if(local_type != JSON_INVALID) {
value = getjs(local_vars, (list)name);
} else while(local_type == JSON_INVALID) {
#ifdef GENERAL_DEBUG
echo(">> get looking for " + name + " in " + (string)temp_parent);
#endif
if(~temp_parent) {
string fr = gets(stack, temp_parent);
local_type = jstype(fr, ["v", name]);
value = getjs(fr, ["v", name]);
#ifdef GENERAL_DEBUG
if(local_type != JSON_INVALID)
echo(">> get found " + name + " = " + value + " in " + (string)temp_parent);
#endif
temp_parent = (integer)getjs(fr, (list)"p");
} else {
integer si = index(v_names, name);
if(~si) {
integer global_type = llGetListEntryType(v_values, si);
if(global_type == TYPE_FLOAT)
stack += getf(v_values, si);
else if(global_type == TYPE_INTEGER)
stack += geti(v_values, si);
else
stack += gets(v_values, si);
// stack += value;
jump next;
} else {
// value = "?uninitialized?";
echo(">> uninitialized variable: " + s);
return [];
}
}
}
if(local_type == JSON_NUMBER) {
if(llFabs((float)value - (float)( (integer)((float)value + 0.5) )) < ROUNDING_ERROR)
stack += (integer)value;
else
stack += (float)value;
} else {
stack += value;
}
/*} else if(s == "") {
echo(">> Literal null string");*/
} else { // no idea; transmit literally
// echo(">> Literal oddity " + s);
stack += s;
}
} else if(backtrack) { // we are leaving; pop stack
// integer pcount = geti(ix, LAST);
/*
if(!~last_symbol) {
echo(">>attempted to dereference void symbol");
return stack;
}
*/
string verbf = gets(stack, last_symbol);
string verb = getjs(verbf, (list)"s");
if(verb == "") {
last_symbol = (integer)getjs(verbf, (list)"sp");
backtrack = descending = FALSE;
jump next;
}
integer c;
list params;
{
integer scount = count(stack) - last_symbol;
c = scount - 1;
if(c)
params = sublist(stack, last_symbol + 1, LAST);
}
#ifdef GENERAL_DEBUG
echo(">> " + verbf + " execute: (" + verb + " " + concat(params, " ") + "), c=" + (string)c);
#endif
stack = delrange(stack, last_symbol, LAST);
last_symbol = (integer)getjs(verbf, (list)"sp");
/*
echo(">> Backtrack verb: " + verb);
if(~parent)
echo(">> Stack: " + concat(alter(stack, ["~"], parent, parent), "; "));
else
echo(">> Stack: " + concat(stack, "; "));*/
if(verb == "list") {
stack += list2js(JSON_ARRAY, params);
} else if(verb == "print") {
#ifdef COMPANION
pipe_send((string)(params), user);
#elif defined(ARES)
print(outs, user, (string)params);
#else
echo((string)params);
#endif
} else if(verb == "global") {
string name = gets(params, 0);
string data;
if(c > 2)
data = list2js(JSON_ARRAY, sublist(params, 1, LAST));
else if(c == 2)
data = gets(params, 1);
integer si = index(v_names, name);
if(~si) {
v_values = alter(v_values, [data], si, si);
} else {
v_names += name;
v_values += data;
}
} else if(verb == "set") {
if(c < 2) {
echo(">> unary set.");
return [];
}
string name = gets(params, 0);
list data = sublist(params, 1, LAST);
string local_type = jstype(local_vars, (list)name);
integer old_parent = NOWHERE;
integer temp_parent = parent;
string fr;
while(local_type == JSON_INVALID) {
#ifdef GENERAL_DEBUG
echo(">> set looking for " + name + " in " + (string)temp_parent);
echo(">> stack: " + list2js(JSON_ARRAY, stack));
#endif
if(~temp_parent) {
fr = gets(stack, temp_parent);
local_type = jstype(fr, ["v", name]);
old_parent = temp_parent;
temp_parent = (integer)getjs(fr, (list)"p");
#ifdef GENERAL_DEBUG
if(local_type != JSON_INVALID)
echo(">> set storing " + list2js(JSON_ARRAY, data) + " in frame " + (string)old_parent + " as " + name);
#endif
} else {
integer si = index(v_names, name);
if(~si) {
#ifdef GENERAL_DEBUG
echo(">> set storing " + list2js(JSON_ARRAY, data) + " in globals as " + name);
#endif
v_values = alter(v_values, data, si, si);
} else {
local_vars = setjs(local_vars, (list)name, (string)data);
#ifdef GENERAL_DEBUG
echo(">> set creating " + list2js(JSON_ARRAY, data) + " in locals as " + name);
#endif
}
jump done_set;
}
}
if(~old_parent)
stack = alter(stack, (list)setjs(fr, ["v", name], (string)data), old_parent, old_parent);
else
local_vars = setjs(local_vars, (list)name, (string)data);
@done_set;
// } else if(verb == "sum" || verb == "+" || verb == "add") {
} else if(verb == "+") {
float out = 0;
while(c--)
out += getf(params, c);
stack += out;
//stack += llListStatistics(LIST_STAT_SUM, params);
//} else if(verb == "product" || verb == "*") {
} else if(verb == "*") {
float out = 1;
while(c--)
out *= getf(params, c);
stack += out;
//} else if(verb == "eq" || verb == "==") {
} else if(verb == "==") {
if(etype(params, 0) == TYPE_STRING)
stack += gets(params, 0) == gets(params, 1);
else if(etype(params, 0) == TYPE_INTEGER)
stack += geti(params, 0) == geti(params, 1);
else
stack += getf(params, 0) == getf(params, 1);
// } else if(verb == "ne" || verb == "!=") {
} else if(verb == "!=") {
if(etype(params, 0) == TYPE_STRING)
stack += gets(params, 0) != gets(params, 1);
else if(etype(params, 0) == TYPE_INTEGER)
stack += geti(params, 0) ^ geti(params, 1);
else
stack += getf(params, 0) != getf(params, 1);
// } else if(verb == "lt" || verb == "<") {
} else if(verb == "<") {
stack += getf(params, 0) < getf(params, 1);
//} else if(verb == "gt" || verb == ">") {
} else if(verb == ">") {
stack += getf(params, 0) > getf(params, 1);
//} else if(verb == "lte" || verb == "<=") {
} else if(verb == "<=") {
stack += getf(params, 0) <= getf(params, 1);
//} else if(verb == "gte" || verb == ">=") {
} else if(verb == ">=") {
stack += getf(params, 0) >= getf(params, 1);
} else if(verb == "and") {
integer out = 1;
while(c--)
out = out & geti(params, c);
stack += out;
} else if(verb == "or") {
integer out = 0;
while(c--)
out = out | geti(params, c);
stack += out;
} else if(verb == "not" || verb == "!") {
stack += !geti(params, 0);
//} else if(verb == "-" || verb == "neg" || verb == "sub") {
} else if(verb == "-") {
if(c > 1) {
float out = getf(params, 0);
--c;
while(c--) {
out -= getf(params, c + 1);
}
stack += out;
} else {
stack += -getf(params, 0);
}
//} else if(verb == "/" || verb == "div") {
} else if(verb == "/") {
if(c > 1) {
float out = getf(params, 0);
--c;
while(c--) {
float newv = getf(params, c + 1);
if(newv != 0)
out /= newv;
else {
echo(">> Division by zero.");
return [];
}
}
stack += out;
} else {
float out = getf(params, 0);
if(out != 0)
stack += (1.0 / out);
else {
echo(">> Division by zero.");
// stack += 0;
return [];
}
}
} else if(verb == "apply") {
stack += stack_frame(prog, ix, parent, local_vars, return_mode, last_symbol);
local_vars = "{}";
ix = [NOWHERE];
parent = count(stack) - 1;
{
string func = gets(params, 0);
list arg_names = split(getjs(func, (list)"a"), " ");
// bind args:
integer an = count(arg_names);
while(an--) {
local_vars = setjs(local_vars, (list)gets(arg_names, an), gets(params, 1 + an));
}
prog = "[" + getjs(func, (list)"c") + "]";
#ifdef GENERAL_DEBUG
echo(">> applying function " + prog);
#endif
}
descending = backtrack = FALSE;
jump next;
} else if(verb == "concat") {
stack += concat(js2list(gets(params, 1)), gets(params, 0));
} else if(verb == "split") {
stack += list2js(JSON_ARRAY, split(gets(params, 1), gets(params, 0)));
} else if(verb == "get") {
stack += getjs(gets(params, 0), js2list(gets(params, 1)));
} else if(verb == "index") {
stack += index(js2list(gets(params, 0)), gets(params, 1));
} else if(verb == "count") {
stack += count(js2list(gets(params, 0)));
} else if(verb == "put") {
#ifdef GENERAL_DEBUG
echo("Putting '" + gets(params, 2) + "' into '" + gets(params, 0) + "' at position: " + gets(params, 1));
#endif
stack += setjs(gets(params, 0), js2list(gets(params, 1)), gets(params, 2));
} else if(verb == "char") {
integer p = geti(params, 1);
stack += substr(gets(params, 0), p, p);
} else if(verb == "substr") {
stack += substr(gets(params, 0), geti(params, 1), geti(params, 2));
} else if(verb == "strpos") {
stack += strpos(gets(params, 0), gets(params, 1));
} else if(verb == "rand") {
stack += llFrand(1);
} else if(verb == "int") {
stack += (integer)gets(params, 0);
#ifdef ARES
} else if(verb == "getd") {
stack += getdb(gets(params, 0), gets(params, 1));
} else if(verb == "setd") {
setdb(gets(params, 0), gets(params, 1), gets(params, 2));
} else if(verb == "deleted") {
deletedb(gets(params, 0), gets(params, 1));
} else if(substr(verb, 0, 0) == "@") {
invoke(delstring(verb, 0, 0) + " " + (string)params, outs, NULL_KEY, user);
}
#endif
#ifdef COMPANION
} else if(verb == "external") {
linked(LINK_ROOT, geti(params, 0), gets(params, 1), interrupt = llGenerateKey());
// llSleep(0.25); // prevent self-activation
interrupted_stack = stack;
interrupted_frame = stack_frame(prog, ix, parent, local_vars, return_mode, last_symbol);
// interrupted_program = prog;
// interrupted_ix = ix;
// echo("Paused execution.");
return [];
} else if(substr(verb, 0, 0) == "@") {
start_task(EXECUTE, delstring(verb, 0, 0) + " " + (string)params, user);
} else if(substr(verb, 0, 0) == "#") {
start_task((integer)delstring(verb, 0, 0), (string)params, user);
}
#else
else if(substr(verb, 0, 0) == "#") {
llMessageLinked(LINK_THIS, (integer)delstring(verb, 0, 0), (string)params, user);
}
#endif
else if(verb == JSON_INVALID) { // nil
stack += "[]";
} else if(verb == JSON_NULL) { // progn
// echo(">> Null backtrack verb; params: " + concat(params, ", "));
if(c)
stack += sublist(params, LAST, LAST);
} else {
echo(">> unknown verb: " + verb);
}
} else {
echo(">> unknown data type: " + type);
}
// result += getjs(prog, ix);
descending = backtrack = FALSE;
// echo(concat(ix, ">"));
}
// echo("Finished.");
return stack;
}
string parse(list pq) {
// match parens
list opens; // indices of open parens
integer tcount = count(pq);
integer open_quote = NOWHERE;
integer comment_start = NOWHERE;
integer i;
for(; i < tcount; ++i) {
string t = gets(pq, i);
if(~comment_start) {
if(t == "\n") {
pq = delrange(pq, comment_start, i);
tcount -= (i - comment_start + 1);
i = comment_start;
comment_start = NOWHERE;
}
} else if(~open_quote) {
if(t == "\"") {
string str;
if(open_quote + 1 == i)
str = "";
else
str = "\\\"" + concat(sublist(pq, open_quote + 1, i - 1), " ") + "\\\"";
pq = alter(pq, [str], open_quote, i);
i = open_quote;
open_quote = NOWHERE;
}
} else {
if(t == "") {
pq = delitem(pq, i);
--i;
--tcount;
// --i can't go in macro args; gets executed twice!
} else if(t == "\"") {
open_quote = i;
} else if(t == "(") {
opens += i;
} else if(t == ";") {
comment_start = i;
} else if(t == "\n") {
pq = delitem(pq, i);
--i;
--tcount;
} else if(t == ")") {
if(count(opens) == 0) {
echo("++ Excess closing parens at " + (string)i + ".");
return "";
}
integer L = geti(opens, LAST);
opens = delitem(opens, LAST);
string frag;
if(L + 1 == i)
frag = "[]";
else
frag = list2js(JSON_ARRAY, sublist(pq, L + 1, i - 1));
if(jstype(frag, [0]) == JSON_ARRAY)
frag = list2js(JSON_ARRAY, [JSON_NULL] + sublist(pq, L + 1, i - 1));
pq = alter(pq, [frag], L, i);
tcount = count(pq);
i = L;
}
}
}
if(~open_quote) {
echo("++ unclosed quote following: " + concat(sublist(pq, open_quote - 2, open_quote + 4), " "));
return "";
} else if(opens != []) {
echo("++ imbalanced parens (" + (string)count(opens) + ").");
return "";
} else {
// return json tree:
/* #ifdef GENERAL_DEBUG
echo("++ Parse results: " + list2js(JSON_ARRAY, pq));
#endif */
// return parsed;
return list2js(JSON_ARRAY, pq);
}
}
#ifndef ARES
default {
state_entry() {
#ifdef COMPANION
list messages = [LISP_EXECUTE_FILE, LISP_EXECUTE_FUNC];
list commands = ["lisp", "eval"];
set_identity(MT_SERVICE, "interpreter for LSLisp", messages, commands);
send_identity();
#else
echo((string)(llGetMemoryLimit() - llGetUsedMemory()) + " bytes available.");
#endif
//linked(LINK_THIS, LISP_EXECUTE_FILE, "ls_test", llGetOwner());
}
dataserver(key q, string m) {
if(q == parse_q) {
// llOwnerSay(".");
if(m == EOF) {
// llOwnerSay("-- Executing...");
string prog = parse(parse_queue);
parse_queue = [];
list r = execute([stack_frame(prog, [], NOWHERE, "{}", 0, NOWHERE)]);
if(interrupt) {
// llOwnerSay("-- Interrupted!");
} else if(r != []) {
pipe_send(concat(r, " "), user);
}
} else {
parse_queue += ["\n"] + tokenize(m);
parse_q = llGetNotecardLine(filename, ++parse_n);
}
}
}
link_message(integer src, integer n, string m, key id) {
#ifdef COMPANION
// llOwnerSay("Incoming message from " + (string)src + ": " + (string)n + "; " + (string)m + "; " + string(id));
if(n == EXECUTE || n == COMMAND) {
list argv = split(m, " ");
string cmd = gets(argv, 0);
integer argc = count(argv);
if(cmd == "lisp") {
if(argc == 1 || gets(argv, 1) == "help") {
list file_list;
integer fi = llGetInventoryNumber(INVENTORY_NOTECARD);
while(fi--) {
string nc = llGetInventoryName(INVENTORY_NOTECARD, fi);
if(substr(nc, 0, 2) == "ls_")
file_list = substr(nc, 3, LAST) + file_list;
}
tell(id, 0, "lisp <filename>\n\nExecutes the notecard ls_<filename> using the LSLisp interpreter. See http://develop.nanite-systems.com/lisp for more information. Available cards: " + concat(file_list, ", "));
} else {
start_task(LISP_EXECUTE_FILE, "ls_" + concat(sublist(argv, 1, LAST), " "), id);
}
} else if(cmd == "eval") {
if(argc == 1 || gets(argv, 1) == "help") {
tell(id, 0, "eval <function>\n\nExecutes the provided source code directly using the LSLisp interpreter. See http://develop.nanite-systems.com/lisp for more information.");
} else {
start_task(LISP_EXECUTE_FUNC, concat(sublist(argv, 1, LAST), " "), id);
}
}
} else if(n == PROBE_MODULES && (m == "" || m == llGetScriptName())) {
send_identity();
} else
#endif
if(n == LISP_EXECUTE_FILE) {
if(id)
user = id;
else
user = llGetOwner();
// program = "";
parse_queue = v_names = v_values = interrupted_stack = [];
interrupt = "";
// llOwnerSay("-- Loading...");
parse_q = llGetNotecardLine(filename = m, parse_n = 0);
} else if(n == LISP_EXECUTE_FUNC) {
if(id)
user = id;
else
user = llGetOwner();
// llOwnerSay("-- Evaluating...");
list r = execute([stack_frame(parse(tokenize(m)), [], NOWHERE, "{}", 0, NOWHERE)]);
if(interrupt) {
// llOwnerSay("-- Interrupted!");
} else if(r != []) {
pipe_send(concat(r, " "), user);
}
} else if(interrupt) {
if(id == interrupt) {
interrupted_stack += m;
// interrupt = ""; - now cleared within execute()
list r = execute(interrupted_stack + interrupted_frame);
if(interrupt) {
// llOwnerSay("-- Interrupted!");
} else if(r != []) {
pipe_send(concat(r, " "), user);
}
}
}
}
}
#endif // ARES

68
objects.lsl Normal file
View File

@ -0,0 +1,68 @@
#ifndef _OBJECTS_LSL_
#define _OBJECTS_LSL_
/*
***********************************************************
* *
* NANITE SYSTEMS ADVANCED TACTICAL OPERATING SYSTEM *
* *
* OBJECTS COMPONENT *
* *
* Copyright, (C) Nanite Systems Corp., 2021 *
* *
***********************************************************
THE SOURCE CODE FOR THIS LIBRARY COMPONENT IS PROVIDED TO
NANITE SYSTEMS CUSTOMERS FOR DEVELOPMENT OF COMPATIBLE
SOFTWARE.
BY USING THIS CODE, YOU AGREE NOT TO MODIFY OR DISTRIBUTE
IT FOR COMMERCIAL GAIN, EXCEPT IN A COMPILED PRODUCT. YOU
MAY PROVIDE THE SOURCE CODE OF THIS FILE FOR FREE OR
INCLUDED WITH YOUR OWN DEVELOPMENT PACKAGE AS LONG AS THIS
NOTICE IS INCLUDED AND THE REST OF THE FILE IS UNMODIFIED.
INTERACTIONS WITH OTHER COMPONENTS OF THE OPERATING SYSTEM
ARE SUBJECT TO THE APPROPRIATE END-USER LICENSE AGREEMENT
(EULA).
IDV: F0960303D0002
VENDOR DUNS: 005128988
*/
#ifndef ZR
#define ZR ZERO_ROTATION
#endif
#ifndef ZV
#define ZV ZERO_VECTOR
#endif
#ifndef ONES
#define ONES <1, 1, 1>
#endif
#ifndef setp
#define setp llSetLinkPrimitiveParamsFast
#endif
#ifndef getp
#define getp llGetLinkPrimitiveParams
#endif
#ifndef geto
#define geto llGetObjectDetails
#endif
#ifndef object_pos
#define object_pos(__object) llList2Vector(llGetObjectDetails(__object, [OBJECT_POS]), 0)
#endif
#define OVER_1024 0.0009765625
#define OVER_512 0.001953125
#define OVER_256 0.00390625
#endif // _OBJECTS_LSL_

468
protocols/caps protocol.txt Normal file
View File

@ -0,0 +1,468 @@
A. Lazuli
Nanite Systems Communications Corporation (NANOCOM)
April 2024
Signals Protocol Working Group
PUBLIC MEMO
VERSION 0.4
Status of this Memo
This document specifies a Company-Wide Best Current Practices (CWBCP) for the Nanite Systems Corporate Community, and requests discussion and suggestions for improvements. Distribution of this memo is unlimited.
Copyright Notice
Copyright (C) Nanite Systems Communications Corporation (2024). All Rights Reserved.
Abstract
This document describes a protocol for use in querying and reporting the capabilities and functions provided by devices in Second Life. It includes the HW-CAPS and AV-CAPS subprotocols, which respectively cover individual machinery and available user interfaces.
1. Introduction
1.1. HW-CAPS: IoT for SL
The ORIX (Open Robot Interface) protocol [ORIX API] was designed by Chelton & Brattle in 2014 as a successor to the partly public Autonomy Control Systems (ACS) Central Control Unit (CCU) protocol. Its intended purpose is to provide a standard for electronic communications between attached devices and equipment such as the ACS CCU or NS robot controllers. Since that time, ORIX has only seen limited success, being used only to provide ping messages for the ACS Hub's directory services and the scan feature in the NS ARemote. It is unclear how many manufacturers adopted ORIX as a standard for these ping messages. ARES, Companion, and presumably the ACS ArtCore are known to be compatible, but ACS's flagship CCU product, which has not seen an update since the ORIX protocol was announced, is most likely still incompatible.
A decade onward, Autonomy Control Systems has a much smaller install base than Nanite Systems (an estimated 968 vs 5253 registered users, or 18%). It seems to have abandoned the ORIX project, and is mostly senescent. Without any install base of devices that actually use the ORIX protocol for more than pinging, and the conspicuous incompleteness of the available specification, the upfront costs of actually adopting ORIX for device-controller communications are prohibitive, and unlikely to provide any benefit over the de facto standard usage of the NS Light Bus for routine functions. (Note that two recent manufacturers [KOR] [OeM] advertise NS Light Bus compatibility.)
Nevertheless, ORIX contains the seeds of some very good concepts that have potential use beyond robot-device interfaces. There is currently no method in Second Life for programmatically identifying objects in terms of their function or protocols; SL has consistently been a community of closed vendors and jealously guarded APIs, even though the rest of the software industry has largely moved on to open standards. Hopefully by laying the seeds of a better approach, NS can light the way to a more open model, or at the very least provide a richer experience for our third-party developers.
HW-CAPS (Hardware Capabilities) is a standard (and reference implementation) for a device identification system. It focuses on providing the information that an end user might expect to receive from a port scanning utility in a cyberpunk setting. The protocol and implementation are both designed to be minimalistic, while still producing highly legible output to support discovery through reverse engineering.
1.2. AV-CAPS: A HUD Provisioning System
The Experiences API [SL R1365], introduced in 2014, includes among its facilities a mechanism for automatically providing attachments for immersive gameplay. This is clunky for several reasons. The attachment needs to be rezzed by some linkset already in the Experience, which is an error-prone process; if the attachment has any stored data about the avatar it wants to preserve, this must be done using an external source, which might be prone to bitrot; and perhaps most concerningly, the Experience's availability is subject to the continued payment of a Premium account subscription, which is an inherently tenuous proposition.
These requirements were manageable when Experiences were limited to land-scope only, as the onus fell onto the parcel owner to ensure that rezzable land was available to any attachment provisioning appliance, but with the recent (2023/24) elevation of all Experiences to grid-wide status, the inconvenience of wanting to provide user interfaces is much more poignant.
Interestingly, the somewhat older RestrainedLove system [RLV API] implemented in most third-party Second Life viewers offers the technical platform to build a more robust system. The RLV ecosystem has long provided the SharedWear system, which provides for the transfer of inventory items from one object to the avatar, and automatically attaching said items, without the intermediate step of rezzing them. Moreover it has no associated upkeep costs and is therefore much less likely to fail in the case of ownership turnover.
AV-CAPS (Avatar Capabilities) is a standard (and reference implementation) for an attachment management system that automatically deploys and attaches interfaces to the avatar at the request of compatible equipment. Like HW-CAPS, it is lightweight, stateless, durable, and uses self-documenting message formats that are easy to decipher.
1.3. Terminology and Notation
The "client" is an object or avatar that wishes to utilize CAPS functionality, or access information regarding said functionality.
The "CAPS host" is an object or avatar that provides CAPS functionality.
The "host operator" is the avatar that has control of the CAPS host.
The "inquiring user" is the avatar that has control of the client.
A "device" is any scripted object that performs channel-based communications.
Words in <angle brackets> are variables; the angle brackets should be removed during substitution. For example, <c> in
info <c>
should be replaced with a channel number; using the channel number 1234, the resultant message is
info 1234
and not,
info <1234>
Except where otherwise noted, terms in [square brackets] are optional. Spaces surrounding terms in square brackets are only required if the terms are included. For example, "a [b] c" means either "a b c" or "a c", but not "a c".
Finally, the keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119].
2. Channels
All query messages must be sent on channel 411 to be processed by the CAPS host. Each query message specifies a channel, on which the CAPS host must send its reply, if any.
If the operator does not wish to expose CAPS abilities to a specific client, the CAPS host may ignore incoming messages from that object without any response, or prompt the host operator to intercede. Inquiring clients must consider 15 seconds of silence to be a negative response, and after that time should abolish any temporary listeners that are waiting for a reply.
3. Queries
3.1. INFO
CAPS sessions begin with the message:
info <c>
sent on the query channel, where <c> is the channel number on which the host should respond.
3.2. CONNECT
If the host returns an AV-CAPS signal (see 4.1. AVC), then the client may follow up with:
connect <service> <c>
sent on the query channel, where <c> is the channel number on which the host should respond, and <service> is one of the services offered by the host operator (see 5.2. AV-CAPS Services). The host should activate the service if possible and then reply with a "ready" or "error" message (see 4.3. READY and 4.4. ERROR).
4. Responses
A single CAPS host may send both the "hwc" and "avc" messages in response to an "info" query.
4.1. HWC
In response to the "info" message (see 3.1. INFO), the CAPS host should return the following message if it supports HW-CAPS:
hwc {"vendor":"<vendor>","version":"<version>","purpose":"<purpose>","channel":<channels>,"private":<privacy>,"busy":<business>,"usable":<usability>,"health":<healthiness>,"info":"<URL>"}
This message should be sent on the reply channel, which was provided by the client (see 3.1. INFO) previously. It may be sent up to 15 seconds after the initial request, providing the host operator time to manually reject the inquiry if necessary.
The parameters in this message are as follows:
<vendor>: A short string identifying the name of the company or person responsible for manufacturing the CAPS host.
<version>: The version number of the CAPS host.
<purpose>: A single word identifying the CAPS host's primary function. See Appendix A for a list of defined Purposes. In certain cases multiple purposes may be required, e.g. both hydro and motor to describe a pump; these should be separated by a comma, e.g. "hydro,motor".
<channels>: A JSON object listing channels supported by the CAPS host. Entries must be sorted by name in ascending order using a UTF-8 binary collation. Protocol names should be in lower case with a minimum of punctuation. See Appendix B for a list of well-documented protocols. For example, ARES baseband returns
{"acs":360,"arena":-9990009,"caps":411,"command":1,"lights":-237433740,"orix":-15180925,"public":-9999999,"public-alt":-9999998,"stargate":-900000,"trigger":0,"weather":-78838783}
The braces are required.
<privacy>: 1 if not all avatars may access the primary functions of the host, otherwise 0.
<business>: 1 if new attempts to access the host would be rejected due to capacity limits, otherwise 0.
<usable>: 1 if the host is in working order and powered on, otherwise 0.
<healthiness>: 1.00 if the host is undamaged, otherwise a value in the range [0.00,1.00) reflecting the level of damage.
<URL>: a uniform resource indicator where primary documentation about the host can be obtained.
4.2. AVC
In response to the "info" message (see 3.1. INFO), the CAPS host should return the following message if it supports AV-CAPS:
avc {"platform":"<platform>","version":"<version>","vendor":"<vendor>","service":<services>}
This message should be sent on the reply channel, which was provided by the client (see 3.1. INFO) previously. It may be sent up to 15 seconds after the initial request, providing the host operator time to manually reject the inquiry if necessary.
The parameters in this message are as follows:
<platform>: The name of the product providing AV-CAPS functionality, which might differ from its object name; for example, "ARES"
<version>: As defined in 4.1. HVC.
<vendor>: As defined in 4.1. HVC.
<services>: A JSON object providing names and version numbers for services defined in Appendix C. Each name and version number is wrapped in quotation marks, and joined by a colon. Separate services are separated by commas. For example, a CAPS system loaded to provide HUDware and Facet services might return: {"HUDware":"1.0","Facet":"1.0"} (version 1.0 of HUDware is available, and version 1.0 of Facet is available.)
4.3. READY
To acknowledge a successful "connect" solicitation (defined in 3.2. CONNECT) the AV-CAPS host should respond with:
ready <service> [<uuid> <channel>]
on the reply channel (as provided by the client in the "connect" message), where <service> is the name of the service solicited, and <uuid> is the UUID of the activated service.
In general the onus falls on the client to know the channel in advance and to discover the correct UUID. The CAPS host may specify these fields if the service is CAPS-aware and provided a suitable "service" message (see 6.1. SERVICE).
4.4. ERROR
To acknowledge a failed "connect" solicitation (defined in 3.2. CONNECT) the AV-CAPS host should respond with:
error <service> <reason>
Where <service> is the name of the service solicited, and <reason> is a word indicating why the connection failed, chosen from one of the codes in section 4.4.1.
4.4.1. Error Codes
- full: The service is already at capacity and cannot handle any more interactions. This supplants the 'busy' error message in earlier versions of this document.
- unsupported: The client requested an unknown service.
- banned: The client or inquiring avatar is unwelcome.
- refused: The host does not wish to disclose the reason that the connection attempt failed.
- wait: The service is already in the process of deploying or booting. Try again in a few seconds.
5. Messages from AV-CAPS to Services
Although AV-CAPS can be used to provision any attachments on the avatar, its utility is enhanced when it can inform the client of a service's state. A service is considered "CAPS-aware" if it understands the messages in this section and can respond with the messages in the next section.
Services should ignore these messages if they are sent from a UUID that is not attached to the owning avatar, i.e.
llList2Integer(llGetObjectDetails(source, [OBJECT_ATTACHMENT_POS]), 0) == 0 || llGetOwnerKey(source) != llGetOwner()
where "source" is the UUID of the object sending the messages in question.
5.1. CLIENT
To inform a service that a client is attempting to engage it (see 3.2. CONNECT), the AV-CAPS host should tell it:
client <service> <id> <c>
Where <service> and <c> are copied from the previous "connect" solicitation (see 3.2. CONNECT) and <id> is the UUID of the client that sent the connection.
5.2. SHUTDOWN
The AV-CAPS host may demand that a service close all connections and detach itself. This is typically in response to a user action, such as deactivating AV-CAPS, or leaving the service's operational theater. If a service receives the message:
shutdown
Provided the source is valid (see 5. Messages from AV-CAPS to Services), the service shall clean up any ongoing dialogs with connected clients and detach itself.
The AV-CAPS host may also send the "shutdown" message after the host is rebooted, to ensure that services are in a known good configuration before continuing.
5.3. STATUS
At any time the AV-CAPS host may attempt to poll a service with the message:
status
Upon receipt of this message on the CAPS channel, a CAPS-aware service should reply with the "service" message (see 6.1. SERVICE) on the same channel.
6. Messages from Services to AV-CAPS
6.1. SERVICE
When the service is ready to receive connections, or receives a new connection, it MAY report this information to llGetOwner() on the CAPS channel (see 2. Channels). It shall use one of the following forms:
service ready <service> <channel>
service full <service> <channel>
If the service is capable of handling additional interactions, then the "ready" form is appropriate. Otherwise, it should send the "full" form.
If <channel> is specified, and the AV-CAPS host also provides HW-CAPS functionality, then the service will be listed with the indicated channel in HW-CAPS queries so long as it remains in the "ready" state.
7. Miscellaneous Implementation Requirements
7.1. Naming of Services & Attachments
Service attachments must be named <service>-<version>, where <service> is a short string of ASCII letters, hyphens, and digits uniquely identifying the service, and <version> is a string of ASCII digits and periods (.) uniquely identifying the service's version number. The last hyphen in the string is used to separate the service name from the version number.
Service names must begin with a letter. They may not have multiple consecutive hyphens, nor end in a hyphen. Capitalization is allowed, but will be stripped during communications.
Version numbers must begin and end with a digit. They may not have multiple consecutive periods.
The following service attachment names are legal:
HUDware-1.0.0
a-00000000
zzz-0.0.0.0.0
The following service attachment names are illegal:
My c00l HUD version 1.0 (contains spaces, no hyphen)
facet-1.0a1 (contains a letter in the version number)
-dot-. (does not start with a letter; version number does not start or end with a digit)
bob (no version number at all)
8. Future Developments
TBD
9. Acknowledgements
TBD
10. Author's Address
Atarah Vella Lazuli
NANOCOM Davies Research Institute
1100 Blazkowicz Road
Elysium, Mars
22+90771
11. References
[ORIX API] Chelton, A. and Brattle, J. "ORIX Wiki" <https://orix.fandom.com/wiki/ORIX_Wiki> Last updated 2014-07-10.
[RLV API] Kelley, M. "RestrainedLove API Specification" <https://wiki.secondlife.com/wiki/LSL_Protocol/RestrainedLoveAPI> Last updated 2021-07-01.
[SL R1365] Linden, J. "Experiences in Second Life" <https://community.secondlife.com/knowledgebase/english/experiences-in-second-life-r1365/> July 2014.
[RFC 2119] Bradner, S. "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
[OeM] Hardwick, E. et al. "Obedientia ex Machina Documentation" <https://elvahardwick.github.io/Obedientiae-ex-Machina-Docs/> Accessed 2024-03-11.
[KOR] Snowpaw, D. "KOR Trailblazer" (Product Info) <https://marketplace.secondlife.com/p/KOR-Trailblazer/24133772> Accessed 2024-03-11.
Appendix A: HW-CAPS Purposes
Inquiring clients that present a user interface documenting discovered CAPS hosts should be aware of the following purposes that those hosts could indicate:
- charge: Recharges electronics.
- security: Prevents unauthorized access, or circumvents access prevention measures.
- battery: Stores power.
- power: Distributes power from another source.
- repair: Fixes damage to electronics.
- info: Presents human-readable information.
- light: Provides illumination. Purely decorative objects will generally use this purpose.
- furniture: Can be sat upon. Do not tag with 'storage' unless the furniture is multifunctional.
- server: Central repository for settings or objects.
- defend: Thwarts or harms attackers. Do not specify 'safety' and 'defend' purposes for the same device.
- attack: Deals damage or impairs targets.
- medical: Diagnoses or heals organics.
- control: Integrates signals and makes decisions.
- stasis: Stores organics in suspended animation or medical coma.
- data: Stores or transfers electronic information.
- logistic: Facilitates distribution of equipment, people, or products.
- hydro: Directs or utilizes the flow of water or other liquids and gasses.
- motor: Produces rotational movement using electromagnetism, or electricity from rotation.
- cryo: Removes heat.
- thermal: Generates heat.
- homeostasis: Maintains temperature, pressure, and/or humidity at target values within a body, or removes toxins. Do not specify 'control', 'cryo', or 'thermal' purposes if a device provides the 'homeostasis' purpose.
- safety: Mitigates or prevents unsafe industrial or environmental conditions. Do not specify 'safety' and 'defend' purposes for the same device.
- network: Transfers signals between other connected devices.
- fabricate: Conducts manufacturing.
- enviro: Maintains temperature, pressure, and/or humidity at target values within a space, or removes pollution. Do not specify 'control', 'cryo', or 'thermal' purposes if a device provides the 'enviro' purpose.
- chem: Conducts a chemical reaction or stores chemicals.
- comm: Provides an interface for long-distance or human-readable communications, such as a telephone or satellite receiver.
- nav: Assists with automated driving, typically by providing directions, coordinating traffic, or serving as a travel waypoint.
- detect: Identifies objects meeting set criteria.
- cargo: Holds goods, resources, or robots during transit.
- clean: Removes contaminants.
- engine: Generates motion for a vehicle or machine. Supersedes the 'motor' purpose.
- kinematics: Animation overriders and systems that control articulation or motion of robotic equipment.
- vehicle: Holds individuals and provides transit. Do not combine with purposes necessary for a vehicle to function (e.g. 'motor' or 'engine')
- travel: Facilitates movement in some unspecified way other than a vehicle, e.g. a teleporter or jump pad.
- ammo: Refills ammunition, or can be used as ammunition.
- fuel: Refills fuel, or can be used as fuel.
- respawn: Location where an avatar or object reappears after a defeat.
- objective: Target of a game, e.g. a capture point or flag.
- bio: Biological research or storage equipment.
- storage: General storage. Do not tag with 'cargo' purpose. Only tag with 'furniture' if the object contains both storage and sittable surfaces (e.g. a combined chair and bookcase).
- agri: Farming equipment.
- science: General scientific research or surveying equipment, not otherwise specified. Do not tag with 'bio', 'chem', or 'agri'.
Appendix B: HW-CAPS Channels
The following channels should be listed for HW-CAPS if relevant.
Service Channel Description
----------------------------------------------------------------------------------------
orix-alt 9360 Alternate channel for orix
pds 5201 Nanite Systems Power Distribution System
facet 5200 Nanite Systems Facet interface (http://develop.nanite-systems.com/?facet)
caps 411 CAPS itself (this document; optional)
acs 360 Autonomy Control Systems CCU protocol (http://develop.nanite-systems.com/resources/ACS-charging.pdf)
command 1* Generic term for command handler using a name-based prefix (commonly used by collars)
trigger 0 Generic term for any detection of commands issued in normal local chat
lockmeister -8888 LockMeister system for particle chains (https://wiki.secondlife.com/wiki/LSL_Protocol/LockMeister_System)
lockguard -9119 LockGuard system for particle chains (https://wiki.secondlife.com/wiki/LSL_Protocol/LockGuard)
hudware -82104 NANOCOM HUDware bidirectional (http://develop.nanite-systems.com/HUDware)
ao-link -782690 OpenCollar AO Link
stargate -900000 Alteran Stargate Network
arena -9990009 ATOS Arena
tesi -9999969 NS TESI Lust (http://develop.nanite-systems.com/?id=1924#LC%20messages)
public -9999999 NS public bus (http://develop.nanite-systems.com/?public)
public-alt -9999998 Alternate channel for public
weather -78838783 Nanite Systems Weather Service
orix -15180924 ORIX query (https://orix.fandom.com/wiki/ORIX_Wiki)
rlvrs -1812221819 RLV relay (https://wiki.secondlife.com/wiki/LSL_Protocol/Restrained_Love_Relay/Specification)
lights ** Nanite Systems Light Bus (http://develop.nanite-systems.com/?light_bus)
----------------------------------------------------------------------------------------
** No set channel number, or channel number decided based on object key
* Channel number is only a default; may vary based on user preferences
See also other protocols at https://wiki.secondlife.com/wiki/LSL_Protocol
Appendix C: AV-CAPS Services
The following services should be listed for AV-CAPS if relevant. Services fall into three major categories: general-purpose interface platforms, immersion interfaces, and device-specific utilities.
Appendix C.1. Interface Platforms
Interface platforms are HUDs that are used to build other HUDs.
hudware: HUDware Workspace
facet: Facet menu tool
Appendix C.2. Immersion Interfaces
Immersion interfaces are HUDs that are used to display status or interactions for gameplay.
conversant: Conversant dialog system
ahm: AHM Client
inventory: NS trading & inventory management UI
wallet: myNanite Financial Credit Wallet
hunter: generic treasure hunt UI
hacking: iLLUSiON penetration testing system
dryad: DRYAD-III signal hunting interface
Appendix C.3. Configuration and Access Utilities for Rezzed Objects
anav: ARES Navigation Server Setup
Appendix C.4. Configuration Utilities for Attachments
wavelength: NS Wavelength Selection HUD
bismuth: ARES Color HUD
holoprobe: Holo-Pleasure Probe Deluxe HUD
icon: NS Akashic Icon Control HUD
stallion: NS-24 Stallion HUD
projector: NS-478 Holoprojector Calibration Utility
fw-eyes: Fateweaver Soothsayer Eyes HUD
Appendix D. HW-CAPS Sample Implementation
integer C_CAPS = 411;
default {
state_entry() {
llListen(C_CAPS, "", "", "");
}
listen(integer channel, string name, key id, string message) {
if(channel == C_CAPS) {
if(llGetSubString(message, 0, 4) == "info ") {
integer rc = (integer)llDeleteSubString(message, 0, 4);
tell(id, rc, "hwc " + llList2Json(JSON_OBJECT, [
"vendor", "Nanite Systems Consumer Products",
"version", "0.4",
"purpose", "light",
"channel", llList2Json(JSON_OBJECT, [
"caps", C_CAPS
]),
"private", FALSE,
"busy", FALSE,
"usable", TRUE,
"health", 1.0,
"info", "http://support.nanite-systems.com/example"
]));
}
}
}
}
Full Copyright Statement
Copyright (C) Naanite Systems Communications Corporation (2024). All Rights Reserved.
This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this paragraph are included on all such copies and derivative works. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Nanite Systems Communications Corporation or other Nanite Systems organizations, except as needed for the purpose of developing communications standards in which case the procedures for copyrights defined in the Internet Standards process must be followed, or as required to translate it into languages other than English.
The limited permissions granted above are perpetual and will not be revoked by the Nanite Systems Communications Corporation or its successors or assigns.
This document and the information contained herein is provided on an "AS IS" basis and NANITE SYSTEMS AND THE SIGNALS PROTOCOL WORKING GROUP DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

View File

@ -0,0 +1,214 @@
PHASE PROTOCOL VERSION 1.1
2024-06-25
The following is a general-purpose read/write protocol for data storage systems in Second Life. It originated in Nanite Systems ARES 0.4.4. Importantly, it is opaque enough to handle almost any underlying storage system, from read-only notecard access to a full-access web server.
Data sizes can be specified in either bytes or pages. A page is notionally 1024 bytes, but can be incomplete (known as a "ragged" page.) This size fits in a single SL channel message, and in a single line of a notecard.
Except where otherwise specified, each control message consists of the format:
<handle> <command> <argument> <argument> <argument> ...
Where <handle> is an arbitrary string (typically a UUID) generated by the OS for each interaction, <command> is one of:
connect disconnect access size read write ready delete deleted new update close
and the <argument>s are <command>-specific.
The main control channel is 1608011905, or "phase" in character ordinals (with "a" = 01). Side channels are sometimes created for sending data, in both directions.
READING CHANNELS
For efficiency, whenever the system wants to read a file from the storage device, it opens a listener and passes the channel number to the storage device, asking the storage device to send data there. This is called a reading channel. Messages sent on the reading channel are unadorned, and it is entirely up to the system to close the channel when it is no longer required. This allows data to be read in chunks of up to 1024 bytes (or one notecard line) without splitting.
If reading from the given filename is not possible, the storage device may reject the request by sending "stat none" (meaning that the file is not in a readable format, or does not exist) or "denied" (meaning that the system does not have adequate permissions.)
WRITING CHANNELS
Whenever the system wants to write to a file, it informs the storage device (with a "write" or "append" message), which includes a channel number as one of its arguments. The storage device then opens a listener on that channel number and announces that it is prepared to receive text with the "ready" message. All text received from the system should then be written by the storage device to the indicated file.
The system will announce it is done sending data by sending a "close" message.
If writing to the given filename is not possible, the storage device may reject the request by sending "stat none" (meaning that the file could not be created/accessed) or "denied" (meaning that the system does not have adequate permissions.)
DIRECTORY FORMAT
The phase directory format is simply a list of filenames, separated by 0x0a ('\n'). This is the same as the obsolete wf0 and wf1 formats.
MESSAGES
<handle> connect <mode>
system -> storage
where <mode> is one of: ro rd rw
Initiates a connection with the specified access level. The storage device should respond with an "access" command (below), and store the host UUID and granted level of permission for future reference. The modes are defined as "read only" (ro), "read and delete" (rd), or "full access" (rw).
At this point, the storage device should also check through all existing connections and confirm that the hosts involved still exist (that is, llGetOwnerKey(id) != id). Failing that, delete those connections and close any writing channels (described earlier) associated with them.
However, if <handle> is NULL_KEY, the system is simply pinging and does not want to alter the state of any existing connections. A storage device that wants to be seen by the inquiring party (e.g. for public use) should respond with a "version" message; otherwise, no response is required.
<handle> version <version> <label>
storage -> system
Sent in response to "connect" when <handle> is NULL_KEY. The reply handle should also be NULL_KEY.
<version> is an implementation-specific string up to 64 characters in length with no spaces, e.g. "ARES-_fs-0.4.4"
<label> is a human-readable (space-containing) descriptor of the volume.
<handle> reset
storage -> system
The storage device has been rebooted and has lost its active connections list. Any system that recognizes the storage device's channel and UUID should re-send the "connect" message (above).
The standard implementation of phase sends this message publicly using llRegionSay(). Implementations may alternatively send it selectively, e.g. to the owning avatar (and therefore all its attachments) or to a predetermined whitelist of UUIDs.
<handle> access <mode> <unit> <label>
storage -> system
where <mode> is one of: ro rd rw
Responds to a request to a "connect" command (above). The storage device should indicate the level of access it is willing/capable of giving the system. It should also store the host UUID and granted level of permission for future reference. The useful modes are defined as "read only" (ro), "read and delete" (rd), or "full access" (rw). A mode of "none" indicates the connection was rejected and not stored.
where <unit> is one of: b p l
If the unit is b, then size measurements are in bytes.
If the unit is p, then size measurements are in pages. Each page can hold up to 1024 bytes. Pages may be less than this size, in which case they are called "unsaturated" or "ragged".
If the unit is l, then size measurements are in lines. This is the same as pages but the system should always assume lines are separated by a linebreak character. Writing to line-based storage should be done one line at a time.
<label> is a freeform text label that describes the volume.
If no access is granted, the device should send the "denied" command.
<handle> disconnect
system -> storage
The system no longer wishes to access the device. Forget the session, and close any associated writing channels (described earlier).
<handle> stat <filename>
system -> storage
The system wants to know the size and type of a file. Please send it in reply with the message below.
<handle> stat <amount> <type> <desc>
<handle> stat none
storage -> system
The storage reports the metrics of a file. Amount is a non-negative integer (measured in units as described by "access")
<type> is one of: d f o
where f is a normal (readable) text file, o is a non-readable (object) file, and d is a directory (reserved for future use)
If the file does not exist, return the "stat none" syntax.
<handle> read <filename> <offset>+<length> <channel>
<handle> read <filename> <offset> <channel>
system -> storage
The system wants to read data from a file and has created a listener on <channel> to receive information. Please send the text at <offset> on that channel. If +<length> is not specified, only send 1 unit of storage.
If the file cannot be appended to, or opening it failed for some other reason, the device should reply with "stat none".
If <filename> is *, then provide text from the file directory for the current (root) location. See section on DIRECTORY FORMAT.
<handle> write <filename> <channel>
<handle> append <filename> <channel>
system -> storage
The system wants to begin writing to a file. If the system has rw access, then the device should open a listener on <channel> to receive data for <filename>, and send "ready". Otherwise send "denied".
Writing to line-based storage should be done one line at a time.
If the command is "write", then delete the contents of the file first.
If the command is "append", add data to the end of the file. If the file does not exist, create it first.
If the system does not have permission to write, the device should reply with "denied".
If the file cannot be appended to, or opening it failed for some other reason, the device should reply with "stat none".
<handle> ready
storage -> system
The device sends this in response to a "write" or "append" command. It is listening on the <channel> provided in that command and data can now be sent.
For page or line devices, send data 1 line at a time. Otherwise send it 1024 bytes at a time.
<handle> delete <filename>
system -> storage
Delete a file. The device should respond with a "deleted" message if successful, "denied" if not allowed, or "stat none" if the file did not exist.
<handle> deleted <filename>
storage -> system
A file has been removed from the system.
The storage device may send this spontaneously in response to an outside action. The handle should be NULL_KEY in this case.
<handle> new <filename>
storage -> system
A file has been added from the system.
The storage device may send this spontaneously in response to an outside action. The handle should be NULL_KEY in this case.
<handle> update
storage -> system
A file has changed in stat information (size, type, desc, etc).
The storage device may send this spontaneously in response to an outside action. The handle should be NULL_KEY in this case.
<handle> close
system -> storage
Close the writing channel associated with <handle>. Writing channels are explained earlier in the document.
<handle> denied
storage -> system
The requested action exceeds the permissions granted.

318
qkeys.lsl Normal file
View File

@ -0,0 +1,318 @@
#ifndef QKEYS
#define QKEYS
/*
***********************************************************
* *
* NANITE SYSTEMS ADVANCED TACTICAL OPERATING SYSTEM *
* *
* QKEYS COMPONENT *
* *
* Copyright, (C) Nanite Systems Corp., 1984, 1985, 2018 *
* *
* Copyright, (C) University of Michigan 1977-1981 *
* *
***********************************************************
NOTICE: UNAUTHORIZED DISTRIBUTION, COPYING, MODIFICATION,
OR REVERSE ENGINEERING OF PROPRIETARY NANITE SYSTEMS
MILITARY CONTROL CODE IS A FEDERAL OFFENSE. THIS CODE IS
CONFIDENTIAL.
PRODUCED UNDER CONTRACT TO THE GOVERNMENT OF THE TERRAN
REPUBLIC OR ONE OF ITS DEPENDENT POLITIES.
IDV: F0960303D0002
VENDOR DUNS: 005128988
*/
rotation k2q(key k) {
string kt = (string)llParseStringKeepNulls(k, ["-"], []);
/*integer e0 = (integer)("0x" & llGetSubString(kt, 0, 1)) - 126;
integer m0 = (integer)("0x" & llGetSubString(kt, 2, 7));
integer e1 = (integer)("0x" & llGetSubString(kt, 8, 9)) - 126;
integer m1 = (integer)("0x" & llGetSubString(kt, 10, 15));
integer e2 = (integer)("0x" & llGetSubString(kt, 16, 17)) - 126;
integer m2 = (integer)("0x" & llGetSubString(kt, 18, 23));
integer e3 = (integer)("0x" & llGetSubString(kt, 24, 25)) - 126;
integer m3 = (integer)("0x" & llGetSubString(kt, 26, 31));
return <(float)m0 * llPow(2, e0),
(float)m1 * llPow(2, e1),
(float)m2 * llPow(2, e2),
(float)m3 * llPow(2, e3)>; */
return <
iuf((integer)llGetSubString(kt, 0, 7)),
iuf((integer)llGetSubString(kt, 8, 15)),
iuf((integer)llGetSubString(kt, 16, 23)),
iuf((integer)llGetSubString(kt, 24, 31))
>;
}
key q2k(rotation q) {
return llInsertString(llInsertString(
i2h(fui(q.x)) + "-"
+ i2h(fui(q.y)) + "-"
+ i2h(fui(q.z))
+ i2h(fui(q.s)),
21, "-"), 12, "-");
}
// Strife Onizuka's Float-Union-Integer implementation
integer fui(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
{//union float to integer
integer b = 0x80000000 & ~llSubStringIndex(llList2CSV([a]), "-");//the sign
if((a)){//is it nonzero?
if((a = llFabs(a)) < 2.3509887016445750159374730744445e-38)//Denormalized range check & last stride of normalized range
return b | (integer)(a / 1.4012984643248170709237295832899e-45);//the math overlaps; saves cpu time.
if(a > 3.4028234663852885981170418348452e+38)//Round up to infinity
return b | 0x7F800000;//Positive or negative infinity
if(a > 1.4012984643248170709237295832899e-45){//It should at this point, except if it's NaN
integer c = ~-llFloor(llLog(a) * 1.4426950408889634073599246810019);//extremes will error towards extremes. following yuch corrects it
return b | (0x7FFFFF & (integer)(a * (0x1000000 >> c))) | ((126 + (c = ((integer)a - (3 <= (a *= llPow(2, -c))))) + c) * 0x800000);
}//the previous requires a lot of unwinding to understand it.
return b | 0x7FC00000;//NaN time! We have no way to tell NaN's apart so lets just choose one.
}//Mono does not support indeterminates so I'm not going to worry about them.
return b;//for grins, detect the sign on zero. it's not pretty but it works.
}
float iuf(integer a) { //union integer to float
if(0x7F800000 & ~a)
return llPow(2, (a | !a) + 0xffffff6a) * (((!!(a = (0xff & (a >> 23)))) * 0x800000) | (a & 0x7fffff)) * (1 | (a >> 31));
return (!(a & 0x7FFFFF)) * (float)"inf" * ((a >> 31) | 1);
}
// int2hexdword, also by Strife Onizuka
string i2h (integer I) {
integer A = (I >> 2) & 0x3C000000;//not an unsigned rshift
integer B = (I & 0x0F000000) >> 4;
integer C = (I & 0x00F00000) >> 6;
integer D = (I & 0x000F0000) >> 8;
integer E = (I & 0x0000F000) << 14;
integer F = (I & 0x00000F00) << 12;
integer G = (I & 0x000000F0) << 10;
integer H = (I & 0x0000000F) << 8;
return llGetSubString(
llInsertString(
llIntegerToBase64(
A + B + C + D + 0xD34D3400
- (0xF8000000 * (A / 0x28000000))//lowercase=0x90000000, uppercase=0xF8000000
- (0x03E00000 * (B / 0x00A00000))//lowercase=0x02400000, uppercase=0x03E00000
- (0x000F8000 * (C / 0x00028000))//lowercase=0x00090000, uppercase=0x000F8000
- (0x00003E00 * (D / 0x00000A00))//lowercase=0x00002400, uppercase=0x00003E00
),
4,
llIntegerToBase64(
E + F + G + H + 0xD34D3400
- (0xF8000000 * (E / 0x28000000))//lowercase=0x90000000, uppercase=0xF8000000
- (0x03E00000 * (F / 0x00A00000))//lowercase=0x02400000, uppercase=0x03E00000
- (0x000F8000 * (G / 0x00028000))//lowercase=0x00090000, uppercase=0x000F8000
- (0x00003E00 * (H / 0x00000A00))//lowercase=0x00002400, uppercase=0x00003E00
)
),
0,
7
);
}
// Copyright (C) 2009 Adam Wozniak and Doran Zemlja
// Released into the public domain.
// Free for anyone to use for any purpose they like.
//
// deep voodoo base 4096 key compression
//
// It produces fixed length encodings of 11 characters.
string compress_key(key k) {
string s = llToLower((string)llParseString2List((string)k, ["-"], []) + "0");
string ret;
integer i;
string A;
string B;
string C;
// string D;
while(i < 32) {
A = llGetSubString(s, i, i);
++i;
B = llGetSubString(s, i, i);
++i;
C = llGetSubString(s, i, i);
++i;
/*if(A == "0") {
A = "e";
D = "8";
} else if(A == "d") {
A = "e";
D = "9";
} else if(A == "f") {
A = "e";
D = "a";
} else
D = "b";
ret += "%e" + A + "%" + D + B + "%b" + C;*/
if(A == "0")
ret += "%ee%8";
else if(A == "d")
ret += "%ee%9";
else if(A == "f")
ret += "%ee%a";
else
ret += "%e" + A + "%b";
ret += B + "%b" + C;
}
return llUnescapeURL(ret);
}
key uncompress_key(string s) {
integer i;
string ret;
string A;
string B;
string C;
string D;
s = llToLower(llEscapeURL(s));
for(i = 0; i < 99; i += 9) {
A = llGetSubString(s,i+2,i+2);
B = llGetSubString(s,i+5,i+5);
C = llGetSubString(s,i+8,i+8);
D = llGetSubString(s,i+4,i+4);
if(D == "8") {
A = "0";
} else if(D == "9") {
A = "d";
} else if(D == "a") {
A = "f";
}
ret += A + B + C;
}
return (key)(llGetSubString(ret, 0, 7) + "-" +
llGetSubString(ret, 8,11) + "-" +
llGetSubString(ret,12,15) + "-" +
llGetSubString(ret,16,19) + "-" +
llGetSubString(ret,20,31));
}
/*
encode/decode 8 for compact keys
minimum weight of 3774-4338 bytes with 1 invocation of each function
rhet0rica, August 9, 2021
encodes keys into exactly 8 chars weighing 24 bytes on average
using the new llChar() and llOrd() built-ins
*/
string encode_8(key k) {
string o;
string in = llDumpList2String(llParseString2List(k, ["-"], []), "");
integer ri = 8;
while(ri--) {
integer cn = (integer)("0x" + substr(in, ri << 2, (ri << 2) + 3));
string c = llChar(cn);
if(llOrd(c, 0) != cn || cn < 256)
o += llChar(cn + 0x10000);
else
o += c;
}
return o;
}
key decode_8(string s) {
string o;
integer ri = 8;
while(ri--) {
integer c = llOrd(s, ri) & 0x0ffff;
integer rii = 4;
string word;
while(rii--) {
integer cn = (c >> (rii << 2)) & 0x0f;
if(cn < 10)
word += llChar(cn + 0x30);
else
word += llChar(cn + 0x57);
}
o += word;
}
return (key)(
substr(o, 0, 7) + "-" +
substr(o, 8, 11) + "-" +
substr(o, 12, 15) + "-" +
substr(o, 16, 19) + "-" +
substr(o, 20, 31)
);
}
/*
encode/decode 22 for compact keys
weight of 3778-4392 bytes with 1 invocation of each function
rhet0rica, August 13, 2022
encodes keys into 16 chars weighing up to 32 bytes
special NULL and EMPTY values for NULL_KEY and ""
less likely than encode_8 to break mono serialization (!) on uplifted regions
uses llChar() and llOrd()
*/
string encode_22(key u) {
if(u == "")
return "EMPTY";
if(u == NULL_KEY)
return "NULL";
/*string unhyphenated = substr(u, 0, 7) + substr(u, 9, 12)
+ substr(u, 14, 17) + substr(u, 19, 22)
+ substr(u, 24, 35);*/
string unhyphenated = llDumpList2String(llParseString2List(u, ["-"], []), "");
string outs;
integer cc = 16;
while(cc--) {
integer icc = cc << 1;
integer code = (integer)("0x" + substr(unhyphenated, icc, icc + 1));
if(code < 35 || code > 126) code += 0x100;
outs = llChar(code) + outs;
}
return outs;
}
key decode_22(string c) {
if(c == "EMPTY")
return "";
if(c == "NULL")
return NULL_KEY;
string prec;
integer cc = 16;
while(cc--) {
integer bits = llOrd(c, cc);
integer low = bits & 0xf;
integer high = (bits & 0xf0) >> 4;
#define decode_22_lookup "0123456789abcdef"
prec = substr(decode_22_lookup, high, high) + substr(decode_22_lookup, low, low) + prec;
#undef decode_22_lookup
}
// inexplicably, leaving this concatenation here is better for memory than putting it in the return line:
key hyphenated = (key)(substr(prec, 0, 7) + "-" + substr(prec, 8, 11) + "-"
+ substr(prec, 12, 15) + "-" + substr(prec, 16, 19) + "-"
+ substr(prec, 20, 31));
return hyphenated;
}
#endif // QKEYS

67
unix2slt.lsl Normal file
View File

@ -0,0 +1,67 @@
// Convert Unix Time to SLT, identifying whether it is currently PST or PDT (i.e. Daylight Saving aware)
// Omei Qunhua December 2013
list weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
string Unix2PST_PDT(integer insecs)
{
string str = Convert(insecs - (3600 * 8) ); // PST is 8 hours behind GMT
if (llGetSubString(str, -3, -1) == "PDT") // if the result indicates Daylight Saving Time ...
str = Convert(insecs - (3600 * 7) ); // ... Recompute at 1 hour later
return str;
}
// This leap year test is correct for all years from 1901 to 2099 and hence is quite adequate for Unix Time computations
integer LeapYear(integer year)
{
return !(year & 3);
}
integer DaysPerMonth(integer year, integer month)
{
if (month == 2) return 28 + LeapYear(year);
return 30 + ( (month + (month > 7) ) & 1); // Odd months up to July, and even months after July, have 31 days
}
string Convert(integer insecs)
{
integer w; integer month; integer daysinyear;
integer mins = insecs / 60;
integer secs = insecs % 60;
integer hours = mins / 60;
mins = mins % 60;
integer days = hours / 24;
hours = hours % 24;
integer DayOfWeek = (days + 4) % 7; // 0=Sun thru 6=Sat
integer years = 1970 + 4 * (days / 1461);
days = days % 1461; // number of days into a 4-year cycle
@loop;
daysinyear = 365 + LeapYear(years);
if (days >= daysinyear)
{
days -= daysinyear;
++years;
jump loop;
}
++days;
for (w = month = 0; days > w; )
{
days -= w;
w = DaysPerMonth(years, ++month);
}
string str = ((string) years + "-" + llGetSubString ("0" + (string) month, -2, -1) + "-" + llGetSubString ("0" + (string) days, -2, -1) + " " +
llGetSubString ("0" + (string) hours, -2, -1) + ":" + llGetSubString ("0" + (string) mins, -2, -1) );
integer LastSunday = days - DayOfWeek;
string PST_PDT = " PST"; // start by assuming Pacific Standard Time
// Up to 2006, PDT is from the first Sunday in April to the last Sunday in October
// After 2006, PDT is from the 2nd Sunday in March to the first Sunday in November
if (years > 2006 && month == 3 && LastSunday > 7) PST_PDT = " PDT";
if (month > 3) PST_PDT = " PDT";
if (month > 10) PST_PDT = " PST";
if (years < 2007 && month == 10 && LastSunday > 24) PST_PDT = " PST";
return (llList2String(weekdays, DayOfWeek) + " " + str + PST_PDT);
}

370
utils.lsl Normal file
View File

@ -0,0 +1,370 @@
#ifndef UTILS
#define UTILS
/*
***********************************************************
* *
* NANITE SYSTEMS ADVANCED TACTICAL OPERATING SYSTEM *
* *
* UTILS COMPONENT *
* *
* Copyright, (C) Nanite Systems Corp., 1984-85, 2017-24 *
* *
* Copyright, (C) University of Michigan 1977-1981 *
* *
***********************************************************
THE SOURCE CODE FOR THIS LIBRARY COMPONENT IS PROVIDED TO
NANITE SYSTEMS CUSTOMERS FOR DEVELOPMENT OF COMPATIBLE
SOFTWARE.
BY USING THIS CODE, YOU AGREE NOT TO MODIFY OR DISTRIBUTE
IT FOR COMMERCIAL GAIN, EXCEPT IN A COMPILED PRODUCT. YOU
MAY PROVIDE THE SOURCE CODE OF THIS FILE FOR FREE OR
INCLUDED WITH YOUR OWN DEVELOPMENT PACKAGE AS LONG AS THIS
NOTICE IS INCLUDED AND THE REST OF THE FILE IS UNMODIFIED.
INTERACTIONS WITH OTHER COMPONENTS OF THE OPERATING SYSTEM
ARE SUBJECT TO THE APPROPRIATE END-USER LICENSE AGREEMENT
(EULA).
IDV: F0960303D0002
VENDOR DUNS: 005128988
*/
// integer contains(list haystack, any needle): returns a non-zero value if needle is in haystack; otherwise 0. This macro cannot handle list literals, so wrap them in (parens)
#define contains(_haystack, _needle) ~llListFindList(_haystack, (list)(_needle))
// integer index(list haystack, any needle): returns the index of needle's first occurrence if it is in haystack; otherwise -1. This macro cannot handle list literals, so wrap them in (parens)
#define index(_haystack, _needle) llListFindList(_haystack, (list)(_needle))
// integer strlen(string s): returns the length of the specified string in characters; see also strlen_byte()
#define strlen llStringLength
// string substr(string s, integer x, integer y): returns a contiguous substring of the specified string, starting from position x and ending at position y, inclusive of both end-points; negative indices count from the end of the string
#define substr(_haystack, _start, _end) llGetSubString(_haystack, _start, _end)
// integer strpos(string haystack, string needle): returns the index of needle's first occurrence if it is in haystack; otherwise -1
#define strpos(_haystack, _needle) llSubStringIndex(_haystack, _needle)
// string strdelete(string s, integer x, integer y): returns a copy of a string with the subrange [x, y] removed, inclusive of both endpoints; negative indices count from the end of the string
#define strdelete llDeleteSubString
// string delstring(string s, integer x, integer y): returns a copy of a string with the subrange [x, y] removed, inclusive of both endpoints; negative indices count from the end of the string
#define delstring llDeleteSubString
// integer ucount(list x): returns TRUE if x is non-empty
#define ucount(...) (__VA_ARGS__ != [])
// integer count(list x): returns the number of elements in x
#define count llGetListLength
// integer geti(list x, integer n): returns the value of an integer element at position n in list x, or 0 if element n is not an integer; negative indices count from the end of the list
#define geti llList2Integer
// string gets(list x, integer n): returns the value of a string (or key) element at position n in list x, or "" if element n is not a string (or key); negative indices count from the end of the list
#define gets llList2String
// key getk(list x, integer n): returns the value of a key (or string) element at position n in list x, or "" if element n is not a key (or string); negative indices count from the end of the list
#define getk llList2Key
// vector getv(list x, integer n): returns the value of a vector element at position n in list x, or ZERO_VECTOR if element n is not a vector; negative indices count from the end of the list
#define getv llList2Vector
// rotation getr(list x, integer n): returns the value of a rotation element at position n in list x, or ZERO_ROTATION if element n is not a rotation; negative indices count from the end of the list
#define getr llList2Rot
// float getf(list x, integer n): returns the value of a float element at position n in list x, or 0.0 if element n is not a float; negative indices count from the end of the list
#define getf llList2Float
// list sublist(list b, integer x, integer y): returns a new list containing the elements of list b, from the subrange [x, y], inclusive of both endpoints
#define sublist llList2List
// list alter(list haystack, list needle, integer x, integer y): returns a new list containing the elements of list haystack prior to position x, followed by the elements of list needle, and then the elements of list haystack after list y, thereby replacing the range [x, y] in list haystack (inclusive) with the contents of list needle; negative indices count from the end of list haystack
#define alter llListReplaceList
// string getjs(string json, list subscripts): extracts a fragment of JSON from a larger JSON string by following the specified subscripts. Atomic strings are returned unquoted. An empty subscript list returns the entire string json, unaltered. If the subscripts do not address an extant element, JSON_INVALID is returned. JSON objects must be indexed by string and JSON arrays must be indexed by integers.
#define getjs llJsonGetValue
// string setjs(string json, list subscripts, string value): returns a copy of string json with an element substituted. The element to substitute must be specified by the provided subscripts. Subscripts can be used to force the creation of new elements, either by appending string subscripts (to create nested objects) or through the use of the special JSON_APPEND subscript value to add to the end of an array. Values are interpreted as JSON first, numbers second, and strings third, so "1.0" will become a number, but "[1.0]" will become an array containing a number. The special value JSON_DELETE can be used to remove an element, and the values JSON_TRUE and JSON_FALSE are used to store booleans. You should sanitize your input to prevent storing the characters [, ], {, or } in string values, as the JSON parser is incomplete and will interpret these as evidence of corruption even when other implementations accept them. Always check the return value for JSON_INVALID, as this is evidence of an unsuccessful operation and will occur even when trying to JSON_DELETE an element that does not exist--otherwise you will lose data.
#define setjs llJsonSetValue
// integer etype(list x, integer n): returns a numeric constant describing the type of the element at position n in list x. Possible return values are TYPE_INVALID, TYPE_INTEGER, TYPE_FLOAT, TYPE_STRING, TYPE_KEY, TYPE_VECTOR, and TYPE_ROTATION (0-6).
#define etype llGetListEntryType
// string jstype(string json, list subscripts): as getjs(), but returns the type of an element instead of its actual value. Possible return values are JSON_INVALID, JSON_OBJECT, JSON_ARRAY, JSON_NUMBER, JSON_STRING, JSON_NULL, JSON_TRUE, and JSON_FALSE (U+FDD0 through UFDD7).
#define jstype llJsonValueType
// echo(string message): sends a string directly to the owning agent's viewer; equivalent to llOwnerSay(). Maximum limit is 1024 bytes.
#define echo llOwnerSay
// tell(key id, integer c, string message): sends a string to the specified object id on channel c. If id is an avatar, all attachments will receive it. If id is the root of a linkset, all links will receive it. Maximum limit for message is 256 bytes if c is negative, otherwise 1024 bytes.
#define tell llRegionSayTo
// string list2js(string type, list x): returns a string that is a representation of the values in list x as encoded into the specified type (which must be one of JSON_OBJECT or JSON_ARRAY). All types other than integers and floats will be converted into quoted strings, but strings that resemble JSON (due to starting and ending with [] or {}) will be interpreted as JSON. If converting into JSON_OBJECT, the source list will be interpreted as having a stride of 2, with even-numbered elements becoming the keys of the new object, and odd-numbered elements becoming values.
#define list2js llList2Json
// list js2list(string json): returns a list that contains every element of the original JSON object or array atomized. Nested values are left as strings. Vectors, rotations, and keys previously converted into strings by list2js() are left as strings. Objects are converted into lists with a stride of 2, where even-numbered elements are the original JSON's keys and odd-numbered elements are their corresponding values.
#define js2list llJson2List
// string jsarray(list x): returns a JSON string containing the elements of list x. All types other than integers and floats will be converted into quoted strings, but strings that resemble JSON (due to starting and ending with [] or {}) will be interpreted as JSON.
#define jsarray(...) llList2Json(JSON_ARRAY, __VA_ARGS__)
// string jsobject(list x): eturns a string that is a representation of the values in list x encoded into a JSON object. The source list will be interpreted as having a stride of 2, with even-numbered elements becoming the keys of the new object, and odd-numbered elements becoming values. For values, all types other than integers and floats will be converted into quoted strings, but strings that resemble JSON (due to starting and ending with [] or {}) will be interpreted as JSON.
#define jsobject(...) llList2Json(JSON_OBJECT, __VA_ARGS__)
// list jskeys(string json): returns a list containing only the key names of the provided JSON object. Will misbehave if fed a JSON array by accident.
#define jskeys(__jso) llList2ListStrided(llJson2List(__jso), 0, LAST, 2)
// linked(integer t, integer n, string m, key id): sends a link_message() event to linked prim t, with the parameters n, m, and id. Be careful when using link_message() for complex applications, as it can not only run out of event queue space (~64 events can be queued before silent dropping occurs), but also trigger an immense amount of pointless LSL executions (64 messages received by 64 scripts = 4096 events) that severely impact sim performance. For a tight, script-to-script communication method, use llListen() with the UUID set, as this filtering is done outside LSL.
#define linked llMessageLinked
// list split(string haystack, string sep): returns the fragments of a string haystack split at instances of a single separator string, sep. Consecutive separators will be merged.
#define split(_haystack, _needle) llParseString2List(_haystack, (list)(_needle), [])
// list splitnulls(string haystack, string sep): returns the fragments of a string haystack split at instances of a single separator string, sep. Consecutive separators will result in empty elements.
#define splitnulls(_haystack, _needle) llParseStringKeepNulls(_haystack, (list)(_needle), [])
// string concat(list x, string sep): returns a concatenated form of the elements of list x, inserting string sep between each pair of elements.
#define concat llDumpList2String
// list delrange(list b, integer x, integer y): returns a copy of a list with the subrange [x, y] removed, inclusive of both endpoints; negative indices count from the end of the list
#define delrange llDeleteSubList
// list delitem(list b, integer n): returns a copy of a list with the element at position n removed; negative indices count from the end of the list. This macro cannot handle list literals, so wrap them in (parens)
#define delitem(_haystack, _index) llDeleteSubList(_haystack, _index, _index)
// list insert(list a, list b, integer n): returns a copy of list a with the contents of list b inserted before element n; to append to the end of a list, use: a + b
#define insert llListInsertList
// list shuffle(list x): returns the list with its elements in random order
#define shuffle llListRandomize
// string replace(string haystack, string needle, string replacement): returns a copy of haystack with all instances of needle replaced by replacement
#define replace(_haystack, _old, _new) llReplaceSubString(_haystack, _old, _new, 0)
// string vec2str(vector v): returns a string "x y z" from a vector <x, y, z>; argument must be a variable name, not a literal, see also vec2str2() which is slower but does not have this restriction
#define vec2str(_vec) ((string)_vec.x + " " + (string)_vec.y + " " + (string)_vec.z)
// string vec2str2(vector v): returns a string "x y z" from a vector <x, y, z>; argument may be a literal, but generated code is worse than vec2str()
#define vec2str2(_vec) substr(replace((string)_vec, ", ", " "), 1, -2)
// vector str2vec(string v): returns a vector <x, y, z> from a string that describes a vector in the format "x y z".
#define str2vec(__str) (vector)("<" + replace(__str, " ", ",") + ">")
// "Nothing is true - all is permissible."
// - Hassan i Sabbah
// string format_percentage(float f): converts a float to a percentage, e.g. 0.5 to 50%, or -2.31 to -231%. Always returns an integer.
#define format_percentage(xxx) ((string)((integer)((xxx) * 100)) + "%")
// string format_float(float f): truncates a float to the specified precision after the decimal point, e.g. format_float(0.010999, 3) == "0.010"; always rounds toward 0
#define format_float(_number, _places) llGetSubString( (string) _number, 0, llSubStringIndex( (string) _number, ".") + _places )
// list list_union(list x, list y): returns a list containing the elements of list x, followed by any elements of list y that did not appear in list x
list list_union(list ly, list lx) { /* add the lists, eliminating duplicates */
list lz = [];
integer x = count(ly);
while(x--)
if(!~llListFindList(lx, sublist(ly, x, x)))
lz = sublist(ly, x, x) + lz;
return lx + lz;
}
// list list_exclude(list x, list y): returns a list containing the elements of list x that did not appear in list y
list list_exclude(list ly, list lx) { /* remove entries in second list from first */
list lz = [];
integer x = count(ly);
while(x--)
if(!~llListFindList(lx, sublist(ly, x, x)))
lz = sublist(ly, x, x) + lz;
return lz;
}
// list list_intersect(list x, list y): returns a list containing the elements from list x that are also in list y; if x contains duplicates, these will be preserved
list list_intersect(list ly, list lx) { /* remove entries not in both lists */
list lz = [];
integer x = count(ly);
while(x--)
if(~llListFindList(lx, sublist(ly, x, x)))
lz = sublist(ly, x, x) + lz;
return lz;
}
// list list_unique(list x): returns a list containing only the unique elements of list x; if x contains duplicates, these will appear only at their first position
list list_unique(list lx) { /* remove duplicates */
integer x = count(lx);
while(x--)
if(llListFindList(lx, sublist(lx, x, x)) != x)
lx = delitem(lx, x);
return lx;
}
// float sum(list x): returns the total sum of all numeric elements in the list
#define sum(...) llListStatistics(LIST_STAT_SUM, __VA_ARGS__)
// float sum_sq(list x): returns the total sum of all numeric elements in the list, squaring each one first
#define sum_sq(...) llListStatistics(LIST_STAT_SUM_SQUARES, __VA_ARGS__)
// float mean(list x): returns the average of all numeric elements in the list
#define mean(...) llListStatistics(LIST_STAT_MEAN, __VA_ARGS__)
// float min(list x): returns the lowest of all numeric elements in the list
#define min(...) llListStatistics(LIST_STAT_MIN, __VA_ARGS__)
// float max(list x): returns the highest of all numeric elements in the list
#define max(...) llListStatistics(LIST_STAT_MAX, __VA_ARGS__)
// float median(list x): returns the median (middle value) of all numeric elements in the list; if the number of numeric elements is even, returns the average of those two
#define median(...) llListStatistics(LIST_STAT_MEDIAN, __VA_ARGS__)
// float range(list x): returns the difference between the lowest and highest of the numeric elements in the list
#define range(...) llListStatistics(LIST_STAT_RANGE, __VA_ARGS__)
// float std_dev(list x): calculates the average of all numeric elements in the list, then the squared differences between each numeric element and that average, then returns the square root of the average of these squared differences
#define std_dev(...) llListStatistics(LIST_STAT_STD_DEV, __VA_ARGS__)
// float geom_mean(list x): returns the product of all the numeric elements in the list, raised to the power 1/n, where n is the number of numeric elements in the list
#define geom_mean(...) llListStatistics(LIST_STAT_GEOMETRIC_MEAN, __VA_ARGS__)
// float countn(list x): returns the number of numeric elements in the list
#define countn(...) llListStatistics(LIST_STAT_NUM_COUNT, __VA_ARGS__)
// integer validate_key(key k): returns TRUE if k is a valid UUID, otherwise FALSE
integer validate_key(key k) { /* slow, but correct */
string sk = llToLower((string)k);
return (
strlen(sk) == 36
&& !count(split((string)llParseString2List((string)llParseString2List(sk, ["0","1","2","3","4","5","6","7"], []), ["8", "9", "a", "b", "c", "d", "e", "f"], []), "-")) /* llParseString2List can't handle more than 8 separators */
&& count(splitnulls(sk, "-")) == 5
&& (substr(sk, 8, 8) + substr(sk, 13, 13) + substr(sk, 18, 18) + substr(sk, 23, 23)) == "----"
);
}
// list remap(list old, list new, list input): iterates over input, translating its elements from the old list to the new list, assuming the elements of old and new correspond in indices; e.g. remap(["red", "green", "blue"], ["cyan", "magenta", "yellow"], ["blue", "blue", "green"]) ==> ["yellow", "yellow", "magenta"]
list remap(list keys, list values, list input) {
list results;
integer i = count(input);
while(i--) {
integer j = llListFindList(keys, sublist(input, i, i));
if(~j)
results = sublist(values, j, j) + results;
}
return results;
}
/* unicode management functions */
// string strleft_byte(string s, integer b): returns a string containing characters from the start of s, so long as they take up no more than b bytes. Fragmented characters are discarded.
string strleft_byte(string str, integer bytes) {
string temp = llStringToBase64(str);
if(strlen(temp) <= (bytes * 8) / 6)
return str;
temp = llBase64ToString(substr(temp+"==", (bytes * -2) % 3, (bytes * 4 - 1) / 3));
integer i = strlen(temp) - 1;
if(substr(temp, i, i) != substr(str, i, i))
return delstring(temp, i, i); /* last char was multi-byte and was truncated */
return temp;
}
// integer strlen_byte(string s): returns the length of the provided Unicode string in bytes rather than characters (as strlen() would)
integer strlen_byte(string str) {
return (( (3 * strpos(llStringToBase64(str)+"=", str = "=")) >> 2 ));
}
// integer strlen_byte_inline(string s): returns the length of the provided Unicode string in bytes rather than characters (as strlen() would) - inline macro version of the strlen_byte() function
#define strlen_byte_inline(str) (( (3 * strpos(llStringToBase64(str)+"=", "=")) >> 2 ))
// string format_time(float number): returns "x days, hh:NN:SS" format for a number of provided seconds, leaving off days or hours for smaller values; negative values are presented wrapped in -()
string format_time(float number) {
integer negative;
if(number < 0) {
negative = TRUE;
number = llFabs(number);
}
string output;
number += 0.5;
integer secs = (integer)(number) % 60;
integer minutes = (integer)(number / 60) % 60;
integer hours = (integer)(number / 3600) % 24;
integer days = (integer)(number / 86400);
if(days == 1)
output = "1 day, ";
else if(days)
output = (string)days + " days, ";
// if(hours > 0 || days > 0)
output += (string)hours + ":";
if(minutes < 10)
output += "0";
output += (string)minutes + ":";
if(secs < 10)
output += "0";
output += (string)secs;
if(negative)
output = "-(" + output + ")";
return output;
}
// string hex(integer n): converts an integer to lower-case hexadecimal using as few characters as possible; no prefix or suffix is appended
string hex(integer bits) {
string nybbles = "";
do {
integer lsn = bits & 0xF; // least significant nybble
nybbles = substr("0123456789abcdef", lsn, lsn) + nybbles;
} while (bits = (0xfffFFFF & (bits >> 4)));
return nybbles;
}
/* from http://wiki.secondlife.com/wiki/Efficient_Hex */
// various semantic negative one replacements:
// NOWHERE: use this to represent -1 when it indicates an element is not present, e.g. strpos("x", "y") == NOWHERE
#define NOWHERE 0xFFFFFFFF
// LAST: use this to represent -1 when it indicates the last position in a string or list, e.g. substr(s, 0, LAST) == s
#define LAST 0xFFFFFFFF
// SAFE_EOF: a string encoding of U+0004 (the ASCII EOF control code), used as an in-band record separator in some protocols, especially Refactor Robotics Aurora. Some preprocessors do not like seeing literal ASCII control codes. ARES redefines this as U+007F (ASCII DEL), which has much better support and will not cause deserialization failures if it is stored on the heap during a sim transfer.
#define SAFE_EOF llChar(4)
// SOUND_DEFAULT: a placeholder sound, meant to complement SL's TEXTURE_DEFAULT
#define SOUND_DEFAULT "f1adc36c-4c3f-081b-4ab2-fa5105d80561"
#endif // UTILS

235
variatype.h.lsl Normal file
View File

@ -0,0 +1,235 @@
#ifndef _VARIATYPE_H_
#define _VARIATYPE_H_
/* =========================================================================
*
* Nanite Systems Applied Research Execution System
*
* Copyright (c) 20222023 Nanite Systems Corporation
*
* =========================================================================
*
* VariaType text engine
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* 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.
*
* =========================================================================
*
*/
// define this to prevent positioning (for pre-positioned labels):
// #define VT_NO_POSITIONING
// otherwise use this:
#ifndef VT_NO_POSITIONING
#ifndef PIXEL_SCALE
#define PIXEL_SCALE
float pixel_scale;
#endif
#endif
#define PSYCHIC_INSTABILITY 1
#define IGNEOUS_EXTRUSIVE 2
#define MURKY_TRUTH 3
#define CLOCK_SKEW 4
#if VT_FONT == PSYCHIC_INSTABILITY
// Psychic Instability
list FONT = [TEXTURE_TRANSPARENT, "fa2be25b-b84e-ae3a-39b7-be9159562538", "9586e45b-81d7-30af-7fef-944456ad2a4c", "95c1288d-b624-3ba0-98e1-766c4976cb4c",
TEXTURE_TRANSPARENT, "f2616b27-bba8-33af-0d38-b83706dc32ce", "3e1225be-2c07-160d-e047-0b27aed9a80c", "5bcccd5f-3b07-1a78-0086-f1c78d5d9e6a"];
#define FONT_charwidths "21111111111111111111111111111111111222211122121122222222221122222222222221222332222222232221112212222212211213222212122322211121"
#elif VT_FONT == IGNEOUS_EXTRUSIVE
// Igneous Extrusive
list FONT = [TEXTURE_TRANSPARENT, "62974d79-964a-5f6f-da7a-fa71d0729fbf", "3e775d1a-47b7-987d-950e-76d86dba4d65", "ff7b660d-4404-041f-34ec-29c99fe2299d",
TEXTURE_TRANSPARENT, "8ad2c9e1-f8d6-744b-daef-5dbe06e07477", "29d05527-9dff-2595-6d43-179558027cfa", "a251039a-3fdf-70c1-9664-14ef2a0e4558"];
#define FONT_charwidths "21111111111111111111111111111111111222211122121122222222221122222222222221222332222222232221112212222212211213222212122322211121"
#elif VT_FONT == MURKY_TRUTH
// Murky Truth
list FONT = [TEXTURE_TRANSPARENT, "b17c85ad-dbb4-5e08-0bcd-f9bcc911958d", "63d0b7da-3ed9-4eb2-6878-317d4b810f16", "266f9977-0a97-f4b2-0dd8-58b54b6a6858",
TEXTURE_TRANSPARENT, "2edab492-6ddc-5c9f-3bbc-e2ee1cb4d5f0", "d1fad11b-97fd-de31-2dbe-aad04a3b51ab", "fc1fd1b3-f691-3e16-a0d5-5415375e4d6e"];
// Murky Truth uses a different character width table from PI and IX. The capital N is only 2 cells wide instead of 3.
#define FONT_charwidths "21111111111111111111111111111111111222211122121122222222221122222222222221222322222222232221112212222212211213222212122322211121"
#elif VT_FONT == CLOCK_SKEW || !defined(VT_FONT)
// Clock Skew
list FONT = ["8dcd4a48-2d37-4909-9f78-f7a9eb4ef903", "66f0c644-a340-9772-f870-9b30051db9d9", "57b9b8a5-ccb1-72e0-2429-916d25ace382", "d8903765-b390-4707-0397-18d558fcdfa5",
"8dcd4a48-2d37-4909-9f78-f7a9eb4ef903", "ad63bb36-f8e1-074a-24a2-40fcd1ea8baa", "bd83ac93-51c8-6515-c98f-33ca21c6516a", "251c5c6f-14c2-cd25-6e91-e9c08e06a677"];
// Clock Skew's characters use 2 cells for r, f, t, /, \, {, and }, and 3 cells for @
string FONT_charwidths = "21111111111111111111111111111111111222211122121222222222221122223222222221222322222222232221212212222222211213222222222322221221";
#endif
// with VT_NO_POSITIONING: variatype(text, start_prim, prim_limit)
// otherwise: variatype(text, start_prim, prim_limit, origin) and remember to set pixel_scale
// if using positioning, Riders 8 Face vert/horiz prims are expected;
// other shapes will require adaptation
// send all setp commands twice? (helps against packet loss)
integer DOUBLE_PRINT;
variatype(string text, integer start_prim, integer prim_limit
#ifndef VT_NO_POSITIONING
, vector origin
#endif
#ifdef VT_COLOR
, vector color
#endif
) {
text += " ";
integer c = 0; // character
integer f = 0; // face
integer p = 0; // pixel
integer prim;
integer face;
integer cmax = strlen(text) + 1;
integer fmax = prim_limit << 3;
list acts;
while(c < cmax && f < fmax) {
integer c0 = llOrd(text, c);
if(c0 == 0x09) {
f = (f + 8);
f -= f % 8;
c += 1;
p = 0;
c0 = llOrd(text, c);
}
integer c1 = llOrd(text, c + 1);
integer p0 = (integer)substr(FONT_charwidths, c0, c0) << 2;
integer p1 = (integer)substr(FONT_charwidths, c1, c1) << 2;
integer si = (c0 >> 5) + 4 * (c1 >> 6);
string section = gets(FONT, si);
// echo("c=" + (string)c + " printed (" + llChar(c0) + llChar(c1) + "), width " + (string)(p0 + p1) + " column offset " + (string)p);
prim = start_prim + (f >> 3);
face = f & 7;
if(!face) {
if(llGetFreeMemory() < 4092) {
setp(0, acts);
if(DOUBLE_PRINT) {
llSleep(0.01);
setp(0, acts);
}
acts = [];
}
acts += [
PRIM_LINK_TARGET, prim,
#ifndef VT_NO_POSITIONING
PRIM_SIZE, <64, 16, 0> * pixel_scale,
PRIM_POSITION, (origin - <0, 64 * (f >> 3), 0>) * pixel_scale,
#ifdef VISIBLE
PRIM_ROTATION, VISIBLE,
#endif
#endif
#ifdef VT_COLOR
PRIM_COLOR, ALL_SIDES, color, 1,
#endif
PRIM_TEXTURE, ALL_SIDES, TEXTURE_TRANSPARENT, ZV, ZV, 0
];
}
acts += [
PRIM_TEXTURE, face, section,
<0.0078125, 0.015625, 0>,
<(float)(c0 & 31) / 32.0 - 0.5 + ((4.0 + p) / 1024.0),
0.5 - (float)(c1 & 63) / 64.0 - 0.0078125, 0>, 0
];
++f;
integer w = p0 + p1;
if(p == 0) {
if(w == 8) {
c += 2;
} else if(p0 == 4) {
c += 1;
p = 4;
} else if(p0 == 8) {
c += 1;
} else if(p0 == 12) {
p = 8;
}
} else if(p == 4) { // p0 == 8 or p0 == 12
if(p0 == 8) {
if(p1 == 4) {
c += 2;
p = 0;
} else
c += 1;
} else if(p0 == 12) {
c += 1;
p = 0;
}
} else if(p == 8) { // p0 == 12
if(p1 == 4) {
c += 2;
p = 0;
} else {
c += 1;
p = 4;
}
}
if(c1 == 0x09) {
p = 0;
f = (f + 8);
f -= f % 8;
}
}
if(face < 7) {
while(face < 8) {
acts += [
PRIM_TEXTURE, face++, TEXTURE_TRANSPARENT, ZV, ZV, 0
];
}
}
integer last_prim = start_prim + prim_limit - 1;
if(prim < last_prim) {
while(prim < last_prim) {
++prim;
acts += [
PRIM_LINK_TARGET, prim,
#ifndef VT_NO_POSITIONING
PRIM_POSITION, (origin - <0, 64 * (prim - start_prim), 0>) * pixel_scale,
#endif
PRIM_TEXTURE, ALL_SIDES, TEXTURE_TRANSPARENT, ZV, ZV, 0
];
}
}
setp(0, acts);
if(DOUBLE_PRINT) {
llSleep(0.01);
setp(0, acts);
}
}
#endif // _VARIATYPE_H_

13
version-strings.txt Normal file
View File

@ -0,0 +1,13 @@
Known Version Strings
8.0.4
8.2.0
8.2.11
Companion/8.3.3
Companion/8.3.11
Companion/8.6.4
Clockwork/8.6.3
ATOS/12.1.0
ARES/0.4.0
OeM/4.0
KOR/24.03.08