Core Keeper Dedicated Server 설치 및 설정 가이드


1. 시스템 준비

1.1 환경 정보

이 가이드는 Ubuntu 24.04.1 LTS 에 맞게 작성되었습니다.

1.2 사용자 계정 생성

별도 사용자를 생성하여 steamCMDCore Keeper Dedicated Server를 관리합니다.

sudo adduser corekeeper

1.3 패키지 저장소 사용 설정

steamCMD를 패키지 저장소를 통해 설치합니다.

sudo add-apt-repository multiverse; sudo dpkg --add-architecture i386; sudo apt update
sudo apt install steamcmd

2. Core Keeper Dedicated Server 설치

2.1 steamCMD 활용 설치

steamCMD를 사용하여 Core Keeper Dedicated Server와 Steamworks SDK Redist를 설치합니다.

2.1.1 Core Keeper Dedicated Server 설치
steamcmd +force_install_dir /home/corekeeper/server +login anonymous +app_update 1963720 validate +quit
2.1.2 Steamworks SDK Redist 설치
steamcmd +force_install_dir /home/corekeeper/sdk +login anonymous +app_update 1007 validate +quit

3. Systemd 서비스 설정

3.1 실행 파일 수정

/home/corekeeper/server/_launch.sh 파일의 구문을 수정합니다.

sed -i 's|Steamworks SDK Redist/linux64|sdk|g' /home/corekeeper/server/_launch.sh

3.2 Systemd 서비스 파일 생성

Core Keeper Dedicated Server를 systemd로 관리하기 위한 서비스 파일을 생성합니다.

3.2.1 서비스 파일 생성

다음 내용을 /etc/systemd/system/corekeeper.service에 추가합니다.

sudo vi /etc/systemd/system/corekeeper.service

서비스 파일 내용

[Unit]
Description=Core Keeper Dedicated Server
After=network.target

[Service]
Type=simple
User=corekeeper
WorkingDirectory=/home/corekeeper/server
ExecStartPre=/bin/sleep 5
ExecStart=/home/corekeeper/server/_launch.sh
KillMode=process
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
3.2.2 서비스 리로드 및 활성화

systemd를 리로드하고 서비스를 활성화합니다.

sudo systemctl daemon-reload
sudo systemctl start corekeeper.service
sudo systemctl enable corekeeper.service

4. 기존 월드 가져오기

4.1 월드 파일 경로

기존 월드를 가져와 서버 경로에 복사합니다.

4.1.1 리눅스 서버 월드 경로
/home/corekeeper/.config/unity3d/Pugstorm/Core Keeper/DedicatedServer/worlds
4.1.2 윈도우 싱글 플레이 월드 경로
C:\Users\{Users}\AppData\LocalLow\Pugstorm\Core Keeper\Steam\253642204\worlds
4.1.3 윈도우 Dedicated 서버 월드 경로
C:\Users\{Users}\AppData\LocalLow\Pugstorm\Core Keeper\DedicatedServer\worlds

Note
테스트 실행 후 생성된 파일은 삭제한 뒤 기존 파일을 복사하세요.


5. 시스템 모니터링 툴 설정

5.1 추가 패키지 설치

시스템 모니터링을 위해 screenglances를 설치합니다.

sudo apt install -y screen glances

5.2 Glances 실행 및 모니터링

screen을 사용하여 glances를 백그라운드에서 실행합니다.

5.2.1 Glances 백그라운드 실행
screen -dmS corekeeper bash -c "sudo glances"
5.2.2 Screen 세션 접속
screen -r corekeeper
5.2.3 Screen 세션 종료(유지)
Ctrl + A, D

6. 전체 실행 흐름

  1. steamCMDCore Keeper Dedicated Servercorekeeper 계정에서 실행됩니다.
  2. corekeeper.service는 systemd를 통해 자동으로 시작되며, 서버가 중단되면 자동으로 재시작됩니다.
  3. screenglances를 사용하여 시스템 모니터링을 할 수 있습니다.

7. Trouble Shooting

7.1 서버 업데이트 후

2.1.1 Core Keeper Dedicated Server 설치와 동일

3.1 실행 파일 수정 절차를 다시 반복한다.

/home/corekeeper/server/_launch.sh 파일의 구문을 수정합니다.

sed -i 's|Steamworks SDK Redist/linux64|sdk|g' /home/corekeeper/server/_launch.sh

7.2 SDK 업데이트 후

