Update factorio_rcon.py - RCON 클라이언트 최신 버전
This commit is contained in:
@@ -1 +1,80 @@
|
||||
IiIiCmZhY3RvcmlvX3Jjb24ucHkK7Yyp7Yag66as7JikIOyEnOuyhOyXkCBSQ09O7Jy866GcIOyXsOqysO2VmOqzoCBMdWEg66qF66C57J2EIOyLpO2Wie2VmOuKlCDrqqjrk4gKIiIiCmltcG9ydCBzb2NrZXQKaW1wb3J0IHN0cnVjdAppbXBvcnQgdGltZQoKCmNsYXNzIEZhY3RvcmlvUkNPTjoKICAgICIiIu2Mqe2GoOumrOyYpCBSQ09OIO2BtOudvOydtOyWuO2KuCIiIgoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBob3N0PSIxMjcuMC4wLjEiLCBwb3J0PTI1NTc1LCBwYXNzd29yZD0iZmFjdG9yaW9fYWkiKToKICAgICAgICBzZWxmLmhvc3QgPSBob3N0CiAgICAgICAgc2VsZi5wb3J0ID0gcG9ydAogICAgICAgIHNlbGYucGFzc3dvcmQgPSBwYXNzd29yZAogICAgICAgIHNlbGYuc29jayA9IE5vbmUKICAgICAgICBzZWxmLl9yZXFfaWQgPSAxCgogICAgZGVmIGNvbm5lY3Qoc2VsZik6CiAgICAgICAgc2VsZi5zb2NrID0gc29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCwgc29ja2V0LlNPQ0tfU1RSRUFNKQogICAgICAgIHNlbGYuc29jay5zZXR0aW1lb3V0KDEwKQogICAgICAgIHNlbGYuc29jay5jb25uZWN0KChzZWxmLmhvc3QsIHNlbGYucG9ydCkpCiAgICAgICAgIyDsnbjspp0KICAgICAgICBzZWxmLl9zZW5kX3BhY2tldCgzLCBzZWxmLnBhc3N3b3JkKSAgIyB0eXBlIDMgPSBBVVRICiAgICAgICAgcmVzcCA9IHNlbGYuX3JlY3ZfcGFja2V0KCkKICAgICAgICBpZiByZXNwWyJpZCJdID09IC0xOgogICAgICAgICAgICByYWlzZSBFeGNlcHRpb24oIlJDT04g7J247KadIOyLpO2MqDog67mE67CA67KI7Zi466W8IO2ZleyduO2VmOyEuOyalCIpCiAgICAgICAgcHJpbnQoZiJbUkNPTl0g7Yyp7Yag66as7JikIOyEnOuyhCDsl7DqsrAg7ISx6rO1ICh7c2VsZi5ob3N0fTp7c2VsZi5wb3J0fSkiKQoKICAgIGRlZiBkaXNjb25uZWN0KHNlbGYpOgogICAgICAgIGlmIHNlbGYuc29jazoKICAgICAgICAgICAgc2VsZi5zb2NrLmNsb3NlKCkKICAgICAgICAgICAgc2VsZi5zb2NrID0gTm9uZQoKICAgIGRlZiBydW4oc2VsZiwgbHVhX2NvbW1hbmQ6IHN0cikgLT4gc3RyOgogICAgICAgICIiIkx1YSDrqoXroLkg7Iuk7ZaJIO2bhCDqsrDqs7wg67CY7ZmYIiIiCiAgICAgICAgIyAvYyDsl4bsnbQg7Iic7IiYIEx1YeuhnCDsoITshqEgKFJDT07snYAg7J6Q64+Z7Jy866GcIOy9mOyGlCDrqoXroLnsnLzroZwg7LKY66asKQogICAgICAgIGNtZCA9IGYiL2Mge2x1YV9jb21tYW5kfSIKICAgICAgICByZXFfaWQgPSBzZWxmLl9zZW5kX3BhY2tldCgyLCBjbWQpICAjIHR5cGUgMiA9IEVYRUNDT01NQU5ECiAgICAgICAgcmVzdWx0ID0gc2VsZi5fcmVjdl9wYWNrZXQoKQogICAgICAgIHJldHVybiByZXN1bHQuZ2V0KCJib2R5IiwgIiIpLnN0cmlwKCkKCiAgICBkZWYgbHVhKHNlbGYsIGNvZGU6IHN0cikgLT4gc3RyOgogICAgICAgICIiIuyXrOufrCDspIQgTHVhIOy9lOuTnCDsi6TtlokiIiIKICAgICAgICByZXR1cm4gc2VsZi5ydW4oY29kZSkKCiAgICBkZWYgX3NlbmRfcGFja2V0KHNlbGYsIHB0eXBlOiBpbnQsIGJvZHk6IHN0cikgLT4gaW50OgogICAgICAgIHJlcV9pZCA9IHNlbGYuX3JlcV9pZAogICAgICAgIHNlbGYuX3JlcV9pZCArPSAxCiAgICAgICAgZW5jb2RlZCA9IGJvZHkuZW5jb2RlKCJ1dGYtOCIpICsgYiJceDAwIgogICAgICAgIHNpemUgPSA0ICsgNCArIGxlbihlbmNvZGVkKSArIDEKICAgICAgICBkYXRhID0gc3RydWN0LnBhY2soIjxpaWkiLCBzaXplLCByZXFfaWQsIHB0eXBlKSArIGVuY29kZWQgKyBiIlx4MDAiCiAgICAgICAgc2VsZi5zb2NrLnNlbmRhbGwoZGF0YSkKICAgICAgICByZXR1cm4gcmVxX2lkCgogICAgZGVmIF9yZWN2X3BhY2tldChzZWxmKSAtPiBkaWN0OgogICAgICAgICMg7Yyo7YK3IO2BrOq4sCDsnb3quLAKICAgICAgICByYXcgPSBzZWxmLl9yZWN2X2J5dGVzKDQpCiAgICAgICAgc2l6ZSA9IHN0cnVjdC51bnBhY2soIjxpIiwgcmF3KVswXQogICAgICAgIGRhdGEgPSBzZWxmLl9yZWN2X2J5dGVzKHNpemUpCiAgICAgICAgcmVxX2lkLCBwdHlwZSA9IHN0cnVjdC51bnBhY2soIjxpaSIsIGRhdGFbOjhdKQogICAgICAgIGJvZHkgPSBkYXRhWzg6XS5yc3RyaXAoYiJceDAwIikuZGVjb2RlKCJ1dGYtOCIsIGVycm9ycz0icmVwbGFjZSIpCiAgICAgICAgcmV0dXJuIHsiaWQiOiByZXFfaWQsICJ0eXBlIjogcHR5cGUsICJib2R5IjogYm9keX0KCiAgICBkZWYgX3JlY3ZfYnl0ZXMoc2VsZiwgbjogaW50KSAtPiBieXRlczoKICAgICAgICBidWYgPSBiIiIKICAgICAgICB3aGlsZSBsZW4oYnVmKSA8IG46CiAgICAgICAgICAgIGNodW5rID0gc2VsZi5zb2NrLnJlY3YobiAtIGxlbihidWYpKQogICAgICAgICAgICBpZiBub3QgY2h1bms6CiAgICAgICAgICAgICAgICByYWlzZSBFeGNlcHRpb24oIlJDT04g7Jew6rKw7J20IOuBiuyWtOyhjOyKteuLiOuLpCIpCiAgICAgICAgICAgIGJ1ZiArPSBjaHVuawogICAgICAgIHJldHVybiBidWYKCiAgICBkZWYgX19lbnRlcl9fKHNlbGYpOgogICAgICAgIHNlbGYuY29ubmVjdCgpCiAgICAgICAgcmV0dXJuIHNlbGYKCiAgICBkZWYgX19leGl0X18oc2VsZiwgKmFyZ3MpOgogICAgICAgIHNlbGYuZGlzY29ubmVjdCgpCg==
|
||||
"""
|
||||
factorio_rcon.py
|
||||
팩토리오 서버에 RCON으로 연결하고 Lua 명령을 실행하는 모듈
|
||||
"""
|
||||
import socket
|
||||
import struct
|
||||
import time
|
||||
|
||||
|
||||
class FactorioRCON:
|
||||
"""팩토리오 RCON 클라이언트"""
|
||||
|
||||
def __init__(self, host="127.0.0.1", port=25575, password="factorio_ai"):
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.password = password
|
||||
self.sock = None
|
||||
self._req_id = 1
|
||||
|
||||
def connect(self):
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.sock.settimeout(10)
|
||||
self.sock.connect((self.host, self.port))
|
||||
# 인증
|
||||
self._send_packet(3, self.password) # type 3 = AUTH
|
||||
resp = self._recv_packet()
|
||||
if resp["id"] == -1:
|
||||
raise Exception("RCON 인증 실패: 비밀번호를 확인하세요")
|
||||
print(f"[RCON] 팩토리오 서버 연결 성공 ({self.host}:{self.port})")
|
||||
|
||||
def disconnect(self):
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
|
||||
def run(self, lua_command: str) -> str:
|
||||
"""Lua 명령 실행 후 결과 반환"""
|
||||
# /c 없이 순수 Lua로 전송 (RCON은 자동으로 콘솔 명령으로 처리)
|
||||
cmd = f"/c {lua_command}"
|
||||
req_id = self._send_packet(2, cmd) # type 2 = EXECCOMMAND
|
||||
result = self._recv_packet()
|
||||
return result.get("body", "").strip()
|
||||
|
||||
def lua(self, code: str) -> str:
|
||||
"""여러 줄 Lua 코드 실행"""
|
||||
return self.run(code)
|
||||
|
||||
def _send_packet(self, ptype: int, body: str) -> int:
|
||||
req_id = self._req_id
|
||||
self._req_id += 1
|
||||
encoded = body.encode("utf-8") + b"\x00"
|
||||
size = 4 + 4 + len(encoded) + 1
|
||||
data = struct.pack("<iii", size, req_id, ptype) + encoded + b"\x00"
|
||||
self.sock.sendall(data)
|
||||
return req_id
|
||||
|
||||
def _recv_packet(self) -> dict:
|
||||
# 패킷 크기 읽기
|
||||
raw = self._recv_bytes(4)
|
||||
size = struct.unpack("<i", raw)[0]
|
||||
data = self._recv_bytes(size)
|
||||
req_id, ptype = struct.unpack("<ii", data[:8])
|
||||
body = data[8:].rstrip(b"\x00").decode("utf-8", errors="replace")
|
||||
return {"id": req_id, "type": ptype, "body": body}
|
||||
|
||||
def _recv_bytes(self, n: int) -> bytes:
|
||||
buf = b""
|
||||
while len(buf) < n:
|
||||
chunk = self.sock.recv(n - len(buf))
|
||||
if not chunk:
|
||||
raise Exception("RCON 연결이 끊어졌습니다")
|
||||
buf += chunk
|
||||
return buf
|
||||
|
||||
def __enter__(self):
|
||||
self.connect()
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.disconnect()
|
||||
|
||||
Reference in New Issue
Block a user