13
config = configparser.ConfigParser()
14
ini_path = os.path.join(os.path.dirname(__file__), 'game_manager_real.ini')
17
except Exception as e:
18
logging.error(f"Error reading configuration: {e}")
22
OPENVPN_PATH = config.get('Paths', 'openvpn_path')
23
VPN_LOG = config.get('Paths', 'vpn_log')
24
OVPN_DIR = config.get('Paths', 'ovpn_dir')
25
BANNED_FILE = config.get('Paths', 'banned_file')
26
COMMANDS_FILE = config.get('Paths', 'commands_file')
27
BANNED_LOG = config.get('Paths', 'banned_log')
28
OUTPUT_LOG = config.get('Paths', 'output_log')
29
EXE_PATH = config.get('Paths', 'exe_path')
30
UPDATE_FILE_CHECK = config.get('Paths', 'update_file_check')
31
REAL_IP = config.get('Settings', 'real_ip')
33
except Exception as e:
34
logging.error(f"Error retrieving configuration values: {e}")
38
log_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s", datefmt="%H:%M:%S")
40
file_handler = logging.FileHandler(OUTPUT_LOG)
41
file_handler.setFormatter(log_formatter)
45
class StreamToUTF8(io.TextIOWrapper):
49
except UnicodeEncodeError:
50
super().write(b.encode('utf-8', errors='replace').decode('utf-8'))
52
console_handler = logging.StreamHandler(StreamToUTF8(sys.stdout.buffer, encoding='utf-8', errors='replace'))
53
console_handler.setFormatter(log_formatter)
56
logger = logging.getLogger()
57
logger.setLevel(logging.INFO)
58
logger.addHandler(file_handler)
59
logger.addHandler(console_handler)
64
force_rebuild_active = False
65
rebuild_start_time = 0
66
last_rebuild_ban_trigger = -1
67
script_start_time = time.time()
69
reset_after_ban_applied = False
70
current_mode = "Default"
71
last_applied_mode = None
72
command_write_count = 0
77
def print_log(message):
84
elapsed = int(time.time() - script_start_time)
85
hrs, rem = divmod(elapsed, 3600)
86
mins, secs = divmod(rem, 60)
87
uptime_str = f"{hrs}h {mins}m {secs}s"
88
logging.info("-------- STATS --------")
89
logging.info(f"Total Bans: {ban_count}")
90
logging.info(f"Active Bans: {active_ban_count}")
91
logging.info(f"Script Uptime: {uptime_str}")
93
since_last = int(time.time() - last_ban_time)
94
m, s = divmod(since_last, 60)
95
last_ban_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(last_ban_time))
96
logging.info(f"Last Ban Time: {last_ban_str}")
97
logging.info(f"Time Since Last Ban: {m}m {s}s")
98
logging.info(f"Current Mode: {current_mode}")
99
logging.info(f"Force Rebuild Active: {force_rebuild_active}")
100
if force_rebuild_active:
101
remaining = int(300 - (time.time() - rebuild_start_time))
102
m, s = divmod(remaining, 60)
103
logging.info(f"Rebuild Cooldown: {m}m {s}s remaining")
104
logging.info(f"Alt Mode: {'ON' if alt_mode else 'OFF'}")
105
logging.info("-----------------------")
106
except Exception as e:
107
logging.error(f"Error logging stats: {e}")
111
admin = os.getuid() == 0
112
except AttributeError:
114
admin = ctypes.windll.shell32.IsUserAnAdmin() != 0
115
except Exception as e:
116
logging.error(f"Error checking admin privileges: {e}")
119
logging.warning("Elevating privileges...")
121
params = " ".join(sys.argv)
122
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, params, None, 1)
123
except Exception as e:
124
logging.error(f"Error during privilege elevation: {e}")
127
def kill_existing_vpn():
129
logging.info("Killing any existing OpenVPN processes...")
130
for proc in psutil.process_iter(['name']):
132
if proc.info['name'] and "openvpn.exe" in proc.info['name'].lower():
133
logging.info(f"Killing process {proc.pid} ({proc.info['name']})")
135
except Exception as e:
136
logging.error(f"Error killing process {proc.pid}: {e}")
137
except Exception as e:
138
logging.error(f"Error iterating processes for VPN kill: {e}")
140
def parse_ban_duration(reason):
142
m = re.search(r'at least (\d+)', reason)
144
return int(m.group(1)) * 60
145
except Exception as e:
146
logging.error(f"Error parsing ban duration from reason '{reason}': {e}")
149
def add_to_banned_log(ovpn_file, banned_ip, ban_reason):
151
if "kicked" in ban_reason.lower():
152
logging.info("Kick detected; entry not logged: " + ban_reason)
155
ban_duration = parse_ban_duration(ban_reason)
157
entry = f"{banned_ip} | {ovpn_file} | {ts} | {ban_duration} | {ban_reason}"
159
file_exists = os.path.exists(BANNED_LOG)
160
file_empty = not file_exists or os.path.getsize(BANNED_LOG) == 0
162
with open(BANNED_LOG, 'a') as f:
164
header = "Banned IP | OVPN File | Timestamp | Duration (sec) | Ban Reason"
165
f.write(header + "\n")
166
f.write(entry + "\n")
168
logging.info(f"Added banned log entry: {entry}")
169
except Exception as e:
170
logging.error(f"Error adding to banned log: {e}")
172
def perform_ban_actions():
173
global current_mode, last_applied_mode, force_rebuild_active, rebuild_start_time, last_rebuild_ban_trigger, alt_mode
175
if active_ban_count == 3 and last_applied_mode != "RANDOM_SMART":
176
update_commands(COMMANDS_FILE, "PLAYER_NAME_RANDOM_SMART 1")
177
current_mode = last_applied_mode = "RANDOM_SMART"
178
elif active_ban_count == 5 and last_applied_mode != "RANDOM_STEAL":
179
update_commands(COMMANDS_FILE, "PLAYER_NAME_RANDOM_SMART 0")
180
update_commands(COMMANDS_FILE, "PLAYER_NAME_RANDOM_STEAL 1")
181
current_mode = last_applied_mode = "RANDOM_STEAL"
182
elif active_ban_count == 7 and last_applied_mode != "PLAYERIDS":
183
update_commands(COMMANDS_FILE, "PLAYER_NAME_RANDOM_SMART 0")
184
update_commands(COMMANDS_FILE, "PLAYER_NAME_RANDOM_STEAL 0")
185
update_commands(COMMANDS_FILE, "PLAYER_NAME_PLAYERIDS 1")
186
current_mode = last_applied_mode = "PLAYERIDS"
187
elif active_ban_count >= 8:
188
if active_ban_count % 8 == 0 and active_ban_count != last_rebuild_ban_trigger:
189
update_commands(COMMANDS_FILE, "FORCE_PLAYER_ZREBUILD 1")
190
rebuild_start_time = time.time()
191
force_rebuild_active = True
192
last_rebuild_ban_trigger = active_ban_count
193
logging.info("FORCE_PLAYER_ZREBUILD activated.")
195
update_commands(COMMANDS_FILE, "PLAYER_NAME_PLAYERIDS 1")
196
update_commands(COMMANDS_FILE, "PLAYER_NAME_RANDOM_STEAL 0")
197
current_mode = "RANDOM_STEAL"
199
update_commands(COMMANDS_FILE, "PLAYER_NAME_PLAYERIDS 0")
200
update_commands(COMMANDS_FILE, "PLAYER_NAME_RANDOM_STEAL 1")
201
current_mode = "RANDOM_STEAL"
202
alt_mode = not alt_mode
203
except Exception as e:
204
logging.error(f"Error performing ban actions: {e}")
206
def load_banned_log():
209
if os.path.exists(BANNED_LOG):
210
with open(BANNED_LOG, 'r') as f:
211
header_skipped = False
213
if not header_skipped:
214
if line.strip().startswith("Banned IP"):
215
header_skipped = True
217
header_skipped = True
218
parts = line.strip().split(" | ")
220
ip, ovpn_file, ts, duration, reason = parts
221
banned[ip] = (ovpn_file, float(ts), int(duration), reason)
222
except Exception as e:
223
logging.error(f"Error loading banned log: {e}")
226
def is_ip_banned(ip):
228
banned = load_banned_log()
230
ovpn_file, ts, duration, reason = banned[ip]
231
if time.time() < ts + duration:
232
logging.warning(f"IP {ip} is banned until {time.strftime('%H:%M:%S', time.localtime(ts+duration))}.")
234
except Exception as e:
235
logging.error(f"Error checking if IP {ip} is banned: {e}")
238
def update_commands(commands_file, command_str):
239
global command_write_count
241
with open(commands_file, 'a') as f:
242
f.write(command_str + "\n")
243
command_write_count += 1
244
logging.info(f"-> {command_str} (Total Written: {command_write_count})")
245
except Exception as e:
246
logging.error(f"Error updating commands file: {e}")
249
while os.path.exists(UPDATE_FILE_CHECK):
250
logging.info("Update in progress—waiting for it to finish…")
253
logging.info("Waiting for Armagetronad executable to be available…")
258
while not os.path.isfile(EXE_PATH):
259
if waited >= max_wait:
260
logging.error("Timed out waiting for Armagetronad.exe.")
265
logging.info("Armagetronad executable found. Launching…")
266
subprocess.Popen([EXE_PATH])
268
def connect_vpn_filtered(vpn_path, ovpn_dir, log_path):
272
open(log_path, 'w').close()
273
except Exception as e:
274
logging.error(f"Error clearing VPN log: {e}")
278
all_ovpn_files = [os.path.join(ovpn_dir, f) for f in os.listdir(ovpn_dir)
279
if f.lower().endswith('.ovpn')]
280
except Exception as e:
281
logging.error(f"Error listing ovpn files: {e}")
283
total_count = len(all_ovpn_files)
284
if not all_ovpn_files:
285
logging.error("No .ovpn files found!")
288
banned = load_banned_log()
289
banned_ovpn_files = set(entry[0] for entry in banned.values() if time.time() < entry[1] + entry[2])
290
banned_count = sum(1 for f in all_ovpn_files if f in banned_ovpn_files)
292
available_ovpn_files = [f for f in all_ovpn_files if f not in banned_ovpn_files]
293
available_count = len(available_ovpn_files)
295
logging.info(f"OVPN Files - Total: {total_count}, Available: {available_count}, Banned: {banned_count}")
297
if not available_ovpn_files:
298
logging.warning("All ovpn files are banned; using one anyway.")
299
available_ovpn_files = all_ovpn_files
301
rand_file = random.choice(available_ovpn_files)
302
logging.info(f"Using VPN configuration file: {rand_file}")
304
proc = subprocess.Popen(
305
[vpn_path, '--config', rand_file, '--dev', 'tun', '--ifconfig', '10.8.0.2', '10.8.0.1'],
306
stdout=open(log_path, 'w'), stderr=subprocess.STDOUT
308
return proc, rand_file
309
except Exception as e:
310
logging.error(f"Error in connect_vpn_filtered: {e}")
313
def wait_for_vpn_initialization(log_path, timeout=30, interval=1):
314
start_time = time.time()
315
while time.time() - start_time < timeout:
317
with open(log_path, 'r') as f:
319
if "Initialization Sequence Completed" in content:
320
logging.info("VPN initialization complete.")
322
if "AUTH: Received control message: AUTH_FAILED" in content:
323
logging.error("VPN authentication failed.")
325
except Exception as e:
326
logging.error(f"Error reading VPN log: {e}")
328
logging.warning("Timeout reached waiting for VPN initialization.")
331
def get_current_ip(real_ip, retry_interval=1, max_retries=30):
333
while retries < max_retries:
335
resp = requests.get('https://httpbin.org/ip', timeout=5)
336
current_ip = resp.json()['origin'].strip()
337
logging.info(f"Fetched IP: {current_ip}")
338
if current_ip != real_ip:
340
logging.warning("Current IP still matches REAL_IP. Waiting for new IP...")
341
except Exception as e:
342
logging.error(f"Error fetching IP (retrying): {e}")
343
time.sleep(retry_interval)
347
def connect_until_new_ip(real_ip, banned_ip=None):
350
proc, used_ovpn = connect_vpn_filtered(OPENVPN_PATH, OVPN_DIR, VPN_LOG)
352
logging.error("VPN process not started; retrying...")
355
if not wait_for_vpn_initialization(VPN_LOG):
356
if proc and proc.poll() is None:
360
candidate_ip = get_current_ip(real_ip)
361
if candidate_ip is None:
363
if banned_ip and candidate_ip == banned_ip:
364
logging.warning("Candidate IP matches banned IP. Killing VPN and retrying...")
368
if is_ip_banned(candidate_ip):
369
logging.warning(f"Candidate IP {candidate_ip} is in banned log. Retrying...")
373
logging.info(f"VPN IP acquired: {candidate_ip}")
374
return candidate_ip, used_ovpn
375
except Exception as e:
376
logging.error(f"Error in connect_until_new_ip: {e}")
379
def check_banned(banned_file):
381
if os.path.exists(banned_file):
382
with open(banned_file, 'r') as f:
383
content = f.read().strip()
385
logging.warning("Ban detected!")
386
logging.info(f"Ban Reason: {content}")
388
except Exception as e:
389
logging.error(f"Error reading ban file: {e}")
393
global alt_mode, force_rebuild_active, rebuild_start_time, last_rebuild_ban_trigger
394
global last_ban_time, reset_after_ban_applied, current_mode, last_applied_mode, initial_launch
395
global ban_count, active_ban_count
399
except Exception as e:
400
logging.error(f"Error in run_as_admin: {e}")
404
"PLAYER_NAME_RANDOM_SMART 0",
405
"PLAYER_NAME_RANDOM_STEAL 0",
406
"PLAYER_NAME_PLAYERIDS 0",
407
"FORCE_PLAYER_ZREBUILD 0"
409
update_commands(COMMANDS_FILE, cmd)
415
if any("armagetronad.exe" in p.name() for p in psutil.process_iter()):
416
logging.info("Armagetronad is running.")
418
logging.info("Armagetronad is not running. Checking VPN...")
420
resp = requests.get('https://httpbin.org/ip', timeout=5)
421
current_ip = resp.json()['origin'].strip()
422
except Exception as e:
423
logging.error(f"IP fetch error: {e}")
426
new_ip, used_ovpn = connect_until_new_ip(REAL_IP)
428
if not initial_launch and check_banned(BANNED_FILE):
430
active_ban_count += 1
431
last_ban_time = time.time()
432
reset_after_ban_applied = False
434
with open(BANNED_FILE, 'r') as f:
435
ban_reason = f.read().strip()
436
except Exception as e:
437
logging.error(f"Error reading banned file: {e}")
438
ban_reason = "Unknown"
439
logging.warning(f"[Ban {ban_count}] Triggered at {time.strftime('%H:%M:%S')}")
441
add_to_banned_log(used_ovpn, new_ip, ban_reason)
443
perform_ban_actions()
447
new_ip, used_ovpn = connect_until_new_ip(REAL_IP, banned_ip=new_ip)
449
if force_rebuild_active:
450
update_commands(COMMANDS_FILE, "FORCE_PLAYER_ZREBUILD 1")
455
initial_launch = False
457
if last_ban_time and (time.time() - last_ban_time > 1800) and not reset_after_ban_applied:
458
logging.info("30 minutes since last ban. Resetting command modes to default and active ban count.")
460
"PLAYER_NAME_RANDOM_SMART 0",
461
"PLAYER_NAME_RANDOM_STEAL 0",
462
"PLAYER_NAME_PLAYERIDS 0",
463
"FORCE_PLAYER_ZREBUILD 0"
465
update_commands(COMMANDS_FILE, cmd)
466
reset_after_ban_applied = True
467
current_mode = last_applied_mode = "Default"
475
if force_rebuild_active and time.time() - rebuild_start_time > 300:
476
update_commands(COMMANDS_FILE, "FORCE_PLAYER_ZREBUILD 0")
477
logging.info("5-minute punishment ended.")
478
force_rebuild_active = False
481
except Exception as e:
482
logging.error(f"Error in main loop: {e}")
485
if __name__ == '__main__':
488
except Exception as e:
489
logging.critical(f"Fatal error in main: {e}")