2.1.2 Steamworks SDK Redist 설치와 동일

64bit so file을 찾지 못해 Library Error가 발생하는 경우, 아래 명령을 수행
ln -s /home/corekeeper/sdk/linux64 ~/.steam/sdk64

Core Keeper Dedicated Server 상태 정보 설정 가이드


1. 시스템 준비

1.1 환경 정보

이 가이드는 Ubuntu 24.04.1 LTSPython 에 맞게 작성되었습니다.
본 코드의 명칭은 watchcore이며, chatGPT를 통해 작성되었습니다.


2. Python 코드

24-12-11. watchcore 상태 정보가 corekeeper에 의존적인 문제를 수정하였습니다.

Python Requirement는 별도로 작성하지 않았으며,
필요한 Module은 설치하고 코드를 기동합니다.

#!/usr/bin/env python3

import subprocess
import requests
import schedule
import time
import logging
import signal
import sys
from datetime import datetime
import re

# 로깅 설정
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[logging.StreamHandler()]
)

# Discord 웹훅 URL 및 관련 변수 설정
WEBHOOK_URL = "https://discord.com/api/v10/webhooks/123123123123/qgqgdqgqeg"
SERVICE_CK = "corekeeper"  # CoreKeeper 서비스 이름
SERVICE_WC = "watchcore"  # WatchCore 서비스 이름
TARGET_STRING = "Game ID:"  # 로그에서 게임 ID를 찾기 위한 문자열
WATCHCORE_STATUS_MESSAGE = "ID:"  # Watchcore 상태 메시지에서 찾을 문자열
MESSAGE_ID = None  # Discord 메시지 ID 초기화
LAST_STATUS_MESSAGE = None  # 마지막 서버 상태 메시지

# 초기 메시지 ID를 설정하는 함수
def initialize_message_id():
    global MESSAGE_ID
    if MESSAGE_ID is None:
        MESSAGE_ID = get_last_message_id_from_journal()
        if MESSAGE_ID:
            logging.info(f"초기 메시지 ID 설정: {MESSAGE_ID}")
        else:
            logging.info("초기 메시지 ID를 찾지 못했습니다.")

