← 목록으로

Steam · STOVE · EOS 멀티플레이 및 계정 구조

Steam · STOVE · EOS 멀티플레이 및 계정 구조

1. 목표


2. 전체 구조

flowchart TB
  Game["Game.exe"]
  Steam["Steam 배포<br/>Steam 플랫폼 모듈/DLL"]
  Stove["STOVE 배포<br/>STOVE 플랫폼 모듈/DLL"]
  EOS["EOS"]
  Lobby["Lobby"]
  Session["Session"]
  P2P["P2P"]
  Listen["Unreal Listen Server"]
  ClientA["Client"]
  ClientB["Client"]

  Game --> Steam
  Game --> Stove
  Steam --> EOS
  Stove --> EOS
  EOS --> Lobby
  EOS --> Session
  EOS --> P2P
  Session --> Listen
  Listen --> ClientA
  Listen --> ClientB

실제 멀티플레이 로직은 EOS를 통해 공통화한다.

Steam과 STOVE는 가능한 한 플랫폼 초기화, 플랫폼 사용자 정보 획득 등 플랫폼 종속 기능만 담당한다.

EOS Connect와 EOS Lobby를 통해 인증·식별·매칭을 공통화하고, 게임 상태는 Unreal Replication으로 동기화한다. 실제 네트워크 전송은 EOS P2P를 사용하며, 직접 연결이 불가능한 개별 peer만 EOS Relay로 전환한다.


3. 플랫폼 코드 분리

플랫폼별 기능은 게임 본체와 분리하는 구조를 사용한다.

Game.exe

IPlatform
 ├─ Init()
 ├─ GetUserId()
 ├─ GetNickname()
 ├─ GetAuthToken()
 └─ Shutdown()

구현체
 ├─ PlatformSteam.dll
 └─ PlatformStove.dll

Steam 배포

Game.exe
PlatformSteam.dll
Steamworks Runtime
EOS Runtime
공통 게임 데이터

STOVE 배포

Game.exe
PlatformStove.dll
STOVE PC SDK Runtime
EOS Runtime
공통 게임 데이터

가능하면 Game.exe 자체는 동일하게 유지하고 배포 패키징 단계에서 플랫폼 DLL 및 런타임만 변경한다.

단, 유지보수나 SDK 제약 때문에 이 방식이 불필요하게 복잡해질 경우에는 동일 코드베이스에서 Steam/STOVE 빌드를 각각 생성하는 방식도 허용한다.


4. EOS의 역할

EOS는 스토어와 독립적인 공통 온라인 서비스 계층으로 사용한다.

주요 사용 대상:

Steam 또는 STOVE 자체의 멀티플레이 시스템에 게임 구조를 종속시키지 않는다.

4.1 인증과 PUID

Steam/STOVE의 인증 결과를 EOS Connect와 연결하여 공통 PUID를 사용한다. 사용자에게 Epic Games 계정 로그인을 요구하지 않는 방향을 기본으로 한다.

flowchart TD
  Steam[Steam 사용자] --> SA[Steam 인증]
  Stove[STOVE 사용자] --> STA[STOVE 인증]
  SA --> EOS[EOS Connect]
  STA --> EOS
  EOS --> PUID[EOS Product User ID / PUID]
  PUID --> Services[EOS 게임 서비스]

PUID는 온라인 시스템 동작을 위한 내부 식별자로 사용하며 일반 사용자에게 직접 노출할 필요는 없다.

4.2 EOS Lobby

EOS Lobby는 2 / 3 / 4인 랜덤 매칭과 참가자 목록 관리에 사용한다. 향후 최대 8인까지 확장할 수 있도록 필요한 인원수를 Lobby 속성으로 관리한다.

Lobby 검색용 정보는 최소한으로 유지한다.

EOS Lobby는 매칭과 참가자 관리 계층이며, 게임 상태의 Authority를 갖는 게임 서버 자체는 아니다. 게임 시작 후 Authority는 Unreal Listen Host가 갖는다.


5. 네트워크 구조

Dedicated Server는 운영하지 않는다.

방을 생성한 플레이어 또는 매칭 Lobby의 Owner가 Unreal Listen Server가 된다. 플레이어 한 명의 클라이언트가 Listen Server 역할을 겸한다.

flowchart TB
  Lobby["EOS Lobby"] --> Host["Lobby Owner<br/>Unreal Listen Server Host"]
  Host --> ClientA["Client"]
  Host --> ClientB["Client"]

장점:

호스트 조작 및 클라이언트 치팅에 대한 강력한 방어는 목표로 하지 않는다.

5.1 랜덤 매치메이킹

매칭 조건은 MMR, 지역, 아이템 수준 등의 조건 없이 필요 인원수 중심으로 구성한다.

현재 계획:

