iflow-mcp_bethington-cheat-engine-server-python 0.1.0__py3-none-any.whl

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 (40) hide show
  1. iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/METADATA +16 -0
  2. iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/RECORD +40 -0
  3. iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/WHEEL +5 -0
  4. iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/entry_points.txt +2 -0
  5. iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/licenses/LICENSE +21 -0
  6. iflow_mcp_bethington_cheat_engine_server_python-0.1.0.dist-info/top_level.txt +1 -0
  7. server/cheatengine/__init__.py +19 -0
  8. server/cheatengine/ce_bridge.py +1670 -0
  9. server/cheatengine/lua_interface.py +460 -0
  10. server/cheatengine/table_parser.py +1221 -0
  11. server/config/__init__.py +20 -0
  12. server/config/settings.py +347 -0
  13. server/config/whitelist.py +378 -0
  14. server/gui_automation/__init__.py +43 -0
  15. server/gui_automation/core/__init__.py +8 -0
  16. server/gui_automation/core/integration.py +951 -0
  17. server/gui_automation/demos/__init__.py +8 -0
  18. server/gui_automation/demos/basic_demo.py +754 -0
  19. server/gui_automation/demos/notepad_demo.py +460 -0
  20. server/gui_automation/demos/simple_demo.py +319 -0
  21. server/gui_automation/tools/__init__.py +8 -0
  22. server/gui_automation/tools/mcp_tools.py +974 -0
  23. server/main.py +519 -0
  24. server/memory/__init__.py +0 -0
  25. server/memory/analyzer.py +0 -0
  26. server/memory/reader.py +0 -0
  27. server/memory/scanner.py +0 -0
  28. server/memory/symbols.py +0 -0
  29. server/process/__init__.py +16 -0
  30. server/process/launcher.py +608 -0
  31. server/process/manager.py +185 -0
  32. server/process/monitors.py +202 -0
  33. server/process/permissions.py +131 -0
  34. server/process_whitelist.json +119 -0
  35. server/pyautogui/__init__.py +0 -0
  36. server/utils/__init__.py +37 -0
  37. server/utils/data_types.py +368 -0
  38. server/utils/formatters.py +430 -0
  39. server/utils/validators.py +340 -0
  40. server/window_automation/__init__.py +59 -0
