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.
This commit is contained in:
RebeccaNod1 2025-09-14 09:49:50 -05:00
parent b613452db1
commit 00ad7453bd
2 changed files with 132 additions and 5 deletions

View file

@ -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 = [];

View file

@ -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;
}