flowchart TD
  Start[매칭 시작] --> Select[2 / 3 / 4인 선택]
  Select --> Search[해당 인원용 대기 Lobby 검색]
  Search --> Exists{참가 가능한 Lobby가 있는가?}
  Exists -- Yes --> Join[Lobby 참가]
  Exists -- No --> Create[새 Lobby 생성]
  Create --> Owner[생성자 = Lobby Owner]
  Join --> Wait[필요 인원까지 대기]
  Owner --> Wait
  Wait --> Full{필요 인원 도달?}
  Full -- No --> Wait
  Full -- Yes --> Game[게임 시작]

5.2 Lobby Owner와 Listen Server

EOS Lobby를 생성한 사용자를 그대로 Unreal Listen Server Host로 사용한다. 별도의 Host 선출 과정은 두지 않는다.

flowchart TD
  A[A가 Lobby 생성] --> Owner[A = EOS Lobby Owner]
  Owner --> BC[B / C / D 참가]
  BC --> Ready[필요 인원 충족]
  Ready --> Listen[A가 Unreal Listen Server 시작]
  Listen --> Clients[B / C / D가 A에 접속]
  Listen --> Authority[A = Server Authority]

핵심 규칙은 다음과 같다.

EOS Lobby Owner = Unreal Listen Server Host

5.3 Host Migration 정책

Host Migration은 구현하지 않는다.

flowchart TD
  Host[A = Lobby Owner + Listen Host] --> Playing[게임 진행]
  Playing --> Leave{A가 이탈했는가?}
  Leave -- No --> Playing
  Leave -- Yes --> End[매치 종료 / 방 해산]

Host가 종료되거나 연결이 끊기면 새로운 Host를 선출하거나 게임 상태를 복원하지 않고 해당 매치를 종료한다.


6. Steam ↔ STOVE 크로스플레이

목표 구조:

flowchart LR
  Steam["Steam Player"] --> EOS["EOS Lobby / P2P"]
  Stove["STOVE Player"] --> EOS

플랫폼이 달라도 동일한 EOS Lobby/Session에 참가할 수 있도록 구성한다.

따라서 게임 플레이 계층에서는 상대방이 Steam 사용자인지 STOVE 사용자인지가 중요하지 않도록 설계한다.

플랫폼 정보가 UI 등에 필요한 경우에만 별도 메타데이터로 취급한다. Steam/STOVE 인증 방식은 플랫폼 모듈 안에 격리하고, EOS Lobby·P2P·Listen Server 및 게임 플레이 코드는 공통으로 유지한다.


7. 사용자 계정 정책

게임 자체에서 복잡한 사용자 계정 시스템은 만들지 않는다.

필요한 것은:

이메일, 비밀번호, 실명 등의 사용자 개인정보는 게임에서 관리하지 않는다.

EOS의 PUID와 같은 내부 식별자는 온라인 시스템 동작에만 사용하고 일반 사용자에게 노출할 필요가 없다.

MMR, 아이템, 성장 정보 등은 현재 영구 사용자 데이터에 포함하지 않는다.


8. 닉네임

최초 기본 닉네임은 실행 플랫폼의 닉네임을 사용한다.

flowchart TB
  SteamRun["Steam 실행"] --> SteamName["Steam 닉네임"]
  StoveRun["STOVE 실행"] --> StoveName["STOVE 닉네임"]

게임 내에서 사용자가 닉네임을 수정할 수 있다.

수정한 경우 이후에는 게임에서 지정한 닉네임을 우선 사용한다.

flowchart TB
  HasName{"저장된 게임 닉네임 있음?"}
  Saved["저장된 닉네임 사용"]
  Platform["플랫폼 닉네임 사용"]

  HasName -->|YES| Saved
  HasName -->|NO| Platform

9. 프로필 이미지

사용자가 임의 이미지를 업로드하는 기능은 제공하지 않는다.

게임에 여러 개의 프로필 이미지 프리셋을 포함하고 사용자가 그중 하나를 선택한다.

EOS에는 이미지 자체나 파일 경로가 아니라 문자열 ID만 저장한다.

예:

avatar_01
avatar_02
avatar_cat
avatar_robot

클라이언트가 ID를 실제 리소스에 매핑한다.

flowchart TB
  AvatarId["avatar_cat"] --> Registry["게임 내부 Avatar Registry"]
  Registry --> Asset["실제 프로필 이미지 Asset"]

따라서 이미지 파일의 이름, 포맷 또는 실제 경로가 변경되어도 기존 사용자 데이터에는 영향을 주지 않는다.


10. EOS에 저장할 영구 프로필

현재 요구사항에서는 사용자별 영구 데이터가 매우 작다.

{
  "nickname": "PlayerName",
  "avatarId": "avatar_cat"
}
항목형식설명
nicknameString게임에서 사용할 닉네임
avatarIdString프로필 이미지 프리셋 ID

EOS 내부 PUID는 별도로 EOS 사용자 식별에 사용한다.

아이템, 인벤토리, 장착 정보, 아이템 수량, 아이템 소유권, 계정 귀속 정보는 사용자 영구 프로필에 저장하지 않는다.


11. Unreal Replication과 EOS P2P

게임 상태의 Authority는 Listen Host가 가지며 Unreal Replication을 사용한다. 실제 네트워크 전송은 EOS P2P 계층을 사용한다.