# journalctl에서 마지막 메시지 ID를 찾는 함수
def get_last_message_id_from_journal():
    try:
        # 'watchcore' 서비스의 로그에서 마지막 메시지 ID를 추출
        process = subprocess.run(
            ["journalctl", "-u", SERVICE_WC, "-n", "20", "--grep", WATCHCORE_STATUS_MESSAGE],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        if process.returncode == 0 and process.stdout:
            for line in process.stdout.splitlines():
                message_id = extract_message_id_from_log(line)
                if message_id:
                    return message_id
        return None
    except Exception as e:
        logging.error(f"journalctl에서 ID 추출 중 오류 발생: {e}", exc_info=True)
        return None

# 로그에서 메시지 ID를 추출하는 함수
def extract_message_id_from_log(log_line):
    match = re.search(r"ID:\s*(\d+)", log_line)
    if match:
        return match.group(1)
    return None

# Discord 메시지를 전송하거나 업데이트하는 함수
def send_or_update_discord_message(content):
    global MESSAGE_ID

    data = {"content": content}

    try:
        # 기존 메시지 ID가 유효하지 않으면 새로운 메시지를 생성
        if MESSAGE_ID:
            if not is_message_id_valid(MESSAGE_ID):
                logging.warning("MESSAGE_ID가 유효하지 않습니다. 새로운 메시지를 생성합니다.")
                MESSAGE_ID = None

        # 메시지가 이미 존재하면 PATCH로 업데이트, 없으면 새로 생성
        if MESSAGE_ID:
            response = requests.patch(f"{WEBHOOK_URL}/messages/{MESSAGE_ID}", json=data)
        else:
            response = requests.post(f"{WEBHOOK_URL}?wait=1", json=data)

        if response.status_code in {200, 204}:  # 성공적인 응답 처리
            if not MESSAGE_ID:
                message_data = response.json()
                MESSAGE_ID = message_data.get("id")
                logging.info(f"메시지 생성 성공, ID: {MESSAGE_ID}")
            else:
                logging.info("메시지 업데이트 성공")
        else:
            logging.warning(f"메시지 처리 실패: {response.status_code}, {response.text}")
    except Exception as e:
        logging.error(f"메시지 처리 중 오류 발생: {e}", exc_info=True)

# 메시지 ID 유효성 체크 함수
def is_message_id_valid(message_id):
    try:
        response = requests.get(f"{WEBHOOK_URL}/messages/{message_id}")
        return response.status_code == 200
    except Exception as e:
        logging.error(f"메시지 유효성 확인 중 오류 발생: {e}", exc_info=True)
        return False

# CoreKeeper 서비스 상태를 확인하고 Discord 메시지를 업데이트하는 함수
def check_service_status():
    global LAST_STATUS_MESSAGE

    try:
        # systemctl 명령어로 CoreKeeper 서비스 상태 확인
        process = subprocess.run(
            ["systemctl", "is-active", SERVICE_CK],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )

        # CoreKeeper 상태에 따른 메시지 설정
        if process.returncode == 0 and process.stdout.strip() == "active":
            # CoreKeeper 서버가 정상일 때
            last_start_time = get_last_start_time()
            server_id = get_server_id() or "알 수 없음"
            corekeeper_status = f"{SERVICE_CK} 서버가 정상입니다."
        else:
            # CoreKeeper 서버가 비정상일 때
            last_start_time = "알 수 없음"
            server_id = "알 수 없음"
            corekeeper_status = f"{SERVICE_CK} 서버가 비정상입니다."

        # Watchcore 상태는 항상 실행 중으로 설정
        watchcore_status = "Watchcore가 실행 중입니다."

        # 새 상태 메시지 구성
        new_status_message = f"""**서버 ID**
```{server_id}```
**서버 상태**
```{corekeeper_status}```
**마지막 서버 기동 시간**
```{last_start_time}```
**Watchcore 상태**
```{watchcore_status}```"""

        # 메시지가 변경된 경우에만 Discord 업데이트
        if new_status_message != LAST_STATUS_MESSAGE:
            send_or_update_discord_message(new_status_message)
            LAST_STATUS_MESSAGE = new_status_message
    except Exception as e:
        logging.error(f"서버 상태 확인 중 오류 발생: {e}", exc_info=True)

# 마지막 서버 기동 시간을 확인하는 함수
def get_last_start_time():
    try:
        process = subprocess.run(
            ["journalctl", "-u", SERVICE_CK, "--reverse", "-n", "10", "--grep", TARGET_STRING],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        if process.returncode == 0 and process.stdout:
            for line in process.stdout.splitlines():
                if TARGET_STRING in line:
                    timestamp_str = " ".join(line.split()[:3])
                    full_timestamp = datetime.strptime(f"{datetime.now().year} {timestamp_str}", "%Y %b %d %H:%M:%S")
                    return full_timestamp.strftime("%Y년 %m월 %d일 %H시 %M분 %S초")
        return "알 수 없음"
    except Exception as e:
        logging.error(f"시간 추출 중 오류 발생: {str(e)}", exc_info=True)
        return "시간 추출 중 오류 발생"

# 서버 ID를 가져오는 함수
def get_server_id():
    try:
        process = subprocess.run(
            ["journalctl", "-u", SERVICE_CK, "--reverse", "-n", "10"],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        for line in process.stdout.splitlines():
            if TARGET_STRING in line:
                return line.split(TARGET_STRING, 1)[1].strip()
        return None
    except Exception as e:
        logging.error(f"서버 ID 추출 중 오류 발생: {str(e)}", exc_info=True)
        return None

# SIGTERM 신호 처리 함수, 프로그램 종료 시 Discord에 종료 메시지를 보냄
def signal_handler(sig, frame):
    logging.info("SIGTERM 신호 발생, Watchcore를 종료합니다.")
    if MESSAGE_ID:
        send_or_update_discord_message("**Watchcore 상태**\n```Watchcore가 종료되었습니다.```")
    sys.exit(0)

# 주기적으로 서버 상태를 확인하고 메시지를 업데이트하는 watchdog 함수
def watchdog():
    logging.info("Watchdog이 실행 중입니다.")
    initialize_message_id()  # 초기 메시지 ID 설정
    signal.signal(signal.SIGTERM, signal_handler)  # SIGTERM 신호 처리기 설정

    # 5초마다 서버 상태를 체크하는 스케줄러 설정
    schedule.every(5).seconds.do(check_service_status)

    while True:
        try:
            schedule.run_pending()
            time.sleep(5)  # 5초 대기 후 반복
        except Exception as e:
            logging.error(f"에러 발생: {e}", exc_info=True)
            time.sleep(5)

# 프로그램 실행
if __name__ == "__main__":
    watchdog()

3. Systemd 서비스 설정

3.1 Systemd 서비스 파일 생성

watchcore를 systemd로 관리하기 위한 서비스 파일을 생성합니다.

3.2.1 서비스 파일 생성

다음 내용을 /etc/systemd/system/watchcore.service에 추가합니다.

sudo vi /etc/systemd/system/watchcore.service

서비스 파일 내용

[Unit]
Description=Core Keeper Dedicated Server Watchdog
After=network.target

[Service]
Type=simple
User=corekeeper
WorkingDirectory=/home/corekeeper/
ExecStartPre=/bin/sleep 5
ExecStart=/home/corekeeper/watchcore
KillMode=process
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
3.2.2 서비스 리로드 및 활성화

systemd를 리로드하고 서비스를 활성화합니다.

sudo systemctl daemon-reload
sudo systemctl start watchcore.service
sudo systemctl enable watchcore.service

4. 전체 실행 흐름

watchcore의 동작 방식

  1. watchcorecorekeeper의 journal 로그를 파싱하여 정제합니다.
  2. 정제된 로그는 Discord Webhook을 통해 발송합니다.
  3. corekeeper가 이상 발생한 경우, watchcore는 발송된 메시지를 수정합니다.
  • 수정되는 항목은 서버 ID서버 상태 입니다.
  1. watchcore가 종료된 경우, 발송된 메시지를 수정합니다.
  • 수정되는 항목은 Watchcore 상태이며, 나머지 상태 정보는 삭제됩니다.

'core keeper' 카테고리의 다른 글

Core Keeper Dedicated Server 설치 방법 (Ubuntu, steamCMD)  (0) 2025.04.08

실제 플레이를 통해 얻은 정보를 기반으로 작성합니다.

 

1280x720(720p) 업스케일링이 가장 쾌적한 부분을 확인했습니다.
 
로갈리X 전역 설정은 아래와 같습니다.

커맨드 센터 옵션 설정 값
작동 모드 17W (성능 모드)
RSR (Radeon™ Super Resolution) ON (선명도 90)
AMD Fluid Motion Frames ON
Radeon 지연 방지 ON
CPU 부스트 OFF
작동모드 성능
주사율 120Hz

 
 
그럼 로스트아크 옵션 설정 방법 입니다.

옵션 설정 값
해상도 1280x720
화면 전체화면
FPS 제한 비활성화 (활성화 시 60)
백그라운드 FPS 제한 비활성화 (활성화 시 10)
21:9 강제 설정 비활성화
화면 밝기 50
텍스처 품질
그림자 품질
캐릭터 품질
파티클 품질
계단 현상 방지 미사용
간접 그림자 낮음
아웃 포커싱 품질 향상 비활성화
실시간 최적화 성능 우선
후처리 효과 세부 설정 화면 공간 반사 외 비활성화

 
위와 같이 설정하고 FPS 결과값은 아래와 같습니다.

 

마을에서 60~110 FPS 정도 얻을 수 있습니다.

다만, 캐릭터들을 표현하는 양이나 타캐릭터의 움직임에 따라 프레임이 많이 등락합니다.

 

평균적으로 60~70 FPS 정도 얻을 수 있습니다.

(쿠르잔 남부 베스페르 에포나 근처 기준)

 

인게임 진입 시,

카오스 던전 및 쿠르잔 전선은 30~80 FPS 정도 얻을 수 있고

가디언 토벌은 4인 기준 30~80 FPS 정도 얻을 수 있습니다.

 

변수는 Wi-Fi의 품질에 따라 좌우되는 부분도 있기에

이 부분은 인게임 네트워크 품질 체커에 경고(노란색, 빨간색) 색상이 표현되지 않는 수준의 결과 입니다.

 

온라인 게임의 문제인 CPU 사용량이 높은 부분도 다소 문제가 있을 수 있으나,

AFMF1에서는 다소 안정적이지 못한 프레임 드랍 구간이 존재합니다.

 

AFMF2에서는 이 부분들이 많이 조정되었다고 하니 기대해볼만 하지 않나 생각이 됩니다.

'rog ally x' 카테고리의 다른 글

로갈리X 엘든링 옵션  (0) 2024.11.20
로갈리X 젠레스 존 제로 옵션  (1) 2024.11.12
로갈리X 발더스 게이트3 옵션  (0) 2024.07.29

+ Recent posts