- Add error handling for malformed release notes - Parse critical fields first (tag_name, html_url) - Gracefully handle JSON parsing failures in release body - Add verbose logging for debugging API responses This fixes parsing errors when GitHub releases contain special characters.
404 lines
No EOL
18 KiB
Text
404 lines
No EOL
18 KiB
Text
// ====================================================================
|
||
// Update Checker - GitHub-based Update System for Peril Dice
|
||
// ====================================================================
|
||
// VERIFIED LINKSET PLACEMENT: Link 1 (Root Prim) "Peril Dice Controller V2"
|
||
// Based on actual linkset scan: 84 prims total, exactly matching architecture
|
||
// Replaces eliminated UpdateHelper.lsl from v2.8.5 memory optimization
|
||
//
|
||
// INTEGRATION:
|
||
// - Integrates with existing admin menu system via Player_DialogHandler.lsl
|
||
// - Uses link message communication following existing patterns
|
||
// - Memory optimized to fit alongside 13 other Link 1 scripts
|
||
//
|
||
// VERIFIED STRUCTURE (from linkset scan):
|
||
// - Link 1: Root controller with 13+ scripts
|
||
// - Link 12: "scoreboard manager cube" - Game_Scoreboard_Manager_Linkset.lsl
|
||
// - Link 35: "leaderboard row 1 col 1" - Leaderboard_Communication_Linkset.lsl
|
||
// - Links 35-82: XyzzyText prims (48 total) - each needs xyzzy_Master_script.lsl
|
||
// - Link 83: "dice col 1" - XyzzyText_Dice_Bridge_Linkset.lsl
|
||
// - Link 84: "dice col 2" - no script needed
|
||
// ====================================================================
|
||
|
||
string CURRENT_VERSION = "2.8.7";
|
||
string GITHUB_API_URL = "https://api.github.com/repos/RebeccaNod1/Peril/releases/latest";
|
||
string GITHUB_RAW_URL = "https://raw.githubusercontent.com/RebeccaNod1/Peril/main/";
|
||
|
||
// VERIFIED Link 1 Scripts (from actual scan of "Peril Dice Controller V2")
|
||
list LINK1_SCRIPTS = [
|
||
"Main_Controller_Linkset.lsl", // Master coordination
|
||
"Game_Manager.lsl", // Round management
|
||
"Controller_Memory.lsl", // Memory monitoring
|
||
"Controller_MessageHandler.lsl", // Message routing
|
||
"Player_RegistrationManager.lsl", // Player registry
|
||
"Player_DialogHandler.lsl", // Admin menus
|
||
"NumberPicker_DialogHandler.lsl", // Number picking
|
||
"Floater_Manager.lsl", // Floating HUDs
|
||
"Roll_ConfettiModule.lsl", // Dice rolling
|
||
"Bot_Manager.lsl", // AI players
|
||
"Game_Calculator.lsl", // Dice calculations
|
||
"Verbose_Logger.lsl", // Logging system
|
||
"System_Debugger.lsl" // Debug tools
|
||
];
|
||
|
||
// VERIFIED Link-Specific Scripts (from actual linkset scan)
|
||
list LINKSET_SPECIFIC_SCRIPTS = [
|
||
"Game_Scoreboard_Manager_Linkset.lsl", // Link 12: "scoreboard manager cube"
|
||
"Leaderboard_Communication_Linkset.lsl", // Link 35: "leaderboard row 1 col 1"
|
||
"XyzzyText_Dice_Bridge_Linkset.lsl" // Link 83: "dice col 1"
|
||
];
|
||
|
||
// XyzzyText system - VERIFIED 48 prims (Links 35-82)
|
||
string XYZZY_SCRIPT = "xyzzy_Master_script.lsl";
|
||
|
||
// Essential documentation files
|
||
list ESSENTIAL_DOCS = [
|
||
"README.md", "CHANGELOG.md", "README_Notecard.txt", "CHANGELOG_Notecard.txt"
|
||
];
|
||
|
||
// HTTP operation tracking
|
||
key currentHttpRequest = NULL_KEY;
|
||
string currentOperation = "";
|
||
integer updateCheckInProgress = FALSE;
|
||
integer downloadMode = FALSE;
|
||
string downloadingFile = "";
|
||
|
||
// Message constants - following existing Peril Dice patterns
|
||
integer MSG_ADMIN_MENU_RESPONSE = 888; // Integration with admin menu
|
||
integer MSG_UPDATE_CHECK_REQUEST = 2100; // Request from admin menu
|
||
integer MSG_TOGGLE_VERBOSE_LOGS = 9999; // Verbose logging toggle
|
||
|
||
// Internal verbose logging (follows existing pattern)
|
||
integer VERBOSE_LOGGING = FALSE;
|
||
|
||
// Memory usage reporting (matches existing scripts)
|
||
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)");
|
||
}
|
||
|
||
// Version comparison (handles "v2.8.6" and "2.8.6" formats)
|
||
integer isNewerVersion(string latestVersion, string currentVersion) {
|
||
// Remove 'v' prefix if present
|
||
if (llGetSubString(latestVersion, 0, 0) == "v") {
|
||
latestVersion = llGetSubString(latestVersion, 1, -1);
|
||
}
|
||
if (llGetSubString(currentVersion, 0, 0) == "v") {
|
||
currentVersion = llGetSubString(currentVersion, 1, -1);
|
||
}
|
||
|
||
return (latestVersion != currentVersion);
|
||
}
|
||
|
||
// Check GitHub API for updates
|
||
checkForUpdates() {
|
||
if (updateCheckInProgress) {
|
||
llOwnerSay("⏳ Update check already in progress...");
|
||
return;
|
||
}
|
||
|
||
updateCheckInProgress = TRUE;
|
||
currentOperation = "version_check";
|
||
llOwnerSay("🔍 Checking GitHub for Peril Dice updates...");
|
||
if (VERBOSE_LOGGING) {
|
||
llOwnerSay("🔗 API URL: " + GITHUB_API_URL);
|
||
}
|
||
|
||
currentHttpRequest = llHTTPRequest(GITHUB_API_URL, [
|
||
HTTP_METHOD, "GET"
|
||
], "");
|
||
}
|
||
|
||
// Download file content from GitHub raw repository
|
||
downloadFileContent(string filename) {
|
||
if (updateCheckInProgress && !downloadMode) {
|
||
llOwnerSay("⏳ Please wait for version check to complete...");
|
||
return;
|
||
}
|
||
|
||
// Validate file is in essential lists
|
||
if (llListFindList(LINK1_SCRIPTS, [filename]) == -1 &&
|
||
llListFindList(LINKSET_SPECIFIC_SCRIPTS, [filename]) == -1 &&
|
||
llListFindList(ESSENTIAL_DOCS, [filename]) == -1 &&
|
||
filename != XYZZY_SCRIPT) {
|
||
llOwnerSay("❌ File '" + filename + "' is not in the essential files list.");
|
||
llOwnerSay("📋 Say '/1 list' to see available files");
|
||
return;
|
||
}
|
||
|
||
downloadMode = TRUE;
|
||
downloadingFile = filename;
|
||
currentOperation = "file_download";
|
||
llOwnerSay("📥 Downloading " + filename + " from GitHub main branch...");
|
||
|
||
string url = GITHUB_RAW_URL + filename;
|
||
if (VERBOSE_LOGGING) {
|
||
llOwnerSay("🔗 Download URL: " + url);
|
||
}
|
||
|
||
currentHttpRequest = llHTTPRequest(url, [
|
||
HTTP_METHOD, "GET"
|
||
], "");
|
||
}
|
||
|
||
// Show help commands
|
||
showUpdateCommands() {
|
||
llOwnerSay("=== 🔄 PERIL DICE UPDATE SYSTEM ===");
|
||
llOwnerSay("📱 Current Version: v" + CURRENT_VERSION);
|
||
llOwnerSay("🌐 Repository: github.com/RebeccaNod1/Peril");
|
||
llOwnerSay("📍 Location: Link 1 (Root Prim) - 84-prim linkset verified");
|
||
llOwnerSay("💡 Note: GitHub releases start with v2.8.7+");
|
||
llOwnerSay("");
|
||
llOwnerSay("💬 Available Commands (say in local chat):");
|
||
llOwnerSay(" /1 check - Check GitHub for updates");
|
||
llOwnerSay(" /1 download ScriptName.lsl - Download specific file");
|
||
llOwnerSay(" /1 list - Show essential files with placement info");
|
||
llOwnerSay(" /1 help - Show these commands");
|
||
llOwnerSay("");
|
||
llOwnerSay("🎛️ Or use Owner Menu → Troubleshooting → Check for Updates");
|
||
llOwnerSay("📋 Example: /1 download xyzzy_Master_script.lsl");
|
||
}
|
||
|
||
// Show essential files list with VERIFIED linkset placement info
|
||
showEssentialFiles() {
|
||
llOwnerSay("=== 📋 ESSENTIAL GAME FILES (VERIFIED LINKSET STRUCTURE) ===");
|
||
|
||
llOwnerSay("🔧 Link 1 Scripts (Root Prim: \"Peril Dice Controller V2\"):");
|
||
integer i;
|
||
for (i = 0; i < llGetListLength(LINK1_SCRIPTS); i++) {
|
||
llOwnerSay(" • " + llList2String(LINK1_SCRIPTS, i));
|
||
}
|
||
llOwnerSay(" • Update_Checker.lsl (THIS SCRIPT)");
|
||
llOwnerSay("");
|
||
|
||
llOwnerSay("🎯 Link-Specific Scripts (VERIFIED placement):");
|
||
llOwnerSay(" • Game_Scoreboard_Manager_Linkset.lsl");
|
||
llOwnerSay(" → Link 12: \"scoreboard manager cube\"");
|
||
llOwnerSay(" • Leaderboard_Communication_Linkset.lsl");
|
||
llOwnerSay(" → Link 35: \"leaderboard row 1 col 1\"");
|
||
llOwnerSay(" • XyzzyText_Dice_Bridge_Linkset.lsl");
|
||
llOwnerSay(" → Link 83: \"dice col 1\"");
|
||
llOwnerSay("");
|
||
|
||
llOwnerSay("📺 XyzzyText Display System (VERIFIED 48 prims):");
|
||
llOwnerSay(" • xyzzy_Master_script.lsl");
|
||
llOwnerSay(" → Links 35-82: All leaderboard prims");
|
||
llOwnerSay(" → 4 banks × 12 rows each");
|
||
llOwnerSay(" → Bank 1: Links 35-46 (col 1)");
|
||
llOwnerSay(" → Bank 2: Links 47-58 (col 2)");
|
||
llOwnerSay(" → Bank 3: Links 59-70 (col 3)");
|
||
llOwnerSay(" → Bank 4: Links 71-82 (col 4)");
|
||
llOwnerSay(" ⚠️ Each prim needs this exact script!");
|
||
llOwnerSay("");
|
||
|
||
llOwnerSay("📖 Documentation (" + (string)llGetListLength(ESSENTIAL_DOCS) + " files):");
|
||
for (i = 0; i < llGetListLength(ESSENTIAL_DOCS); i++) {
|
||
llOwnerSay(" • " + llList2String(ESSENTIAL_DOCS, i));
|
||
}
|
||
llOwnerSay("");
|
||
llOwnerSay("ℹ️ Development files excluded: lsl_validator.py, test_*.py, etc.");
|
||
llOwnerSay("📊 Total Essential Files: " + (string)(llGetListLength(LINK1_SCRIPTS) + llGetListLength(LINKSET_SPECIFIC_SCRIPTS) + llGetListLength(ESSENTIAL_DOCS) + 1) + " unique files");
|
||
}
|
||
|
||
default {
|
||
state_entry() {
|
||
reportMemoryUsage("Update Checker");
|
||
llOwnerSay("🔄 Update Checker ready - GitHub integration active");
|
||
llOwnerSay("📍 Location: Link 1 (\"Peril Dice Controller V2\") - VERIFIED 84-prim structure");
|
||
llOwnerSay("💬 Say '/1 help' for commands or use Owner Menu → Troubleshooting");
|
||
llListen(1, "", llGetOwner(), "");
|
||
}
|
||
|
||
on_rez(integer start_param) {
|
||
// Reset state on rez (follows existing script patterns)
|
||
updateCheckInProgress = FALSE;
|
||
downloadMode = FALSE;
|
||
currentOperation = "";
|
||
downloadingFile = "";
|
||
currentHttpRequest = NULL_KEY;
|
||
llResetScript();
|
||
}
|
||
|
||
listen(integer channel, string name, key id, string message) {
|
||
if (id != llGetOwner()) return;
|
||
|
||
string msg = llToLower(llStringTrim(message, STRING_TRIM));
|
||
|
||
if (msg == "help") {
|
||
showUpdateCommands();
|
||
}
|
||
else if (msg == "check") {
|
||
checkForUpdates();
|
||
}
|
||
else if (msg == "list") {
|
||
showEssentialFiles();
|
||
}
|
||
else if (llGetSubString(msg, 0, 7) == "download") {
|
||
string filename = llStringTrim(llGetSubString(message, 8, -1), STRING_TRIM);
|
||
if (filename != "") {
|
||
downloadFileContent(filename);
|
||
} else {
|
||
llOwnerSay("❌ Usage: /1 download ScriptName.lsl");
|
||
llOwnerSay("📋 Say '/1 list' to see available files");
|
||
}
|
||
}
|
||
else {
|
||
llOwnerSay("❓ Unknown command. Say '/1 help' for available commands");
|
||
}
|
||
}
|
||
|
||
link_message(integer sender_num, integer num, string str, key id) {
|
||
// Handle verbose logging toggle (follows existing pattern)
|
||
if (num == MSG_TOGGLE_VERBOSE_LOGS) {
|
||
VERBOSE_LOGGING = !VERBOSE_LOGGING;
|
||
if (VERBOSE_LOGGING) {
|
||
llOwnerSay("🔊 [UpdateChecker] Verbose logging ENABLED");
|
||
} else {
|
||
llOwnerSay("🔊 [UpdateChecker] Verbose logging DISABLED");
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Handle update check requests from admin menu
|
||
if (num == MSG_UPDATE_CHECK_REQUEST) {
|
||
if (VERBOSE_LOGGING) {
|
||
llOwnerSay("🔄 [UpdateChecker] Received update check request from admin menu");
|
||
}
|
||
checkForUpdates();
|
||
return;
|
||
}
|
||
|
||
// Respond to admin menu status queries
|
||
if (num == MSG_ADMIN_MENU_RESPONSE && str == "update_status") {
|
||
llMessageLinked(LINK_SET, MSG_ADMIN_MENU_RESPONSE,
|
||
"update_version|v" + CURRENT_VERSION, id);
|
||
return;
|
||
}
|
||
}
|
||
|
||
http_response(key request_id, integer status, list metadata, string body) {
|
||
if (request_id != currentHttpRequest) return;
|
||
|
||
currentHttpRequest = NULL_KEY;
|
||
|
||
if (currentOperation == "version_check") {
|
||
updateCheckInProgress = FALSE;
|
||
|
||
if (status == 200) {
|
||
// Parse GitHub release JSON - with robust error handling
|
||
string latestVersion = llJsonGetValue(body, ["tag_name"]);
|
||
string htmlUrl = llJsonGetValue(body, ["html_url"]);
|
||
string publishedAt = llJsonGetValue(body, ["published_at"]);
|
||
|
||
if (latestVersion == JSON_INVALID) {
|
||
llOwnerSay("❌ Could not parse GitHub API response");
|
||
if (VERBOSE_LOGGING) {
|
||
llOwnerSay("🔍 Response preview: " + llGetSubString(body, 0, 200) + "...");
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Parse release notes with error handling
|
||
string releaseNotes = llJsonGetValue(body, ["body"]);
|
||
if (releaseNotes == JSON_INVALID) {
|
||
releaseNotes = "Release notes unavailable";
|
||
if (VERBOSE_LOGGING) {
|
||
llOwnerSay("⚠️ Could not parse release notes - may contain special characters");
|
||
}
|
||
}
|
||
|
||
llOwnerSay("=== 🔍 UPDATE CHECK RESULTS ===");
|
||
llOwnerSay("📊 Current Version: v" + CURRENT_VERSION);
|
||
llOwnerSay("✨ Latest GitHub Release: " + latestVersion);
|
||
|
||
if (isNewerVersion(latestVersion, CURRENT_VERSION)) {
|
||
llOwnerSay("🆕 NEW VERSION AVAILABLE!");
|
||
llOwnerSay("📅 Published: " + llGetSubString(publishedAt, 0, 9));
|
||
llOwnerSay("🌐 View Release: " + htmlUrl);
|
||
|
||
// Show release notes preview
|
||
if (releaseNotes != JSON_INVALID && releaseNotes != "") {
|
||
string shortNotes = releaseNotes;
|
||
if (llStringLength(shortNotes) > 200) {
|
||
shortNotes = llGetSubString(shortNotes, 0, 197) + "...";
|
||
}
|
||
llOwnerSay("📋 What's New: " + shortNotes);
|
||
}
|
||
|
||
llOwnerSay("");
|
||
llOwnerSay("📥 Individual Files: /1 download ScriptName.lsl");
|
||
llOwnerSay("📋 Available Files: /1 list");
|
||
llOwnerSay("🎁 Complete Release: " + htmlUrl);
|
||
} else {
|
||
llOwnerSay("✅ You have the latest version!");
|
||
llOwnerSay("🔄 Individual file updates available via /1 download");
|
||
}
|
||
} else {
|
||
llOwnerSay("❌ Update check failed (HTTP " + (string)status + ")");
|
||
if (status == 403) {
|
||
llOwnerSay(" ⏳ GitHub rate limited - try again in a few minutes");
|
||
} else if (status == 404) {
|
||
llOwnerSay(" 📋 No releases found yet - this is normal for new repositories");
|
||
llOwnerSay(" 🌐 Repository exists at: https://github.com/RebeccaNod1/Peril");
|
||
llOwnerSay(" 💡 Individual file downloads still available via /1 download");
|
||
llOwnerSay(" 🔄 Releases will appear here when v2.8.7+ is tagged");
|
||
}
|
||
}
|
||
}
|
||
else if (currentOperation == "file_download") {
|
||
downloadMode = FALSE;
|
||
|
||
if (status == 200) {
|
||
integer contentLength = llStringLength(body);
|
||
llOwnerSay("✅ Downloaded " + downloadingFile);
|
||
llOwnerSay("📊 Size: " + (string)contentLength + " characters");
|
||
|
||
// Show VERIFIED deployment instructions based on actual linkset scan
|
||
if (downloadingFile == "xyzzy_Master_script.lsl") {
|
||
llOwnerSay("📺 XyzzyText Script - Deploy to ALL Links 35-82 (48 prims)");
|
||
llOwnerSay(" 🎯 VERIFIED: \"leaderboard row X col Y\" prims");
|
||
llOwnerSay(" ⚠️ Each leaderboard prim needs this exact script!");
|
||
} else if (downloadingFile == "Game_Scoreboard_Manager_Linkset.lsl") {
|
||
llOwnerSay("🎯 Scoreboard Manager - Deploy to Link 12 ONLY");
|
||
llOwnerSay(" 📍 VERIFIED: \"scoreboard manager cube\"");
|
||
} else if (downloadingFile == "Leaderboard_Communication_Linkset.lsl") {
|
||
llOwnerSay("🎯 Leaderboard Bridge - Deploy to Link 35 ONLY");
|
||
llOwnerSay(" 📍 VERIFIED: \"leaderboard row 1 col 1\"");
|
||
} else if (downloadingFile == "XyzzyText_Dice_Bridge_Linkset.lsl") {
|
||
llOwnerSay("🎯 Dice Display Bridge - Deploy to Link 83 ONLY");
|
||
llOwnerSay(" 📍 VERIFIED: \"dice col 1\"");
|
||
} else if (llListFindList(LINK1_SCRIPTS, [downloadingFile]) != -1) {
|
||
llOwnerSay("🎯 Root Prim Script - Deploy to Link 1 ONLY");
|
||
llOwnerSay(" 📍 VERIFIED: \"Peril Dice Controller V2\"");
|
||
} else {
|
||
llOwnerSay("📖 Documentation file - no deployment needed");
|
||
}
|
||
|
||
// Show content preview
|
||
llOwnerSay("───── CONTENT PREVIEW (200 chars) ─────");
|
||
string preview = body;
|
||
if (llStringLength(preview) > 200) {
|
||
preview = llGetSubString(preview, 0, 197) + "...";
|
||
}
|
||
llOwnerSay(preview);
|
||
llOwnerSay("──────────────────────────────────────");
|
||
|
||
if (llGetSubString(downloadingFile, -4, -1) == ".lsl") {
|
||
llOwnerSay("🔧 Copy → Paste into script editor → SAVE → RESET");
|
||
llOwnerSay("📍 Deployment verified for 84-prim linkset structure");
|
||
}
|
||
} else {
|
||
llOwnerSay("❌ Download failed: " + downloadingFile + " (HTTP " + (string)status + ")");
|
||
if (status == 404) {
|
||
llOwnerSay(" 📁 File not found on GitHub - check spelling");
|
||
}
|
||
}
|
||
|
||
downloadingFile = "";
|
||
}
|
||
|
||
currentOperation = "";
|
||
}
|
||
} |