Steam · STOVE · EOS 멀티플레이 및 계정 구조
Steam · STOVE · EOS 멀티플레이 및 계정 구조
1. 목표
- 배포 플랫폼: Steam / STOVE
- 가능한 한 동일한 게임 바이너리 및 코드베이스 사용
- 멀티플레이 공통 계층으로 Epic Online Services(EOS) 사용
- Steam / STOVE의 기존 사용자 인증 사용
- 사용자에게 Epic Games 계정 로그인을 요구하지 않음
- EOS Connect를 통해 공통 PUID 사용
- EOS Lobby로 2 / 3 / 4인 랜덤 매칭
- 향후 최대 8인까지 확장 가능
- Dedicated Server 운영하지 않음
- 자체 매치메이킹 서버, MMR 서버, Relay 서버를 운영하지 않음
- Lobby Owner가 Unreal Listen Server 역할
- Host Migration 없음
- Host가 나가면 매치 종료
- Unreal Replication 사용
- 실제 전송 계층은 EOS P2P 사용
- UDP Hole Punching 실패 시 해당 연결만 EOS Relay로 자동 폴백
- 경쟁/승패 중심 게임이 아니므로 강한 Anti-Cheat 및 서버 권위 검증은 우선순위에서 제외
- 자체 계정 서버 및 자체 사용자 DB를 운영하지 않는 방향
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는 스토어와 독립적인 공통 온라인 서비스 계층으로 사용한다.
주요 사용 대상:
- EOS Connect
- Lobby
- Session
- P2P 네트워킹
- 플레이어 식별(PUID)
- 최소한의 영구 프로필 데이터
- 필요할 경우 Achievements / Stats
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 검색용 정보는 최소한으로 유지한다.
RequiredPlayers: 2 / 3 / 4GameVersionState:Waiting/Playing
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"]
장점:
- Dedicated Server 비용 없음
- 별도 서버 인프라 최소화
- 운영 복잡도 감소
- Steam/STOVE 공통 멀티플레이 구현 가능
- 소규모 협동/비경쟁 게임에 적합
호스트 조작 및 클라이언트 치팅에 대한 강력한 방어는 목표로 하지 않는다.
5.1 랜덤 매치메이킹
매칭 조건은 MMR, 지역, 아이템 수준 등의 조건 없이 필요 인원수 중심으로 구성한다.
현재 계획:
- 2인
- 3인
- 4인
- 향후 최대 8인 가능
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 내부 사용자 식별자
- 닉네임
- 프로필 이미지 ID
이메일, 비밀번호, 실명 등의 사용자 개인정보는 게임에서 관리하지 않는다.
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"
}
| 항목 | 형식 | 설명 |
|---|---|---|
nickname | String | 게임에서 사용할 닉네임 |
avatarId | String | 프로필 이미지 프리셋 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의 연결 상태를 기준으로 게임 내부 정보를 구성할 수 있다.
예:
- B: Direct
- C: Relay
- D: Direct
이 정보를 모든 방 멤버에게 보여주고 싶다면 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 서버를 두지 않는 것을 기본 방향으로 한다.