// SPDX-License-Identifier: MIT // Copyright (c) 2026 Incoprea // Check installed package versions against requirements // Not compiled or tested in-world // Setup: Create a saved plain-text notecard named Data. Use package|minVersion rows. Edit INSTALLED package/version pairs in the script. Touch as owner to load it; touch again to use the cached content. // Stop / restore: Remove or disable. Reset or an inventory change discards the cache. No notecards are changed. // Notes: At most 60 lines, 300 characters per line, and 6000 total characters; blank lines and # comments are omitted. Incomplete reads are rejected. Both versions use three components 0–999. These are authored version labels, not an automatic security or compatibility guarantee. // License: see LICENSE in this distribution. // Original Incoprea example. MIT. integer whole(string s) { if(s=="" || llStringLength(s)>6)return FALSE; integer i; for(i=0;i999)return FALSE; return TRUE; } integer versionCode(string s) { list p=llParseStringKeepNulls(s,["."],[]); return llList2Integer(p,0)*1000000+llList2Integer(p,1)*1000+llList2Integer(p,2); } list INSTALLED=["lighting","1.2.0","door","2.0.1"]; list lines; integer lineNumber; integer characters; integer loaded; key pending=NULL_KEY; request() { pending=llGetNotecardLine("Data",lineNumber); llSetTimerEvent(20); } run() { if(llGetListLength(INSTALLED)%2) { llOwnerSay("Installed manifest malformed."); return; } list keys; integer i; for(i=0;i=60 || llStringLength(data)>300 || characters>6000) { pending=NULL_KEY; llSetTimerEvent(0); lines=[]; llOwnerSay("Read rejected: card exceeds bounds."); return; } data=llStringTrim(data,STRING_TRIM); if(data!="" && llGetSubString(data,0,0)!="#")lines += [data]; ++lineNumber; request(); } timer() { pending=NULL_KEY; lines=[]; llSetTimerEvent(0); llOwnerSay("Read timed out; touch to retry."); } changed(integer flags) { if(flags & (CHANGED_OWNER|CHANGED_INVENTORY))llResetScript(); } }