cheatengine 5.8.21 → 5.8.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ce_mcp_bridge.lua +206 -185
- package/package.json +1 -1
package/ce_mcp_bridge.lua
CHANGED
|
@@ -26,15 +26,14 @@ local pairs = pairs
|
|
|
26
26
|
local ipairs = ipairs
|
|
27
27
|
local os_clock = os.clock
|
|
28
28
|
|
|
29
|
-
-- ============ Config ============
|
|
30
|
-
local Config = {
|
|
31
|
-
|
|
32
|
-
--
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
--
|
|
36
|
-
|
|
37
|
-
AUTH_TOKEN = os.getenv("CE_MCP_AUTH_TOKEN") or nil,
|
|
29
|
+
-- ============ Config ============
|
|
30
|
+
local Config = {
|
|
31
|
+
-- Pipe name: supports environment variable override for anti-detection
|
|
32
|
+
-- Set CE_MCP_PIPE_NAME environment variable to customize
|
|
33
|
+
PIPE_NAME = os.getenv("CE_MCP_PIPE_NAME") or "ce_mcp_bridge",
|
|
34
|
+
-- Authentication token: optional security layer for pipe communication
|
|
35
|
+
-- Set CE_MCP_AUTH_TOKEN environment variable to enable authentication
|
|
36
|
+
AUTH_TOKEN = os.getenv("CE_MCP_AUTH_TOKEN") or nil,
|
|
38
37
|
DEBUG_MODE = false,
|
|
39
38
|
MAX_MESSAGE_SIZE = 10 * 1024 * 1024,
|
|
40
39
|
PIPE_BUFFER_SIZE = 1024 * 1024,
|
|
@@ -1459,17 +1458,16 @@ local function getDebuggerStatus()
|
|
|
1459
1458
|
return status
|
|
1460
1459
|
end
|
|
1461
1460
|
|
|
1462
|
-
Handlers.ping = function()
|
|
1463
|
-
return {
|
|
1464
|
-
status = "ok",
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
end
|
|
1461
|
+
Handlers.ping = function()
|
|
1462
|
+
return {
|
|
1463
|
+
status = "ok",
|
|
1464
|
+
timestamp = os.time(),
|
|
1465
|
+
process = process or "N/A",
|
|
1466
|
+
pid = getOpenedProcessID() or 0,
|
|
1467
|
+
connections = Context.connectionCount,
|
|
1468
|
+
debugger = getDebuggerStatus()
|
|
1469
|
+
}
|
|
1470
|
+
end
|
|
1473
1471
|
|
|
1474
1472
|
Handlers.get_process_info = function()
|
|
1475
1473
|
-- Refresh symbol table using ModuleManager
|
|
@@ -1558,17 +1556,16 @@ Handlers.stats = function(p)
|
|
|
1558
1556
|
})
|
|
1559
1557
|
end
|
|
1560
1558
|
|
|
1561
|
-
return {
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
sessions = scanSessions
|
|
1559
|
+
return {
|
|
1560
|
+
uptime = metrics.uptime,
|
|
1561
|
+
commands = metrics.commands,
|
|
1562
|
+
summary = metrics.summary,
|
|
1563
|
+
cache = metrics.cache,
|
|
1564
|
+
logging = logStats,
|
|
1565
|
+
scanSessions = {
|
|
1566
|
+
active = scanSessionCount,
|
|
1567
|
+
maxAllowed = Config.MAX_SCAN_SESSIONS,
|
|
1568
|
+
sessions = scanSessions
|
|
1572
1569
|
},
|
|
1573
1570
|
connections = Context.connectionCount,
|
|
1574
1571
|
debugger = getDebuggerStatus()
|
|
@@ -7488,121 +7485,148 @@ function Pipe.executeRequest(req)
|
|
|
7488
7485
|
end
|
|
7489
7486
|
end
|
|
7490
7487
|
|
|
7491
|
-
function
|
|
7492
|
-
|
|
7493
|
-
local
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
Context.
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7488
|
+
-- Helper function to send error response without breaking connection
|
|
7489
|
+
local function sendErrorResponse(errorMsg)
|
|
7490
|
+
local resp = { error = errorMsg }
|
|
7491
|
+
local respStr = JSON.encode(resp)
|
|
7492
|
+
local respLen = #respStr
|
|
7493
|
+
|
|
7494
|
+
pcall(function()
|
|
7495
|
+
Context.pipeServer.lock()
|
|
7496
|
+
pcall(function()
|
|
7497
|
+
Context.pipeServer.writeDword(respLen)
|
|
7498
|
+
Context.pipeServer.writeString(respStr, false)
|
|
7499
|
+
end)
|
|
7500
|
+
Context.pipeServer.unlock()
|
|
7501
|
+
end)
|
|
7502
|
+
end
|
|
7503
|
+
|
|
7504
|
+
function Pipe.workerLoop()
|
|
7505
|
+
Utils.debugPrint("Worker started")
|
|
7506
|
+
local consecutiveErrors = 0
|
|
7507
|
+
|
|
7508
|
+
while Context.serverRunning do
|
|
7509
|
+
local acceptOk, acceptErr = pcall(function()
|
|
7510
|
+
Context.pipeServer.acceptConnection()
|
|
7511
|
+
end)
|
|
7512
|
+
|
|
7513
|
+
if not acceptOk then
|
|
7514
|
+
consecutiveErrors = consecutiveErrors + 1
|
|
7515
|
+
if consecutiveErrors > Config.MAX_CONSECUTIVE_ERRORS then
|
|
7516
|
+
Utils.debugPrint("Too many consecutive errors, restarting pipe...")
|
|
7517
|
+
pcall(function() Context.pipeServer.destroy() end)
|
|
7518
|
+
Context.pipeServer = createPipe(Config.PIPE_NAME, Config.PIPE_BUFFER_SIZE, Config.PIPE_BUFFER_SIZE)
|
|
7519
|
+
consecutiveErrors = 0
|
|
7520
|
+
end
|
|
7521
|
+
if Context.serverRunning then
|
|
7522
|
+
sleep(Config.HEARTBEAT_INTERVAL)
|
|
7523
|
+
end
|
|
7524
|
+
else
|
|
7525
|
+
consecutiveErrors = 0
|
|
7526
|
+
Context.connectionCount = Context.connectionCount + 1
|
|
7527
|
+
Context.lastActivityTime = os_clock() -- Heartbeat: record connection time
|
|
7528
|
+
Utils.debugPrint("Client connected (#" .. Context.connectionCount .. ")")
|
|
7529
|
+
|
|
7530
|
+
local sessionErrors = 0
|
|
7531
|
+
|
|
7532
|
+
while Context.serverRunning and Context.pipeServer and Context.pipeServer.Valid do
|
|
7533
|
+
Utils.periodicCleanup()
|
|
7534
|
+
|
|
7535
|
+
-- Read size
|
|
7536
|
+
local ok, size = false, nil
|
|
7537
|
+
pcall(function()
|
|
7538
|
+
Context.pipeServer.lock()
|
|
7539
|
+
ok, size = pcall(Context.pipeServer.readDword)
|
|
7540
|
+
Context.pipeServer.unlock()
|
|
7541
|
+
end)
|
|
7542
|
+
|
|
7543
|
+
if not ok or not size or size == 0 then
|
|
7544
|
+
sessionErrors = sessionErrors + 1
|
|
7545
|
+
if sessionErrors >= Config.MAX_SESSION_ERRORS then
|
|
7546
|
+
Utils.debugPrint("Too many session errors, disconnecting client")
|
|
7547
|
+
-- Send error before disconnecting
|
|
7548
|
+
sendErrorResponse("Too many consecutive read errors")
|
|
7549
|
+
break
|
|
7550
|
+
end
|
|
7551
|
+
sleep(10)
|
|
7552
|
+
goto continue
|
|
7553
|
+
end
|
|
7554
|
+
|
|
7555
|
+
if size >= Config.MAX_MESSAGE_SIZE then
|
|
7556
|
+
Utils.debugPrint("Message too large: " .. size)
|
|
7557
|
+
sendErrorResponse("Message too large: " .. size .. " bytes (max: " .. Config.MAX_MESSAGE_SIZE .. ")")
|
|
7558
|
+
break
|
|
7559
|
+
end
|
|
7560
|
+
|
|
7561
|
+
sessionErrors = 0
|
|
7562
|
+
|
|
7563
|
+
-- Read payload
|
|
7564
|
+
local ok2, payload = false, nil
|
|
7565
|
+
pcall(function()
|
|
7566
|
+
Context.pipeServer.lock()
|
|
7567
|
+
ok2, payload = pcall(Context.pipeServer.readString, size)
|
|
7568
|
+
Context.pipeServer.unlock()
|
|
7569
|
+
end)
|
|
7570
|
+
|
|
7571
|
+
if ok2 and payload and #payload == size then
|
|
7572
|
+
local decodeOk, req = pcall(JSON.decode, payload)
|
|
7573
|
+
if not decodeOk or not req then
|
|
7574
|
+
Utils.debugPrint("Failed to decode JSON payload")
|
|
7575
|
+
sendErrorResponse("Invalid JSON payload")
|
|
7576
|
+
goto continue
|
|
7577
|
+
end
|
|
7578
|
+
|
|
7579
|
+
local resp = nil
|
|
7580
|
+
|
|
7581
|
+
-- Use optimized request execution (sync on demand)
|
|
7582
|
+
resp = Pipe.executeRequest(req)
|
|
7583
|
+
|
|
7584
|
+
local respStr = JSON.encode(resp)
|
|
7585
|
+
local respLen = #respStr
|
|
7586
|
+
|
|
7587
|
+
if respLen > Config.MAX_MESSAGE_SIZE then
|
|
7588
|
+
respStr = JSON.encode({ error = "Response too large" })
|
|
7589
|
+
respLen = #respStr
|
|
7590
|
+
end
|
|
7591
|
+
|
|
7592
|
+
local writeOk = false
|
|
7593
|
+
pcall(function()
|
|
7594
|
+
Context.pipeServer.lock()
|
|
7595
|
+
writeOk = pcall(function()
|
|
7596
|
+
Context.pipeServer.writeDword(respLen)
|
|
7597
|
+
Context.pipeServer.writeString(respStr, false)
|
|
7598
|
+
end)
|
|
7599
|
+
Context.pipeServer.unlock()
|
|
7600
|
+
end)
|
|
7601
|
+
|
|
7602
|
+
if writeOk then
|
|
7603
|
+
Context.lastActivityTime = os_clock() -- Heartbeat: update on successful communication
|
|
7604
|
+
else
|
|
7605
|
+
Utils.debugPrint("Failed to write response")
|
|
7606
|
+
break
|
|
7607
|
+
end
|
|
7608
|
+
else
|
|
7609
|
+
Utils.debugPrint("Failed to read payload: expected " .. size .. " bytes")
|
|
7610
|
+
sendErrorResponse("Failed to read payload: expected " .. size .. " bytes, got " .. tostring(#(payload or "")))
|
|
7611
|
+
-- Don't break, allow client to retry
|
|
7612
|
+
sleep(50)
|
|
7613
|
+
end
|
|
7614
|
+
|
|
7615
|
+
::continue::
|
|
7616
|
+
end
|
|
7617
|
+
|
|
7618
|
+
pcall(function()
|
|
7619
|
+
if Context.pipeServer and Context.pipeServer.Valid then
|
|
7620
|
+
Context.pipeServer.disconnect()
|
|
7621
|
+
end
|
|
7622
|
+
end)
|
|
7623
|
+
Context.lastActivityTime = 0 -- Heartbeat: reset on disconnect
|
|
7624
|
+
Utils.debugPrint("Client disconnected, waiting for new connection...")
|
|
7625
|
+
sleep(50)
|
|
7626
|
+
end
|
|
7627
|
+
end
|
|
7628
|
+
Utils.debugPrint("Worker stopped")
|
|
7629
|
+
end
|
|
7606
7630
|
|
|
7607
7631
|
-- ============ Server Lifecycle ============
|
|
7608
7632
|
local Server = {}
|
|
@@ -7631,13 +7655,13 @@ function Server.stop()
|
|
|
7631
7655
|
Utils.debugPrint("Server stopped (handled " .. Context.connectionCount .. " connections)")
|
|
7632
7656
|
end
|
|
7633
7657
|
|
|
7634
|
-
function Server.start()
|
|
7635
|
-
Server.stop() -- This calls cleanupZombieState()
|
|
7636
|
-
|
|
7637
|
-
-- Additional cleanup for fresh start
|
|
7638
|
-
Utils.debugPrint("Starting MCP Bridge
|
|
7639
|
-
|
|
7640
|
-
Context.pipeServer = createPipe(Config.PIPE_NAME, Config.PIPE_BUFFER_SIZE, Config.PIPE_BUFFER_SIZE)
|
|
7658
|
+
function Server.start()
|
|
7659
|
+
Server.stop() -- This calls cleanupZombieState()
|
|
7660
|
+
|
|
7661
|
+
-- Additional cleanup for fresh start
|
|
7662
|
+
Utils.debugPrint("Starting MCP Bridge")
|
|
7663
|
+
|
|
7664
|
+
Context.pipeServer = createPipe(Config.PIPE_NAME, Config.PIPE_BUFFER_SIZE, Config.PIPE_BUFFER_SIZE)
|
|
7641
7665
|
if not Context.pipeServer or not Context.pipeServer.Valid then
|
|
7642
7666
|
print("[CE-MCP] Failed to create pipe: " .. Config.PIPE_NAME)
|
|
7643
7667
|
return false
|
|
@@ -7670,15 +7694,14 @@ function Server.stats()
|
|
|
7670
7694
|
bpCount = bpCount + 1
|
|
7671
7695
|
end
|
|
7672
7696
|
|
|
7673
|
-
return {
|
|
7674
|
-
running = Context.serverRunning,
|
|
7675
|
-
connections = Context.connectionCount,
|
|
7676
|
-
cached_symbols = symbolCacheCount,
|
|
7677
|
-
cached_modules = moduleCacheCount,
|
|
7678
|
-
active_breakpoints = bpCount
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
end
|
|
7697
|
+
return {
|
|
7698
|
+
running = Context.serverRunning,
|
|
7699
|
+
connections = Context.connectionCount,
|
|
7700
|
+
cached_symbols = symbolCacheCount,
|
|
7701
|
+
cached_modules = moduleCacheCount,
|
|
7702
|
+
active_breakpoints = bpCount
|
|
7703
|
+
}
|
|
7704
|
+
end
|
|
7682
7705
|
|
|
7683
7706
|
-- ============ Global API ============
|
|
7684
7707
|
-- Cleanup old instance
|
|
@@ -7697,19 +7720,18 @@ if CE_MCP_STATUS_LABEL then
|
|
|
7697
7720
|
end)
|
|
7698
7721
|
end
|
|
7699
7722
|
|
|
7700
|
-
CE_MCP = {
|
|
7701
|
-
start = Server.start,
|
|
7702
|
-
stop = Server.stop,
|
|
7703
|
-
stats = Server.stats,
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
}
|
|
7723
|
+
CE_MCP = {
|
|
7724
|
+
start = Server.start,
|
|
7725
|
+
stop = Server.stop,
|
|
7726
|
+
stats = Server.stats,
|
|
7727
|
+
-- Logger API
|
|
7728
|
+
Logger = Logger,
|
|
7729
|
+
LogLevel = LogLevel,
|
|
7730
|
+
-- Expose for debugging
|
|
7731
|
+
_config = Config,
|
|
7732
|
+
_context = Context,
|
|
7733
|
+
_handlers = Handlers,
|
|
7734
|
+
}
|
|
7713
7735
|
|
|
7714
7736
|
CE_MCP_BRIDGE_INSTANCE = CE_MCP
|
|
7715
7737
|
|
|
@@ -7751,16 +7773,15 @@ function StatusLabel.create()
|
|
|
7751
7773
|
label.Top = 5
|
|
7752
7774
|
end
|
|
7753
7775
|
|
|
7754
|
-
-- Click to show stats
|
|
7755
|
-
label.OnClick = function()
|
|
7756
|
-
local stats = Server.stats()
|
|
7757
|
-
local connected = Context.lastActivityTime > 0 and
|
|
7758
|
-
(os_clock() - Context.lastActivityTime) < Config.HEARTBEAT_TIMEOUT
|
|
7759
|
-
showMessage(string_format("CE MCP
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
end
|
|
7776
|
+
-- Click to show stats
|
|
7777
|
+
label.OnClick = function()
|
|
7778
|
+
local stats = Server.stats()
|
|
7779
|
+
local connected = Context.lastActivityTime > 0 and
|
|
7780
|
+
(os_clock() - Context.lastActivityTime) < Config.HEARTBEAT_TIMEOUT
|
|
7781
|
+
showMessage(string_format("CE MCP\nStatus: %s\nConnections: %d",
|
|
7782
|
+
connected and "Connected" or "Waiting",
|
|
7783
|
+
stats.connections or 0))
|
|
7784
|
+
end
|
|
7764
7785
|
|
|
7765
7786
|
StatusLabel.label = label
|
|
7766
7787
|
StatusLabel.updateColor(false)
|