cheatengine 5.8.21 → 5.8.22
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 +232 -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,174 @@ 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
|
+
-- Handshake: send ready signal
|
|
7531
|
+
local handshakeOk = pcall(function()
|
|
7532
|
+
Context.pipeServer.lock()
|
|
7533
|
+
local ok = pcall(function()
|
|
7534
|
+
local readyMsg = JSON.encode({ ready = true, timestamp = os.time() })
|
|
7535
|
+
Context.pipeServer.writeDword(#readyMsg)
|
|
7536
|
+
Context.pipeServer.writeString(readyMsg, false)
|
|
7537
|
+
end)
|
|
7538
|
+
Context.pipeServer.unlock()
|
|
7539
|
+
return ok
|
|
7540
|
+
end)
|
|
7541
|
+
|
|
7542
|
+
if not handshakeOk then
|
|
7543
|
+
Utils.debugPrint("Handshake failed, disconnecting client")
|
|
7544
|
+
pcall(function()
|
|
7545
|
+
if Context.pipeServer and Context.pipeServer.Valid then
|
|
7546
|
+
Context.pipeServer.disconnect()
|
|
7547
|
+
end
|
|
7548
|
+
end)
|
|
7549
|
+
sleep(50)
|
|
7550
|
+
goto next_connection
|
|
7551
|
+
end
|
|
7552
|
+
|
|
7553
|
+
Utils.debugPrint("Handshake completed")
|
|
7554
|
+
local sessionErrors = 0
|
|
7555
|
+
|
|
7556
|
+
while Context.serverRunning and Context.pipeServer and Context.pipeServer.Valid do
|
|
7557
|
+
Utils.periodicCleanup()
|
|
7558
|
+
|
|
7559
|
+
-- Read size
|
|
7560
|
+
local ok, size = false, nil
|
|
7561
|
+
pcall(function()
|
|
7562
|
+
Context.pipeServer.lock()
|
|
7563
|
+
ok, size = pcall(Context.pipeServer.readDword)
|
|
7564
|
+
Context.pipeServer.unlock()
|
|
7565
|
+
end)
|
|
7566
|
+
|
|
7567
|
+
if not ok or not size or size == 0 then
|
|
7568
|
+
sessionErrors = sessionErrors + 1
|
|
7569
|
+
if sessionErrors >= Config.MAX_SESSION_ERRORS then
|
|
7570
|
+
Utils.debugPrint("Too many session errors, disconnecting client")
|
|
7571
|
+
-- Send error before disconnecting
|
|
7572
|
+
sendErrorResponse("Too many consecutive read errors")
|
|
7573
|
+
break
|
|
7574
|
+
end
|
|
7575
|
+
sleep(10)
|
|
7576
|
+
goto continue
|
|
7577
|
+
end
|
|
7578
|
+
|
|
7579
|
+
if size >= Config.MAX_MESSAGE_SIZE then
|
|
7580
|
+
Utils.debugPrint("Message too large: " .. size)
|
|
7581
|
+
sendErrorResponse("Message too large: " .. size .. " bytes (max: " .. Config.MAX_MESSAGE_SIZE .. ")")
|
|
7582
|
+
break
|
|
7583
|
+
end
|
|
7584
|
+
|
|
7585
|
+
sessionErrors = 0
|
|
7586
|
+
|
|
7587
|
+
-- Read payload
|
|
7588
|
+
local ok2, payload = false, nil
|
|
7589
|
+
pcall(function()
|
|
7590
|
+
Context.pipeServer.lock()
|
|
7591
|
+
ok2, payload = pcall(Context.pipeServer.readString, size)
|
|
7592
|
+
Context.pipeServer.unlock()
|
|
7593
|
+
end)
|
|
7594
|
+
|
|
7595
|
+
if ok2 and payload and #payload == size then
|
|
7596
|
+
local decodeOk, req = pcall(JSON.decode, payload)
|
|
7597
|
+
if not decodeOk or not req then
|
|
7598
|
+
Utils.debugPrint("Failed to decode JSON payload")
|
|
7599
|
+
sendErrorResponse("Invalid JSON payload")
|
|
7600
|
+
goto continue
|
|
7601
|
+
end
|
|
7602
|
+
|
|
7603
|
+
local resp = nil
|
|
7604
|
+
|
|
7605
|
+
-- Use optimized request execution (sync on demand)
|
|
7606
|
+
resp = Pipe.executeRequest(req)
|
|
7607
|
+
|
|
7608
|
+
local respStr = JSON.encode(resp)
|
|
7609
|
+
local respLen = #respStr
|
|
7610
|
+
|
|
7611
|
+
if respLen > Config.MAX_MESSAGE_SIZE then
|
|
7612
|
+
respStr = JSON.encode({ error = "Response too large" })
|
|
7613
|
+
respLen = #respStr
|
|
7614
|
+
end
|
|
7615
|
+
|
|
7616
|
+
local writeOk = false
|
|
7617
|
+
pcall(function()
|
|
7618
|
+
Context.pipeServer.lock()
|
|
7619
|
+
writeOk = pcall(function()
|
|
7620
|
+
Context.pipeServer.writeDword(respLen)
|
|
7621
|
+
Context.pipeServer.writeString(respStr, false)
|
|
7622
|
+
end)
|
|
7623
|
+
Context.pipeServer.unlock()
|
|
7624
|
+
end)
|
|
7625
|
+
|
|
7626
|
+
if writeOk then
|
|
7627
|
+
Context.lastActivityTime = os_clock() -- Heartbeat: update on successful communication
|
|
7628
|
+
else
|
|
7629
|
+
Utils.debugPrint("Failed to write response")
|
|
7630
|
+
break
|
|
7631
|
+
end
|
|
7632
|
+
else
|
|
7633
|
+
Utils.debugPrint("Failed to read payload: expected " .. size .. " bytes")
|
|
7634
|
+
sendErrorResponse("Failed to read payload: expected " .. size .. " bytes, got " .. tostring(#(payload or "")))
|
|
7635
|
+
-- Don't break, allow client to retry
|
|
7636
|
+
sleep(50)
|
|
7637
|
+
end
|
|
7638
|
+
|
|
7639
|
+
::continue::
|
|
7640
|
+
end
|
|
7641
|
+
|
|
7642
|
+
pcall(function()
|
|
7643
|
+
if Context.pipeServer and Context.pipeServer.Valid then
|
|
7644
|
+
Context.pipeServer.disconnect()
|
|
7645
|
+
end
|
|
7646
|
+
end)
|
|
7647
|
+
Context.lastActivityTime = 0 -- Heartbeat: reset on disconnect
|
|
7648
|
+
Utils.debugPrint("Client disconnected, waiting for new connection...")
|
|
7649
|
+
sleep(50)
|
|
7650
|
+
end
|
|
7651
|
+
|
|
7652
|
+
::next_connection::
|
|
7653
|
+
end
|
|
7654
|
+
Utils.debugPrint("Worker stopped")
|
|
7655
|
+
end
|
|
7606
7656
|
|
|
7607
7657
|
-- ============ Server Lifecycle ============
|
|
7608
7658
|
local Server = {}
|
|
@@ -7631,13 +7681,13 @@ function Server.stop()
|
|
|
7631
7681
|
Utils.debugPrint("Server stopped (handled " .. Context.connectionCount .. " connections)")
|
|
7632
7682
|
end
|
|
7633
7683
|
|
|
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)
|
|
7684
|
+
function Server.start()
|
|
7685
|
+
Server.stop() -- This calls cleanupZombieState()
|
|
7686
|
+
|
|
7687
|
+
-- Additional cleanup for fresh start
|
|
7688
|
+
Utils.debugPrint("Starting MCP Bridge")
|
|
7689
|
+
|
|
7690
|
+
Context.pipeServer = createPipe(Config.PIPE_NAME, Config.PIPE_BUFFER_SIZE, Config.PIPE_BUFFER_SIZE)
|
|
7641
7691
|
if not Context.pipeServer or not Context.pipeServer.Valid then
|
|
7642
7692
|
print("[CE-MCP] Failed to create pipe: " .. Config.PIPE_NAME)
|
|
7643
7693
|
return false
|
|
@@ -7670,15 +7720,14 @@ function Server.stats()
|
|
|
7670
7720
|
bpCount = bpCount + 1
|
|
7671
7721
|
end
|
|
7672
7722
|
|
|
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
|
|
7723
|
+
return {
|
|
7724
|
+
running = Context.serverRunning,
|
|
7725
|
+
connections = Context.connectionCount,
|
|
7726
|
+
cached_symbols = symbolCacheCount,
|
|
7727
|
+
cached_modules = moduleCacheCount,
|
|
7728
|
+
active_breakpoints = bpCount
|
|
7729
|
+
}
|
|
7730
|
+
end
|
|
7682
7731
|
|
|
7683
7732
|
-- ============ Global API ============
|
|
7684
7733
|
-- Cleanup old instance
|
|
@@ -7697,19 +7746,18 @@ if CE_MCP_STATUS_LABEL then
|
|
|
7697
7746
|
end)
|
|
7698
7747
|
end
|
|
7699
7748
|
|
|
7700
|
-
CE_MCP = {
|
|
7701
|
-
start = Server.start,
|
|
7702
|
-
stop = Server.stop,
|
|
7703
|
-
stats = Server.stats,
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
}
|
|
7749
|
+
CE_MCP = {
|
|
7750
|
+
start = Server.start,
|
|
7751
|
+
stop = Server.stop,
|
|
7752
|
+
stats = Server.stats,
|
|
7753
|
+
-- Logger API
|
|
7754
|
+
Logger = Logger,
|
|
7755
|
+
LogLevel = LogLevel,
|
|
7756
|
+
-- Expose for debugging
|
|
7757
|
+
_config = Config,
|
|
7758
|
+
_context = Context,
|
|
7759
|
+
_handlers = Handlers,
|
|
7760
|
+
}
|
|
7713
7761
|
|
|
7714
7762
|
CE_MCP_BRIDGE_INSTANCE = CE_MCP
|
|
7715
7763
|
|
|
@@ -7751,16 +7799,15 @@ function StatusLabel.create()
|
|
|
7751
7799
|
label.Top = 5
|
|
7752
7800
|
end
|
|
7753
7801
|
|
|
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
|
|
7802
|
+
-- Click to show stats
|
|
7803
|
+
label.OnClick = function()
|
|
7804
|
+
local stats = Server.stats()
|
|
7805
|
+
local connected = Context.lastActivityTime > 0 and
|
|
7806
|
+
(os_clock() - Context.lastActivityTime) < Config.HEARTBEAT_TIMEOUT
|
|
7807
|
+
showMessage(string_format("CE MCP\nStatus: %s\nConnections: %d",
|
|
7808
|
+
connected and "Connected" or "Waiting",
|
|
7809
|
+
stats.connections or 0))
|
|
7810
|
+
end
|
|
7764
7811
|
|
|
7765
7812
|
StatusLabel.label = label
|
|
7766
7813
|
StatusLabel.updateColor(false)
|