@@ -0,0 +1,460 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ MCP Cheat Engine Server - PyAutoGUI Notepad Automation Demo
4
+
5
+ This script demonstrates the complete integration of:
6
+ 1. PyAutoGUI for screen automation and keystroke injection
7
+ 2. MCP Cheat Engine Server for memory scanning and text location
8
+ 3. Process management and window control
9
+
10
+ Workflow:
11
+ 1. Launch Notepad using PyAutoGUI
12
+ 2. Send "Hello World" text using PyAutoGUI
13
+ 3. Find the text in Notepad's memory using MCP tools
14
+ 4. Display memory addresses and current text content
15
+ """
16
+
17
+ import os
18
+ import sys
19
+ import time
20
+ import logging
21
+ import subprocess
22
+ import psutil
23
+ from typing import Dict, Any, List, Optional
24
+
25
+ # Add server directory to path for MCP integration
26
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'server'))
27
+
28
+ # Import our PyAutoGUI integration
29
+ from pyautogui_integration import PyAutoGUIController
30
+ from pyautogui_tools import PyAutoGUIToolHandler
31
+
32
+ # Configure logging
33
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
34
+ logger = logging.getLogger(__name__)
35
+
36
+ class NotepadAutomationDemo:
37
+ """Demonstrates PyAutoGUI + MCP integration for Notepad automation"""
38
+
39
+ def __init__(self):
40
+ self.pyautogui_controller = None
41
+ self.pyautogui_handler = None
42
+ self.notepad_process = None
43
+ self.memory_reader = None
44
+ self.target_text = "Hello World from PyAutoGUI and MCP!"
45
+
46
+ def initialize_systems(self) -> bool:
47
+ """Initialize PyAutoGUI and MCP systems"""
48
+ try:
49
+ # Initialize PyAutoGUI
50
+ self.pyautogui_controller = PyAutoGUIController()
51
+ self.pyautogui_handler = PyAutoGUIToolHandler()
52
+ logger.info("✅ PyAutoGUI systems initialized")
53
+
54
+ # Initialize memory reader
55
+ try:
56
+ from memory.reader import MemoryReader
57
+ self.memory_reader = MemoryReader()
58
+ logger.info("✅ MCP Memory Reader initialized")
59
+ except Exception as e:
60
+ logger.warning(f"⚠️ Memory reader initialization failed: {e}")
61
+ logger.info("Continuing with basic functionality...")
62
+
63
+ return True
64
+
65
+ except Exception as e:
66
+ logger.error(f"❌ System initialization failed: {e}")
67
+ return False
68
+
69
+ def launch_notepad(self) -> bool:
70
+ """Launch Notepad using PyAutoGUI"""
71
+ try:
72
+ logger.info("🚀 Launching Notepad...")
73
+
74
+ # Use PyAutoGUI to open Run dialog
75
+ result = self.pyautogui_controller.key_combination(['win', 'r'])
76
+ if not result.success:
77
+ logger.error(f"Failed to open Run dialog: {result.error}")
78
+ return False
79
+
80
+ time.sleep(1)
81
+
82
+ # Type notepad command
83
+ result = self.pyautogui_controller.type_text("notepad")
84
+ if not result.success:
85
+ logger.error(f"Failed to type notepad: {result.error}")
86
+ return False
87
+
88
+ time.sleep(0.5)
89
+
90
+ # Press Enter to launch
91
+ result = self.pyautogui_controller.press_key("enter")
92
+ if not result.success:
93
+ logger.error(f"Failed to press Enter: {result.error}")
94
+ return False
95
+
96
+ # Wait for Notepad to load
97
+ time.sleep(2)
98
+
99
+ # Find Notepad process
100
+ self.notepad_process = self._find_notepad_process()
101
+ if self.notepad_process:
102
+ logger.info(f"✅ Notepad launched successfully (PID: {self.notepad_process.pid})")
103
+ return True
104
+ else:
105
+ logger.error("❌ Could not find Notepad process")
106
+ return False
107
+
108
+ except Exception as e:
109
+ logger.error(f"❌ Failed to launch Notepad: {e}")
110
+ return False
111
+
112
+ def send_text_to_notepad(self) -> bool:
113
+ """Send text to Notepad using PyAutoGUI"""
114
+ try:
115
+ logger.info(f"📝 Sending text to Notepad: '{self.target_text}'")
116
+
117
+ # Take a screenshot first to see current state
118
+ screenshot_result = self.pyautogui_controller.take_screenshot()
119
+ if screenshot_result.success:
120
+ logger.info("📷 Screenshot taken for reference")
121
+
122
+ # Click in the Notepad window to ensure focus
123
+ # Get screen center as a safe click location
124
+ screen_info = self.pyautogui_controller.get_screen_info()
125
+ center_x = screen_info.data['width'] // 2
126
+ center_y = screen_info.data['height'] // 2
127
+
128
+ click_result = self.pyautogui_controller.click_mouse(center_x, center_y)
129
+ if click_result.success:
130
+ logger.info("🖱️ Clicked to focus window")
131
+
132
+ time.sleep(0.5)
133
+
134
+ # Type the target text
135
+ type_result = self.pyautogui_controller.type_text(self.target_text, interval=0.02)
136
+ if not type_result.success:
137
+ logger.error(f"Failed to type text: {type_result.error}")
138
+ return False
139
+
140
+ logger.info("✅ Text sent successfully to Notepad")
141
+
142
+ # Add a newline and timestamp for better identification
143
+ self.pyautogui_controller.press_key("enter")
144
+ time.sleep(0.1)
145
+ timestamp_text = f"Timestamp: {time.strftime('%Y-%m-%d %H:%M:%S')}"
146
+ self.pyautogui_controller.type_text(timestamp_text, interval=0.01)
147
+
148
+ time.sleep(1) # Allow text to be processed
149
+ return True
150
+
151
+ except Exception as e:
152
+ logger.error(f"❌ Failed to send text to Notepad: {e}")
153
+ return False
154
+
155
+ def find_text_in_memory(self) -> List[Dict[str, Any]]:
156
+ """Find the text in Notepad's memory using MCP tools"""
157
+ try:
158
+ logger.info(f"🔍 Searching for text in Notepad memory...")
159
+
160
+ if not self.notepad_process:
161
+ logger.error("No Notepad process found")
162
+ return []
163
+
164
+ # Initialize memory reader for Notepad process
165
+ if self.memory_reader:
166
+ try:
167
+ process_info = {
168
+ 'pid': self.notepad_process.pid,
169
+ 'name': self.notepad_process.name(),
170
+ 'handle': None
171
+ }
172
+ self.memory_reader.set_process(process_info)
173
+ logger.info("🎯 Memory reader attached to Notepad")
174
+ except Exception as e:
175
+ logger.error(f"Failed to attach memory reader: {e}")
176
+ return []
177
+ else:
178
+ logger.error("Memory reader not available")
179
+ return []
180
+
181
+ found_locations = []
182
+
183
+ # Search for our target text
184
+ search_texts = [
185
+ self.target_text,
186
+ "Hello World", # Partial search
187
+ "PyAutoGUI", # Another partial search
188
+ ]
189
+
190
+ for search_text in search_texts:
191
+ logger.info(f"🔎 Searching for: '{search_text}'")
192
+ locations = self._search_memory_for_text(search_text)
193
+
194
+ for location in locations:
195
+ found_locations.append({
196
+ 'search_term': search_text,
197
+ 'address': location['address'],
198
+ 'address_hex': f"0x{location['address']:X}",
199
+ 'encoding': location['encoding'],
200
+ 'content': location['content'],
201
+ 'context': location.get('context', ''),
202
+ 'size': location.get('size', 0)
203
+ })
204
+
205
+ if locations:
206
+ logger.info(f"✅ Found {len(locations)} occurrences of '{search_text}'")
207
+ else:
208
+ logger.info(f"ℹ️ No occurrences found for '{search_text}'")
209
+
210
+ return found_locations
211
+
212
+ except Exception as e:
213
+ logger.error(f"❌ Memory search failed: {e}")
214
+ return []
215
+
216
+ def _search_memory_for_text(self, search_text: str) -> List[Dict[str, Any]]:
217
+ """Search for text in process memory"""
218
+ try:
219
+ # Get readable memory regions
220
+ regions = self.memory_reader.get_memory_regions()
221
+ readable_regions = [r for r in regions if r.get('readable', False)]
222
+
223
+ found_locations = []
224
+ search_count = 0
225
+ max_regions = 50 # Limit search to prevent long execution times
226
+
227
+ logger.info(f"📊 Scanning {min(len(readable_regions), max_regions)} memory regions...")
228
+
229
+ for region in readable_regions[:max_regions]:
230
+ search_count += 1
231
+ try:
232
+ # Limit scan size to prevent excessive memory usage
233
+ scan_size = min(region['size'], 1 * 1024 * 1024) # 1MB max per region
234
+
235
+ memory_data = self.memory_reader.read_memory(
236
+ region['base_address'],
237
+ scan_size
238
+ )
239
+
240
+ # Search for ASCII text
241
+ ascii_matches = self._find_text_in_bytes(
242
+ memory_data,
243
+ search_text.encode('ascii', errors='ignore'),
244
+ region['base_address'],
245
+ 'ascii'
246
+ )
247
+ found_locations.extend(ascii_matches)
248
+
249
+ # Search for Unicode text (UTF-16 LE)
250
+ unicode_matches = self._find_text_in_bytes(
251
+ memory_data,
252
+ search_text.encode('utf-16le'),
253
+ region['base_address'],
254
+ 'unicode'
255
+ )
256
+ found_locations.extend(unicode_matches)
257
+
258
+ if search_count % 10 == 0:
259
+ logger.info(f"⏳ Scanned {search_count} regions, found {len(found_locations)} matches so far...")
260
+
261
+ except Exception as e:
262
+ logger.debug(f"Skipped region {hex(region['base_address'])}: {e}")
263
+ continue
264
+
265
+ logger.info(f"🔍 Memory scan completed: {search_count} regions scanned")
266
+ return found_locations
267
+
268
+ except Exception as e:
269
+ logger.error(f"Memory search error: {e}")
270
+ return []
271
+
272
+ def _find_text_in_bytes(self, data: bytes, search_bytes: bytes, base_address: int, encoding: str) -> List[Dict[str, Any]]:
273
+ """Find text occurrences in a byte array"""
274
+ locations = []
275
+ offset = 0
276
+
277
+ while True:
278
+ pos = data.find(search_bytes, offset)
279
+ if pos == -1:
280
+ break
281
+
282
+ address = base_address + pos
283
+
284
+ # Read context around the found text (64 bytes before and after)
285
+ context_start = max(0, pos - 64)
286
+ context_end = min(len(data), pos + len(search_bytes) + 64)
287
+ context_data = data[context_start:context_end]
288
+
289
+ # Extract readable text from context
290
+ try:
291
+ if encoding == 'ascii':
292
+ content = search_bytes.decode('ascii', errors='ignore')
293
+ context = context_data.decode('ascii', errors='ignore')
294
+ else:
295
+ content = search_bytes.decode('utf-16le', errors='ignore')
296
+ context = context_data.decode('utf-16le', errors='ignore')
297
+
298
+ # Clean up text (remove null bytes and non-printable chars)
299
+ content = ''.join(c for c in content if c.isprintable() or c.isspace()).strip()
300
+ context = ''.join(c for c in context if c.isprintable() or c.isspace()).strip()
301
+
302
+ except Exception:
303
+ content = f"<decode error: {encoding}>"
304
+ context = f"<context decode error>"
305
+
306
+ locations.append({
307
+ 'address': address,
308
+ 'size': len(search_bytes),
309
+ 'encoding': encoding,
310
+ 'content': content,
311
+ 'context': context,
312
+ 'hex_data': context_data[:32].hex() # First 32 bytes as hex
313
+ })
314
+
315
+ offset = pos + 1
316
+
317
+ # Limit results to prevent excessive output
318
+ if len(locations) >= 10:
319
+ break
320
+
321
+ return locations
322
+
323
+ def _find_notepad_process(self) -> Optional[psutil.Process]:
324
+ """Find the Notepad process"""
325
+ try:
326
+ for proc in psutil.process_iter(['pid', 'name', 'create_time']):
327
+ if proc.info['name'].lower() == 'notepad.exe':
328
+ # Return the most recently created Notepad process
329
+ return proc
330
+ except Exception as e:
331
+ logger.error(f"Error finding Notepad process: {e}")
332
+ return None
333
+
334
+ def display_results(self, found_locations: List[Dict[str, Any]]):
335
+ """Display the memory search results in a formatted way"""
336
+ print("\n" + "="*80)
337
+ print("🎯 MEMORY SEARCH RESULTS")
338
+ print("="*80)
339
+
340
+ if not found_locations:
341
+ print("❌ No text found in Notepad memory")
342
+ print("This could be due to:")
343
+ print(" • Text might be stored in encrypted/compressed form")
344
+ print(" • Memory regions might not be accessible")
345
+ print(" • Text might be in graphics buffer rather than text buffer")
346
+ return
347
+
348
+ print(f"✅ Found {len(found_locations)} text occurrences in memory")
349
+ print()
350
+
351
+ # Group by search term
352
+ by_search_term = {}
353
+ for location in found_locations:
354
+ term = location['search_term']
355
+ if term not in by_search_term:
356
+ by_search_term[term] = []
357
+ by_search_term[term].append(location)
358
+
359
+ for search_term, locations in by_search_term.items():
360
+ print(f"🔍 Search Term: '{search_term}' - {len(locations)} matches")
361
+ print("-" * 60)
362
+
363
+ for i, loc in enumerate(locations[:5], 1): # Show first 5 matches per term
364
+ print(f" Match #{i}:")
365
+ print(f" 📍 Address: {loc['address_hex']} (decimal: {loc['address']})")
366
+ print(f" 📝 Encoding: {loc['encoding']}")
367
+ print(f" 📄 Content: '{loc['content']}'")
368
+ print(f" 🔍 Context: '{loc['context'][:100]}{'...' if len(loc['context']) > 100 else ''}'")
369
+ print(f" 📊 Size: {loc['size']} bytes")
370
+ print(f" 🔢 Hex Data: {loc['hex_data']}")
371
+ print()
372
+
373
+ if len(locations) > 5:
374
+ print(f" ... and {len(locations) - 5} more matches")
375
+ print()
376
+
377
+ def run_complete_demo(self):
378
+ """Run the complete Notepad automation demo"""
379
+ print("🤖 MCP Cheat Engine Server + PyAutoGUI Notepad Demo")
380
+ print("="*60)
381
+ print("This demo will:")
382
+ print("1. 🚀 Launch Notepad using PyAutoGUI")
383
+ print("2. 📝 Send 'Hello World' text using PyAutoGUI")
384
+ print("3. 🔍 Find the text in Notepad's memory using MCP")
385
+ print("4. 📊 Display memory addresses and content")
386
+ print()
387
+
388
+ # Step 1: Initialize systems
389
+ print("🔧 Initializing systems...")
390
+ if not self.initialize_systems():
391
+ print("❌ System initialization failed!")
392
+ return False
393
+
394
+ # Step 2: Launch Notepad
395
+ print("\n🚀 Launching Notepad...")
396
+ if not self.launch_notepad():
397
+ print("❌ Failed to launch Notepad!")
398
+ return False
399
+
400
+ # Step 3: Send text
401
+ print("\n📝 Sending text to Notepad...")
402
+ if not self.send_text_to_notepad():
403
+ print("❌ Failed to send text to Notepad!")
404
+ return False
405
+
406
+ # Step 4: Search memory
407
+ print("\n🔍 Searching for text in memory...")
408
+ found_locations = self.find_text_in_memory()
409
+
410
+ # Step 5: Display results
411
+ self.display_results(found_locations)
412
+
413
+ # Step 6: Additional PyAutoGUI demonstration
414
+ print("\n🎮 Additional PyAutoGUI Features Demo:")
415
+ self._demonstrate_additional_features()
416
+
417
+ print("\n✅ Demo completed successfully!")
418
+ return True
419
+
420
+ def _demonstrate_additional_features(self):
421
+ """Demonstrate additional PyAutoGUI features"""
422
+ try:
423
+ # Get current mouse position
424
+ mouse_pos = self.pyautogui_controller.get_mouse_position()
425
+ if mouse_pos.success:
426
+ print(f"🖱️ Current mouse position: {mouse_pos.data}")
427
+
428
+ # Get screen information
429
+ screen_info = self.pyautogui_controller.get_screen_info()
430
+ if screen_info.success:
431
+ print(f"🖥️ Screen resolution: {screen_info.data['width']}x{screen_info.data['height']}")
432
+
433
+ # Take a final screenshot
434
+ screenshot = self.pyautogui_controller.take_screenshot()
435
+ if screenshot.success and 'file_path' in screenshot.data:
436
+ print(f"📷 Final screenshot saved: {screenshot.data['file_path']}")
437
+
438
+ except Exception as e:
439
+ logger.error(f"Additional features demo error: {e}")
440
+
441
+ def main():
442
+ """Main entry point"""
443
+ demo = NotepadAutomationDemo()
444
+
445
+ try:
446
+ success = demo.run_complete_demo()
447
+ if success:
448
+ print("\n🎉 All systems working perfectly!")
449
+ print("🔗 PyAutoGUI + MCP Cheat Engine Server integration is complete!")
450
+ else:
451
+ print("\n⚠️ Demo completed with some issues")
452
+
453
+ except KeyboardInterrupt:
454
+ print("\n🛑 Demo interrupted by user")
455
+ except Exception as e:
456
+ print(f"\n💥 Demo failed with error: {e}")
457
+ logger.exception("Demo exception details:")
458
+
459
+ if __name__ == "__main__":
460
+ main()