refactor: remove legacy MCP and development tools, and update documentation to reflect the current single-linkset architecture.
This commit is contained in:
parent
ccf472585a
commit
d0b11ef474
12 changed files with 17 additions and 1046 deletions
|
|
@ -4,6 +4,13 @@
|
||||||
|
|
||||||
All notable changes to Peril Dice will be documented in this file.
|
All notable changes to Peril Dice will be documented in this file.
|
||||||
|
|
||||||
|
## [3.2.6] - 2026-04-09
|
||||||
|
### 🏁 **Final Production Sweep (Tournament Edition)**
|
||||||
|
- **Global Silence**: Implemented unconditional stripping of all debug string literals using the `DEBUG_LOGS 0` pre-processor macro across the entire codebase.
|
||||||
|
- **Memory Hardening**: Set `SHOW_MEMORY 0` to eliminate runtime memory reporting overhead during high-traffic tournament play.
|
||||||
|
- **Repository Sanitization**: Purged all legacy GitHub updater documentation and development tools (`lsl_validator`, etc.) for a clean production package.
|
||||||
|
- **Sentinel finalization**: Verified "Auto-Pilot" Sentinel 7.9.2 for automated, no-touch land diagnostics.
|
||||||
|
|
||||||
## [3.2.6] - 2026-04-05
|
## [3.2.6] - 2026-04-05
|
||||||
|
|
||||||
### 🎰 **Hybrid HUD Evolution**
|
### 🎰 **Hybrid HUD Evolution**
|
||||||
|
|
|
||||||
|
|
@ -1,149 +0,0 @@
|
||||||
#include "peril/Peril_Constants.lsl"
|
|
||||||
|
|
||||||
// ====================================================================
|
|
||||||
// Enhanced Link Scanner for Peril Dice Linkset
|
|
||||||
// ====================================================================
|
|
||||||
// Extended version of Temp_LinkScanner.lsl that identifies:
|
|
||||||
// 1. Current linkset structure
|
|
||||||
// 2. Which scripts belong on which links
|
|
||||||
// 3. Missing scripts or misplaced scripts
|
|
||||||
//
|
|
||||||
// USAGE: Place this script on any prim in the linkset, touch to scan
|
|
||||||
// ====================================================================
|
|
||||||
|
|
||||||
// Expected script locations based on WARP.md architecture
|
|
||||||
list LINK1_SCRIPTS = [
|
|
||||||
"Main_Controller_Linkset.lsl",
|
|
||||||
"Game_Manager.lsl",
|
|
||||||
"Controller_Memory.lsl",
|
|
||||||
"Controller_MessageHandler.lsl",
|
|
||||||
"Player_RegistrationManager.lsl",
|
|
||||||
"Player_DialogHandler.lsl",
|
|
||||||
"NumberPicker_DialogHandler.lsl",
|
|
||||||
"Floater_Manager.lsl",
|
|
||||||
"Roll_ConfettiModule.lsl",
|
|
||||||
"Bot_Manager.lsl",
|
|
||||||
"Game_Calculator.lsl",
|
|
||||||
"Verbose_Logger.lsl",
|
|
||||||
"System_Debugger.lsl"
|
|
||||||
];
|
|
||||||
|
|
||||||
// Link-specific scripts (based on architecture documentation)
|
|
||||||
list LINKSET_SPECIFIC_SCRIPTS = [
|
|
||||||
"Game_Scoreboard_Manager_Linkset.lsl", // Link 12
|
|
||||||
"Leaderboard_Communication_Linkset.lsl", // Link 35
|
|
||||||
"XyzzyText_Dice_Bridge_Linkset.lsl" // Link 83
|
|
||||||
];
|
|
||||||
|
|
||||||
#define XYZZY_SCRIPT "xyzzy_Master_script.lsl" // Links 35-82 (48 prims)
|
|
||||||
|
|
||||||
// Scan results
|
|
||||||
integer totalPrims = 0;
|
|
||||||
list linkNames = [];
|
|
||||||
list linkPositions = [];
|
|
||||||
|
|
||||||
// Function to perform the full linkset scan
|
|
||||||
performFullScan() {
|
|
||||||
totalPrims = llGetNumberOfPrims();
|
|
||||||
linkNames = [];
|
|
||||||
linkPositions = [];
|
|
||||||
|
|
||||||
llOwnerSay("=== 🔍 PERIL DICE LINKSET ANALYSIS ===");
|
|
||||||
llOwnerSay("📊 Total Prims: " + (string)totalPrims);
|
|
||||||
llOwnerSay("🎯 Expected Architecture: 84-prim single linkset");
|
|
||||||
llOwnerSay("");
|
|
||||||
|
|
||||||
// Scan all links
|
|
||||||
integer i;
|
|
||||||
for (i = 1; i <= totalPrims; i++) {
|
|
||||||
string primName = llGetLinkName(i);
|
|
||||||
vector pos = llList2Vector(llGetLinkPrimitiveParams(i, [PRIM_POSITION]), 0);
|
|
||||||
|
|
||||||
linkNames += [primName];
|
|
||||||
linkPositions += [pos];
|
|
||||||
|
|
||||||
// Basic link info
|
|
||||||
llOwnerSay("Link " + (string)i + ": " + primName);
|
|
||||||
|
|
||||||
// Add delays to prevent spam
|
|
||||||
if (i % 15 == 0) {
|
|
||||||
llSleep(DELAY_SCAN_LINKSET);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
llOwnerSay("");
|
|
||||||
llOwnerSay("=== 📋 SCRIPT PLACEMENT ANALYSIS ===");
|
|
||||||
|
|
||||||
// Analyze Link 1 (Root Prim)
|
|
||||||
llOwnerSay("🔧 LINK 1 (ROOT PRIM) ANALYSIS:");
|
|
||||||
llOwnerSay(" Name: " + llList2String(linkNames, 0));
|
|
||||||
llOwnerSay(" Expected Scripts: " + (string)llGetListLength(LINK1_SCRIPTS) + " scripts");
|
|
||||||
|
|
||||||
integer j;
|
|
||||||
for (j = 0; j < llGetListLength(LINK1_SCRIPTS); j++) {
|
|
||||||
string scriptName = llList2String(LINK1_SCRIPTS, j);
|
|
||||||
llOwnerSay(" • " + scriptName);
|
|
||||||
}
|
|
||||||
llOwnerSay("");
|
|
||||||
|
|
||||||
// Analyze scoreboard
|
|
||||||
llOwnerSay("📊 SCOREBOARD SECTION (Links 2-24):");
|
|
||||||
if (totalPrims >= 12) {
|
|
||||||
llOwnerSay(" Link 12: " + llList2String(linkNames, 11) + " (Scoreboard Manager)");
|
|
||||||
llOwnerSay(" Expected Script: Game_Scoreboard_Manager_Linkset.lsl");
|
|
||||||
}
|
|
||||||
llOwnerSay(" Links 2-11: Overlay prims (elimination markers)");
|
|
||||||
llOwnerSay(" Links 13-24: Player display prims (profiles & hearts)");
|
|
||||||
llOwnerSay("");
|
|
||||||
|
|
||||||
// Analyze leaderboard
|
|
||||||
llOwnerSay("🏆 LEADERBOARD SECTION (Links 35-82):");
|
|
||||||
if (totalPrims >= 35) {
|
|
||||||
llOwnerSay(" Link 35: " + llList2String(linkNames, 34) + " (Leaderboard Bridge)");
|
|
||||||
llOwnerSay(" Expected Script: Leaderboard_Communication_Linkset.lsl");
|
|
||||||
}
|
|
||||||
llOwnerSay(" Links 35-82: XyzzyText display prims (48 total)");
|
|
||||||
llOwnerSay(" Each prim needs: xyzzy_Master_script.lsl");
|
|
||||||
llOwnerSay(" Organization: 4 banks × 12 prims each");
|
|
||||||
llOwnerSay("");
|
|
||||||
|
|
||||||
// Analyze dice display
|
|
||||||
llOwnerSay("🎲 DICE DISPLAY SECTION (Links 83-84):");
|
|
||||||
if (totalPrims >= 83) {
|
|
||||||
llOwnerSay(" Link 83: " + llList2String(linkNames, 82) + " (Dice Bridge)");
|
|
||||||
llOwnerSay(" Expected Script: XyzzyText_Dice_Bridge_Linkset.lsl");
|
|
||||||
}
|
|
||||||
if (totalPrims >= 84) {
|
|
||||||
llOwnerSay(" Link 84: " + llList2String(linkNames, 83) + " (Dice Display)");
|
|
||||||
llOwnerSay(" No script needed");
|
|
||||||
}
|
|
||||||
llOwnerSay("");
|
|
||||||
|
|
||||||
llOwnerSay("🔄 ESSENTIAL FILES FOR GAMEPLAY:");
|
|
||||||
llOwnerSay(" 📂 Link 1 Scripts (" + (string)llGetListLength(LINK1_SCRIPTS) + " files)");
|
|
||||||
llOwnerSay(" 📂 Link-Specific Scripts (3 files)");
|
|
||||||
llOwnerSay(" 📂 XyzzyText Script (1 file × 48 prims)");
|
|
||||||
llOwnerSay(" 📂 Documentation (4 files)");
|
|
||||||
llOwnerSay("");
|
|
||||||
|
|
||||||
llOwnerSay("⚠️ EXCLUDED FROM UPDATES:");
|
|
||||||
llOwnerSay(" 🚫 Development files: lsl_validator.py, debug_mcp.py");
|
|
||||||
llOwnerSay(" 🚫 Test files: test_*.py, MCP_README.md");
|
|
||||||
llOwnerSay(" 🚫 Templates: MEMORY_REPORT_TEMPLATE.lsl");
|
|
||||||
llOwnerSay(" 🚫 Utilities: Temp_LinkScanner.lsl, Enhanced_LinkScanner.lsl");
|
|
||||||
|
|
||||||
llOwnerSay("");
|
|
||||||
llOwnerSay("✅ Linkset analysis complete!");
|
|
||||||
}
|
|
||||||
|
|
||||||
default {
|
|
||||||
state_entry() {
|
|
||||||
llOwnerSay("🔍 Enhanced Link Scanner ready for Peril Dice linkset");
|
|
||||||
llOwnerSay("👆 Touch to perform full linkset analysis");
|
|
||||||
}
|
|
||||||
|
|
||||||
touch_start(integer total_number) {
|
|
||||||
if (llDetectedKey(0) != llGetOwner()) return;
|
|
||||||
performFullScan();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
// ============================================================================
|
|
||||||
// Experience Admin HUD
|
|
||||||
// ============================================================================
|
|
||||||
// Instructions:
|
|
||||||
// 1. Create a new box, put this script inside.
|
|
||||||
// 2. DO NOT FORGET to check "Use Experience" and select Final Girlz I.N.C!
|
|
||||||
// 3. Attach it to your HUD or click it if it's rezzed.
|
|
||||||
//
|
|
||||||
// Commands (type in local chat):
|
|
||||||
// /99 keys - Lists all keys currently in the entire database
|
|
||||||
// /99 get [Key] - Reads and displays the data inside a specific key
|
|
||||||
// /99 del [Key] - Deletes a specific key from the database
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
key requestKeys;
|
|
||||||
key requestRead;
|
|
||||||
key requestDelete;
|
|
||||||
key requestWrite;
|
|
||||||
key lastUser;
|
|
||||||
string pendingSetKey;
|
|
||||||
string pendingSetValue;
|
|
||||||
|
|
||||||
default {
|
|
||||||
state_entry() {
|
|
||||||
lastUser = llGetOwner();
|
|
||||||
llRegionSayTo(lastUser, 0, "🔧 Experience Admin Terminal Ready.");
|
|
||||||
llRegionSayTo(lastUser, 0, "💬 Type commands in local chat:");
|
|
||||||
llRegionSayTo(lastUser, 0, " /99 keys");
|
|
||||||
llRegionSayTo(lastUser, 0, " /99 get key_name");
|
|
||||||
llRegionSayTo(lastUser, 0, " /99 set key_name=Hello World");
|
|
||||||
llRegionSayTo(lastUser, 0, " /99 del key_name");
|
|
||||||
llListen(99, "", NULL_KEY, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
listen(integer channel, string name, key id, string msg) {
|
|
||||||
// SECURITY CHECK: Strictly whitelist authorized Admin usernames!
|
|
||||||
string uname = llStringTrim(llToLower(llGetUsername(id)), STRING_TRIM);
|
|
||||||
if (id != llGetOwner() && uname != "djmusica28" && uname != "finalgirlzbot") {
|
|
||||||
llRegionSayTo(id, 0, "❌ Access Denied: You are not on the authorized Administrator whitelist for this database.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lastUser = id;
|
|
||||||
msg = llStringTrim(msg, STRING_TRIM);
|
|
||||||
|
|
||||||
if (msg == "keys") {
|
|
||||||
llRegionSayTo(lastUser, 0, "🔍 Requesting list of database keys (up to 100)...");
|
|
||||||
requestKeys = llKeysKeyValue(0, 100);
|
|
||||||
}
|
|
||||||
else if (llGetSubString(msg, 0, 3) == "get ") {
|
|
||||||
string targetKey = llStringTrim(llGetSubString(msg, 4, -1), STRING_TRIM);
|
|
||||||
llRegionSayTo(lastUser, 0, "🔍 Reading key: " + targetKey + "...");
|
|
||||||
requestRead = llReadKeyValue(targetKey);
|
|
||||||
}
|
|
||||||
else if (llGetSubString(msg, 0, 3) == "set ") {
|
|
||||||
string payload = llStringTrim(llGetSubString(msg, 4, -1), STRING_TRIM);
|
|
||||||
integer equalSign = llSubStringIndex(payload, "=");
|
|
||||||
if (equalSign != -1) {
|
|
||||||
pendingSetKey = llStringTrim(llGetSubString(payload, 0, equalSign - 1), STRING_TRIM);
|
|
||||||
pendingSetValue = llStringTrim(llGetSubString(payload, equalSign + 1, -1), STRING_TRIM);
|
|
||||||
llRegionSayTo(lastUser, 0, "💾 Writing key: " + pendingSetKey + "...");
|
|
||||||
requestWrite = llUpdateKeyValue(pendingSetKey, pendingSetValue, FALSE, "");
|
|
||||||
} else {
|
|
||||||
llRegionSayTo(lastUser, 0, "❌ Format: /99 set key=value");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (llGetSubString(msg, 0, 3) == "del ") {
|
|
||||||
string targetKey = llStringTrim(llGetSubString(msg, 4, -1), STRING_TRIM);
|
|
||||||
llRegionSayTo(lastUser, 0, "🗑️ Deleting key: " + targetKey + "...");
|
|
||||||
requestDelete = llDeleteKeyValue(targetKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dataserver(key queryid, string data) {
|
|
||||||
// llKeysKeyValue returns data as CSV:
|
|
||||||
// Success: 1,keyname1,keyname2...
|
|
||||||
// Failure: 0,error_code
|
|
||||||
if (queryid == requestKeys) {
|
|
||||||
list result = llCSV2List(data);
|
|
||||||
if (llList2Integer(result, 0) == 1) {
|
|
||||||
// Success
|
|
||||||
integer numKeys = llGetListLength(result) - 1;
|
|
||||||
llRegionSayTo(lastUser, 0, "✅ Found " + (string)numKeys + " keys in database:");
|
|
||||||
|
|
||||||
integer i;
|
|
||||||
for(i = 1; i < llGetListLength(result); i++) {
|
|
||||||
llRegionSayTo(lastUser, 0, " 🔑 " + llList2String(result, i));
|
|
||||||
}
|
|
||||||
} else if (llList2Integer(result, 1) == 3) {
|
|
||||||
llRegionSayTo(lastUser, 0, "📭 The database is completely empty! No keys exist.");
|
|
||||||
} else {
|
|
||||||
llRegionSayTo(lastUser, 0, "❌ Failed to read keys (Error " + llList2String(result, 1) + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// llReadKeyValue returns data as: status,value
|
|
||||||
else if (queryid == requestRead) {
|
|
||||||
integer comma = llSubStringIndex(data, ",");
|
|
||||||
string status = llGetSubString(data, 0, comma - 1);
|
|
||||||
string val = llGetSubString(data, comma + 1, -1);
|
|
||||||
|
|
||||||
if (status == "1") {
|
|
||||||
llRegionSayTo(lastUser, 0, "📄 Content:\n" + val);
|
|
||||||
} else if (status == "3") {
|
|
||||||
llRegionSayTo(lastUser, 0, "❌ That key doesn't exist!");
|
|
||||||
} else {
|
|
||||||
llRegionSayTo(lastUser, 0, "❌ Read failed (Status: " + status + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// llDeleteKeyValue dataserver logic
|
|
||||||
else if (queryid == requestDelete) {
|
|
||||||
integer comma = llSubStringIndex(data, ",");
|
|
||||||
string status = llGetSubString(data, 0, comma - 1);
|
|
||||||
|
|
||||||
if (status == "1") {
|
|
||||||
llRegionSayTo(lastUser, 0, "✅ Key successfully deleted!");
|
|
||||||
} else if (status == "3") {
|
|
||||||
llRegionSayTo(lastUser, 0, "❌ Key didn't exist to delete.");
|
|
||||||
} else {
|
|
||||||
llRegionSayTo(lastUser, 0, "❌ Delete failed (Status: " + status + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write logic
|
|
||||||
else if (queryid == requestWrite) {
|
|
||||||
integer comma = llSubStringIndex(data, ",");
|
|
||||||
string status = llGetSubString(data, 0, comma - 1);
|
|
||||||
if (status == "1") {
|
|
||||||
llRegionSayTo(lastUser, 0, "✅ Key saved successfully!");
|
|
||||||
} else if (status == "3") {
|
|
||||||
// Key didn't exist to update, manually create it!
|
|
||||||
requestWrite = llCreateKeyValue(pendingSetKey, pendingSetValue);
|
|
||||||
} else {
|
|
||||||
llRegionSayTo(lastUser, 0, "❌ Write failed (Status: " + status + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
# LSL Development MCP Server
|
|
||||||
|
|
||||||
This MCP (Model Context Protocol) server provides AI assistants with direct access to your LSL development tools and Peril Dice project.
|
|
||||||
|
|
||||||
## What it provides
|
|
||||||
|
|
||||||
### Available Tools:
|
|
||||||
1. **lint_lsl_file** - Lint/validate LSL syntax for a specific file using lslint
|
|
||||||
2. **lint_all_lsl** - Lint/validate all LSL files in the project using lslint
|
|
||||||
3. **list_lsl_files** - List all LSL files in the project (currently shows 20 files)
|
|
||||||
4. **git_status** - Get git status of the project
|
|
||||||
5. **analyze_script_architecture** - Analyze script architecture and communication patterns
|
|
||||||
6. **get_memory_usage_info** - Extract memory usage information from LSL files
|
|
||||||
|
|
||||||
### Available Resources:
|
|
||||||
- Project README.md
|
|
||||||
- CHANGELOG.md
|
|
||||||
- WARP.md (development guide)
|
|
||||||
|
|
||||||
## Files Created:
|
|
||||||
- `lsl_mcp_server.py` - The main MCP server
|
|
||||||
- `mcp_config.json` - Configuration for MCP clients
|
|
||||||
- `test_mcp_server.py` - Test script to verify server functionality
|
|
||||||
|
|
||||||
## How to Use:
|
|
||||||
|
|
||||||
### Testing the Server:
|
|
||||||
```bash
|
|
||||||
python3 test_mcp_server.py
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manual Server Start:
|
|
||||||
```bash
|
|
||||||
python3 lsl_mcp_server.py
|
|
||||||
```
|
|
||||||
|
|
||||||
### Integration with AI Tools:
|
|
||||||
The `mcp_config.json` file can be used to configure MCP-compatible AI tools to connect to this server.
|
|
||||||
|
|
||||||
## Benefits:
|
|
||||||
|
|
||||||
With this MCP server, AI assistants can:
|
|
||||||
- Directly lint and validate your LSL code using lslint
|
|
||||||
- List and examine all 20 LSL files in your Peril project
|
|
||||||
- Check git status and project state
|
|
||||||
- Analyze your script architecture (Controllers, Handlers, Modules, Linkset scripts)
|
|
||||||
- Access project documentation
|
|
||||||
- Monitor memory usage information from script comments
|
|
||||||
|
|
||||||
This creates a much more interactive and helpful development experience, where the AI can actually run your development tools and provide real-time feedback on your Peril Dice project.
|
|
||||||
|
|
||||||
## Security:
|
|
||||||
|
|
||||||
The server only has access to:
|
|
||||||
- Your Peril project directory (`/home/richard/peril`)
|
|
||||||
- Your lslint tool (`/home/richard/lslint/lslint`)
|
|
||||||
- Git commands within the project directory
|
|
||||||
- Read-only access to project documentation
|
|
||||||
|
|
||||||
It cannot modify files outside the project scope or execute arbitrary system commands.
|
|
||||||
|
|
@ -36,7 +36,7 @@ key EXPERIENCE_ID = "06926390-26e6-11f1-a452-0242ac110003"; // Final Girlz I.N.C
|
||||||
#define FW_DONE "fw_done"
|
#define FW_DONE "fw_done"
|
||||||
|
|
||||||
// --- System/Debug (9000-9999) ---
|
// --- 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
|
#if DEBUG_LOGS
|
||||||
#define dbg(msg) llOwnerSay(msg)
|
#define dbg(msg) llOwnerSay(msg)
|
||||||
#else
|
#else
|
||||||
|
|
@ -143,7 +143,7 @@ key EXPERIENCE_ID = "06926390-26e6-11f1-a452-0242ac110003"; // Final Girlz I.N.C
|
||||||
#define MSG_QUERY_OWNER_STATUS 213
|
#define MSG_QUERY_OWNER_STATUS 213
|
||||||
|
|
||||||
// --- Memory Management (6000-6099) ---
|
// --- Memory Management (6000-6099) ---
|
||||||
#define SHOW_MEMORY 1 // Toggle this to 1 to see memory usage on rez/reset
|
#define SHOW_MEMORY 0 // Toggle this to 1 to see memory usage on rez/reset
|
||||||
|
|
||||||
#define REPORT_MEMORY() { \
|
#define REPORT_MEMORY() { \
|
||||||
if (SHOW_MEMORY) { \
|
if (SHOW_MEMORY) { \
|
||||||
|
|
|
||||||
67
README.md
67
README.md
|
|
@ -97,67 +97,16 @@ Peril Dice is a multiplayer elimination game where each player selects numbers b
|
||||||
- **Microscopic Position Offsets**: Resolved persistent Z-fighting on the custom UI (scoreboard, header, start button) by applying microscopic local axis offsets (`0.005m` and `0.008m` relative to the backboard).
|
- **Microscopic Position Offsets**: Resolved persistent Z-fighting on the custom UI (scoreboard, header, start button) by applying microscopic local axis offsets (`0.005m` and `0.008m` relative to the backboard).
|
||||||
- **Initialization Stability**: Patched Scoreboard, Leaderboard, and Dice Bridge scripts with `on_rez` auto-reset handlers to ensure flawless texture synchronization and state recovery upon rezzing.
|
- **Initialization Stability**: Patched Scoreboard, Leaderboard, and Dice Bridge scripts with `on_rez` auto-reset handlers to ensure flawless texture synchronization and state recovery upon rezzing.
|
||||||
|
|
||||||
## Major v2.8.7 Improvements 🔄
|
## System Architecture (v3.2.6) 🏗️
|
||||||
|
|
||||||
### 🔄 **GitHub Update System (Superseded)**
|
Peril Dice is now a unified single-linkset system utilizing modern LSL pre-processor techniques and mesh text displays.
|
||||||
> **⚠️ Note**: The GitHub Update System was fully replaced by the CasperVend update system in v3.0.0 for commercial release.
|
|
||||||
- **In-World Update Checker**: Revolutionary `Update_Checker.lsl` script provides direct GitHub integration
|
|
||||||
- **GitHub API Integration**: Connects to `https://github.com/RebeccaNod1/Peril/releases` for automatic update detection
|
|
||||||
- **Smart File Filtering**: Only downloads essential game files, excludes development/test files
|
|
||||||
- **Precise Deployment Instructions**: Tells you exactly which link each script belongs on
|
|
||||||
- **Owner Menu Integration**: Access via Owner Menu → Troubleshooting → Check for Updates
|
|
||||||
- **Chat Commands**: `/1 check`, `/1 download ScriptName.lsl`, `/1 list` for direct access
|
|
||||||
- **Memory Optimized**: Fits perfectly in the slot freed by eliminated UpdateHelper.lsl (v2.8.5)
|
|
||||||
- **Professional Error Handling**: Clear feedback for network issues, missing releases, file validation
|
|
||||||
|
|
||||||
### 🔍 **Enhanced LinkScanner System**
|
### **Core Components:**
|
||||||
- **Complete 84-Prim Analysis**: `Enhanced_LinkScanner.lsl` provides detailed linkset structure mapping
|
- **Main Controller (Root)**: Authoritative source of truth for all game states.
|
||||||
- **VERIFIED Structure**: Based on actual linkset scan showing exact prim names and positions
|
- **Experience Sentinel (v7.9.2)**: Automated "Auto-Pilot" land diagnostics.
|
||||||
- **Script Placement Guide**: Shows which scripts belong on which links with verification
|
- **Hybrid HUD System**: personal HUDs for humans + in-world floaters for bots.
|
||||||
- **XyzzyText Mapping**: Detailed analysis of all 48 leaderboard prims (Links 35-82)
|
- **FURWARE Text Mesh**: High-performance mesh displays for dice and leaderboard.
|
||||||
- **Update Deployment Guide**: Generates precise instructions for Update_Checker placement
|
- **Dynamic Link Discovery**: Root-level discovery of all sibling prims by name.
|
||||||
- **Real-Time Validation**: Automated "Invisible Guardian" scans land permissions and verifies if the "Final Girlz I.N.C." experience is correctly configured on every rez and reset.
|
|
||||||
|
|
||||||
### 🎯 **Smart File Classification**
|
|
||||||
- **Essential Game Files Only**: Update system intelligently filters files for end users
|
|
||||||
- **✅ Included**: All LSL scripts, documentation (README, CHANGELOG, notecards)
|
|
||||||
- **🚫 Excluded**: lsl_validator.py, debug_mcp.py, test_*.py, MCP files, templates
|
|
||||||
- **Link-Specific Instructions**: Downloads include verified deployment locations
|
|
||||||
- **Link 1**: 13 root prim scripts + Update_Checker.lsl
|
|
||||||
- **Link 12**: "scoreboard manager cube" - Game_Scoreboard_Manager_Linkset.lsl
|
|
||||||
- **Link 35**: "leaderboard row 1 col 1" - Leaderboard_Communication_Linkset.lsl
|
|
||||||
- **Links 35-82**: All 48 XyzzyText prims need xyzzy_Master_script.lsl
|
|
||||||
- **Link 83**: "dice col 1" - XyzzyText_Dice_Bridge_Linkset.lsl
|
|
||||||
|
|
||||||
### 🚀 **Zero-Infrastructure Updates**
|
|
||||||
- **No Update Server Required**: Leverages existing GitHub + Jenkins CI/CD pipeline perfectly
|
|
||||||
- **Automatic Release Detection**: Finds new versions as soon as they're tagged and released
|
|
||||||
- **Individual File Updates**: Download specific scripts without full release packages
|
|
||||||
- **GitHub Raw Integration**: Direct access to latest files from main branch
|
|
||||||
- **Multi-Instance Safe**: Multiple game tables can check for updates independently
|
|
||||||
|
|
||||||
### 🎨 **Professional User Experience**
|
|
||||||
- **Clear Version Information**: Shows current vs latest versions with release dates
|
|
||||||
- **Release Notes Preview**: Displays first 200 characters of GitHub release notes
|
|
||||||
- **Download Progress**: File size reporting and content preview for verification
|
|
||||||
- **Deployment Verification**: Confirms updates are designed for verified 84-prim structure
|
|
||||||
- **Graceful Error Handling**: Helpful messages for GitHub rate limits, missing files, etc.
|
|
||||||
|
|
||||||
### 📊 **System Integration Benefits**
|
|
||||||
- **Replaces Eliminated UpdateHelper**: Perfect use of memory freed in v2.8.5 optimization
|
|
||||||
- **Existing Admin Menu**: Integrates seamlessly with current troubleshooting interface
|
|
||||||
- **Message Protocol Compatibility**: Uses established link message patterns and constants
|
|
||||||
- **Verbose Logging Support**: Follows existing debug toggle system
|
|
||||||
- **Memory Reporting**: Includes standard memory usage reporting like other scripts
|
|
||||||
|
|
||||||
### 💱 **Before vs After v2.8.7**
|
|
||||||
| **Before** | **After** |
|
|
||||||
|------------|----------|
|
|
||||||
| ❌ Manual update distribution via inventory | ✅ Automatic GitHub integration with update detection |
|
|
||||||
| ❌ No way to check for new versions | ✅ One-click update checking via `/1 check` or admin menu |
|
|
||||||
| ❌ Guessing which scripts go where | ✅ Precise deployment instructions verified by linkset scan |
|
|
||||||
| ❌ Development files mixed with game files | ✅ Smart filtering shows only essential files |
|
|
||||||
| ❌ No linkset structure validation | ✅ Complete 84-prim analysis and verification tools |
|
|
||||||
|
|
||||||
## Major v2.8.6 Improvements 🏆
|
## Major v2.8.6 Improvements 🏆
|
||||||
|
|
||||||
|
|
|
||||||
50
debug_mcp.py
50
debug_mcp.py
|
|
@ -1,50 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
def debug_main():
|
|
||||||
"""Debug version to see what's happening"""
|
|
||||||
print('{"debug": "server started"}', flush=True)
|
|
||||||
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
line = sys.stdin.readline()
|
|
||||||
print(f'{{"debug": "received line: {line.strip()[:100]}"}}', flush=True)
|
|
||||||
|
|
||||||
if not line:
|
|
||||||
print('{"debug": "EOF received, exiting"}', flush=True)
|
|
||||||
break
|
|
||||||
|
|
||||||
line = line.strip()
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
|
|
||||||
try:
|
|
||||||
request = json.loads(line)
|
|
||||||
print(f'{{"debug": "parsed request: {request.get("method", "unknown")}"}}', flush=True)
|
|
||||||
|
|
||||||
if request.get("method") == "initialize":
|
|
||||||
response = {
|
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"id": request.get("id"),
|
|
||||||
"result": {
|
|
||||||
"protocolVersion": "2025-03-26",
|
|
||||||
"capabilities": {"tools": {}, "resources": {}},
|
|
||||||
"serverInfo": {"name": "Debug LSL Server", "version": "1.0.0"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
print(json.dumps(response), flush=True)
|
|
||||||
print('{"debug": "sent initialize response"}', flush=True)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f'{{"debug": "error parsing: {str(e)}"}}', flush=True)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f'{{"debug": "main loop error: {str(e)}"}}', flush=True)
|
|
||||||
traceback.print_exc(file=sys.stderr)
|
|
||||||
|
|
||||||
print('{"debug": "server exiting"}', flush=True)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
debug_main()
|
|
||||||
|
|
@ -1,414 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
LSL Development MCP Server for Peril Dice Project
|
|
||||||
Provides tools for LSL validation, preprocessing, and project management.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import sys
|
|
||||||
import subprocess
|
|
||||||
import os
|
|
||||||
import glob
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Dict, List, Any, Optional
|
|
||||||
|
|
||||||
|
|
||||||
class LSLMCPServer:
|
|
||||||
def __init__(self, project_root: str = "/home/richard/peril"):
|
|
||||||
self.project_root = Path(project_root)
|
|
||||||
self.lsl_validator_path = self.project_root / "lsl_validator.py"
|
|
||||||
|
|
||||||
def handle_request(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
|
||||||
"""Handle incoming MCP requests"""
|
|
||||||
method = request.get("method", "")
|
|
||||||
params = request.get("params", {})
|
|
||||||
|
|
||||||
if method == "initialize":
|
|
||||||
return self._handle_initialize(params)
|
|
||||||
elif method == "tools/list":
|
|
||||||
return self._list_tools()
|
|
||||||
elif method == "tools/call":
|
|
||||||
return self._call_tool(params)
|
|
||||||
elif method == "resources/list":
|
|
||||||
return self._list_resources()
|
|
||||||
elif method == "resources/read":
|
|
||||||
return self._read_resource(params)
|
|
||||||
else:
|
|
||||||
return {"error": f"Unknown method: {method}"}
|
|
||||||
|
|
||||||
def _handle_initialize(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
||||||
"""Handle MCP initialization request"""
|
|
||||||
# Use the client's protocol version if provided
|
|
||||||
client_version = params.get("protocolVersion", "2024-11-05")
|
|
||||||
return {
|
|
||||||
"protocolVersion": client_version,
|
|
||||||
"capabilities": {
|
|
||||||
"tools": {},
|
|
||||||
"resources": {}
|
|
||||||
},
|
|
||||||
"serverInfo": {
|
|
||||||
"name": "LSL Development Server",
|
|
||||||
"version": "1.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def _list_tools(self) -> Dict[str, Any]:
|
|
||||||
"""List available LSL development tools"""
|
|
||||||
return {
|
|
||||||
"tools": [
|
|
||||||
{
|
|
||||||
"name": "validate_lsl_file",
|
|
||||||
"description": "Comprehensive LSL validation for a specific file (syntax, scope, style, performance, memory)",
|
|
||||||
"inputSchema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"file_path": {"type": "string", "description": "Path to LSL file to validate"}
|
|
||||||
},
|
|
||||||
"required": ["file_path"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "validate_all_lsl",
|
|
||||||
"description": "Comprehensive LSL validation for all files in the project",
|
|
||||||
"inputSchema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "list_lsl_files",
|
|
||||||
"description": "List all LSL files in the project",
|
|
||||||
"inputSchema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "git_status",
|
|
||||||
"description": "Get git status of the project",
|
|
||||||
"inputSchema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "analyze_script_architecture",
|
|
||||||
"description": "Analyze the script architecture and communication patterns",
|
|
||||||
"inputSchema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "get_memory_usage_info",
|
|
||||||
"description": "Extract memory usage information from LSL files",
|
|
||||||
"inputSchema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
def _call_tool(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
||||||
"""Execute a specific tool"""
|
|
||||||
tool_name = params.get("name", "")
|
|
||||||
arguments = params.get("arguments", {})
|
|
||||||
|
|
||||||
try:
|
|
||||||
if tool_name == "validate_lsl_file":
|
|
||||||
return self._validate_lsl_file(arguments["file_path"])
|
|
||||||
elif tool_name == "validate_all_lsl":
|
|
||||||
return self._validate_all_lsl()
|
|
||||||
elif tool_name == "list_lsl_files":
|
|
||||||
return self._list_lsl_files()
|
|
||||||
elif tool_name == "git_status":
|
|
||||||
return self._git_status()
|
|
||||||
elif tool_name == "analyze_script_architecture":
|
|
||||||
return self._analyze_script_architecture()
|
|
||||||
elif tool_name == "get_memory_usage_info":
|
|
||||||
return self._get_memory_usage_info()
|
|
||||||
else:
|
|
||||||
return {"error": f"Unknown tool: {tool_name}"}
|
|
||||||
except Exception as e:
|
|
||||||
return {"error": f"Tool execution failed: {str(e)}"}
|
|
||||||
|
|
||||||
def _validate_lsl_file(self, file_path: str) -> Dict[str, Any]:
|
|
||||||
"""Validate a specific LSL file using the comprehensive LSL validator"""
|
|
||||||
full_path = self.project_root / file_path if not os.path.isabs(file_path) else Path(file_path)
|
|
||||||
|
|
||||||
if not full_path.exists():
|
|
||||||
return {"error": f"File not found: {full_path}"}
|
|
||||||
|
|
||||||
if not self.lsl_validator_path.exists():
|
|
||||||
return {"error": f"LSL validator not found at: {self.lsl_validator_path}"}
|
|
||||||
|
|
||||||
try:
|
|
||||||
result = subprocess.run([
|
|
||||||
"python3", str(self.lsl_validator_path), str(full_path)
|
|
||||||
], capture_output=True, text=True, cwd=str(self.project_root))
|
|
||||||
|
|
||||||
return {
|
|
||||||
"content": [
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"text": f"LSL Validation Result for {file_path}:\n\n"
|
|
||||||
f"Exit Code: {result.returncode}\n"
|
|
||||||
f"Output:\n{result.stdout}\n"
|
|
||||||
f"Errors:\n{result.stderr}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
return {"error": f"Failed to run LSL validator: {str(e)}"}
|
|
||||||
|
|
||||||
def _validate_all_lsl(self) -> Dict[str, Any]:
|
|
||||||
"""Validate all LSL files in the project using the comprehensive LSL validator"""
|
|
||||||
if not self.lsl_validator_path.exists():
|
|
||||||
return {"error": f"LSL validator not found at: {self.lsl_validator_path}"}
|
|
||||||
|
|
||||||
try:
|
|
||||||
result = subprocess.run([
|
|
||||||
"python3", str(self.lsl_validator_path), "."
|
|
||||||
], capture_output=True, text=True, cwd=str(self.project_root))
|
|
||||||
|
|
||||||
return {
|
|
||||||
"content": [
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"text": f"LSL Validation Results for All Files:\n\n"
|
|
||||||
f"Exit Code: {result.returncode}\n\n"
|
|
||||||
f"Output:\n{result.stdout}\n"
|
|
||||||
f"Errors:\n{result.stderr}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
return {"error": f"Failed to run LSL validator on all files: {str(e)}"}
|
|
||||||
|
|
||||||
def _list_lsl_files(self) -> Dict[str, Any]:
|
|
||||||
"""List all LSL files in the project"""
|
|
||||||
lsl_files = list(self.project_root.glob("*.lsl"))
|
|
||||||
file_list = [f.name for f in sorted(lsl_files)]
|
|
||||||
|
|
||||||
return {
|
|
||||||
"content": [
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"text": f"LSL Files in Project ({len(file_list)} total):\n\n" +
|
|
||||||
"\n".join([f"• {f}" for f in file_list])
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
def _git_status(self) -> Dict[str, Any]:
|
|
||||||
"""Get git status of the project"""
|
|
||||||
try:
|
|
||||||
result = subprocess.run([
|
|
||||||
"git", "status", "--porcelain"
|
|
||||||
], capture_output=True, text=True, cwd=str(self.project_root))
|
|
||||||
|
|
||||||
branch_result = subprocess.run([
|
|
||||||
"git", "branch", "--show-current"
|
|
||||||
], capture_output=True, text=True, cwd=str(self.project_root))
|
|
||||||
|
|
||||||
status_text = "Git Status:\n\n"
|
|
||||||
status_text += f"Current Branch: {branch_result.stdout.strip()}\n\n"
|
|
||||||
|
|
||||||
if result.stdout.strip():
|
|
||||||
status_text += "Modified Files:\n"
|
|
||||||
for line in result.stdout.strip().split('\n'):
|
|
||||||
status_text += f" {line}\n"
|
|
||||||
else:
|
|
||||||
status_text += "Working directory clean\n"
|
|
||||||
|
|
||||||
return {
|
|
||||||
"content": [
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"text": status_text
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
return {"error": f"Failed to get git status: {str(e)}"}
|
|
||||||
|
|
||||||
def _analyze_script_architecture(self) -> Dict[str, Any]:
|
|
||||||
"""Analyze the script architecture"""
|
|
||||||
lsl_files = list(self.project_root.glob("*.lsl"))
|
|
||||||
|
|
||||||
architecture_info = "Peril Dice Script Architecture Analysis:\n\n"
|
|
||||||
architecture_info += f"Total Scripts: {len(lsl_files)}\n\n"
|
|
||||||
|
|
||||||
# Categorize scripts by type
|
|
||||||
controllers = [f for f in lsl_files if "Controller" in f.name or "Manager" in f.name]
|
|
||||||
handlers = [f for f in lsl_files if "Handler" in f.name]
|
|
||||||
modules = [f for f in lsl_files if "Module" in f.name]
|
|
||||||
linkset_scripts = [f for f in lsl_files if "Linkset" in f.name]
|
|
||||||
|
|
||||||
architecture_info += f"Controllers/Managers ({len(controllers)}):\n"
|
|
||||||
for script in controllers:
|
|
||||||
architecture_info += f" • {script.name}\n"
|
|
||||||
|
|
||||||
architecture_info += f"\nHandlers ({len(handlers)}):\n"
|
|
||||||
for script in handlers:
|
|
||||||
architecture_info += f" • {script.name}\n"
|
|
||||||
|
|
||||||
architecture_info += f"\nModules ({len(modules)}):\n"
|
|
||||||
for script in modules:
|
|
||||||
architecture_info += f" • {script.name}\n"
|
|
||||||
|
|
||||||
architecture_info += f"\nLinkset-Specific Scripts ({len(linkset_scripts)}):\n"
|
|
||||||
for script in linkset_scripts:
|
|
||||||
architecture_info += f" • {script.name}\n"
|
|
||||||
|
|
||||||
return {
|
|
||||||
"content": [
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"text": architecture_info
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
def _get_memory_usage_info(self) -> Dict[str, Any]:
|
|
||||||
"""Extract memory usage information from comments in LSL files"""
|
|
||||||
lsl_files = list(self.project_root.glob("*.lsl"))
|
|
||||||
memory_info = "Memory Usage Information:\n\n"
|
|
||||||
|
|
||||||
for lsl_file in lsl_files:
|
|
||||||
try:
|
|
||||||
with open(lsl_file, 'r', encoding='utf-8') as f:
|
|
||||||
content = f.read()
|
|
||||||
# Look for memory usage comments
|
|
||||||
lines = content.split('\n')
|
|
||||||
memory_lines = [line for line in lines if 'memory' in line.lower() and ('%' in line or 'usage' in line.lower())]
|
|
||||||
|
|
||||||
if memory_lines:
|
|
||||||
memory_info += f"{lsl_file.name}:\n"
|
|
||||||
for line in memory_lines:
|
|
||||||
memory_info += f" {line.strip()}\n"
|
|
||||||
memory_info += "\n"
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
|
|
||||||
return {
|
|
||||||
"content": [
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"text": memory_info if memory_info.strip() != "Memory Usage Information:" else "No memory usage information found in comments."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
def _list_resources(self) -> Dict[str, Any]:
|
|
||||||
"""List available project resources"""
|
|
||||||
return {
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"uri": "file:///home/richard/peril/README.md",
|
|
||||||
"name": "Project README",
|
|
||||||
"description": "Main project documentation",
|
|
||||||
"mimeType": "text/markdown"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uri": "file:///home/richard/peril/CHANGELOG.md",
|
|
||||||
"name": "Changelog",
|
|
||||||
"description": "Project changelog",
|
|
||||||
"mimeType": "text/markdown"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uri": "file:///home/richard/peril/WARP.md",
|
|
||||||
"name": "WARP Development Guide",
|
|
||||||
"description": "Development guidance for WARP",
|
|
||||||
"mimeType": "text/markdown"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
def _read_resource(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
||||||
"""Read a specific resource"""
|
|
||||||
uri = params.get("uri", "")
|
|
||||||
|
|
||||||
if uri.startswith("file://"):
|
|
||||||
file_path = Path(uri[7:]) # Remove file:// prefix
|
|
||||||
|
|
||||||
if not file_path.exists():
|
|
||||||
return {"error": f"File not found: {file_path}"}
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(file_path, 'r', encoding='utf-8') as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
return {
|
|
||||||
"contents": [
|
|
||||||
{
|
|
||||||
"uri": uri,
|
|
||||||
"mimeType": "text/markdown" if file_path.suffix == ".md" else "text/plain",
|
|
||||||
"text": content
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
return {"error": f"Failed to read file: {str(e)}"}
|
|
||||||
|
|
||||||
return {"error": f"Unsupported URI: {uri}"}
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Main server loop"""
|
|
||||||
server = LSLMCPServer()
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Read requests from stdin continuously
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
line = sys.stdin.readline()
|
|
||||||
if not line: # EOF
|
|
||||||
break
|
|
||||||
|
|
||||||
line = line.strip()
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
|
|
||||||
request = json.loads(line)
|
|
||||||
response = server.handle_request(request)
|
|
||||||
|
|
||||||
# Wrap response in proper JSON-RPC format
|
|
||||||
json_rpc_response = {
|
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"id": request.get("id", None)
|
|
||||||
}
|
|
||||||
|
|
||||||
if "error" in response:
|
|
||||||
json_rpc_response["error"] = response["error"]
|
|
||||||
else:
|
|
||||||
json_rpc_response["result"] = response
|
|
||||||
|
|
||||||
print(json.dumps(json_rpc_response))
|
|
||||||
sys.stdout.flush() # Ensure immediate output
|
|
||||||
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
error_response = {
|
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"error": {"code": -32700, "message": "Parse error"},
|
|
||||||
"id": None
|
|
||||||
}
|
|
||||||
print(json.dumps(error_response))
|
|
||||||
sys.stdout.flush()
|
|
||||||
except Exception as e:
|
|
||||||
error_response = {
|
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"error": {"code": -32603, "message": str(e)},
|
|
||||||
"id": None
|
|
||||||
}
|
|
||||||
print(json.dumps(error_response))
|
|
||||||
sys.stdout.flush()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
except EOFError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Test script to verify lslint functionality with MCP server
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
def test_lslint_functionality():
|
|
||||||
"""Test lslint functionality through MCP server"""
|
|
||||||
|
|
||||||
# Test list_lsl_files first
|
|
||||||
list_request = {
|
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"id": 1,
|
|
||||||
"method": "tools/call",
|
|
||||||
"params": {
|
|
||||||
"name": "list_lsl_files",
|
|
||||||
"arguments": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Start the MCP server process
|
|
||||||
process = subprocess.Popen([
|
|
||||||
'python3', '/home/richard/peril/lsl_mcp_server.py'
|
|
||||||
], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
||||||
|
|
||||||
# Send the request
|
|
||||||
stdout, stderr = process.communicate(input=json.dumps(list_request) + '\n', timeout=10)
|
|
||||||
|
|
||||||
print("=== LSL MCP Server - LSLint Test ===")
|
|
||||||
print("\nListing LSL Files:")
|
|
||||||
|
|
||||||
lines = stdout.strip().split('\n')
|
|
||||||
for line in lines:
|
|
||||||
if line.strip():
|
|
||||||
try:
|
|
||||||
response = json.loads(line)
|
|
||||||
if 'content' in response:
|
|
||||||
print(response['content'][0]['text'])
|
|
||||||
elif 'tools' in response:
|
|
||||||
print("Server initialized successfully")
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
print(f"Non-JSON output: {line}")
|
|
||||||
|
|
||||||
if stderr:
|
|
||||||
print(f"\nServer Errors:\n{stderr}")
|
|
||||||
|
|
||||||
print("\n=== Test Complete ===")
|
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
print("Server test timed out")
|
|
||||||
process.kill()
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Test failed: {e}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
test_lslint_functionality()
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Simple test script to verify the LSL MCP server is working
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
def test_mcp_server():
|
|
||||||
"""Test basic MCP server functionality"""
|
|
||||||
|
|
||||||
# Test tools/list
|
|
||||||
test_request = {
|
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"id": 1,
|
|
||||||
"method": "tools/list",
|
|
||||||
"params": {}
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Start the MCP server process
|
|
||||||
process = subprocess.Popen([
|
|
||||||
'python3', '/home/richard/peril/lsl_mcp_server.py'
|
|
||||||
], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
||||||
|
|
||||||
# Send the request
|
|
||||||
stdout, stderr = process.communicate(input=json.dumps(test_request) + '\n', timeout=10)
|
|
||||||
|
|
||||||
print("=== MCP Server Test Results ===")
|
|
||||||
print("\nServer Output:")
|
|
||||||
for line in stdout.strip().split('\n'):
|
|
||||||
if line.strip():
|
|
||||||
try:
|
|
||||||
response = json.loads(line)
|
|
||||||
print(json.dumps(response, indent=2))
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
print(f"Non-JSON output: {line}")
|
|
||||||
|
|
||||||
if stderr:
|
|
||||||
print(f"\nServer Errors:\n{stderr}")
|
|
||||||
|
|
||||||
print("\n=== Test Complete ===")
|
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
print("Server test timed out")
|
|
||||||
process.kill()
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Test failed: {e}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
test_mcp_server()
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Test script to validate a specific LSL file using the comprehensive validator
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
def test_validate_lsl():
|
|
||||||
"""Test LSL validation through MCP server"""
|
|
||||||
|
|
||||||
# Test validate_lsl_file with Main_Controller_Linkset.lsl
|
|
||||||
validate_request = {
|
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"id": 1,
|
|
||||||
"method": "tools/call",
|
|
||||||
"params": {
|
|
||||||
"name": "validate_lsl_file",
|
|
||||||
"arguments": {
|
|
||||||
"file_path": "Main_Controller_Linkset.lsl"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Start the MCP server process
|
|
||||||
process = subprocess.Popen([
|
|
||||||
'python3', '/home/richard/peril/lsl_mcp_server.py'
|
|
||||||
], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
||||||
|
|
||||||
# Send the request
|
|
||||||
stdout, stderr = process.communicate(input=json.dumps(validate_request) + '\n', timeout=15)
|
|
||||||
|
|
||||||
print("=== LSL Comprehensive Validation Test ===")
|
|
||||||
print("\nValidating Main_Controller_Linkset.lsl:")
|
|
||||||
|
|
||||||
lines = stdout.strip().split('\n')
|
|
||||||
for line in lines:
|
|
||||||
if line.strip():
|
|
||||||
try:
|
|
||||||
response = json.loads(line)
|
|
||||||
if 'content' in response:
|
|
||||||
print(response['content'][0]['text'])
|
|
||||||
elif 'method' in response and response['method'] == 'server/initialized':
|
|
||||||
print("✅ Server initialized successfully")
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
print(f"Non-JSON output: {line}")
|
|
||||||
|
|
||||||
if stderr:
|
|
||||||
print(f"\nServer Errors:\n{stderr}")
|
|
||||||
|
|
||||||
print("\n=== Test Complete ===")
|
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
print("Server test timed out")
|
|
||||||
process.kill()
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Test failed: {e}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
test_validate_lsl()
|
|
||||||
Loading…
Reference in a new issue