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.
Files changed (2) hide show
  1. package/ce_mcp_bridge.lua +206 -185
  2. 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
- VERSION = "5.8.4",
32
- -- Pipe name: supports environment variable override for anti-detection
33
- -- Set CE_MCP_PIPE_NAME environment variable to customize
34
- PIPE_NAME = os.getenv("CE_MCP_PIPE_NAME") or "ce_mcp_bridge",
35
- -- Authentication token: optional security layer for pipe communication
36
- -- Set CE_MCP_AUTH_TOKEN environment variable to enable authentication
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
- version = Config.VERSION,
1466
- timestamp = os.time(),
1467
- process = process or "N/A",
1468
- pid = getOpenedProcessID() or 0,
1469
- connections = Context.connectionCount,
1470
- debugger = getDebuggerStatus()
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
- version = Config.VERSION,
1563
- uptime = metrics.uptime,
1564
- commands = metrics.commands,
1565
- summary = metrics.summary,
1566
- cache = metrics.cache,
1567
- logging = logStats,
1568
- scanSessions = {
1569
- active = scanSessionCount,
1570
- maxAllowed = Config.MAX_SCAN_SESSIONS,
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 Pipe.workerLoop()
7492
- Utils.debugPrint("Worker started")
7493
- local consecutiveErrors = 0
7494
-
7495
- while Context.serverRunning do
7496
- local acceptOk, acceptErr = pcall(function()
7497
- Context.pipeServer.acceptConnection()
7498
- end)
7499
-
7500
- if not acceptOk then
7501
- consecutiveErrors = consecutiveErrors + 1
7502
- if consecutiveErrors > Config.MAX_CONSECUTIVE_ERRORS then
7503
- Utils.debugPrint("Too many consecutive errors, restarting pipe...")
7504
- pcall(function() Context.pipeServer.destroy() end)
7505
- Context.pipeServer = createPipe(Config.PIPE_NAME, Config.PIPE_BUFFER_SIZE, Config.PIPE_BUFFER_SIZE)
7506
- consecutiveErrors = 0
7507
- end
7508
- if Context.serverRunning then
7509
- sleep(Config.HEARTBEAT_INTERVAL)
7510
- end
7511
- else
7512
- consecutiveErrors = 0
7513
- Context.connectionCount = Context.connectionCount + 1
7514
- Context.lastActivityTime = os_clock() -- Heartbeat: record connection time
7515
- Utils.debugPrint("Client connected (#" .. Context.connectionCount .. ")")
7516
-
7517
- local sessionErrors = 0
7518
-
7519
- while Context.serverRunning and Context.pipeServer and Context.pipeServer.Valid do
7520
- Utils.periodicCleanup()
7521
-
7522
- -- Read size
7523
- local ok, size = false, nil
7524
- pcall(function()
7525
- Context.pipeServer.lock()
7526
- ok, size = pcall(Context.pipeServer.readDword)
7527
- Context.pipeServer.unlock()
7528
- end)
7529
-
7530
- if not ok or not size or size == 0 then
7531
- sessionErrors = sessionErrors + 1
7532
- if sessionErrors >= Config.MAX_SESSION_ERRORS then
7533
- Utils.debugPrint("Too many session errors, disconnecting client")
7534
- break
7535
- end
7536
- sleep(10)
7537
- goto continue
7538
- end
7539
-
7540
- if size >= Config.MAX_MESSAGE_SIZE then
7541
- Utils.debugPrint("Message too large: " .. size)
7542
- break
7543
- end
7544
-
7545
- sessionErrors = 0
7546
-
7547
- -- Read payload
7548
- local ok2, payload = false, nil
7549
- pcall(function()
7550
- Context.pipeServer.lock()
7551
- ok2, payload = pcall(Context.pipeServer.readString, size)
7552
- Context.pipeServer.unlock()
7553
- end)
7554
-
7555
- if ok2 and payload and #payload == size then
7556
- local req = JSON.decode(payload)
7557
- local resp = nil
7558
-
7559
- -- Use optimized request execution (sync on demand)
7560
- resp = Pipe.executeRequest(req)
7561
-
7562
- local respStr = JSON.encode(resp)
7563
- local respLen = #respStr
7564
-
7565
- if respLen > Config.MAX_MESSAGE_SIZE then
7566
- respStr = JSON.encode({ error = "Response too large" })
7567
- respLen = #respStr
7568
- end
7569
-
7570
- local writeOk = false
7571
- pcall(function()
7572
- Context.pipeServer.lock()
7573
- writeOk = pcall(function()
7574
- Context.pipeServer.writeDword(respLen)
7575
- Context.pipeServer.writeString(respStr, false)
7576
- end)
7577
- Context.pipeServer.unlock()
7578
- end)
7579
-
7580
- if writeOk then
7581
- Context.lastActivityTime = os_clock() -- Heartbeat: update on successful communication
7582
- else
7583
- Utils.debugPrint("Failed to write response")
7584
- break
7585
- end
7586
- else
7587
- Utils.debugPrint("Failed to read payload")
7588
- break
7589
- end
7590
-
7591
- ::continue::
7592
- end
7593
-
7594
- pcall(function()
7595
- if Context.pipeServer and Context.pipeServer.Valid then
7596
- Context.pipeServer.disconnect()
7597
- end
7598
- end)
7599
- Context.lastActivityTime = 0 -- Heartbeat: reset on disconnect
7600
- Utils.debugPrint("Client disconnected, waiting for new connection...")
7601
- sleep(50)
7602
- end
7603
- end
7604
- Utils.debugPrint("Worker stopped")
7605
- end
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 v" .. Config.VERSION)
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
- version = Config.VERSION
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
- version = Config.VERSION,
7705
- -- Logger API
7706
- Logger = Logger,
7707
- LogLevel = LogLevel,
7708
- -- Expose for debugging
7709
- _config = Config,
7710
- _context = Context,
7711
- _handlers = Handlers,
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 v%s\nStatus: %s\nConnections: %d",
7760
- Config.VERSION,
7761
- connected and "Connected" or "Waiting",
7762
- stats.connections or 0))
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)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cheatengine",
3
- "version": "5.8.21",
3
+ "version": "5.8.23",
4
4
  "description": "Cheat Engine MCP Server - AI-assisted reverse engineering bridge",
5
5
  "main": "ce_mcp_server.js",
6
6
  "bin": {