flowchart TD
  Gameplay[Unreal Gameplay] --> Rep[Unreal Replication]
  Rep --> EOSNet[EOS P2P 전송 계층]
  EOSNet --> Network[Direct P2P 또는 EOS Relay]

일반적인 IP 주소/포트 기반 IpNetDriver 연결이 아니라 EOS P2P가 실제 Unreal 네트워크 트래픽을 전달하도록 구성해야 한다.


12. UDP Hole Punching

각 Client는 Listen Host와 개별적으로 P2P 연결을 구성한다.

예를 들어 4인 게임에서는 B↔A, C↔A, D↔A의 NAT Traversal 결과가 서로 독립적이다.

flowchart LR
  B[Client B] --> A[Listen Host A]
  C[Client C] --> A
  D[Client D] --> A

한 Client의 UDP Hole Punching 실패가 방 전체의 연결 방식을 변경하지 않는다.


13. EOS Relay 자동 폴백

Relay를 허용한 경우 EOS P2P는 가능한 경우 직접 연결을 사용하고, 직접 연결이 불가능한 peer 연결은 EOS Relay를 사용할 수 있다.

flowchart TD
  Client[Client] --> Try[Host와 Direct P2P / NAT Traversal 시도]
  Try --> Success{직접 연결 성공?}
  Success -- Yes --> Direct[Direct P2P]
  Success -- No --> Relay[EOS Relay]
  Direct --> Host[Unreal Listen Host]
  Relay --> Host

한 명의 Hole Punching 실패가 방 전체를 Relay로 변경하지 않는다. 따라서 하나의 방에서도 Direct와 Relay 연결이 동시에 존재할 수 있다.

flowchart LR
  B[Client B] -- Direct --> A[Listen Host A]
  C[Client C] --> R[EOS Relay]
  R --> A
  D[Client D] -- Direct --> A

EOS Relay는 게임 서버가 아니다. 패킷을 중계할 뿐이며 게임 Authority는 계속 Listen Host에게 있다.


14. Direct / Relay 상태 공유

Host는 자신과 연결된 각 Client의 연결 상태를 기준으로 게임 내부 정보를 구성할 수 있다.

예:

이 정보를 모든 방 멤버에게 보여주고 싶다면 Host가 Unreal의 replicated state에 연결 종류를 저장하여 전달한다.

flowchart TD
  EOS[EOS P2P 연결 상태] --> Host[Listen Host]
  Host --> State[PlayerState 등 Replicated State]
  State --> B[Client B]
  State --> C[Client C]
  State --> D[Client D]

게임 UI에 반드시 노출할 필요는 없으며, 디버그 정보로 Ping과 함께 표시하는 용도로도 사용할 수 있다.


15. 추후 선택적으로 사용할 EOS 기능

현재 필수는 아니지만 필요해질 경우 다음 기능을 별도로 추가할 수 있다.

Achievements

게임 업적을 추가할 경우 EOS Achievements 사용을 검토한다.

Stats

필요할 경우 총 플레이 시간, 게임 실행 횟수, 특정 행동 수행 횟수 등의 단순 누적 통계를 저장할 수 있다.

현재 게임 구조상 랭킹이나 경쟁 기록은 필수가 아니다.


16. 최종 목표 구조

flowchart TD
  Steam[Steam Client] --> SteamAuth[Steam 인증]
  Stove[STOVE Client] --> StoveAuth[STOVE 인증]

  SteamAuth --> Connect[EOS Connect]
  StoveAuth --> Connect

  Connect --> PUID[PUID]
  PUID --> Profile[닉네임 + 프로필 이미지 ID]
  PUID --> Lobby[EOS Lobby]

  Lobby --> Match[2 / 3 / 4인 랜덤 매칭]
  Match --> Owner[Lobby Owner]
  Owner --> Listen[Unreal Listen Server]

  Listen --> Rep[Unreal Replication]
  Rep --> P2P[EOS P2P]

  P2P --> Hole{Direct 연결 가능?}
  Hole -- Yes --> Direct[Direct P2P]
  Hole -- No --> Relay[EOS Relay]

  Direct --> Play[게임 진행]
  Relay --> Play

  Listen --> Exit{Owner / Host 이탈?}
  Exit -- Yes --> End[매치 종료]
  Exit -- No --> Play

핵심 원칙

스토어는 배포 및 플랫폼 진입점, EOS는 인증·매칭·공통 온라인 계층, Unreal Listen Server는 게임 Authority, EOS P2P는 전송 계층으로 구성한다.

Steam과 STOVE의 차이는 가능한 한 플랫폼 모듈 내부에 격리하고, Lobby/P2P/Listen Server 및 게임 플레이 코드는 공통으로 유지한다.

사용자 영구 데이터는 최소화하여 현재 기준으로 nickname + avatarId만 저장한다.

게임사가 직접 운영하는 Dedicated Server, MMR 서버, 자체 매치메이킹 서버, NAT Traversal 서버, Relay 서버를 두지 않는 것을 기본 방향으로 한다.