Profile picture

[Python] 팰월드 서버 자동 관리 스크립트

JaehyoJJAng2024년 01월 20일

◾️ 개요

24시간으로 서버 운영을 하게되면 발생하는 문제가 하나 있는데

팰 주위에 유저가 아무도 없는 경우, 아예 AI가 활성화되지 않아서 아무것도 먹지 않고 움직이지 않아 팰이 금방 병에 걸리게 된다.

해당 오류에 대해 패치 예정이 없는 지금으로써는 팰 주위에 사람을 두는게 유일한 해결법이다.

또는, 사람이 없으면 서버를 잠시 일시정지(Docker로 서버를 배포한 경우에만 해당) 하거나.


지금부터 만들어볼 스크립트의 구동 방식은 아래와 같다.

  • 1분마다 사용자 수 확인
    • 사용자가 아무도 없으면 서버 재시작(램 최적화, docker restart or docker-compose restart)후 서버 일시정지(docker pause or docker-compose pause)
  • 서버 일시정지 상태
    • 무한 루프를 돌면서 외부망 포트로 들어오는 TCP 트래픽을 계속 탐색
    • 접속 시도 발견 시 서버 재기동

▪️ 구동 환경


▪️ 사전 준비

tcpdump 설치

sudo apt-get install -y tcpdump

▪️ 스크립트 작성

import subprocess
import time

class StatusCheck():
    def __init__(self) -> None:
        self._TARGET_PORT : int = 8211
        self._CONTAINER_NAME : str = "palworld-server"
    
    def _check_players(self) -> bool:
        players_output: str = self._run_command(command=f"docker exec -i {self._CONTAINER_NAME} rcon-cli ShowPlayers")
                        
        if players_output:
            # 플레이어 목록
            players = [player.replace('\x00','') for player in players_output.split('\n')[1:]]
            
            if len(players) != 0:
                return True
            else:
                return False
    
    def _run_command(self,command: str | tuple) -> str:
        return subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, text=True).stdout.strip()

def main() -> None:
    # Create StatusCheck instance
    status: StatusCheck = StatusCheck()
    
    while True:
        # 플레이어 목록 조회
        player: bool = status._check_players()
        
        if not player:
            print("No Players found. Restart the server")
            status._run_command(command=f'docker restart {status._CONTAINER_NAME}')
            
            # wait for server to restart                
            for sec in range(30, 0, -1):
                print(f'pausing in {sec} seconds')
                time.sleep(1)
            
            # if not players, pause server
            if not status._check_players():
                print('Restart Complete. Pause the server')
                status._run_command(f'docker pause {status._CONTAINER_NAME}')
            else:
                player = True
                
            while not player:
                # use tcpdump to capture incoming traffic on the target port 
                capture_command = (f"sudo tcpdump -n -c 1 -i any port {status._TARGET_PORT} 2>/dev/null")
                capture_result: str = status._run_command(command=capture_command)
                
                # Check if any packets were caputerd
                if capture_result:
                    player = True
                    print(f'Connection attempt detected on port {status._TARGET_PORT}')
                    print(f"Starting server")
                    status._run_command(command=f'docker unpause {status._CONTAINER_NAME}')
                    
                    # after unpausing the server, wait for players to connect
                    for sec in range(60,0,-1):
                        print(f'checking in {sec} seconds')
                        time.sleep(1)
                else:
                    time.sleep(1)
        else:
            print('Players found. server will continue running.')
            time.sleep(60)

if __name__ == '__main__':
    main()

스크립트 실행

nohup python3 main.py > log 2>&1 &

Loading script...