Persistent RCON — single connection per server, auto-reconnect
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+58
-16
@@ -355,25 +355,63 @@ def process_bug_log(player: str, description: str, config):
|
||||
# RCON
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def rcon(cmd, host='127.0.0.1', port=25575, password='REDACTED_RCON'):
|
||||
# --- Persistent RCON connection pool ---
|
||||
_rcon_pool = {}
|
||||
_rcon_pool_lock = threading.Lock()
|
||||
|
||||
class _PersistentRCON:
|
||||
def __init__(self, host, port, password):
|
||||
self.host, self.port, self.password = host, port, password
|
||||
self._sock = None
|
||||
self._lock = threading.Lock()
|
||||
self._connected = False
|
||||
self._req_id = 0
|
||||
|
||||
def _connect(self):
|
||||
if self._sock:
|
||||
try: self._sock.close()
|
||||
except: pass
|
||||
self._sock = socket.socket()
|
||||
self._sock.settimeout(10)
|
||||
self._sock.connect((self.host, self.port))
|
||||
data = self.password.encode() + b'\x00\x00'
|
||||
self._sock.sendall(struct.pack('<iii', len(data)+8, 1, 3) + data)
|
||||
time.sleep(0.1)
|
||||
self._sock.recv(4096)
|
||||
self._connected = True
|
||||
|
||||
def command(self, cmd):
|
||||
with self._lock:
|
||||
for attempt in range(2):
|
||||
try:
|
||||
s = socket.socket()
|
||||
s.settimeout(5)
|
||||
s.connect((host, port))
|
||||
def pkt(i, t, p):
|
||||
p = p.encode() + b'\x00\x00'
|
||||
return struct.pack('<iii', len(p) + 8, i, t) + p
|
||||
s.sendall(pkt(1, 3, password))
|
||||
time.sleep(0.2)
|
||||
s.recv(4096)
|
||||
s.sendall(pkt(2, 2, cmd))
|
||||
time.sleep(0.2)
|
||||
r = s.recv(4096)
|
||||
s.close()
|
||||
return r[12:-2].decode(errors='replace')
|
||||
except Exception as e:
|
||||
if not self._connected or not self._sock:
|
||||
self._connect()
|
||||
self._req_id += 1
|
||||
data = cmd.encode() + b'\x00\x00'
|
||||
self._sock.sendall(struct.pack('<iii', len(data)+8, self._req_id, 2) + data)
|
||||
time.sleep(0.05)
|
||||
raw = self._sock.recv(4096)
|
||||
if len(raw) < 14:
|
||||
self._connected = False
|
||||
if attempt == 0: continue
|
||||
return ''
|
||||
return raw[12:-2].decode(errors='replace')
|
||||
except (OSError, socket.timeout, ConnectionResetError, BrokenPipeError) as e:
|
||||
self._connected = False
|
||||
self._sock = None
|
||||
if attempt == 0:
|
||||
time.sleep(0.3)
|
||||
continue
|
||||
log.error(f"RCON error executing '{cmd}': {e}")
|
||||
return ''
|
||||
return ''
|
||||
|
||||
def rcon(cmd, host='127.0.0.1', port=25575, password='REDACTED_RCON'):
|
||||
key = f"{host}:{port}"
|
||||
with _rcon_pool_lock:
|
||||
if key not in _rcon_pool:
|
||||
_rcon_pool[key] = _PersistentRCON(host, port, password)
|
||||
return _rcon_pool[key].command(cmd)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Server context
|
||||
@@ -1497,6 +1535,10 @@ def write_training_audit(player, mode, user_message, commands_generated,
|
||||
"server_type": config.get("server_type", "vanilla"),
|
||||
"version": "1.21.x",
|
||||
"online_players": context.get("online_players", []),
|
||||
"player_details": context.get("player_details", {}),
|
||||
"time_of_day": context.get("time_of_day", "unknown"),
|
||||
"weather": context.get("weather", "unknown"),
|
||||
"world_border": context.get("world_border"),
|
||||
}
|
||||
entry = {
|
||||
"timestamp": time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
|
||||
|
||||
Reference in New Issue
Block a user