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:
+60
-18
@@ -355,26 +355,64 @@ def process_bug_log(player: str, description: str, config):
|
|||||||
# RCON
|
# RCON
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
def rcon(cmd, host='127.0.0.1', port=25575, password='REDACTED_RCON'):
|
# --- Persistent RCON connection pool ---
|
||||||
try:
|
_rcon_pool = {}
|
||||||
s = socket.socket()
|
_rcon_pool_lock = threading.Lock()
|
||||||
s.settimeout(5)
|
|
||||||
s.connect((host, port))
|
class _PersistentRCON:
|
||||||
def pkt(i, t, p):
|
def __init__(self, host, port, password):
|
||||||
p = p.encode() + b'\x00\x00'
|
self.host, self.port, self.password = host, port, password
|
||||||
return struct.pack('<iii', len(p) + 8, i, t) + p
|
self._sock = None
|
||||||
s.sendall(pkt(1, 3, password))
|
self._lock = threading.Lock()
|
||||||
time.sleep(0.2)
|
self._connected = False
|
||||||
s.recv(4096)
|
self._req_id = 0
|
||||||
s.sendall(pkt(2, 2, cmd))
|
|
||||||
time.sleep(0.2)
|
def _connect(self):
|
||||||
r = s.recv(4096)
|
if self._sock:
|
||||||
s.close()
|
try: self._sock.close()
|
||||||
return r[12:-2].decode(errors='replace')
|
except: pass
|
||||||
except Exception as e:
|
self._sock = socket.socket()
|
||||||
log.error(f"RCON error executing '{cmd}': {e}")
|
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:
|
||||||
|
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 ''
|
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
|
# Server context
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -1497,6 +1535,10 @@ def write_training_audit(player, mode, user_message, commands_generated,
|
|||||||
"server_type": config.get("server_type", "vanilla"),
|
"server_type": config.get("server_type", "vanilla"),
|
||||||
"version": "1.21.x",
|
"version": "1.21.x",
|
||||||
"online_players": context.get("online_players", []),
|
"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 = {
|
entry = {
|
||||||
"timestamp": time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
|
"timestamp": time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
|
||||||
|
|||||||
Reference in New Issue
Block a user