Peril_Dice/Controller_Discovery.lsl

203 lines
10 KiB
Text

// === Controller Discovery Helper ===
// Handles all controller discovery functionality for the Main Controller
// Also handles automatic position configuration for newly rezzed displays
// Communicates with other scripts via link messages
// =============================================================================
// DYNAMIC CHANNEL & DISCOVERY SYSTEM
// =============================================================================
// Fixed discovery channel for client objects to find controller
integer DISCOVERY_CHANNEL = -77000;
// Base channel offset - should match other controller scripts
integer CHANNEL_BASE = -77000;
// Calculate channels dynamically to avoid hardcoded conflicts
integer calculateChannel(integer offset) {
// Use BOTH owner's key AND object's key to make channels unique per game instance
string ownerStr = (string)llGetOwner();
string objectStr = (string)llGetKey();
string combinedStr = ownerStr + objectStr;
// Create a more unique hash using both keys
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; // Creates 0-255 range
return CHANNEL_BASE - (offset * 1000) - combinedHash;
}
// Dynamic channel variables for auto-config communication
integer SCOREBOARD_DATA_CHANNEL;
integer LEADERBOARD_DATA_CHANNEL;
integer DICE_DATA_CHANNEL;
// Client connection tracking
list connectedClients = []; // List of client object keys that have connected
list expectedClientTypes = ["scoreboard", "leaderboard", "dice"]; // Expected client types
integer discoveryBroadcastActive = TRUE;
float DISCOVERY_BROADCAST_INTERVAL = 30.0; // Broadcast availability every 30 seconds
// Message constants for inter-script communication
integer MSG_DISCOVERY_START = 5001;
integer MSG_DISCOVERY_STOP = 5002;
integer MSG_CLIENT_CONNECTED = 5003;
integer MSG_DISCOVERY_STATUS = 5004;
// Channel initialization function
initializeChannels() {
SCOREBOARD_DATA_CHANNEL = calculateChannel(6); // ~-83000 range
LEADERBOARD_DATA_CHANNEL = calculateChannel(7); // ~-84000 range
DICE_DATA_CHANNEL = calculateChannel(8); // ~-85000 range
// Report channels for debugging
llOwnerSay("🔧 [Discovery] Auto-config channels initialized:");
llOwnerSay(" Scoreboard: " + (string)SCOREBOARD_DATA_CHANNEL);
llOwnerSay(" Leaderboard: " + (string)LEADERBOARD_DATA_CHANNEL);
llOwnerSay(" Dice: " + (string)DICE_DATA_CHANNEL);
}
default {
state_entry() {
llOwnerSay("📡 [Discovery] Controller Discovery Helper ready!");
// Initialize auto-config channels
initializeChannels();
// Listen on discovery channel for external objects seeking controller
llListen(DISCOVERY_CHANNEL, "", "", "");
// Listen on data channels for auto-config requests
llListen(SCOREBOARD_DATA_CHANNEL, "", "", "");
llListen(LEADERBOARD_DATA_CHANNEL, "", "", "");
llListen(DICE_DATA_CHANNEL, "", "", "");
// Start broadcasting controller availability
discoveryBroadcastActive = TRUE;
llOwnerSay("📺 [Discovery] Broadcasting controller availability...");
llRegionSay(DISCOVERY_CHANNEL, "CONTROLLER_AVAILABLE|" + (string)llGetKey()); // Use controller object key
// Set timer to periodically announce availability
llSetTimerEvent(DISCOVERY_BROADCAST_INTERVAL);
// Notify main controller that discovery is active
llMessageLinked(LINK_SET, MSG_DISCOVERY_STATUS, "ACTIVE|" + (string)llGetListLength(connectedClients), NULL_KEY);
}
listen(integer channel, string name, key id, string msg) {
if (channel == DISCOVERY_CHANNEL) {
if (llSubStringIndex(msg, "FIND_CONTROLLER|") == 0) {
list parts = llParseString2List(msg, ["|"], []);
string objectType = llList2String(parts, 1);
string controllerKey = (string)llGetKey(); // Use actual controller object key
llOwnerSay("📡 [Discovery] Request from " + objectType + " (" + (string)id + ")");
llRegionSayTo(id, DISCOVERY_CHANNEL, "CONTROLLER_FOUND|" + controllerKey);
llOwnerSay("📡 [Discovery] Sent controller key to " + objectType + ": " + controllerKey);
}
else if (llSubStringIndex(msg, "CLIENT_CONNECTED|") == 0) {
// Handle client connection notifications
list parts = llParseString2List(msg, ["|"], []);
if (llGetListLength(parts) >= 3) {
string clientType = llList2String(parts, 1);
key clientKey = (key)llList2String(parts, 2);
// Track connected client
if (llListFindList(connectedClients, [clientKey]) == -1) {
connectedClients += [clientKey];
llOwnerSay("✅ [Discovery] " + clientType + " connected (" + (string)clientKey + ")");
llOwnerSay("🔗 [Discovery] Connected clients: " + (string)llGetListLength(connectedClients));
// Notify main controller of connection
llMessageLinked(LINK_SET, MSG_CLIENT_CONNECTED, clientType + "|" + (string)clientKey, NULL_KEY);
// Check if we should stop broadcasting
if (llGetListLength(connectedClients) >= llGetListLength(expectedClientTypes)) {
llOwnerSay("🎯 [Discovery] All expected clients connected - stopping broadcasts");
discoveryBroadcastActive = FALSE;
llSetTimerEvent(0.0);
// Notify main controller that discovery is complete
llMessageLinked(LINK_SET, MSG_DISCOVERY_STATUS, "COMPLETE|" + (string)llGetListLength(connectedClients), NULL_KEY);
} else {
// Update status
llMessageLinked(LINK_SET, MSG_DISCOVERY_STATUS, "ACTIVE|" + (string)llGetListLength(connectedClients), NULL_KEY);
}
}
}
}
}
// Handle automatic configuration requests from newly rezzed displays
else if ((channel == SCOREBOARD_DATA_CHANNEL || channel == LEADERBOARD_DATA_CHANNEL || channel == DICE_DATA_CHANNEL) &&
llSubStringIndex(msg, "AUTO_CONFIG_REQUEST|") == 0) {
list parts = llParseString2List(msg, ["|"], []);
if (llGetListLength(parts) >= 4) {
string requestType = llList2String(parts, 1);
vector displayPos = (vector)llList2String(parts, 2);
rotation displayRot = (rotation)llList2String(parts, 3);
// Calculate offset and rotation relative to controller
vector controller_current_pos = llGetPos();
rotation controller_current_rot = llGetRot();
vector calculatedOffset = (displayPos - controller_current_pos) / controller_current_rot;
rotation calculatedRotation = displayRot / controller_current_rot;
// Send auto-config response back to the display on the correct channel
integer responseChannel;
if (requestType == "scoreboard") responseChannel = SCOREBOARD_DATA_CHANNEL;
else if (requestType == "leaderboard") responseChannel = LEADERBOARD_DATA_CHANNEL;
else if (requestType == "dice") responseChannel = DICE_DATA_CHANNEL;
else return; // Unknown display type
string responseMsg = "AUTO_CONFIG_RESPONSE|" + requestType + "|" +
(string)calculatedOffset + "|" + (string)calculatedRotation;
llRegionSay(responseChannel, responseMsg);
llOwnerSay("🎆 [Discovery] Auto-configured " + requestType + " display at rez!");
llOwnerSay(" Position: " + (string)displayPos);
llOwnerSay(" Calculated offset: " + (string)calculatedOffset);
llOwnerSay(" Calculated rotation: " + (string)calculatedRotation);
}
}
}
timer() {
if (discoveryBroadcastActive) {
// Broadcast controller availability
llRegionSay(DISCOVERY_CHANNEL, "CONTROLLER_AVAILABLE|" + (string)llGetKey());
llSetTimerEvent(DISCOVERY_BROADCAST_INTERVAL);
} else {
llSetTimerEvent(0.0);
}
}
link_message(integer sender, integer num, string str, key id) {
if (num == MSG_DISCOVERY_START) {
// Restart discovery broadcasts
discoveryBroadcastActive = TRUE;
llOwnerSay("📺 [Discovery] Restarting broadcasts");
llRegionSay(DISCOVERY_CHANNEL, "CONTROLLER_AVAILABLE|" + (string)llGetKey());
llSetTimerEvent(DISCOVERY_BROADCAST_INTERVAL);
}
else if (num == MSG_DISCOVERY_STOP) {
// Stop discovery broadcasts
discoveryBroadcastActive = FALSE;
llSetTimerEvent(0.0);
llOwnerSay("⏹️ [Discovery] Broadcasts stopped");
}
else if (num == -99999 && str == "FULL_RESET") {
// Handle full game reset
connectedClients = [];
discoveryBroadcastActive = TRUE;
llOwnerSay("🔄 [Discovery] Reset - restarting broadcasts");
llRegionSay(DISCOVERY_CHANNEL, "CONTROLLER_AVAILABLE|" + (string)llGetKey());
llSetTimerEvent(DISCOVERY_BROADCAST_INTERVAL);
llMessageLinked(LINK_SET, MSG_DISCOVERY_STATUS, "ACTIVE|0", NULL_KEY);
}
}
}