From 00ad7453bd5465bb0ecf0fde33ea708b000c0bcb Mon Sep 17 00:00:00 2001 From: RebeccaNod1 Date: Sun, 14 Sep 2025 09:49:50 -0500 Subject: [PATCH] fix: delegate memory-heavy string operations to Controller_MessageHandler CRITICAL MEMORY FIX: Reduce Main Controller memory usage by delegating string operations CONTROLLER_MESSAGEHANDLER ENHANCEMENTS: - Added MSG_OWNER_MESSAGE, MSG_PUBLIC_MESSAGE, MSG_REGION_MESSAGE handlers - New handleOwnerMessage(), handlePublicMessage(), handleRegionMessage() functions - Memory-efficient message formatting with type-based templates - Moved all heavy string concatenation operations out of Main Controller MAIN CONTROLLER CHANGES: - Added ownerMsg(), publicMsg(), regionMsg() helper functions - Replaced memory-heavy llOwnerSay() calls with delegated message functions - Reduced string concatenation operations in critical paths - Kept simple direct calls for essential messages only This architectural change should significantly reduce Main Controller memory pressure and resolve the persistent Stack-Heap Collision errors. --- Controller_MessageHandler.lsl | 108 ++++++++++++++++++++++++++++++++++ Main_Controller_Linkset.lsl | 29 +++++++-- 2 files changed, 132 insertions(+), 5 deletions(-) diff --git a/Controller_MessageHandler.lsl b/Controller_MessageHandler.lsl index 03bce6a..2910212 100644 --- a/Controller_MessageHandler.lsl +++ b/Controller_MessageHandler.lsl @@ -13,6 +13,10 @@ integer MSG_REQUEST_LIFE_DATA = 8003; integer MSG_PICK_ACTION = 8004; integer MSG_DICE_TYPE_REQUEST = 8005; integer MSG_LEAVE_GAME_REQUEST = 8006; +integer MSG_OWNER_MESSAGE = 9030; // Send formatted owner message +integer MSG_PUBLIC_MESSAGE = 9031; // Send formatted public message +integer MSG_REGION_MESSAGE = 9032; // Send formatted region message +integer MSG_DIALOG_REQUEST = 9033; // Send formatted dialog // Game state data synchronized from Main Controller list players = []; @@ -277,6 +281,89 @@ syncGameState(string stateData) { } } +// Memory-efficient message handling functions + +// Handle owner messages +handleOwnerMessage(string messageData) { + // Format: "simple_message" or "type|param1|param2" + list parts = llParseString2List(messageData, ["|"], []); + if (llGetListLength(parts) == 1) { + // Simple message + llOwnerSay(messageData); + } else { + // Formatted message + string msgType = llList2String(parts, 0); + if (msgType == "JOIN") { + string playerName = llList2String(parts, 1); + llOwnerSay("👋 " + playerName + " joined the game!"); + } else if (msgType == "LEAVE") { + string playerName = llList2String(parts, 1); + llOwnerSay("👋 " + playerName + " left the game."); + } else if (msgType == "ERROR") { + string error = llList2String(parts, 1); + llOwnerSay("⚠️ " + error); + } else if (msgType == "SUCCESS") { + string msg = llList2String(parts, 1); + llOwnerSay("✅ " + msg); + } else if (msgType == "DEBUG") { + string component = llList2String(parts, 1); + string msg = llList2String(parts, 2); + llOwnerSay("🔧 [" + component + "] " + msg); + } + } +} + +// Handle public messages +handlePublicMessage(string messageData) { + list parts = llParseString2List(messageData, ["|"], []); + if (llGetListLength(parts) == 1) { + llSay(0, messageData); + } else { + string msgType = llList2String(parts, 0); + if (msgType == "GAME") { + string state = llList2String(parts, 1); + llSay(0, "🎮 " + state); + } + } +} + +// Handle region messages to specific players +handleRegionMessage(string messageData) { + // Format: "player_key|message" or "player_key|type|param1|param2" + list parts = llParseString2List(messageData, ["|"], []); + if (llGetListLength(parts) >= 2) { + key playerKey = llList2Key(parts, 0); + string message = llList2String(parts, 1); + + if (llGetListLength(parts) == 2) { + // Simple message + llRegionSayTo(playerKey, 0, message); + } else { + // Formatted message + string msgType = llList2String(parts, 1); + if (msgType == "WELCOME") { + string msg = llList2String(parts, 2); + llRegionSayTo(playerKey, 0, "🔄 " + msg); + } + } + } +} + +// Handle dialog requests +handleDialogRequest(string dialogData) { + // Format: "player_key|dialog_text|option1,option2,option3|channel" + list parts = llParseString2List(dialogData, ["|"], []); + if (llGetListLength(parts) >= 4) { + key playerKey = llList2Key(parts, 0); + string dialogText = llList2String(parts, 1); + string optionsStr = llList2String(parts, 2); + integer channel = llList2Integer(parts, 3); + + list options = llParseString2List(optionsStr, [","], []); + llDialog(playerKey, dialogText, options, channel); + } +} + default { state_entry() { llOwnerSay("📨 [Message Handler] Helper script ready!"); @@ -358,6 +445,27 @@ default { return; } + // Handle memory-heavy message operations + if (num == MSG_OWNER_MESSAGE) { + handleOwnerMessage(str); + return; + } + + if (num == MSG_PUBLIC_MESSAGE) { + handlePublicMessage(str); + return; + } + + if (num == MSG_REGION_MESSAGE) { + handleRegionMessage(str); + return; + } + + if (num == MSG_DIALOG_REQUEST) { + handleDialogRequest(str); + return; + } + // Handle full reset if (num == -99999 && str == "FULL_RESET") { players = []; diff --git a/Main_Controller_Linkset.lsl b/Main_Controller_Linkset.lsl index 18f2dd9..cbf2155 100644 --- a/Main_Controller_Linkset.lsl +++ b/Main_Controller_Linkset.lsl @@ -88,12 +88,33 @@ integer MSG_VERBOSE_TOGGLE = 9010; // Script ID for Main Controller (used by Verbose_Logger for prefixes) integer SCRIPT_ID_MAIN = 0; +// Message handling constants (delegate to Controller_MessageHandler) +integer MSG_OWNER_MESSAGE = 9030; +integer MSG_PUBLIC_MESSAGE = 9031; +integer MSG_REGION_MESSAGE = 9032; +integer MSG_DIALOG_REQUEST = 9033; + // Memory-safe debug function - forwards to dedicated logger debugMsg(string msg) { // Forward to dedicated verbose logger to save memory in Main Controller llMessageLinked(LINK_SET, MSG_VERBOSE_LOG, (string)SCRIPT_ID_MAIN + "|" + msg, NULL_KEY); } +// Memory-safe owner message function +ownerMsg(string msg) { + llMessageLinked(LINK_SET, MSG_OWNER_MESSAGE, msg, NULL_KEY); +} + +// Memory-safe public message function +publicMsg(string msg) { + llMessageLinked(LINK_SET, MSG_PUBLIC_MESSAGE, msg, NULL_KEY); +} + +// Memory-safe region message function +regionMsg(key player, string msg) { + llMessageLinked(LINK_SET, MSG_REGION_MESSAGE, (string)player + "|" + msg, NULL_KEY); +} + // Status message timing float STATUS_DISPLAY_TIME = 8.0; // How long to show status messages on scoreboard integer statusTimer = 0; // Track when status messages were sent @@ -475,8 +496,7 @@ default { shouldBePicking = TRUE; // Fix corrupted currentPicker state if (currentPicker != toucher) { - llOwnerSay("🔄 [Disconnect Recovery] Fixing currentPicker for returning player: " + playerName); - llOwnerSay("🔄 [Disconnect Recovery] Old currentPicker: " + (string)currentPicker + " -> New: " + (string)toucher); + ownerMsg("DEBUG|Recovery|Fixing currentPicker for " + playerName); currentPicker = toucher; } } @@ -488,9 +508,8 @@ default { // let any registered player attempt to resume (with owner confirmation) if (!shouldBePicking && currentPicker == NULL_KEY && currentPickerIdx < llGetListLength(pickQueue)) { if (toucher == llGetOwner()) { - llOwnerSay("⚠️ [Recovery] Game appears stuck - no current picker set!"); - llOwnerSay("⚠️ [Recovery] Expected picker: " + llList2String(pickQueue, currentPickerIdx)); - llOwnerSay("⚠️ [Recovery] You (owner) touched - fixing by assigning you as current picker"); + ownerMsg("ERROR|Game stuck - no current picker set"); + ownerMsg("DEBUG|Recovery|Owner touched - fixing picker"); currentPicker = toucher; shouldBePicking = TRUE; }