From a225244241d87798bd6d62fb122b74b744a83fde Mon Sep 17 00:00:00 2001 From: RebeccaNod1 Date: Sat, 4 Apr 2026 18:16:01 -0600 Subject: [PATCH] added memmory toggle in peril_constants.lsl, added new memory macro script in constants, added macro to all files, optimised scoreboard manager, bug fixes in mulitible scripts --- Bot_Manager.lsl | 16 +- Controller_Memory.lsl | 5 + Controller_MessageHandler.lsl | 16 +- FURWARE_Leaderboard_Communication_Linkset.lsl | 1 + FURWARE_Text_Dice_Bridge_Linkset.lsl | 1 + Floater_Manager.lsl | 142 +++-- Furware_Text_Engine.lsl | 5 + Game_Calculator.lsl | 16 +- Game_Manager.lsl | 4 +- Game_Scoreboard_Manager_Linkset.lsl | 586 +++++++----------- Main_Controller_Linkset.lsl | 12 +- NumberPicker_DialogHandler.lsl | 16 +- Peril_Constants.lsl | 13 +- PlayerStatus_Float.lsl | 18 +- Player_DialogHandler.lsl | 16 +- Player_RegistrationManager.lsl | 42 +- Roll_ConfettiModule.lsl | 20 +- System_Debugger.lsl | 5 + 18 files changed, 386 insertions(+), 548 deletions(-) diff --git a/Bot_Manager.lsl b/Bot_Manager.lsl index 25f9e26..6ed32b2 100644 --- a/Bot_Manager.lsl +++ b/Bot_Manager.lsl @@ -57,18 +57,6 @@ list processedBotCommands = []; // Track sent BOT_PICKED messages to prevent duplicate sending list sentBotMessages = []; -// Memory reporting function -reportMemoryUsage(string scriptName) { - integer used = llGetUsedMemory(); - integer free = llGetFreeMemory(); - integer total = used + free; - float percentUsed = ((float)used / (float)total) * 100.0; - - dbg("๐Ÿง  [" + scriptName + "] Memory: " + - (string)used + " used, " + - (string)free + " free (" + - llGetSubString((string)percentUsed, 0, 4) + "% used)"); -} // Helper function to check and report memory usage checkMemoryUsage(string context) { @@ -201,7 +189,7 @@ doBotRoll(string botName, integer diceMax) { default { state_entry() { - reportMemoryUsage("๐Ÿค– Bot Manager"); + REPORT_MEMORY(); initializeChannels(); LISTEN_CHANNEL = BOT_COMMAND_CHANNEL; @@ -222,7 +210,7 @@ default { } on_rez(integer start_param) { - reportMemoryUsage("๐Ÿค– Bot Manager"); + REPORT_MEMORY(); dbg("๐Ÿค– [Bot Manager] ๐Ÿ”„ Bot Manager rezzed - reinitializing..."); // Re-initialize dynamic channels diff --git a/Controller_Memory.lsl b/Controller_Memory.lsl index 59a999c..ea42874 100644 --- a/Controller_Memory.lsl +++ b/Controller_Memory.lsl @@ -113,12 +113,17 @@ processMainControllerStats(string statsData) { default { state_entry() { + REPORT_MEMORY(); dbg("๐Ÿ“Š [Memory Monitor] Memory Monitor ready!"); // Start periodic memory monitoring llSetTimerEvent(MEMORY_CHECK_INTERVAL); } + on_rez(integer start_param) { + REPORT_MEMORY(); + } + timer() { // Periodic memory check checkMemoryUsage("periodic_check"); diff --git a/Controller_MessageHandler.lsl b/Controller_MessageHandler.lsl index e6ae4a7..3278f12 100644 --- a/Controller_MessageHandler.lsl +++ b/Controller_MessageHandler.lsl @@ -40,18 +40,6 @@ integer calculateChannel(integer offset) { return CHANNEL_BASE - (offset * 1000) - combinedHash; } -// Memory reporting function -reportMemoryUsage(string scriptName) { - integer used = llGetUsedMemory(); - integer free = llGetFreeMemory(); - integer total = used + free; - float percentUsed = ((float)used / (float)total) * 100.0; - - dbg("๐Ÿง  [" + scriptName + "] Memory: " + - (string)used + " used, " + - (string)free + " free (" + - llGetSubString((string)percentUsed, 0, 4) + "% used)"); -} initializeChannels() { SCOREBOARD_CHANNEL_1 = calculateChannel(6); // ~-77600 range @@ -326,13 +314,13 @@ handleDialogRequest(string dialogData) { default { state_entry() { - reportMemoryUsage("๐Ÿ“จ Message Handler"); + REPORT_MEMORY(); initializeChannels(); } on_rez(integer start_param) { - reportMemoryUsage("๐Ÿ“จ Message Handler"); + REPORT_MEMORY(); initializeChannels(); // Reset local state if needed (synced from Main) players = []; diff --git a/FURWARE_Leaderboard_Communication_Linkset.lsl b/FURWARE_Leaderboard_Communication_Linkset.lsl index ad250c3..9138ba6 100644 --- a/FURWARE_Leaderboard_Communication_Linkset.lsl +++ b/FURWARE_Leaderboard_Communication_Linkset.lsl @@ -16,6 +16,7 @@ default { state_entry() { DISCOVER_CORE_LINKS(); + REPORT_MEMORY(); dbg("๐Ÿ“‹ [Leaderboard] Leaderboard Communication Script ready! (FURWARE Version)"); dbg("๐Ÿ“‹ [Leaderboard] Managing 'Leaderboard' FURWARE box"); dbg("๐Ÿ“‹ [Leaderboard] โœ… Discovery complete! Scoreboard: " + (string)LINK_SCOREBOARD); diff --git a/FURWARE_Text_Dice_Bridge_Linkset.lsl b/FURWARE_Text_Dice_Bridge_Linkset.lsl index 9d6a4f3..25fb76e 100644 --- a/FURWARE_Text_Dice_Bridge_Linkset.lsl +++ b/FURWARE_Text_Dice_Bridge_Linkset.lsl @@ -19,6 +19,7 @@ default { } state_entry() { DISCOVER_CORE_LINKS(); + REPORT_MEMORY(); dbg("๐ŸŽฒ [Dice Bridge] Dice Display Bridge ready! (FURWARE Version)"); dbg("๐ŸŽฒ [Dice Bridge] โœ… Discovery complete! Controller: " + (string)LINK_CONTROLLER); diff --git a/Floater_Manager.lsl b/Floater_Manager.lsl index dbc691f..da25e06 100644 --- a/Floater_Manager.lsl +++ b/Floater_Manager.lsl @@ -41,18 +41,6 @@ initializeChannels() { // Debug control - set to TRUE for verbose pick debugging, FALSE for normal operation integer DEBUG_PICKS = FALSE; -// Memory reporting function -reportMemoryUsage(string scriptName) { - integer used = llGetUsedMemory(); - integer free = llGetFreeMemory(); - integer total = used + free; - float percentUsed = ((float)used / (float)total) * 100.0; - - dbg("๐Ÿง  [" + scriptName + "] Memory: " + - (string)used + " used, " + - (string)free + " free (" + - llGetSubString((string)percentUsed, 0, 4) + "% used)"); -} // ALL REGISTERED PLAYERS (for channel mapping - order never changes) list allPlayerNames = []; // Names in registration order @@ -90,7 +78,7 @@ list getPicksFor(string nameInput) { // Main event handler default { state_entry() { - reportMemoryUsage("๐Ÿ’ฌ Floater Manager"); + REPORT_MEMORY(); initializeChannels(); @@ -106,7 +94,7 @@ default { } on_rez(integer start_param) { - reportMemoryUsage("๐Ÿ’ฌ Floater Manager"); + REPORT_MEMORY(); dbg("๐Ÿ’ฌ [Floater Manager] ๐Ÿ”„ Floater Manager rezzed - reinitializing..."); initializeChannels(); @@ -133,47 +121,56 @@ default { return; } - if (num == MSG_REGISTER_PLAYER) { - if (llGetListLength(allPlayerNames) >= MAX_PLAYERS) { - dbg("๐Ÿ’ฌ [Floater Manager] โš ๏ธ Game is full (max " + (string)MAX_PLAYERS + ")."); - return; - } - list info = llParseString2List(str, ["|"], []); - string name = llList2String(info, 0); - key avKey = llList2Key(info, 1); - - if (llListFindList(allPlayerNames, [name]) != -1) return; - - allPlayerNames += [name]; // Permanent map - llSay(0, "๐Ÿ’€ " + name + " has entered the deadly game! ๐Ÿ’€"); - llMessageLinked(LINK_SET, MSG_REZ_FLOAT, name, avKey); - } - else if (num == MSG_REZ_FLOAT) { + // MSG_REGISTER_PLAYER (106) is now handled by Player_RegistrationManager.lsl + // which then sends MSG_REZ_FLOAT (105) here. Legacy handler removed to prevent double-rezzing. + + else if (num == MSG_REZ_FLOAT) { // 105 string name = str; - integer channelIdx = llListFindList(allPlayerNames, [name]); - if (channelIdx == -1) return; + key avKey = id; - key avKey = id; // Passed from MSG_REGISTER_PLAYER or MSG_REZ_FLOAT - - list details = llGetObjectDetails(avKey, [OBJECT_POS]); - vector basePos = llGetPos(); - if (llGetListLength(details) > 0) basePos = llList2Vector(details, 0); - - vector pos; - if (llGetListLength(details) > 0) { - pos = basePos + <1, 0, 1>; // Avatar relative - } else { - // Test players: grid layout - integer row = channelIdx / 5; - integer col = channelIdx % 5; - pos = basePos + <-3.0 - (float)col * 1.5, 2.0 + (float)row * 2.0, 1>; + // Handle name|key format if sent + integer pipeIdx = llSubStringIndex(str, "|"); + if (pipeIdx != -1) { + name = llGetSubString(str, 0, pipeIdx - 1); + if (avKey == NULL_KEY) avKey = (key)llGetSubString(str, pipeIdx + 1, -1); } - integer ch = FLOATER_BASE_CHANNEL + channelIdx; - llRezObject("StatFloat", pos, ZERO_VECTOR, ZERO_ROTATION, ch); - llSleep(0.2); - llRegionSay(ch, "SET_NAME:" + name); - llMessageLinked(LINK_SET, MSG_UPDATE_FLOAT, name, avKey); + // Ensure player is in our permanent map + integer channelIdx = llListFindList(allPlayerNames, [name]); + if (channelIdx == -1) { + if (llGetListLength(allPlayerNames) >= MAX_PLAYERS) return; + + allPlayerNames += [name]; + channelIdx = llGetListLength(allPlayerNames) - 1; + + // --- AUTHENTIC REZ BLOCK --- + // Only rez if we had to add them to the list (first time seeing them) + list details = llGetObjectDetails(avKey, [OBJECT_POS]); + vector basePos = llGetPos(); + if (llGetListLength(details) > 0) basePos = llList2Vector(details, 0); + + vector pos; + if (llGetListLength(details) > 0) { + pos = basePos + <1, 0, 1>; // Avatar relative + } else { + // Test players: grid layout + integer row = channelIdx / 5; + integer col = channelIdx % 5; + pos = basePos + <-3.0 - (float)col * 1.5, 2.0 + (float)row * 2.0, 1>; + } + + integer ch = FLOATER_BASE_CHANNEL + channelIdx; + llRezObject("StatFloat", pos, ZERO_VECTOR, ZERO_ROTATION, ch); + llSleep(0.2); + llRegionSay(ch, "SET_NAME:" + name); + + // Initial update to populate text/life display + llMessageLinked(LINK_SET, MSG_UPDATE_FLOAT, name, avKey); + } else { + // Already in list - just trigger an update in case it was a re-registration + dbg("๐Ÿ›ก๏ธ [Rez Guard] " + name + " is already rezzed. Skipping redundant creation."); + llMessageLinked(LINK_SET, MSG_UPDATE_FLOAT, name, avKey); + } } else if (num == MSG_UPDATE_FLOAT) { string name = str; @@ -240,6 +237,12 @@ default { integer foundIdx = ch - FLOATER_BASE_CHANNEL; if (foundIdx >= 0 && foundIdx < llGetListLength(allPlayerNames)) { string removedPlayer = llList2String(allPlayerNames, foundIdx); + + // --- CRITICAL FIX: Clear from Rez Guard registry --- + // We don't remove from allPlayerNames (to maintain channel indices) + // BUT we replace it with an empty string so the slot is 'available' again + allPlayerNames = llListReplaceList(allPlayerNames, [""], foundIdx, foundIdx); + integer currentIdx = llListFindList(names, [removedPlayer]); if (currentIdx != -1) { if (currentIdx < llGetListLength(players)) players = llDeleteSubList(players, currentIdx, currentIdx); @@ -258,6 +261,19 @@ default { if (pickIdx != -1) picksData = llDeleteSubList(picksData, pickIdx, pickIdx); } } + else if (num == MSG_LEAVE_GAME) { // 8007 + // Format: LEAVE_GAME|NAME|KEY + list parts = llParseString2List(str, ["|"], []); + string leaveName = llList2String(parts, 1); + integer chanIdx = llListFindList(allPlayerNames, [leaveName]); + + if (chanIdx != -1) { + // Remove their floater and clear the slot for re-entry + dbg("๐Ÿšฎ [Floater Manager] Player " + leaveName + " left - cleaning up floater."); + llRegionSay(FLOATER_BASE_CHANNEL + chanIdx, "CLEANUP"); + allPlayerNames = llListReplaceList(allPlayerNames, [""], chanIdx, chanIdx); + } + } else if (num == MSG_SYNC_GAME_STATE) { list parts = llParseString2List(str, ["~"], []); if (llGetListLength(parts) < 4) return; @@ -278,7 +294,16 @@ default { } names = llCSV2List(llList2String(parts, 3)); - if (llGetListLength(allPlayerNames) == 0 && llGetListLength(names) > 0) allPlayerNames = names; + // Robustly update permanent map from sync names + integer nIdx; + for (nIdx = 0; nIdx < llGetListLength(names); nIdx++) { + string nName = llList2String(names, nIdx); + if (llListFindList(allPlayerNames, [nName]) == -1) { + if (llGetListLength(allPlayerNames) < MAX_PLAYERS) { + allPlayerNames += [nName]; + } + } + } if (llGetListLength(parts) >= 5) players = llCSV2List(llList2String(parts, 4)); integer idx; @@ -291,11 +316,20 @@ default { } } else if (num == MSG_CLEANUP_ALL_FLOATERS) { + dbg("๐Ÿงน [Floater Manager] Universal Cleanup Triggered - Clearing all " + (string)MAX_PLAYERS + " possible channels."); integer i; - for (i = 0; i < llGetListLength(allPlayerNames); i++) { + for (i = 0; i < MAX_PLAYERS; i++) { + // Ensure we hit EVERY possible channel, even if they aren't in our current memory llRegionSay(FLOATER_BASE_CHANNEL + i, "CLEANUP"); } - players = []; names = []; lives = []; picksData = []; perilPlayer = ""; + // Clear ALL registries to allow fresh re-registration via Rez Guard + allPlayerNames = []; + players = []; + names = []; + lives = []; + picksData = []; + perilPlayer = ""; + dbg("โœ… [Floater Manager] All player registries wiped."); } else if (num == MSG_DEBUG_PICKS_ON) DEBUG_PICKS = TRUE; else if (num == MSG_DEBUG_PICKS_OFF) DEBUG_PICKS = FALSE; diff --git a/Furware_Text_Engine.lsl b/Furware_Text_Engine.lsl index 34f4a20..726fd8a 100644 --- a/Furware_Text_Engine.lsl +++ b/Furware_Text_Engine.lsl @@ -610,7 +610,12 @@ integer setDirty(integer action, integer first, integer last, integer isConf, ////////// STATES ////////////////////////////////////////// default { + on_rez(integer start_param) { + REPORT_MEMORY(); + } + state_entry() { + REPORT_MEMORY(); llOwnerSay("FURWARE text is starting..."); CHARS = llBase64ToString( diff --git a/Game_Calculator.lsl b/Game_Calculator.lsl index ac14f04..7917658 100644 --- a/Game_Calculator.lsl +++ b/Game_Calculator.lsl @@ -5,18 +5,6 @@ // This version chooses a dice size based on the number of players, // ensuring at least three numbers are available per player. -// Memory reporting function -reportMemoryUsage(string scriptName) { - integer used = llGetUsedMemory(); - integer free = llGetFreeMemory(); - integer total = used + free; - float percentUsed = ((float)used / (float)total) * 100.0; - - dbg("๐Ÿง  [" + scriptName + "] Memory: " + - (string)used + " used, " + - (string)free + " free (" + - llGetSubString((string)percentUsed, 0, 4) + "% used)"); -} list lives; list picksData; @@ -120,7 +108,7 @@ integer showPickManager(string player, key id) { default { state_entry() { - reportMemoryUsage("๐Ÿงฎ Game Calculator"); + REPORT_MEMORY(); // Initialize/reset all state variables lives = []; @@ -132,7 +120,7 @@ default { } on_rez(integer start_param) { - reportMemoryUsage("๐Ÿงฎ Game Calculator"); + REPORT_MEMORY(); // Reset all state variables on rez lives = []; diff --git a/Game_Manager.lsl b/Game_Manager.lsl index 8263d61..8fb1618 100644 --- a/Game_Manager.lsl +++ b/Game_Manager.lsl @@ -395,7 +395,7 @@ integer syncStateToMain() { default { state_entry() { DISCOVER_CORE_LINKS(); - reportMemoryUsage("Game Manager"); + REPORT_MEMORY(); dbg("๐ŸŽฏ Game Manager ready - discovery complete! Scoreboard: " + (string)LINK_SCOREBOARD); // Initialize/reset all game state variables @@ -422,7 +422,7 @@ default { on_rez(integer start_param) { DISCOVER_CORE_LINKS(); - reportMemoryUsage("Game Manager"); + REPORT_MEMORY(); dbg("๐Ÿ”„ Game Manager reset via rez..."); // Reset all game state variables on rez diff --git a/Game_Scoreboard_Manager_Linkset.lsl b/Game_Scoreboard_Manager_Linkset.lsl index 5c392a8..739e036 100644 --- a/Game_Scoreboard_Manager_Linkset.lsl +++ b/Game_Scoreboard_Manager_Linkset.lsl @@ -8,20 +8,22 @@ integer BACKGROUND_PRIM = -1; integer ACTIONS_PRIM = -1; -// Link mappings for player grid (Rows 0-4, Columns 0-1) -list profileLinks = [0,0,0,0,0,0,0,0,0,0]; -list heartsLinks = [0,0,0,0,0,0,0,0,0,0]; -list overlayLinks = [0,0,0,0,0,0,0,0,0,0]; +// Grid link mappings for player grid (Stride 3: Profile, Hearts, Overlay) +list gridLinks = [ + 0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0, + 0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0 +]; // Discover all relevant prims by name at startup discoverLinks() { integer i; integer total = llGetNumberOfPrims(); - // Reset lists - profileLinks = [0,0,0,0,0,0,0,0,0,0]; - heartsLinks = [0,0,0,0,0,0,0,0,0,0]; - overlayLinks = [0,0,0,0,0,0,0,0,0,0]; + // Reset list (30 elements for 10 slots * 3 prims) + gridLinks = [ + 0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0, + 0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0 + ]; for (i = 1; i <= total; i++) { string name = llGetLinkName(i); @@ -39,16 +41,17 @@ discoverLinks() { integer index = (row * 2) + col; if (index >= 0 && index < 10) { - if (type == "profile") profileLinks = llListReplaceList(profileLinks, [i], index, index); - else if (type == "life") heartsLinks = llListReplaceList(heartsLinks, [i], index, index); - else if (type == "overlay") overlayLinks = llListReplaceList(overlayLinks, [i], index, index); + integer gridBase = index * 3; + if (type == "profile") gridLinks = llListReplaceList(gridLinks, [i], gridBase, gridBase); + else if (type == "life") gridLinks = llListReplaceList(gridLinks, [i], gridBase + 1, gridBase + 1); + else if (type == "overlay") gridLinks = llListReplaceList(gridLinks, [i], gridBase + 2, gridBase + 2); } } } } - dbg("๐Ÿ“ˆ [Scoreboard Manager] ๐Ÿ” Dynamic Discovery complete!"); - if (BACKGROUND_PRIM == -1) dbg("โš ๏ธ WARNING: Background prim ('backboard:0:0') not found!"); + dbg("Discovery done"); + if (BACKGROUND_PRIM == -1) dbg("No backboard"); } // --- Leaderboard Configuration --- @@ -80,18 +83,14 @@ discoverLinks() { #define TEXTURE_ELIMINATED_X "90524092-03b0-1b3c-bcea-3ea5118c6dba" // Red X overlay for eliminated players // Player data - used for BOTH current game display AND leaderboard -list playerNames = []; // Current game players for display -list playerLives = []; // Current game player lives -list playerProfiles = []; // Maps to profile texture UUIDs +list activePlayers = []; // Current game state (Stride 3: Name, Lives, UUID) // Peril player tracking string currentPerilPlayer = ""; // Track current peril player for glow effects string currentWinner = ""; // Track current winner for green glow effects -// Leaderboard data (persistent - stored in linkset data) -list leaderboardNames = []; // Loaded from linkset data at startup -list leaderboardWins = []; // Loaded from linkset data at startup -list leaderboardLosses = []; // Loaded from linkset data at startup +// Leaderboard data (persistent - stored in linkset data) - Stride of 3 [Name, Wins, Losses] +list leaderboardData = []; // HTTP tracking list profileRequests = []; // Track which profile requests belong to which player @@ -102,25 +101,13 @@ key kvpReadReq; key kvpWriteReq; string pendingSerialize; -// Memory reporting function -integer reportMemoryUsage(string scriptName) { - integer used = llGetUsedMemory(); - integer free = llGetFreeMemory(); - integer total = used + free; - float percentUsed = ((float)used / (float)total) * 100.0; - - dbg("๐Ÿง  [" + scriptName + "] Memory: " + - (string)used + " used, " + - (string)free + " free (" + - llGetSubString((string)percentUsed, 0, 4) + "% used)"); - return 0; -} // Profile picture extraction constants -string profile_key_prefix = ", 0.0 // Remove any text ]); - dbg("๐Ÿ“ˆ [Scoreboard Manager] ๐Ÿ–ค Reset background prim " + (string)BACKGROUND_PRIM + " to black"); + dbg("Backboard Reset"); return 0; } @@ -143,7 +130,7 @@ integer resetManagerCube() { PRIM_TEXT, "", <0,0,0>, 0.0 // Remove any text ]); - dbg("๐Ÿ“ˆ [Scoreboard Manager] ๐Ÿ“ฆ Reset manager cube (link 12) to black"); + dbg("Manager Reset"); return 0; } @@ -184,21 +171,26 @@ integer updateActionsPrim(string status) { // Load leaderboard data from Experience KVP database integer loadLeaderboardData() { kvpReadReq = llReadKeyValue("Peril_LB_Top50"); - dbg("๐Ÿ“ˆ [Scoreboard Manager] ๐Ÿ” Requesting global scoreboard from Experience database..."); + dbg("LB Requesting"); return 0; } // Save leaderboard data to Experience KVP database integer saveLeaderboardData() { string serialized = ""; - integer saveCount = llGetListLength(leaderboardNames); - if (saveCount > 50) saveCount = 50; // Cap at Top 50 to fit safely in KVP 4Kb limit + integer totalEntries = llGetListLength(leaderboardData) / 3; + integer saveCount = totalEntries; + integer saveI = 0; + integer base = 0; + string save_entry = ""; + + if (saveCount > 50) saveCount = 50; // Cap at Top 50 - integer saveI; for (saveI = 0; saveI < saveCount; saveI++) { - string save_entry = llList2String(leaderboardNames, saveI) + ":" + - (string)llList2Integer(leaderboardWins, saveI) + ":" + - (string)llList2Integer(leaderboardLosses, saveI); + base = saveI * 3; + save_entry = llList2String(leaderboardData, base) + ":" + + (string)llList2Integer(leaderboardData, base + 1) + ":" + + (string)llList2Integer(leaderboardData, base + 2); if (serialized != "") serialized += "|"; serialized += save_entry; } @@ -206,22 +198,20 @@ integer saveLeaderboardData() { pendingSerialize = serialized; kvpWriteReq = llUpdateKeyValue("Peril_LB_Top50", serialized, FALSE, ""); - dbg("๐Ÿ“ˆ [Scoreboard Manager] ๐Ÿ’พ Synced " + (string)saveCount + " players to global scoreboard database."); + dbg("LB Saving"); return 0; } // Handle game won - update leaderboard integer handleGameWon(string winnerName) { - integer idx = llListFindList(leaderboardNames, [winnerName]); + integer idx = llListFindList(leaderboardData, [winnerName]); if (idx == -1) { // New player - leaderboardNames += [winnerName]; - leaderboardWins += [1]; - leaderboardLosses += [0]; + leaderboardData += [winnerName, 1, 0]; } else { // Existing player - integer currentWins = llList2Integer(leaderboardWins, idx); - leaderboardWins = llListReplaceList(leaderboardWins, [currentWins + 1], idx, idx); + integer currentWins = llList2Integer(leaderboardData, idx + 1); + leaderboardData = llListReplaceList(leaderboardData, [currentWins + 1], idx + 1, idx + 1); } saveLeaderboardData(); @@ -231,16 +221,14 @@ integer handleGameWon(string winnerName) { // Handle game lost - update leaderboard integer handleGameLost(string loserName) { - integer lostIdx = llListFindList(leaderboardNames, [loserName]); + integer lostIdx = llListFindList(leaderboardData, [loserName]); if (lostIdx == -1) { // New player - leaderboardNames += [loserName]; - leaderboardWins += [0]; - leaderboardLosses += [1]; + leaderboardData += [loserName, 0, 1]; } else { // Existing player - integer currentLosses = llList2Integer(leaderboardLosses, lostIdx); - leaderboardLosses = llListReplaceList(leaderboardLosses, [currentLosses + 1], lostIdx, lostIdx); + integer currentLosses = llList2Integer(leaderboardData, lostIdx + 2); + leaderboardData = llListReplaceList(leaderboardData, [currentLosses + 1], lostIdx + 2, lostIdx + 2); } saveLeaderboardData(); @@ -248,159 +236,112 @@ integer handleGameLost(string loserName) { return 0; } -// Get sorted leaderboard data list getSortedLeaderboard() { - list sortedLbData = []; - integer sortI; - string sortName; - integer sortWins; - integer sortLosses; - string paddedWins; - string paddedLosses; - integer invertedLosses; - string sortKey; - string sorted_entry; - list parts; - list cleanedData = []; - string winsStr; - string lossesStr; + list sortList = []; + integer i = 0; + integer totalEntries = llGetListLength(leaderboardData) / 3; + integer base = 0; + integer wins = 0; + integer losses = 0; + integer sortKey = 0; + list sortedData = []; + integer origIdx = 0; - // Create combined data for sorting with padded wins and losses for proper multi-level sorting - for (sortI = 0; sortI < llGetListLength(leaderboardNames); sortI++) { - sortName = llList2String(leaderboardNames, sortI); - sortWins = llList2Integer(leaderboardWins, sortI); - sortLosses = llList2Integer(leaderboardLosses, sortI); + for (i = 0; i < totalEntries; i++) { + base = i * 3; + wins = llList2Integer(leaderboardData, base + 1); + losses = llList2Integer(leaderboardData, base + 2); - // Pad wins to 4 digits (higher wins = better rank) - paddedWins = (string)(10000 + sortWins); - paddedWins = llGetSubString(paddedWins, 1, -1); // Remove the "1" prefix + if (wins > 65535) wins = 65535; + if (losses > 65535) losses = 65535; - // Pad losses to 4 digits, but INVERTED for tiebreaker (lower losses = better rank) - // Use 9999 - losses so lower losses get higher sort value - invertedLosses = 9999 - sortLosses; - paddedLosses = (string)(10000 + invertedLosses); - paddedLosses = llGetSubString(paddedLosses, 1, -1); // Remove the "1" prefix - - // Format: "PaddedWins-PaddedInvertedLosses:Name:ActualWins:ActualLosses" - // This sorts first by wins (descending), then by losses (ascending) for tiebreaker - sortKey = paddedWins + "-" + paddedLosses; - sorted_entry = sortKey + ":" + sortName + ":" + (string)sortWins + ":" + (string)sortLosses; - sortedLbData += [sorted_entry]; + sortKey = (wins << 16) | (65535 - losses); + sortList += [sortKey, i]; } - // Sort by combined key (wins descending, losses ascending) - FALSE = descending order - sortedLbData = llListSort(sortedLbData, 1, FALSE); + // 2. Sort by key (stride of 2) in descending order (highest score first) + sortList = llListSort(sortList, 2, FALSE); - // Clean up the format back to "Name:Wins:Losses" (skip padded wins in position 0) - cleanedData = []; - for (sortI = 0; sortI < llGetListLength(sortedLbData); sortI++) { - sorted_entry = llList2String(sortedLbData, sortI); - parts = llParseString2List(sorted_entry, [":"], []); - if (llGetListLength(parts) >= 4) { - // parts[0] = paddedWins (skip), parts[1] = name, parts[2] = actualWins, parts[3] = losses - sortName = llList2String(parts, 1); - winsStr = llList2String(parts, 2); - lossesStr = llList2String(parts, 3); - cleanedData += [sortName + ":" + winsStr + ":" + lossesStr]; - } + // 3. Rebuild the leaderboard data in sorted order (Stided list: Name, Wins, Losses) + for (i = 0; i < llGetListLength(sortList); i += 2) { + origIdx = llList2Integer(sortList, i + 1); + base = origIdx * 3; + sortedData += [ + llList2String(leaderboardData, base), + llList2Integer(leaderboardData, base + 1), + llList2Integer(leaderboardData, base + 2) + ]; } - return cleanedData; + return sortedData; } // Generate leaderboard text and send to separate leaderboard object integer generateLeaderboardText() { list sortedData = getSortedLeaderboard(); + string SPACES = " "; + string title = "TOP BATTLE RECORDS"; + integer titleMargin = 0; + string leaderboardText = ""; + integer genI = 0; + string playerName = ""; + integer genWins = 0; + integer genLosses = 0; + string rank = ""; + string leftPart = ""; + string rightPart = ""; + integer spaceNeeded = 0; + integer numSorted = llGetListLength(sortedData) / 3; + integer base = 0; + string sWins = ""; + string sLosses = ""; // 1. Build Title Row (Centered) - string title = "TOP BATTLE RECORDS"; - integer titleLen = llStringLength(title); - integer titleMargin = (LEADERBOARD_WIDTH - titleLen) / 2; + titleMargin = (LEADERBOARD_WIDTH - llStringLength(title)) / 2; if (titleMargin < 0) titleMargin = 0; - string titleSpaces = ""; - integer ts; - for (ts = 0; ts < titleMargin; ts++) titleSpaces += " "; - - string leaderboardText = titleSpaces + title + "\n"; - - integer genI; - string playerData; - list playerParts; - string playerName; - string genWins; - string genLosses; - integer rankNumber; - string rank; - string leftPart; - string rightPart; - integer spaceNeeded; - string spacer; - integer s; - string line; - integer actualPlayers; + leaderboardText = llGetSubString(SPACES, 0, titleMargin - 1) + title + "\n"; // Add actual player data (up to MAX_LEADERBOARD_ENTRIES) - for (genI = 0; genI < llGetListLength(sortedData) && genI < MAX_LEADERBOARD_ENTRIES; genI++) { - playerData = llList2String(sortedData, genI); - playerParts = llParseString2List(playerData, [":"], []); + for (genI = 0; genI < numSorted && genI < MAX_LEADERBOARD_ENTRIES; genI++) { + base = genI * 3; + playerName = llList2String(sortedData, base); + genWins = llList2Integer(sortedData, base + 1); + genLosses = llList2Integer(sortedData, base + 2); - if (llGetListLength(playerParts) >= 3) { - playerName = llList2String(playerParts, 0); - genWins = llList2String(playerParts, 1); - genLosses = llList2String(playerParts, 2); - - // [Rank(4) + Name(14) + Gaps + Stats(10) = 32 chars total] - if (llStringLength(playerName) > 12) { - playerName = llGetSubString(playerName, 0, 9) + "..."; - } - - // Format rank string: "01. " through "10. " (always 4 chars) - rankNumber = genI + 1; - rank = (string)rankNumber + ". "; - if (rankNumber < 10) rank = "0" + rank; - - // Format stats with fixed-width padding to align columns - // Standardizing to 10 characters: "W: 99/L: 9" - string sWins = (string)genWins; - while (llStringLength(sWins) < 3) sWins = " " + sWins; - string sLosses = (string)genLosses; - while (llStringLength(sLosses) < 2) sLosses = " " + sLosses; - - leftPart = rank + playerName; - rightPart = "W:" + sWins + "/L:" + sLosses; // Exactly 10 characters - - // Push rightPart to column 32 - spaceNeeded = LEADERBOARD_WIDTH - llStringLength(leftPart) - llStringLength(rightPart); - if (spaceNeeded < 1) spaceNeeded = 1; - - spacer = ""; - for (s = 0; s < spaceNeeded; s++) spacer += " "; - - line = leftPart + spacer + rightPart; - leaderboardText += line + "\n"; + if (llStringLength(playerName) > 12) { + playerName = llGetSubString(playerName, 0, 9) + "..."; } + + // Format rank: "01. " through "10. " + rank = (string)(genI + 1) + ". "; + if (genI < 9) rank = "0" + rank; + + // Format stats 10-char width: "W: 99/L: 9" + sWins = (string)genWins; + if (llStringLength(sWins) < 3) sWins = llGetSubString(" ", 0, 2 - llStringLength(sWins)) + sWins; + sLosses = (string)genLosses; + if (llStringLength(sLosses) < 2) sLosses = llGetSubString(" ", 0, 1 - llStringLength(sLosses)) + sLosses; + + leftPart = rank + playerName; + rightPart = "W:" + sWins + "/L:" + sLosses; + + spaceNeeded = LEADERBOARD_WIDTH - llStringLength(leftPart) - 10; + if (spaceNeeded < 1) spaceNeeded = 1; + + leaderboardText += leftPart + llGetSubString(SPACES, 0, spaceNeeded - 1) + rightPart + "\n"; } // Fill remaining positions with placeholders - actualPlayers = llGetListLength(sortedData); - for (genI = actualPlayers; genI < MAX_LEADERBOARD_ENTRIES; genI++) { - rankNumber = genI + 1; - rank = (string)rankNumber + ". "; - if (rankNumber < 10) rank = "0" + rank; + for (genI = numSorted; genI < MAX_LEADERBOARD_ENTRIES; genI++) { + rank = (string)(genI + 1) + ". "; + if (genI < 9) rank = "0" + rank; - // Use EXACT same formatting as players so columns align! leftPart = rank + "----------"; - rightPart = "W: 0/L: 0"; // Matches 10-char fixed width + rightPart = "W: 0/L: 0"; + spaceNeeded = LEADERBOARD_WIDTH - llStringLength(leftPart) - 10; - spaceNeeded = LEADERBOARD_WIDTH - llStringLength(leftPart) - llStringLength(rightPart); - if (spaceNeeded < 1) spaceNeeded = 1; - - spacer = ""; - for (s = 0; s < spaceNeeded; s++) spacer += " "; - - line = leftPart + spacer + rightPart; - leaderboardText += line + "\n"; + leaderboardText += leftPart + llGetSubString(SPACES, 0, spaceNeeded - 1) + rightPart + "\n"; } // Send formatted text to leaderboard bridge @@ -416,9 +357,7 @@ integer resetLeaderboard() { return 0; } - leaderboardNames = []; - leaderboardWins = []; - leaderboardLosses = []; + leaderboardData = []; pendingSerialize = ""; kvpWriteReq = llUpdateKeyValue("Peril_LB_Top50", "", FALSE, ""); generateLeaderboardText(); // Send empty leaderboard immediately @@ -427,10 +366,7 @@ integer resetLeaderboard() { // Clear all current players from the scoreboard integer clearAllPlayers() { - // Clear ONLY current game player data lists (NOT leaderboard data) - playerNames = []; - playerLives = []; - playerProfiles = []; + activePlayers = []; // Clear any pending HTTP requests httpRequests = []; @@ -444,9 +380,9 @@ integer clearAllPlayers() { // Reset all player prims to default state for (clearI = 0; clearI < 10; clearI++) { - clearProfilePrimIndex = llList2Integer(profileLinks, clearI); - clearHeartsPrimIndex = llList2Integer(heartsLinks, clearI); - clearOverlayPrimIndex = llList2Integer(overlayLinks, clearI); + clearProfilePrimIndex = getProfilePrimLink(clearI); + clearHeartsPrimIndex = getHeartsPrimLink(clearI); + clearOverlayPrimIndex = getOverlayPrimLink(clearI); // SAFETY CHECK - use if block instead of continue for maximum compatibility if (clearProfilePrimIndex > 0) { @@ -479,24 +415,17 @@ integer clearAllPlayers() { // Remove a single player from the scoreboard display integer removePlayer(string playerName) { - integer removeIdx = llListFindList(playerNames, [playerName]); - if (removeIdx != -1) { - // Remove player from internal lists - playerNames = llDeleteSubList(playerNames, removeIdx, removeIdx); - playerLives = llDeleteSubList(playerLives, removeIdx, removeIdx); - playerProfiles = llDeleteSubList(playerProfiles, removeIdx, removeIdx); - - // Refresh the entire display to shift remaining players up + integer idx = llListFindList(activePlayers, [playerName]); + if (idx != -1) { + activePlayers = llDeleteSubList(activePlayers, idx, idx + 2); refreshPlayerDisplay(); - - dbg("๐Ÿ“ˆ [Scoreboard Manager] ๐Ÿ“Š Removed " + playerName + " from the scoreboard and shifted remaining players."); + dbg("๐Ÿ“ˆ Player removed: " + playerName); } return 0; } -// Refresh the entire player display - shows all current players in order integer refreshPlayerDisplay() { - integer numActivePlayers = llGetListLength(playerNames); + integer numActivePlayers = llGetListLength(activePlayers) / 3; integer refreshI; integer refreshProfilePrimIndex = 0; integer refreshHeartsPrimIndex = 0; @@ -512,9 +441,9 @@ integer refreshPlayerDisplay() { // Only reset prims for unused slots (avoid flickering for active players) for (refreshI = numActivePlayers; refreshI < 10; refreshI++) { - refreshProfilePrimIndex = llList2Integer(profileLinks, refreshI); - refreshHeartsPrimIndex = llList2Integer(heartsLinks, refreshI); - refreshOverlayPrimIndex = llList2Integer(overlayLinks, refreshI); + refreshProfilePrimIndex = getProfilePrimLink(refreshI); + refreshHeartsPrimIndex = getHeartsPrimLink(refreshI); + refreshOverlayPrimIndex = getOverlayPrimLink(refreshI); if (refreshProfilePrimIndex > 0) { // Reset to default only for unused slots @@ -541,14 +470,15 @@ integer refreshPlayerDisplay() { // Now display all current players in their new positions for (refreshI = 0; refreshI < numActivePlayers; refreshI++) { - r_name = llList2String(playerNames, refreshI); - r_lives = llList2Integer(playerLives, refreshI); - r_profileTexture = llList2String(playerProfiles, refreshI); + integer base = refreshI * 3; + r_name = llList2String(activePlayers, base); + r_lives = llList2Integer(activePlayers, base + 1); + r_profileTexture = llList2String(activePlayers, base + 2); // Calculate prim indices for this position - r_profileIdx = llList2Integer(profileLinks, refreshI); - r_heartsIdx = llList2Integer(heartsLinks, refreshI); - r_overlayIdx = llList2Integer(overlayLinks, refreshI); + r_profileIdx = getProfilePrimLink(refreshI); + r_heartsIdx = getHeartsPrimLink(refreshI); + r_overlayIdx = getOverlayPrimLink(refreshI); if (r_profileIdx > 0) { // Determine profile texture to use @@ -602,9 +532,9 @@ integer isBot(string playerName) { // Map player index to profile prim link number // Functions relocated to avoid hardcoded math -integer getProfilePrimLink(integer playerIndex) { return llList2Integer(profileLinks, playerIndex); } -integer getHeartsPrimLink(integer playerIndex) { return llList2Integer(heartsLinks, playerIndex); } -integer getOverlayPrimLink(integer playerIndex) { return llList2Integer(overlayLinks, playerIndex); } +integer getProfilePrimLink(integer playerIndex) { return llList2Integer(gridLinks, playerIndex * 3); } +integer getHeartsPrimLink(integer playerIndex) { return llList2Integer(gridLinks, (playerIndex * 3) + 1); } +integer getOverlayPrimLink(integer playerIndex) { return llList2Integer(gridLinks, (playerIndex * 3) + 2); } string getHeartTexture(integer lives) { if (lives <= 0) return TEXTURE_0_HEARTS; @@ -613,84 +543,37 @@ string getHeartTexture(integer lives) { else return TEXTURE_3_HEARTS; // 3 or more } +// Helper to set glow and color on a prim +setGlow(integer link, float glow, vector color) { + if (link > 0) { + llSetLinkPrimitiveParamsFast(link, [PRIM_GLOW, ALL_SIDES, glow, PRIM_COLOR, ALL_SIDES, color, 1.0]); + } +} + // Update all glow effects (peril and winner) integer updatePlayerGlowEffects() { - // First, remove glow and reset tint from all players + integer numPlayers = llGetListLength(activePlayers) / 3; integer glowI; - integer glowProfilePrimIndex; - integer glowHeartsPrimIndex; - - for (glowI = 0; glowI < llGetListLength(playerNames); glowI++) { - glowProfilePrimIndex = llList2Integer(profileLinks, glowI); - glowHeartsPrimIndex = llList2Integer(heartsLinks, glowI); - - if (glowProfilePrimIndex > 0) { - llSetLinkPrimitiveParamsFast(glowProfilePrimIndex, [ - PRIM_GLOW, ALL_SIDES, 0.0, - PRIM_COLOR, ALL_SIDES, <1.0, 1.0, 1.0>, 1.0 - ]); - } - if (glowHeartsPrimIndex > 0) { - llSetLinkPrimitiveParamsFast(glowHeartsPrimIndex, [ - PRIM_GLOW, ALL_SIDES, 0.0, - PRIM_COLOR, ALL_SIDES, <1.0, 1.0, 1.0>, 1.0 - ]); - } + for (glowI = 0; glowI < numPlayers; glowI++) { + setGlow(getProfilePrimLink(glowI), 0.0, <1,1,1>); + setGlow(getHeartsPrimLink(glowI), 0.0, <1,1,1>); } - // Priority 1: Green glow for winner (overrides peril glow) + integer idx = -1; + vector col = <1,1,1>; + float glow = 0.0; + if (currentWinner != "" && currentWinner != "NONE") { - integer winnerIndex = llListFindList(playerNames, [currentWinner]); - if (winnerIndex != -1) { - integer winnerProfilePrimIndex = getProfilePrimLink(winnerIndex); - integer winnerHeartsPrimIndex = getHeartsPrimLink(winnerIndex); - - // Check if the prim exists (discovered index > 0) - if (winnerProfilePrimIndex > 0) { - // Add green glow + green tint to winner - llSetLinkPrimitiveParamsFast(winnerProfilePrimIndex, [ - PRIM_GLOW, ALL_SIDES, 0.3, - PRIM_COLOR, ALL_SIDES, <0.0, 1.0, 0.0>, 1.0 - ]); - - // Also add glow to hearts if found - if (winnerHeartsPrimIndex > 0) { - llSetLinkPrimitiveParamsFast(winnerHeartsPrimIndex, [ - PRIM_GLOW, ALL_SIDES, 0.3, - PRIM_COLOR, ALL_SIDES, <0.0, 1.0, 0.0>, 1.0 - ]); - } - - dbg("๐Ÿ“ˆ [Scoreboard Manager] ๐Ÿ† Added green glow to winner: " + currentWinner); - } - } + idx = llListFindList(activePlayers, [currentWinner]); + if (idx != -1) { idx = idx / 3; col = <0,1,0>; glow = 0.3; } + } else if (currentPerilPlayer != "" && currentPerilPlayer != "NONE") { + idx = llListFindList(activePlayers, [currentPerilPlayer]); + if (idx != -1) { idx = idx / 3; col = <1,1,0>; glow = 0.2; } } - // Priority 2: Yellow glow for peril player (only if not the winner) - else if (currentPerilPlayer != "" && currentPerilPlayer != "NONE") { - integer perilIndex = llListFindList(playerNames, [currentPerilPlayer]); - if (perilIndex != -1) { - integer perilProfilePrimIndex = getProfilePrimLink(perilIndex); - integer perilHeartsPrimIndex = getHeartsPrimLink(perilIndex); - - // Check if the prim exists (discovered index > 0) - if (perilProfilePrimIndex > 0) { - // Add yellow glow + yellow tint to peril player - llSetLinkPrimitiveParamsFast(perilProfilePrimIndex, [ - PRIM_GLOW, ALL_SIDES, 0.2, - PRIM_COLOR, ALL_SIDES, <1.0, 1.0, 0.0>, 1.0 - ]); - - // Also add glow to hearts if found - if (perilHeartsPrimIndex > 0) { - llSetLinkPrimitiveParamsFast(perilHeartsPrimIndex, [ - PRIM_GLOW, ALL_SIDES, 0.2, - PRIM_COLOR, ALL_SIDES, <1.0, 1.0, 0.0>, 1.0 - ]); - } - - dbg("๐Ÿ“ˆ [Scoreboard Manager] โœจ Added yellow glow to peril player: " + currentPerilPlayer); - } - } + + if (idx != -1) { + setGlow(getProfilePrimLink(idx), glow, col); + setGlow(getHeartsPrimLink(idx), glow, col); } return 0; } @@ -703,26 +586,19 @@ integer updatePerilPlayerGlow() { // Update player display on the grid integer updatePlayerDisplay(string playerName, integer lives, string profileUUID) { - // Find existing player or add new one - integer playerIndex = llListFindList(playerNames, [playerName]); + integer playerIndex = llListFindList(activePlayers, [playerName]); + if (playerIndex != -1) playerIndex = playerIndex / 3; if (playerIndex == -1) { - // New player - find next available slot - playerIndex = llGetListLength(playerNames); + playerIndex = llGetListLength(activePlayers) / 3; if (playerIndex >= 10) { - dbg("๐Ÿ“ˆ [Scoreboard Manager] โš ๏ธ WARNING: Maximum 10 players supported, ignoring: " + playerName); + dbg("๐Ÿ“ˆ MAX players reached: " + playerName); return 0; } - // Add to tracking lists - playerNames += [playerName]; - playerLives += [lives]; - // For bots, always store the bot texture UUID to prevent HTTP requests - if (isBot(playerName)) { - playerProfiles += [TEXTURE_BOT_PROFILE]; - } else { - playerProfiles += [profileUUID]; - } + string activeUUID = profileUUID; + if (isBot(playerName)) activeUUID = TEXTURE_BOT_PROFILE; + activePlayers += [playerName, lives, activeUUID]; // DEBUG: Show prim mapping - DISABLED to save memory // if (VERBOSE_LOGGING) { @@ -739,15 +615,13 @@ integer updatePlayerDisplay(string playerName, integer lives, string profileUUID // } } else { - // Update existing player - playerLives = llListReplaceList(playerLives, [lives], playerIndex, playerIndex); - // Don't overwrite cached profile texture with original UUID on updates + activePlayers = llListReplaceList(activePlayers, [lives], (playerIndex * 3) + 1, (playerIndex * 3) + 1); } // Update the physical prims for this player - integer updateProfilePrimIndex = llList2Integer(profileLinks, playerIndex); - integer updateHeartsPrimIndex = llList2Integer(heartsLinks, playerIndex); - integer updateOverlayPrimIndex = llList2Integer(overlayLinks, playerIndex); + integer updateProfilePrimIndex = getProfilePrimLink(playerIndex); + integer updateHeartsPrimIndex = getHeartsPrimLink(playerIndex); + integer updateOverlayPrimIndex = getOverlayPrimLink(playerIndex); if (updateProfilePrimIndex <= 0) return 0; @@ -761,32 +635,26 @@ integer updatePlayerDisplay(string playerName, integer lives, string profileUUID // CRITICAL: Still need to set updateProfileTexture for eliminated players if (isBot(playerName)) { updateProfileTexture = TEXTURE_BOT_PROFILE; - } else if (playerIndex < llGetListLength(playerProfiles)) { - string updateCachedTexture = llList2String(playerProfiles, playerIndex); + } else { + string updateCachedTexture = llList2String(activePlayers, (playerIndex * 3) + 2); if (updateCachedTexture != "" && updateCachedTexture != profileUUID && updateCachedTexture != TEXTURE_DEFAULT_PROFILE && updateCachedTexture != TEXTURE_BOT_PROFILE && updateCachedTexture != TEXTURE_ELIMINATED_X) { - // We have a previously fetched profile picture, use it updateProfileTexture = updateCachedTexture; } else { updateProfileTexture = TEXTURE_DEFAULT_PROFILE; } - } else { - updateProfileTexture = TEXTURE_DEFAULT_PROFILE; } } else { // Player is alive - determine profile texture to use updateProfileTexture = profileUUID; // Check if we already have a cached profile texture for this player - if (playerIndex < llGetListLength(playerProfiles)) { - string liveCachedTexture = llList2String(playerProfiles, playerIndex); - if (liveCachedTexture != "" && liveCachedTexture != profileUUID && - liveCachedTexture != TEXTURE_DEFAULT_PROFILE && liveCachedTexture != TEXTURE_BOT_PROFILE && - liveCachedTexture != TEXTURE_ELIMINATED_X) { - // We have a previously fetched profile picture, use it - updateProfileTexture = liveCachedTexture; - } + string liveCachedTexture = llList2String(activePlayers, (playerIndex * 3) + 2); + if (liveCachedTexture != "" && liveCachedTexture != profileUUID && + liveCachedTexture != TEXTURE_DEFAULT_PROFILE && liveCachedTexture != TEXTURE_BOT_PROFILE && + liveCachedTexture != TEXTURE_ELIMINATED_X) { + updateProfileTexture = liveCachedTexture; } // If no cached texture or using original UUID, determine what to use @@ -802,7 +670,7 @@ integer updatePlayerDisplay(string playerName, integer lives, string profileUUID // } } else if (updateProfileTexture == "" || updateProfileTexture == "00000000-0000-0000-0000-000000000000") { updateProfileTexture = TEXTURE_DEFAULT_PROFILE; - dbg("๐Ÿ“ˆ [Scoreboard Manager] โ“ [DEBUG] " + playerName + " has empty/null UUID, using default texture"); + dbg("Default texture"); } else { // CRITICAL: Set default texture FIRST, then request profile picture updateProfileTexture = TEXTURE_DEFAULT_PROFILE; @@ -817,10 +685,6 @@ integer updatePlayerDisplay(string playerName, integer lives, string profileUUID // Store the HTTP request mapping: requestID -> playerIndex httpRequests += [httpRequestID, playerIndex]; - - // if (VERBOSE_LOGGING) { - // llOwnerSay("๐Ÿ”„ [Scoreboard] Requesting profile pic for " + playerName + ", showing default for now"); - // } } } } @@ -891,12 +755,8 @@ default { state_entry() { discoverLinks(); llListen(1, "", llGetOwner(), ""); - reportMemoryUsage("Scoreboard Manager"); - dbg("๐Ÿ“ˆ [Scoreboard Manager] ๐Ÿ“ˆ Managing profile and heart prims via dynamic discovery."); - - // Initialize profile picture extraction constants - profile_key_prefix_length = llStringLength(profile_key_prefix); - profile_img_prefix_length = llStringLength(profile_img_prefix); + REPORT_MEMORY(); + dbg("Ready"); // Reset background prim to proper state resetBackgroundPrim(); @@ -916,8 +776,7 @@ default { currentWinner = ""; updatePlayerGlowEffects(); - dbg("๐Ÿ“ˆ [Scoreboard Manager] โœ… Linkset communication active - no discovery needed!"); - dbg("๐Ÿ“ˆ [Scoreboard Manager] ๐Ÿ“Š Loaded " + (string)llGetListLength(leaderboardNames) + " players from leaderboard data"); + dbg("Init complete"); } link_message(integer sender, integer num, string str, key id) { @@ -935,7 +794,7 @@ default { string profileUUID = llList2String(parts, 2); // Debug: Show what data scoreboard received - DISABLED to save memory - dbg("๐Ÿ“ˆ Scoreboard received update: " + playerName + " has " + (string)lives + " hearts"); + dbg("Update: " + playerName); // } updatePlayerDisplay(playerName, lives, profileUUID); @@ -975,7 +834,7 @@ default { currentPerilPlayer = ""; } - dbg("๐Ÿ“ˆ [Scoreboard Manager] ๐ŸŽฏ Peril player changed from '" + oldPeril + "' to '" + currentPerilPlayer + "'"); + dbg("Peril: " + currentPerilPlayer); // Update glow effects updatePlayerGlowEffects(); @@ -988,7 +847,7 @@ default { currentWinner = ""; } - dbg("๐Ÿ“ˆ [Scoreboard Manager] ๐Ÿ† Winner changed from '" + oldWinner + "' to '" + currentWinner + "'"); + dbg("Winner: " + currentWinner); // Update glow effects - winner glow overrides peril glow updatePlayerGlowEffects(); @@ -1004,14 +863,14 @@ default { // Remove this request from tracking httpRequests = llDeleteSubList(httpRequests, requestIndex, requestIndex + 1); - if (status == 200 && playerIndex < llGetListLength(playerNames)) { + if (status == 200 && playerIndex < llGetListLength(activePlayers) / 3) { // Parse the HTML to extract profile image UUID string profileUUID = ""; // Try the meta tag method first - integer metaStart = llSubStringIndex(body, profile_key_prefix); + integer metaStart = llSubStringIndex(body, PROFILE_KEY_PREFIX); if (metaStart != -1) { - metaStart += profile_key_prefix_length; + metaStart += PROFILE_KEY_PREFIX_LENGTH; string remainingBody = llGetSubString(body, metaStart, -1); integer metaEnd = llSubStringIndex(remainingBody, "\""); if (metaEnd != -1) { @@ -1021,9 +880,9 @@ default { // If meta tag failed, try img src method if (profileUUID == "") { - integer imgStart = llSubStringIndex(body, profile_img_prefix); + integer imgStart = llSubStringIndex(body, PROFILE_IMG_PREFIX); if (imgStart != -1) { - imgStart += profile_img_prefix_length; + imgStart += PROFILE_IMG_PREFIX_LENGTH; string remainingBody = llGetSubString(body, imgStart, -1); integer imgEnd = llSubStringIndex(remainingBody, "/"); if (imgEnd != -1) { @@ -1034,8 +893,8 @@ default { // If we got a valid UUID, update the profile prim and cache it if (profileUUID != "" && profileUUID != "00000000-0000-0000-0000-000000000000") { - // Update the cached profile texture - playerProfiles = llListReplaceList(playerProfiles, [profileUUID], playerIndex, playerIndex); + // Update the cached profile texture in stride + activePlayers = llListReplaceList(activePlayers, [profileUUID], (playerIndex * 3) + 2, (playerIndex * 3) + 2); // Update the physical prim using correct mapping integer profilePrimIndex = getProfilePrimLink(playerIndex); @@ -1055,10 +914,10 @@ default { PRIM_GLOW, ALL_SIDES, 0.0 // Remove glow ]); - string playerName = llList2String(playerNames, playerIndex); - dbg("๐Ÿ“ˆ [Scoreboard Manager] ๐Ÿ–ผ๏ธ Updated profile picture for " + playerName + " on prim " + (string)profilePrimIndex + " with forced refresh"); + string playerName = llList2String(activePlayers, playerIndex * 3); + dbg("Profile updated: " + playerName); } else { - dbg("๐Ÿ“ˆ [Scoreboard Manager] โŒ ERROR: HTTP response tried to update invalid prim " + (string)profilePrimIndex + " for player " + (string)playerIndex); + dbg("Invalid prim: " + (string)profilePrimIndex); } } } @@ -1074,9 +933,7 @@ default { string val = llGetSubString(data, comma + 1, -1); if (status == "1") { - leaderboardNames = []; - leaderboardWins = []; - leaderboardLosses = []; + leaderboardData = []; list entries = llParseString2List(val, ["|"], []); integer i; @@ -1084,19 +941,19 @@ default { string data_entry = llList2String(entries, i); list parts = llParseString2List(data_entry, [":"], []); if (llGetListLength(parts) >= 3) { - leaderboardNames += [llList2String(parts, 0)]; - leaderboardWins += [(integer)llList2String(parts, 1)]; - leaderboardLosses += [(integer)llList2String(parts, 2)]; + leaderboardData += [ + llList2String(parts, 0), + (integer)llList2String(parts, 1), + (integer)llList2String(parts, 2) + ]; } } - dbg("๐Ÿ“ˆ [Scoreboard Manager] โœ… Loaded global scoreboard containing " + (string)llGetListLength(leaderboardNames) + " players!"); - } else if (status == "3") { // XP_ERROR_KEY_NOT_FOUND - leaderboardNames = []; - leaderboardWins = []; - leaderboardLosses = []; - dbg("๐Ÿ“ˆ [Scoreboard Manager] โ„น๏ธ Global scoreboard is currently empty (database fresh)."); + dbg("LB Loaded"); + } else if (status == "3") { + leaderboardData = []; + dbg("LB Empty"); } else { - dbg("๐Ÿ“ˆ [Scoreboard Manager] โš ๏ธ Failed to load global scoreboard (Status: " + status + ")"); + dbg("LB Error: " + status); } } // Generate board with downloaded or empty data @@ -1108,8 +965,8 @@ default { if (status == "3") { // Update failed because key didn't exist yet! Create it. llCreateKeyValue("Peril_LB_Top50", pendingSerialize); - } else if (status == "1") { - dbg("๐Ÿ“ˆ [Scoreboard Manager] โœ… Global scoreboard synced successfully."); + dbg("LB Saved"); + pendingSerialize = ""; } } } @@ -1117,7 +974,7 @@ default { timer() { - dbg("๐Ÿ“ˆ [Scoreboard Manager] โฑ๏ธ Auto-refreshing Global Scoreboard Data..."); + dbg("Timer"); loadLeaderboardData(); } @@ -1138,7 +995,6 @@ default { } } else if (msg == "clearx") { - dbg("Refreshing display to clear test X overlays..."); refreshPlayerDisplay(); } } diff --git a/Main_Controller_Linkset.lsl b/Main_Controller_Linkset.lsl index cc2bc6b..d422e66 100644 --- a/Main_Controller_Linkset.lsl +++ b/Main_Controller_Linkset.lsl @@ -299,7 +299,7 @@ resetGame() { default { state_entry() { DISCOVER_CORE_LINKS(); - reportMemoryUsage("Main Controller"); + REPORT_MEMORY(); dbg("๐ŸŽฎ Main Controller ready! (Discovery Mode)"); // Initialize lockout system @@ -317,7 +317,7 @@ default { on_rez(integer start_param) { DISCOVER_CORE_LINKS(); - reportMemoryUsage("Main Controller"); + REPORT_MEMORY(); dbg("๐Ÿ”„ Main Controller reset via rez..."); // Re-initialize lockout system @@ -473,14 +473,6 @@ default { link_message(integer sender, integer num, string str, key id) { // Dialog forwarding now handled by Player_RegistrationManager - // Handle player registration from other scripts (NOT delegated, this is for other internal communication) - if (num == MSG_REGISTER_PLAYER) { - // This is for other scripts that might still use the old MSG_REGISTER_PLAYER message - // Convert it to the new message for the Player_RegistrationManager - llMessageLinked(LINK_SET, MSG_REGISTER_PLAYER_REQUEST, str, id); - return; - } - // Handle registration updates from Player_RegistrationManager if (num == MSG_UPDATE_MAIN_LISTS) { // MSG_UPDATE_MAIN_LISTS // ULTRA-OPTIMIZED: Player_RegistrationManager now handles all heavy processing diff --git a/NumberPicker_DialogHandler.lsl b/NumberPicker_DialogHandler.lsl index 18d0f0c..f053bf1 100644 --- a/NumberPicker_DialogHandler.lsl +++ b/NumberPicker_DialogHandler.lsl @@ -56,18 +56,6 @@ list globallyPickedNumbers = []; // Numbers already picked by other players integer currentPage = 0; #define NUMBERS_PER_PAGE 9 // Leave room for navigation buttons -// Memory reporting function -reportMemoryUsage(string scriptName) { - integer used = llGetUsedMemory(); - integer free = llGetFreeMemory(); - integer total = used + free; - float percentUsed = ((float)used / (float)total) * 100.0; - - dbg("๐Ÿง  [" + scriptName + "] Memory: " + - (string)used + " used, " + - (string)free + " free (" + - llGetSubString((string)percentUsed, 0, 4) + "% used)"); -} list getNumbersForPage(integer page, integer diceType) { // Build list of available numbers (not picked by others OR by current player) @@ -177,7 +165,7 @@ showPickDialog(string name, key id, integer diceType, integer picks) { default { state_entry() { - reportMemoryUsage("๐ŸŽฎ NumberPicker Dialog"); + REPORT_MEMORY(); // Initialize dynamic channels initializeChannels(); @@ -203,7 +191,7 @@ default { } on_rez(integer start_param) { - reportMemoryUsage("๐ŸŽฎ NumberPicker Dialog"); + REPORT_MEMORY(); dbg("๐ŸŽฎ [NumberPicker] ๐Ÿ”„ Number Picker rezzed - reinitializing..."); diff --git a/Peril_Constants.lsl b/Peril_Constants.lsl index 98d4079..fa4ea17 100644 --- a/Peril_Constants.lsl +++ b/Peril_Constants.lsl @@ -28,7 +28,7 @@ integer LINK_LEADERBOARD_BRIDGE = -1; // "FURWARE text mesh:Leaderboard:0:0" #define FW_DONE "fw_done" // --- System/Debug (9000-9999) --- -#define DEBUG_LOGS 1 // GLOBAL MASTER SWITCH - Set to 1 for dev feedback, 0 to STRIP from memory +#define DEBUG_LOGS 0 // GLOBAL MASTER SWITCH - Set to 1 for dev feedback, 0 to STRIP from memory #if DEBUG_LOGS #define dbg(msg) llOwnerSay(msg) #else @@ -113,6 +113,17 @@ integer LINK_LEADERBOARD_BRIDGE = -1; // "FURWARE text mesh:Leaderboard:0:0" #define MSG_QUERY_OWNER_STATUS 213 // --- Memory Management (6000-6099) --- +#define SHOW_MEMORY 1 // Toggle this to 1 to see memory usage on rez/reset + +#define REPORT_MEMORY() { \ + if (SHOW_MEMORY) { \ + integer used = llGetUsedMemory(); \ + integer free = llGetFreeMemory(); \ + float percent = ((float)used / (float)(used + free)) * 100.0; \ + llOwnerSay("๐Ÿง  [" + llGetScriptName() + "] Memory: " + (string)used + " used, " + (string)free + " free (" + llGetSubString((string)percent, 0, 4) + "% used)."); \ + } \ +} + #define MSG_MEMORY_CHECK 6001 #define MSG_MEMORY_STATS 6002 #define MSG_MEMORY_CLEANUP 6003 diff --git a/PlayerStatus_Float.lsl b/PlayerStatus_Float.lsl index bd58bbe..bddf0ba 100644 --- a/PlayerStatus_Float.lsl +++ b/PlayerStatus_Float.lsl @@ -7,31 +7,18 @@ string displayText; string myName; integer listenHandle = -1; -// Memory reporting function - defined before use -reportMemoryUsage(string scriptName) { - integer used = llGetUsedMemory(); - integer free = llGetFreeMemory(); - integer total = used + free; - float percentUsed = ((float)used / (float)total) * 100.0; - - dbg("๐Ÿง  [" + scriptName + "] Memory: " + - (string)used + " used, " + - (string)free + " free (" + - llGetSubString((string)percentUsed, 0, 4) + "% used)"); -} default { state_entry() { + REPORT_MEMORY(); string currentDesc = llGetObjectDesc(); - if (currentDesc == "" || currentDesc == "(No Description)") currentDesc = "Unknown"; - - reportMemoryUsage("๐Ÿ“ฑ Player Float (" + currentDesc + ")"); llSetText("โณ Waiting...", <1,1,1>, 1.0); llSetTimerEvent(1.0); } on_rez(integer start_param) { + REPORT_MEMORY(); if (listenHandle != -1) llListenRemove(listenHandle); listenHandle = llListen(start_param, "", NULL_KEY, ""); myName = ""; @@ -83,7 +70,6 @@ default { else if (llSubStringIndex(message, "SET_NAME:") == 0) { myName = llGetSubString(message, 9, -1); llSetObjectDesc(myName); - reportMemoryUsage("๐Ÿ“ฑ " + myName); } } diff --git a/Player_DialogHandler.lsl b/Player_DialogHandler.lsl index a927418..508f7a5 100644 --- a/Player_DialogHandler.lsl +++ b/Player_DialogHandler.lsl @@ -99,18 +99,6 @@ float pendingMenuTimestamp = 0.0; // Timeout for pending requests integer isLocked = FALSE; key gameOwner; -// Memory reporting function -reportMemoryUsage(string scriptName) { - integer used = llGetUsedMemory(); - integer free = llGetFreeMemory(); - integer total = used + free; - float percentUsed = ((float)used / (float)total) * 100.0; - - dbg("๐Ÿง  [" + scriptName + "] Memory: " + - (string)used + " used, " + - (string)free + " free (" + - llGetSubString((string)percentUsed, 0, 4) + "% used)"); -} // Display the main categorized owner menu with lock/unlock controls showOwnerMenu(key id) { @@ -313,7 +301,7 @@ showUnregisteredOwnerMenu(key id, integer canStartGame) { default { state_entry() { - reportMemoryUsage("๐ŸŽญ Player Dialog"); + REPORT_MEMORY(); // Initialize dynamic channels initializeChannels(); @@ -356,7 +344,7 @@ default { } on_rez(integer start_param) { - reportMemoryUsage("๐ŸŽญ Player Dialog"); + REPORT_MEMORY(); dbg("๐Ÿ”„ Player Dialog Handler rezzed - reinitializing..."); diff --git a/Player_RegistrationManager.lsl b/Player_RegistrationManager.lsl index 0646568..f175950 100644 --- a/Player_RegistrationManager.lsl +++ b/Player_RegistrationManager.lsl @@ -10,23 +10,29 @@ list names = []; list lives = []; list readyPlayers = []; #define MAX_PLAYERS 10 -#define FLOATER_BASE_CHANNEL -86000 + +// Dynamic channel system +#define CHANNEL_BASE -77000 +integer FLOATER_BASE_CHANNEL; integer roundStarted = FALSE; integer gameStarting = FALSE; -// Memory reporting function -reportMemoryUsage(string scriptName) { - integer used = llGetUsedMemory(); - integer free = llGetFreeMemory(); - integer total = used + free; - float percentUsed = ((float)used / (float)total) * 100.0; - - dbg("๐Ÿง  [" + scriptName + "] Memory: " + - (string)used + " used, " + - (string)free + " free (" + - llGetSubString((string)percentUsed, 0, 4) + "% used)"); +integer calculateChannel(integer offset) { + string ownerStr = (string)llGetOwner(); + string objectStr = (string)llGetLinkKey(1); + string combinedStr = ownerStr + objectStr; + string hashStr = llMD5String(combinedStr, 0); + integer hash1 = llSubStringIndex("0123456789abcdef", llGetSubString(hashStr, 0, 0)); + integer hash2 = llSubStringIndex("0123456789abcdef", llGetSubString(hashStr, 1, 1)); + integer combinedHash = hash1 * 16 + hash2; + return CHANNEL_BASE - (offset * 1000) - combinedHash; } +initializeChannels() { + FLOATER_BASE_CHANNEL = calculateChannel(9); +} + + // Helper functions string getPlayerName(key id) { string displayName = llGetDisplayName(id); @@ -134,12 +140,12 @@ handlePlayerRegistration(string regData, key requesterId) { syncMessage = livesStr + "~EMPTY~NONE~" + llList2CSV(names); } - // Send pre-built sync message - llMessageLinked(LINK_SET, MSG_SYNC_GAME_STATE, syncMessage, NULL_KEY); - - // CRITICAL FIX: Send floater rez message to Floater Manager + // CRITICAL FIX: Send floater rez message to Floater Manager first llMessageLinked(LINK_SET, MSG_REZ_FLOATER, newName + "|" + (string)newKey, newKey); + // Then send the sync message to update everyone on the new state + llMessageLinked(LINK_SET, MSG_SYNC_GAME_STATE, syncMessage, NULL_KEY); + // Send minimal update to Main Controller - just the essential data it needs string updateData = (string)ch + "~" + newName; llMessageLinked(LINK_SET, MSG_UPDATE_MAIN_LISTS, updateData, newKey); @@ -156,11 +162,15 @@ handlePlayerRegistration(string regData, key requesterId) { default { state_entry() { DISCOVER_CORE_LINKS(); + initializeChannels(); + REPORT_MEMORY(); dbg("๐ŸŽฏ [RegMgr] Player Registration Manager ready - discovery complete! Scoreboard: " + (string)LINK_SCOREBOARD); } on_rez(integer start_param) { DISCOVER_CORE_LINKS(); + initializeChannels(); + REPORT_MEMORY(); dbg("๐ŸŽฏ [RegMgr] Player Registration Manager rezzed - resetting state..."); // Reset local state players = []; diff --git a/Roll_ConfettiModule.lsl b/Roll_ConfettiModule.lsl index 17a801d..997a19a 100644 --- a/Roll_ConfettiModule.lsl +++ b/Roll_ConfettiModule.lsl @@ -60,18 +60,6 @@ integer lastRollTime = 0; // Track when last roll happened integer diceTypeProcessed = FALSE; // Confetti particle system for winner celebrations #define PARTICLE_TEXTURE "7d8ae121-e171-12ae-f5b6-7cc3c0395c7b" -// Memory reporting function -reportMemoryUsage(string scriptName) { - integer used = llGetUsedMemory(); - integer free = llGetFreeMemory(); - integer total = used + free; - float percentUsed = ((float)used / (float)total) * 100.0; - - dbg("๐Ÿง  [" + scriptName + "] Memory: " + - (string)used + " used, " + - (string)free + " free (" + - llGetSubString((string)percentUsed, 0, 4) + "% used)"); -} list getPicksFor(string nameInput) { integer i; @@ -131,9 +119,13 @@ confetti() { default { state_entry() { DISCOVER_CORE_LINKS(); - reportMemoryUsage("Roll Module"); + REPORT_MEMORY(); dbg("๐ŸŽฒ [Roll Module] ready - discovery complete! Bridge: " + (string)LINK_DICE_BRIDGE); + // Re-initialize dynamic channels + initializeChannels(); + rollDialogChannel = ROLLDIALOG_CHANNEL; + // Clean up any existing listeners if (listenHandle != -1) { llListenRemove(listenHandle); @@ -157,7 +149,7 @@ default { on_rez(integer start_param) { DISCOVER_CORE_LINKS(); - reportMemoryUsage("Roll Module"); + REPORT_MEMORY(); dbg("๐ŸŽฒ [Roll Module] reset via rez..."); // Re-initialize dynamic channels diff --git a/System_Debugger.lsl b/System_Debugger.lsl index a793df8..0e44f09 100644 --- a/System_Debugger.lsl +++ b/System_Debugger.lsl @@ -69,9 +69,14 @@ list validatePicksData(list picksData, list names) { default { state_entry() { + REPORT_MEMORY(); dbg("๐Ÿ” [System Debugger] ๐Ÿ” System Debugger ready!"); } + on_rez(integer start_param) { + REPORT_MEMORY(); + } + link_message(integer sender, integer num, string str, key id) { // Handle legacy verbose logging toggle (OBSELETE)