"매 KvK 는 multiple game shards (kingdoms) 가 same world map 에서 PvP 경쟁하는 cross-shard event". Rise of Kingdoms (Lilith), Lords Mobile (IGG), Evony 등 4X mobile MMO 의 핵심 monetization driver. 매 frontend 입장에서 cross-kingdom matching, real-time map sync, leaderboard 가 challenge.
매 핵심
매 매칭 단계
Pre-KvK: 매 power matchmaking — 매 비슷한 power 의 kingdoms 매칭.
Lost Kingdom (LK) / Pass 1: 매 kingdoms 가 새 map 에 spawn.
Main KvK (Pass 2-3): 매 holy site / pass / altar 점령.
Post-KvK: 매 reward distribution, kingdom power recompute.
매 frontend challenge
Map sync: 매 1000+ players concurrent → 매 viewport-based delta sync.
Leaderboard: 매 cross-shard aggregation — 매 5-min cache.
Chat: 매 kingdom-only / alliance-only / cross-kingdom 매 channel 분리.
Replay: 매 battle replay — 매 server-authoritative state log.
매 응용
Rise of Kingdoms — 매 8 kingdom matchup, 70-day season.
Lords Mobile Kingdom Vs Kingdom.
Evony Server War.
Top War: Battle Game — 매 SvS (Server vs Server).
💻 패턴
Cross-shard matchmaking
// 매 power-bracket 매칭
asyncfunctionmatchKvK(season: number){constkingdoms=awaitdb.kingdoms.where("season",season).where("optedIn",true).orderBy("totalPower","desc").get();// 매 8 kingdoms / bracket
constbrackets: Kingdom[][]=[];for(leti=0;i<kingdoms.length;i+=8){brackets.push(kingdoms.slice(i,i+8));}returnbrackets;}
Viewport-based map sync (frontend)
classKvKMap{privatewsUrl="wss://kvk.game/v1/map";subscribe(viewport: Bounds){this.ws.send({op:"subscribe",bbox: viewport,// 매 viewport bbox 만 sync
lod: viewport.zoom<5?"tile":"unit",});}onMessage({delta}:{delta: TileDelta[]}){delta.forEach((d)=>this.applyTile(d));}}
Leaderboard cache pattern
// 매 cross-shard aggregation 비쌈 → 매 Redis sorted set + 5min TTL
asyncfunctiongetKvKLeaderboard(seasonId: string){constcached=awaitredis.get(`kvk:lb:${seasonId}`);if(cached)returnJSON.parse(cached);constdata=awaitaggregateAcrossShards(seasonId);// 매 fan-out
awaitredis.setex(`kvk:lb:${seasonId}`,300,JSON.stringify(data));returndata;}
interfaceHolySiteState{ownerKingdom: number|null;captureProgress: number;// 0-1
contestedBy: number[];lastUpdate: number;// server timestamp
}// 매 client 는 server time 만 신뢰
functionrenderProgress(state: HolySiteState,serverTime: number){constelapsed=serverTime-state.lastUpdate;returnstate.captureProgress+(elapsed/CAPTURE_DURATION);}