diff --git a/Peril_Dice_Updater.lsl b/Peril_Dice_Updater.lsl index adb66bb..2c388ea 100644 --- a/Peril_Dice_Updater.lsl +++ b/Peril_Dice_Updater.lsl @@ -1,49 +1,66 @@ // ==================================================================== -// Peril Dice GitHub Updater - External Updater Box +// Peril Dice Updater - External Update Box // ==================================================================== -// This script goes in a separate updater box object that users rez temporarily. -// Downloads scripts directly from GitHub and installs them automatically -// in target Peril Dice games via llRemoteLoadScriptPin(). -// -// USAGE: -// 1. User rezzes this updater box near their Peril Dice game -// 2. User touches their game -> "Check for Updates" -> "Install Update" -// 3. Game communicates with this updater box for automatic installation -// 4. User deletes updater box when finished +// This script goes in a standalone object (the "Updater Box") +// It downloads scripts from GitHub and installs them into the game // ==================================================================== -string GITHUB_MANIFEST_URL = "https://raw.githubusercontent.com/RebeccaNod1/Peril/main/peril-dice-manifest.json"; -string UPDATER_VERSION = "2.8.8"; +string CURRENT_VERSION = "2.8.8"; +string GITHUB_API_URL = "https://api.github.com/repos/RebeccaNod1/Peril/releases/latest"; +string GITHUB_RAW_URL = "https://raw.githubusercontent.com/RebeccaNod1/Peril/main/"; // Communication channels -integer UPDATER_CHANNEL = -7723847; // Unique channel for updater communication -integer RESPONSE_TIMEOUT = 30; // seconds +integer UPDATER_CHANNEL = -7723847; // Must match Update_Receiver.lsl -// Current operation state -string currentOperation = ""; -key targetGameKey = NULL_KEY; -integer updatePin = 0; -list scriptQueue = []; -integer currentScriptIndex = 0; -string manifestData = ""; +// State tracking +key targetGame = NULL_KEY; +integer targetPin = 0; +list downloadQueue = []; +string currentFile = ""; +integer installIndex = 0; +integer isBusy = FALSE; // HTTP tracking key currentHttpRequest = NULL_KEY; -string downloadingScript = ""; +string currentOperation = ""; -// UI and status -integer isListening = FALSE; +// Script manifest - List of all scripts to update +// Format: "ScriptName.lsl|LinkNumber" +// Note: Link numbers will be verified via UUID request +list SCRIPT_MANIFEST = [ + "Main_Controller_Linkset.lsl|1", + "Game_Manager.lsl|1", + "Controller_Memory.lsl|1", + "Controller_MessageHandler.lsl|1", + "Player_RegistrationManager.lsl|1", + "Player_DialogHandler.lsl|1", + "NumberPicker_DialogHandler.lsl|1", + "Floater_Manager.lsl|1", + "Roll_ConfettiModule.lsl|1", + "Bot_Manager.lsl|1", + "Game_Calculator.lsl|1", + "Verbose_Logger.lsl|1", + "System_Debugger.lsl|1", + "Update_Receiver.lsl|1", // Updates the receiver itself! + "Game_Scoreboard_Manager_Linkset.lsl|12", + "Leaderboard_Communication_Linkset.lsl|35", + "XyzzyText_Dice_Bridge_Linkset.lsl|83", + "xyzzy_Master_script.lsl|XYZZY" // Special handling for xyzzy +]; -// Memory usage reporting -reportMemoryUsage(string scriptName) { - integer memory = llGetUsedMemory(); - integer freeMemory = llGetFreeMemory(); - float memoryPercent = (float)memory / (memory + freeMemory) * 100.0; - llOwnerSay("📊 " + scriptName + ": " + (string)memory + " bytes used (" + - llGetSubString((string)memoryPercent, 0, 4) + "% memory)"); +// Map of Link Number -> UUID (populated by game response) +list linkUUIDs = []; // Format: [LinkNum, UUID, LinkNum, UUID...] + +// Helper to get UUID for a link number +key getLinkUUID(integer linkNum) { + integer idx = llListFindList(linkUUIDs, [linkNum]); + if (idx != -1) { + return llList2Key(linkUUIDs, idx + 1); + } + return NULL_KEY; } -// Extract JSON value manually (works around LSL 2048-char limit) +// Manual JSON parsing (same as receiver) string extractJsonValue(string json, string keyName) { string quote = "\""; string searchPattern = quote + keyName + quote + ":" + quote; @@ -57,221 +74,208 @@ string extractJsonValue(string json, string keyName) { return llGetSubString(json, valueStart, valueStart + valueEnd - 1); } -// Parse script list from manifest (simplified for LSL limitations) -list parseScriptList(string manifest) { - list scripts = []; - // This is a simplified parser - in production might need more robust parsing - // For now, we'll use the script URLs directly - - // Add all essential scripts in priority order (URLs keep .lsl, but script names don't) - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/Main_Controller_Linkset.lsl|1|Main_Controller_Linkset"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/Game_Manager.lsl|1|Game_Manager"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/Controller_Memory.lsl|1|Controller_Memory"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/Controller_MessageHandler.lsl|1|Controller_MessageHandler"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/Player_RegistrationManager.lsl|1|Player_RegistrationManager"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/Player_DialogHandler.lsl|1|Player_DialogHandler"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/NumberPicker_DialogHandler.lsl|1|NumberPicker_DialogHandler"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/Floater_Manager.lsl|1|Floater_Manager"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/Roll_ConfettiModule.lsl|1|Roll_ConfettiModule"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/Bot_Manager.lsl|1|Bot_Manager"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/Game_Calculator.lsl|1|Game_Calculator"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/Verbose_Logger.lsl|1|Verbose_Logger"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/System_Debugger.lsl|1|System_Debugger"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/Update_Receiver.lsl|1|Update_Receiver"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/Game_Scoreboard_Manager_Linkset.lsl|12|Game_Scoreboard_Manager_Linkset"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/Leaderboard_Communication_Linkset.lsl|35|Leaderboard_Communication_Linkset"]; - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/XyzzyText_Dice_Bridge_Linkset.lsl|83|XyzzyText_Dice_Bridge_Linkset"]; - - // Add XyzzyText script for links 35-82 (48 prims) - integer i; - for (i = 35; i <= 82; i++) { - scripts += ["https://raw.githubusercontent.com/RebeccaNod1/Peril/main/xyzzy_Master_script.lsl|" + (string)i + "|xyzzy_Master_script"]; - } - - return scripts; -} - -// Start update process -startUpdate(key gameKey, integer pin) { - targetGameKey = gameKey; - updatePin = pin; - currentOperation = "updating"; - currentScriptIndex = 0; - - // Get script list (simplified - using hardcoded list for now) - scriptQueue = parseScriptList(""); - - llOwnerSay("🔄 Starting update process..."); - llOwnerSay("📊 Installing " + (string)llGetListLength(scriptQueue) + " scripts"); - - // Start with first script - downloadNextScript(); -} - -// Download next script in queue -downloadNextScript() { - if (currentScriptIndex >= llGetListLength(scriptQueue)) { - // Update complete! - completeUpdate(); +// Start the update process for a specific game +startUpdate(key gameId, string version, integer pin) { + if (isBusy) { + llRegionSayTo(gameId, UPDATER_CHANNEL, "UPDATE_BUSY"); return; } - string scriptEntry = llList2String(scriptQueue, currentScriptIndex); - list parts = llParseString2List(scriptEntry, ["|"], []); - string url = llList2String(parts, 0); - string linkNum = llList2String(parts, 1); - string scriptName = llList2String(parts, 2); // Now using explicit script name + isBusy = TRUE; + targetGame = gameId; + targetPin = pin; - downloadingScript = scriptName; + llOwnerSay("🚀 Starting update for game " + (string)gameId); + llRegionSayTo(targetGame, UPDATER_CHANNEL, "UPDATE_STARTING|" + version); - llOwnerSay("📥 Downloading " + scriptName + " for link " + linkNum + - " (" + (string)(currentScriptIndex + 1) + "/" + (string)llGetListLength(scriptQueue) + ")"); + // Step 1: Request Link UUIDs from the game + // We need these to install scripts to specific links + llRegionSayTo(targetGame, UPDATER_CHANNEL, "REQUEST_LINK_UUIDS"); + // Set timeout for UUID response + llSetTimerEvent(10.0); +} + +// Continue update after getting UUIDs +startDownloadSequence() { + installIndex = 0; + downloadNextScript(); +} + +downloadNextScript() { + if (installIndex >= llGetListLength(SCRIPT_MANIFEST)) { + // All done! + finishUpdate(); + return; + } + + string entry = llList2String(SCRIPT_MANIFEST, installIndex); + list parts = llParseString2List(entry, ["|"], []); + currentFile = llList2String(parts, 0); + string linkTarget = llList2String(parts, 1); + + llOwnerSay("📥 Downloading " + currentFile + "..."); + + string url = GITHUB_RAW_URL + currentFile; + currentOperation = "download_script"; currentHttpRequest = llHTTPRequest(url, [HTTP_METHOD, "GET"], ""); } -// EMERGENCY SAFE MODE - Only add new scripts, don't delete anything -installScript(string scriptContent, string scriptName, integer linkNumber) { - llOwnerSay("🆘 SAFE MODE: Adding " + scriptName + " as new script"); - llOwnerSay("📊 Size: " + (string)llStringLength(scriptContent) + " characters"); +installScript(string content) { + string scriptEntry = llList2String(SCRIPT_MANIFEST, installIndex); + list scriptParts = llParseString2List(scriptEntry, ["|"], []); + string filename = llList2String(scriptParts, 0); + string linkTargetStr = llList2String(scriptParts, 1); - // Only create new script - DO NOT DELETE ANYTHING - // This creates a script with auto-generated name containing the new content - llRemoteLoadScriptPin(targetGameKey, scriptContent, updatePin, TRUE, 0); + llOwnerSay("💾 Installing " + filename + "..."); - llOwnerSay("✅ Added new script with updated content"); - llOwnerSay("📝 You can now copy content from the new script to replace your old " + scriptName); + // Determine target UUIDs + list targets = []; + key targetUUID; - // Move to next script - currentScriptIndex++; - llSetTimerEvent(2.0); -} - -// Complete the update process -completeUpdate() { - llOwnerSay("🎉 Update installation complete!"); - llOwnerSay("📊 Installed " + (string)llGetListLength(scriptQueue) + " scripts successfully"); - - // Notify target game - if (targetGameKey != NULL_KEY) { - llRegionSayTo(targetGameKey, UPDATER_CHANNEL, "UPDATE_COMPLETE|" + UPDATER_VERSION); + if (linkTargetStr == "XYZZY") { + // Install to all xyzzy prims (links 35-82) + integer i; + for (i = 35; i <= 82; i++) { + targetUUID = getLinkUUID(i); + if (targetUUID != NULL_KEY) targets += [targetUUID]; + } + } else { + integer linkNum = (integer)linkTargetStr; + if (linkNum == 1) { + targets = [targetGame]; // Root prim is the game object itself + } else { + targetUUID = getLinkUUID(linkNum); + if (targetUUID != NULL_KEY) targets += [targetUUID]; + } } - // Reset state - currentOperation = ""; - targetGameKey = NULL_KEY; - updatePin = 0; - scriptQueue = []; - currentScriptIndex = 0; - - llOwnerSay("💡 Update complete! You can now delete this updater box."); -} - -// Handle update errors -handleUpdateError(string error) { - llOwnerSay("❌ Update failed: " + error); - - // Notify target game of failure - if (targetGameKey != NULL_KEY) { - llRegionSayTo(targetGameKey, UPDATER_CHANNEL, "UPDATE_FAILED|" + error); + if (llGetListLength(targets) == 0) { + llOwnerSay("⚠️ No valid targets found for " + filename + " (Target: " + linkTargetStr + ")"); + // Skip but continue + installIndex++; + downloadNextScript(); + return; } - // Reset state - currentOperation = ""; - targetGameKey = NULL_KEY; - updatePin = 0; - scriptQueue = []; - currentScriptIndex = 0; + // Install to all targets + integer t; + for (t = 0; t < llGetListLength(targets); t++) { + key target = llList2Key(targets, t); + llRemoteLoadScriptPin(target, filename, targetPin, TRUE, 0); + } + + // Move to next + installIndex++; + downloadNextScript(); +} + +finishUpdate() { + llOwnerSay("✅ Update sequence complete!"); + llRegionSayTo(targetGame, UPDATER_CHANNEL, "UPDATE_COMPLETE|" + CURRENT_VERSION); + + isBusy = FALSE; + targetGame = NULL_KEY; + targetPin = 0; + linkUUIDs = []; } default { state_entry() { - reportMemoryUsage("Peril Dice GitHub Updater"); - llOwnerSay("🔄 Peril Dice GitHub Updater v" + UPDATER_VERSION); - llOwnerSay("📍 Ready to update nearby Peril Dice games"); - llOwnerSay("👆 Touch your Peril Dice game and select 'Check for Updates'"); - - // Listen for update requests from games - if (!isListening) { - llListen(UPDATER_CHANNEL, "", NULL_KEY, ""); - isListening = TRUE; - } - - // Set text to show status - llSetText("🔄 Peril Dice GitHub Updater v" + UPDATER_VERSION + "\\n" + - "Ready to install updates from GitHub\\n" + - "Touch your game to begin", <0.2, 1.0, 0.2>, 1.0); + llSetText("📦 Peril Dice Updater\nv" + CURRENT_VERSION + "\nTouch to check version", <0,1,0>, 1.0); + llListen(UPDATER_CHANNEL, "", NULL_KEY, ""); } - on_rez(integer start_param) { - llResetScript(); + touch_start(integer num) { + llOwnerSay("🔍 Checking GitHub for latest version..."); + currentOperation = "version_check"; + currentHttpRequest = llHTTPRequest(GITHUB_API_URL, [HTTP_METHOD, "GET"], ""); } listen(integer channel, string name, key id, string message) { - if (channel != UPDATER_CHANNEL) return; - - list parts = llParseString2List(message, ["|"], []); - string command = llList2String(parts, 0); - - if (command == "UPDATE_REQUEST") { - string version = llList2String(parts, 1); - integer pin = (integer)llList2String(parts, 2); + if (channel == UPDATER_CHANNEL) { + list parts = llParseString2List(message, ["|"], []); + string command = llList2String(parts, 0); - llOwnerSay("📨 Update request from " + name + " (current: " + version + ")"); - - if (currentOperation != "") { - llRegionSayTo(id, UPDATER_CHANNEL, "UPDATE_BUSY|Another update in progress"); - return; + if (command == "PING_UPDATER") { + llRegionSayTo(id, UPDATER_CHANNEL, "UPDATER_AVAILABLE|" + CURRENT_VERSION); + } + else if (command == "UPDATE_REQUEST") { + string version = llList2String(parts, 1); // Version requested (not used currently, we install latest) + integer pin = (integer)llList2String(parts, 2); + startUpdate(id, version, pin); + } + else if (command == "LINK_UUIDS_RESPONSE") { + if (id != targetGame) return; + + // Parse UUIDs: "12:uuid,35:uuid,..." + string payload = llList2String(parts, 1); + list pairs = llParseString2List(payload, [","], []); + + integer i; + for (i = 0; i < llGetListLength(pairs); i++) { + string pair = llList2String(pairs, i); + list kv = llParseString2List(pair, [":"], []); + if (llGetListLength(kv) == 2) { + integer ln = (integer)llList2String(kv, 0); + key uid = (key)llList2String(kv, 1); + + // Update or add + integer idx = llListFindList(linkUUIDs, [ln]); + if (idx != -1) { + linkUUIDs = llListReplaceList(linkUUIDs, [uid], idx+1, idx+1); + } else { + linkUUIDs += [ln, uid]; + } + } + } + + // Reset timer as we got data + llSetTimerEvent(5.0); // Wait a bit more for other chunks if any + + // If we have enough critical data, we can start + // For now, let's just wait for the timer to trigger the start + // This allows multiple chunks to arrive } - - // Start the update process - startUpdate(id, pin); - llRegionSayTo(id, UPDATER_CHANNEL, "UPDATE_STARTING|" + UPDATER_VERSION); - } - else if (command == "PING_UPDATER") { - // Respond to updater detection pings - llRegionSayTo(id, UPDATER_CHANNEL, "UPDATER_AVAILABLE|" + UPDATER_VERSION); } } timer() { - // Continue with next script installation - if (currentOperation == "updating") { - downloadNextScript(); - } llSetTimerEvent(0.0); + + if (isBusy && installIndex == 0) { + // Timer fired while waiting for UUIDs + if (llGetListLength(linkUUIDs) > 0) { + llOwnerSay("✅ Received link data. Starting installation..."); + startDownloadSequence(); + } else { + llOwnerSay("❌ Timed out waiting for Link UUIDs."); + llRegionSayTo(targetGame, UPDATER_CHANNEL, "UPDATE_FAILED|Timeout waiting for link data"); + isBusy = FALSE; + } + } } http_response(key request_id, integer status, list metadata, string body) { if (request_id != currentHttpRequest) return; - currentHttpRequest = NULL_KEY; - - if (status == 200) { - // Successfully downloaded script - string scriptEntry = llList2String(scriptQueue, currentScriptIndex); - list parts = llParseString2List(scriptEntry, ["|"], []); - integer linkNum = (integer)llList2String(parts, 1); - - // Install the script - installScript(body, downloadingScript, linkNum); - } else { - handleUpdateError("Failed to download " + downloadingScript + " (HTTP " + (string)status + ")"); + if (currentOperation == "version_check") { + if (status == 200) { + string latestVersion = extractJsonValue(body, "tag_name"); + llOwnerSay("📊 Latest GitHub Version: " + latestVersion); + llOwnerSay("📦 Updater Version: v" + CURRENT_VERSION); + } else { + llOwnerSay("❌ GitHub check failed: " + (string)status); + } } - } - - touch_start(integer total_number) { - key toucher = llDetectedKey(0); - - if (currentOperation == "updating") { - llOwnerSay("⏳ Update in progress - please wait..."); - llOwnerSay("📊 Installing script " + (string)(currentScriptIndex + 1) + - " of " + (string)llGetListLength(scriptQueue)); - } else { - llOwnerSay("🔄 Peril Dice GitHub Updater v" + UPDATER_VERSION); - llOwnerSay("📍 Ready to update Peril Dice games from GitHub"); - llOwnerSay("👆 Touch your Peril Dice game (not this updater) and select 'Check for Updates'"); - llOwnerSay("💡 This updater will automatically download and install scripts"); + else if (currentOperation == "download_script") { + if (status == 200) { + installScript(body); + } else { + llOwnerSay("❌ Failed to download " + currentFile + ": " + (string)status); + llRegionSayTo(targetGame, UPDATER_CHANNEL, "UPDATE_FAILED|Download failed for " + currentFile); + isBusy = FALSE; + } } } } \ No newline at end of file diff --git a/Traditional_Updater.lsl b/Traditional_Updater.lsl deleted file mode 100644 index 830a3a3..0000000 --- a/Traditional_Updater.lsl +++ /dev/null @@ -1,351 +0,0 @@ -// ==================================================================== -// Traditional Peril Dice Updater - Inventory-Based Script Copying -// ==================================================================== -// This script goes in an updater box that contains all the updated scripts -// in its inventory. It copies scripts from its inventory to the target game. -// -// SETUP: -// 1. Copy all updated LSL scripts into this updater box's inventory -// 2. User rezzes updater box near their Peril Dice game -// 3. User touches their game -> "Check for Updates" -> "Install Update" -// 4. Updater copies scripts from its inventory to their game -// 5. User deletes updater box when finished -// ==================================================================== - -string UPDATER_VERSION = "2.8.8"; -integer UPDATER_CHANNEL = -7723847; - -// Update state -key targetGameKey = NULL_KEY; -integer updatePin = 0; -list scriptList = []; -integer currentScriptIndex = 0; -string currentOperation = ""; - -// Script to link mapping - each script knows where it belongs -list SCRIPT_LINK_MAP = [ - // Root prim scripts (Link 1) - "Main_Controller_Linkset", "1", - "Game_Manager", "1", - "Controller_Memory", "1", - "Controller_MessageHandler", "1", - "Player_RegistrationManager", "1", - "Player_DialogHandler", "1", - "NumberPicker_DialogHandler", "1", - "Floater_Manager", "1", - "Roll_ConfettiModule", "1", - "Bot_Manager", "1", - "Game_Calculator", "1", - "Verbose_Logger", "1", - "System_Debugger", "1", - "Update_Receiver", "1", - // Other link scripts - "Game_Scoreboard_Manager_Linkset", "12", - "Leaderboard_Communication_Linkset", "35", - "XyzzyText_Dice_Bridge_Linkset", "83", - "xyzzy_Master_script", "35-82" // Special case - multiple links -]; - -// Extract unique script names for inventory checking -// NOTE: Update_Receiver is listed LAST to avoid interrupting the update process -list REQUIRED_SCRIPTS = [ - "Main_Controller_Linkset", - "Game_Manager", - "Controller_Memory", - "Controller_MessageHandler", - "Player_RegistrationManager", - "Player_DialogHandler", - "NumberPicker_DialogHandler", - "Floater_Manager", - "Roll_ConfettiModule", - "Bot_Manager", - "Game_Calculator", - "Verbose_Logger", - "System_Debugger", - "Game_Scoreboard_Manager_Linkset", - "Leaderboard_Communication_Linkset", - "XyzzyText_Dice_Bridge_Linkset", - "xyzzy_Master_script", - "Update_Receiver" // ALWAYS LAST - don't interrupt update process! -]; - -// Required objects that should be in updater inventory -list REQUIRED_OBJECTS = [ - "StatFloat" // Player status floater object -]; - -// Memory reporting -reportMemoryUsage(string scriptName) { - integer memory = llGetUsedMemory(); - integer freeMemory = llGetFreeMemory(); - float memoryPercent = (float)memory / (memory + freeMemory) * 100.0; - llOwnerSay("📊 " + scriptName + ": " + (string)memory + " bytes used (" + - llGetSubString((string)memoryPercent, 0, 4) + "% memory)"); -} - -// Get target link number for a script -string getTargetLink(string scriptName) { - integer i; - for (i = 0; i < llGetListLength(SCRIPT_LINK_MAP); i += 2) { - if (llList2String(SCRIPT_LINK_MAP, i) == scriptName) { - return llList2String(SCRIPT_LINK_MAP, i + 1); - } - } - return "1"; // Default to root prim if not found -} - -// Install script to specific link using llRemoteLoadScriptPin -installScriptToLink(string scriptName, string targetLink) { - if (targetLink == "35-82") { - // Special case: install xyzzy_Master_script to multiple links - llOwnerSay("🔄 Installing " + scriptName + " to links 35-82 (48 copies)..."); - integer i; - for (i = 35; i <= 82; i++) { - // Use link number as start_param to target specific link - llRemoteLoadScriptPin(targetGameKey, scriptName, updatePin, TRUE, i); - llOwnerSay("✅ Installed " + scriptName + " to link " + (string)i); - llSleep(0.1); // Brief pause between installations - } - } else { - // Regular installation to single link - integer linkNum = (integer)targetLink; - llOwnerSay("📝 Installing " + scriptName + " to link " + targetLink); - llRemoteLoadScriptPin(targetGameKey, scriptName, updatePin, TRUE, linkNum); - llOwnerSay("✅ Installed " + scriptName + " to link " + targetLink); - } -} - -// Check which scripts and objects are available in inventory -checkInventory() { - integer totalItems = 0; - integer availableItems = 0; - - llOwnerSay("📋 Checking updater inventory with link mapping..."); - - // Check scripts - integer i; - for (i = 0; i < llGetListLength(REQUIRED_SCRIPTS); i++) { - string scriptName = llList2String(REQUIRED_SCRIPTS, i); - string targetLink = getTargetLink(scriptName); - totalItems++; - - if (llGetInventoryType(scriptName) == INVENTORY_SCRIPT) { - availableItems++; - llOwnerSay("✅ [SCRIPT] " + scriptName + " → Link " + targetLink + " - Ready"); - } else { - llOwnerSay("❌ [SCRIPT] " + scriptName + " → Link " + targetLink + " - MISSING"); - } - } - - // Check objects - for (i = 0; i < llGetListLength(REQUIRED_OBJECTS); i++) { - string objectName = llList2String(REQUIRED_OBJECTS, i); - totalItems++; - - if (llGetInventoryType(objectName) == INVENTORY_OBJECT) { - availableItems++; - llOwnerSay("✅ [OBJECT] " + objectName + " - Ready"); - } else { - llOwnerSay("❌ [OBJECT] " + objectName + " - MISSING"); - } - } - - llOwnerSay("📊 Inventory Status: " + (string)availableItems + "/" + (string)totalItems + " items available"); - - if (availableItems == totalItems) { - llOwnerSay("✅ All required scripts and objects present - updater ready!"); - llOwnerSay("🔗 Scripts will be installed to their correct links automatically"); - llOwnerSay("📦 Objects will be given to target game inventory"); - } else { - llOwnerSay("⚠️ Missing items - please add them to updater inventory"); - } -} - -// Transfer all objects to target game first -transferObjects() { - llOwnerSay("📦 Transferring objects to target game..."); - - integer i; - for (i = 0; i < llGetListLength(REQUIRED_OBJECTS); i++) { - string objectName = llList2String(REQUIRED_OBJECTS, i); - if (llGetInventoryType(objectName) == INVENTORY_OBJECT) { - llGiveInventory(targetGameKey, objectName); - llOwnerSay("✅ Transferred " + objectName + " to target game"); - } - } - - llOwnerSay("📦 Object transfer complete - starting script installation..."); -} - -// Start the update process -startUpdate(key gameKey, integer pin) { - targetGameKey = gameKey; - updatePin = pin; - currentOperation = "updating"; - currentScriptIndex = 0; - - // Build script list for this specific update - scriptList = []; - - // Add all available scripts - integer i; - for (i = 0; i < llGetListLength(REQUIRED_SCRIPTS); i++) { - string scriptName = llList2String(REQUIRED_SCRIPTS, i); - if (llGetInventoryType(scriptName) == INVENTORY_SCRIPT) { - scriptList += [scriptName]; - } - } - - llOwnerSay("🔄 Starting update process..."); - llOwnerSay("📊 Installing " + (string)llGetListLength(scriptList) + " scripts"); - - // Transfer objects first, then install scripts - transferObjects(); - llSetTimerEvent(2.0); // Brief pause before starting script installation -} - -// Install next script in the queue -installNextScript() { - if (currentScriptIndex >= llGetListLength(scriptList)) { - // Update complete! - completeUpdate(); - return; - } - - string scriptName = llList2String(scriptList, currentScriptIndex); - string targetLink = getTargetLink(scriptName); - - llOwnerSay("🚀 Progress: " + (string)(currentScriptIndex + 1) + "/" + (string)llGetListLength(scriptList)); - - // Install script to correct link(s) - installScriptToLink(scriptName, targetLink); - - // Move to next script - currentScriptIndex++; - - // Longer pause for xyzzy_Master_script (48 installations) - if (targetLink == "35-82") { - llSetTimerEvent(10.0); // Extra time for multiple installations - } else { - llSetTimerEvent(2.0); // Normal pause - } -} - -// Complete the update process -completeUpdate() { - llOwnerSay("🎉 Update installation complete!"); - llOwnerSay("📊 Successfully installed " + (string)llGetListLength(scriptList) + " scripts"); - - // Notify target game - if (targetGameKey != NULL_KEY) { - llRegionSayTo(targetGameKey, UPDATER_CHANNEL, "UPDATE_COMPLETE|" + UPDATER_VERSION); - } - - // Reset state and stop timer - currentOperation = ""; - targetGameKey = NULL_KEY; - updatePin = 0; - scriptList = []; - currentScriptIndex = 0; - llSetTimerEvent(0.0); // Stop the timer - - llOwnerSay("💡 Update complete! You can now delete this updater box."); - llSetText("🎉 Update Complete!\nYou can delete this updater box\nThank you for updating!", - <0.2, 1.0, 0.2>, 1.0); -} - -// Handle update errors -handleUpdateError(string error) { - llOwnerSay("❌ Update failed: " + error); - - // Notify target game of failure - if (targetGameKey != NULL_KEY) { - llRegionSayTo(targetGameKey, UPDATER_CHANNEL, "UPDATE_FAILED|" + error); - } - - // Reset state and stop timer - currentOperation = ""; - targetGameKey = NULL_KEY; - updatePin = 0; - scriptList = []; - currentScriptIndex = 0; - llSetTimerEvent(0.0); // Stop the timer -} - -default { - state_entry() { - reportMemoryUsage("Traditional Peril Dice Updater"); - llOwnerSay("🔄 Traditional Peril Dice Updater v" + UPDATER_VERSION); - llOwnerSay("📦 Inventory-based script copying system"); - llOwnerSay("👆 Touch your Peril Dice game and select 'Check for Updates'"); - - // Check our inventory - checkInventory(); - - // Listen for update requests - llListen(UPDATER_CHANNEL, "", NULL_KEY, ""); - - // Set helpful text - llSetText("🔄 Peril Dice Updater v" + UPDATER_VERSION + "\n" + - "Traditional inventory-based system\n" + - "Touch your game, not this updater", <0.2, 1.0, 0.2>, 1.0); - } - - on_rez(integer start_param) { - llResetScript(); - } - - listen(integer channel, string name, key id, string message) { - if (channel != UPDATER_CHANNEL) return; - - list parts = llParseString2List(message, ["|"], []); - string command = llList2String(parts, 0); - - if (command == "UPDATE_REQUEST") { - string version = llList2String(parts, 1); - integer pin = (integer)llList2String(parts, 2); - - llOwnerSay("📨 Update request from " + name + " (current: " + version + ", PIN: " + (string)pin + ")"); - - if (currentOperation != "") { - llRegionSayTo(id, UPDATER_CHANNEL, "UPDATE_BUSY|Another update in progress"); - return; - } - - // Start the update process - startUpdate(id, pin); - llRegionSayTo(id, UPDATER_CHANNEL, "UPDATE_STARTING|" + UPDATER_VERSION); - } - else if (command == "PING_UPDATER") { - // Respond to updater detection pings - llRegionSayTo(id, UPDATER_CHANNEL, "UPDATER_AVAILABLE|" + UPDATER_VERSION); - } - } - - timer() { - // Continue with next script installation - if (currentOperation == "updating") { - installNextScript(); - // Don't turn off timer - installNextScript() will manage timing - } else { - // Only turn off timer if we're not updating - llSetTimerEvent(0.0); - } - } - - touch_start(integer total_number) { - key toucher = llDetectedKey(0); - - if (currentOperation == "updating") { - llOwnerSay("⏳ Update in progress for " + llKey2Name(targetGameKey)); - llOwnerSay("📊 Installing script " + (string)(currentScriptIndex + 1) + - " of " + (string)llGetListLength(scriptList)); - } else { - llOwnerSay("🔄 Traditional Peril Dice Updater v" + UPDATER_VERSION); - llOwnerSay("📦 Ready to update Peril Dice games using inventory scripts"); - llOwnerSay("👆 Touch your Peril Dice game (not this updater) and select 'Check for Updates'"); - llOwnerSay("📋 Checking inventory..."); - checkInventory(); - } - } -} \ No newline at end of file diff --git a/Update_Receiver.lsl b/Update_Receiver.lsl index f43dbea..c09fa86 100644 --- a/Update_Receiver.lsl +++ b/Update_Receiver.lsl @@ -316,6 +316,53 @@ default { else if (command == "UPDATE_BUSY") { llOwnerSay("⏳ Updater is busy with another installation"); } + else if (command == "REQUEST_LINK_UUIDS") { + // Updater is requesting our link UUIDs for proper script installation + llOwnerSay("📡 Updater requesting link UUIDs for linkset script installation..."); + + integer linkCount = llGetNumberOfPrims(); + + // Send critical links first + list criticalLinks = [12, 35, 83]; // Scoreboard, Leaderboard, Dice Bridge + string criticalPayload = ""; + + integer i; + for (i = 0; i < llGetListLength(criticalLinks); i++) { + integer linkNum = llList2Integer(criticalLinks, i); + if (linkNum <= linkCount) { + key linkUUID = llGetLinkKey(linkNum); + if (criticalPayload != "") criticalPayload += ","; + criticalPayload += (string)linkNum + ":" + (string)linkUUID; + } + } + llRegionSayTo(id, UPDATER_CHANNEL, "LINK_UUIDS_RESPONSE|" + criticalPayload); + llOwnerSay("✅ Sent critical link UUIDs (" + (string)llGetListLength(criticalLinks) + " links)"); + if (VERBOSE_LOGGING) llOwnerSay("📊 Critical UUID Data: " + criticalPayload); + + // Now send all xyzzy links (35-82) in safe-sized chunks as additional LINK_UUIDS_RESPONSE messages + if (linkCount >= 82) { + string chunk = ""; + integer countInChunk = 0; + integer j; + for (j = 35; j <= 82; j++) { + key luuid = llGetLinkKey(j); + string piece = (string)j + ":" + (string)luuid; + // Flush if adding would exceed ~900 chars or ~20 entries + if ((llStringLength(chunk) + 1 + llStringLength(piece)) > 900 || countInChunk >= 20) { + llRegionSayTo(id, UPDATER_CHANNEL, "LINK_UUIDS_RESPONSE|" + chunk); + chunk = ""; + countInChunk = 0; + } + if (chunk != "") chunk += ","; + chunk += piece; + countInChunk++; + } + if (chunk != "") { + llRegionSayTo(id, UPDATER_CHANNEL, "LINK_UUIDS_RESPONSE|" + chunk); + } + llOwnerSay("✅ Sent xyzzy UUIDs (links 35-82) in chunks"); + } + } } } diff --git a/lsl_validator.py b/lsl_validator.py index 5cb6af0..c6560d7 100755 --- a/lsl_validator.py +++ b/lsl_validator.py @@ -28,6 +28,11 @@ class LSLValidator: # Events 'touch_start', 'touch_end', 'listen', 'timer', 'link_message', 'collision_start', 'collision', 'collision_end', 'http_response', + 'sensor', 'no_sensor', 'control', 'land_collision_start', 'land_collision', + 'land_collision_end', 'at_target', 'not_at_target', 'at_rot_target', + 'not_at_rot_target', 'money', 'email', 'run_time_permissions', 'changed', + 'attach', 'dataserver', 'moving_start', 'moving_end', 'transaction_result', + 'path_update', 'remote_data', # String functions 'llStringLength', 'llGetSubString', 'llSubStringIndex', 'llToUpper', 'llToLower', 'llMD5String', 'llSHA1String', 'llEscapeURL', 'llUnescapeURL',