분산로비시스템 예제 프로그램은 예제 온라인 게임 프로그램입니다..
분산로비시스템 예제 프로그램 은 분산 서버 구조의 온라인 게임의 일반적인 요구 기능을 갖추고 있습니다. 예를 들어 캐릭터 만들기나 게임 로비 등을 제공합니다.
분산로비시스템 예제 프로그램 은 소규모 또는 대규모 멀티플레이 온라인 게임을 만드는데 필요한 여러가지 힌트를 제공합니다.
특징
로그온,캐릭터 만들기,로비에서 채팅하기,방 만들기 또는 들어가기,방장의 게임 시작, 게임 종료 후 과정 등의 구현 예제입니다.
기본적으로 학습을 위한 용도로 만들어져 있습니다. 그리고 게임 데이터 로딩, 게임 로딩중 이탈하는 유저, 방장과 타 멤버들간 상호 작용 등 온라인 게임 개발에서 고려해야 하는 주요 사안들도 처리하도록 만들어져 있습니다.
ProudNet과 ProudDB2를 모두 활용한 분산 서버 구조의 예입니다. 여러 서버 컴퓨터로 분산 수행하는 로비 시스템과 서버간 load balancing하는 게임플레이 방(혹은 던전 인스턴스)을 유지하는 기능도 갖추고 있습니다.
캐쥬얼샘플 2는 간단한 총알 피하기 게임입니다. 비행기를 방향키로 조종하여 움직이고 아이템을 획득할 수 있습니다. 파란색 총알은 위로 지나가는 총알 빨간색 총알은 아래로 지나가는 총알입니다. 스페이스바를 눌러 비행기를 위 아래로 조종할수 있습니다.
분산로비시스템 예제 프로그램은
ProudNet: 서버와 네트워크
1. ProudNet Database Cache System version 2
를 모두 사용하고 있습니다.
분산로비시스템 예제 프로그램 의 실행 방법은 캐주얼 게임 샘플2 사용법 에 설명되어 있습니다.
분산로비시스템 예제 프로그램 의 서버 구성에 대해서는 캐주얼 게임 샘플2 프로그램의 서버 분산 를 참고하십시오.
분산로비시스템 예제 프로그램은 소스 코드의 양이 많은 편입니다. (주석 포함해서 약 8000라인) 그렇기 때문에 이해를 돕기 위해 샘플 소스 코드에 대한 API 리퍼런스를 참고하시는 것이 좋습니다.
• 본 샘플 프로그램의 API 리퍼런스는 Casual Game Sample 도움말을 참조 부탁드립니다.
• 물론, 분산로비시스템 예제 프로그램 이해에 필요한 것이 있으면 Part XIV. Nettention 기술 지원에 문의하셔도 됩니다.
분산로비시스템 예제 프로그램 에서의 캐주얼 게임 시스템이 개발하려는 게임에서의 시스템과 비슷하지 않을 경우를 대비해서, 분산로비시스템 예제 프로그램 는 참고 용도로만 사용하고 실제 캐주얼 게임 개발에서는
분산로비시스템 예제 프로그램 와 별개로 새로 제작하는 것이 괜찮은 선택입니다.
물론, 분산로비시스템 예제 프로그램 을 수정해서 캐주얼 게임 개발에 사용하는 것도 가능합니다. 하지만 이렇게 될 경우 ProudNet의 지속적 업데이트를 받을 때마다 업데이트 내역을 매번 찾아야 하는 번거로운 일이 발생할 것입니다. 따라서 Source Control의 Branch 기능을 활용하는 것을 권장합니다.
분산로비시스템 예제 프로그램 에서 보여주는 기능 구현은 다음과 같습니다.
8.1캐주얼 게임 샘플2 사용법
먼저, 샘플 데이터베이스 구축하기를 하십시오.
그 다음, 서버 프로그램을 실행해야 합니다.
먼저, 사용하고 있는 SQL Server의 버전을 확인하십시오. 사용하는 버전에 따라 아래와 같이 DBMS 연결 문자열을 수정해야 합니다. DBMS 연결 문자열은 서버 프로그램 GCServer의 CFarmSettings::CFarmSettings 메서드에 있습니다.
m_dbmsConnectionString = L"Data Source=localhost;Database=ProudDB2-Test;Trusted_Connection=yes";
참고로 Mysql을 사용하시는 분들은 Sample에 SimpleAdoMysql예제를 참조하시면 됩니다.
DBMS의 버젼에 따라 Sample-schema 의 해당 버젼별로 있는 폴더에서 .sql을 실행하여 테이블 및 프로시져를 DBMS에 만드십시오.
서버 프로그램은 GCServer를 빌드하면 됩니다. 이때 생성된 실행 파일을 실행하면 처음 실행되는 프로세스는 팜 서버의 역할을 합니다. 그리고 팜 서버는 실행되자마자 1개의 Entry 서버와 여러개의 로비 서버, 배틀 서버를 연달아 실행합니다. 이는 본 샘플의 게임 서버가 분산 서버 구조를 갖추고 있음을 보여줍니다.
만약 서버 프로그램을 쉽게 디버깅하기 위해 1개 프로세스에서 각 역할의 서버를 모두 담당하게 하고자 한다면 실행 파라메터로 "-debug"를 입력하면 됩니다.
이제, 클라이언트 프로그램을 실행하도록 합시다.
클라이언트 프로그램은 GCClient를 빌드하면 만들어집니다.
클라이언트를 처음 실행하면 회원가입 화면이 나타납니다. 시작하기에 앞서 서버로 연결하기 위해 [connect] 버튼을 누릅니다. 서버에 접속이 되면 아이디와 비밀번호를 입력 가능한 칸이 활성화 됩니다. 처음 가입하는 사용자는 회원가입을 하고 로그인페이지로 넘어가서 로그인을 하고 기존 아이디가 있는 사용자는 바로 로그인페이지로 가시면 됩니다. 본 샘플에서는 게이머 인증을 위한 stored procedure를 실행하여 실제 아이디와 비밀번호를 검사합니다.
실행 후에는 새 플레이어 캐릭터를 만들거나 구 플레이어 캐릭터를 제거할 수 있으며, 캐릭터를 선택한 후 [확인]을 누르면 로비 채널을 선택하는 화면이 나옵니다.
로비 채널을 선택한 후에는 로비 화면이 나타납니다. 로비 화면에서는 전체 채팅과 방 만들기, 방 들어가기 혹은 전 화면으로 돌아가기를 할 수 있습니다.
방을 만든 후에는 게임방 대기실에 들어가게 되며, 게임방 대기실에서는 다른 게이머의 진입을 기다리거나 게임 시작을 결정할 수 있습니다. 게임 시작을 위해서는 게임방에 들어온 다른 게이머(방돌이) 들이 전원 [준비!] 버튼을 눌러서 게임 시작에 동의함을 체크해야 합니다.
본 샘플 프로그램의 조작법은 간단합니다. 방향키와 스페이스키로 자신의 캐릭을 조절할수 있으며 화면에 움직이는 총알들을 피하면서 Item을 먹는 단순 게임입니다. 파란색bullet은 아래서 빨간색 bulletd은 위로 지나 다닙니다. 스페이스 키를 이용해 적절히 피해주시면 됩니다. 캐릭의 점수가 0이되면 로비로 추방되며 점수는 10점으로 다시 초기화됩니다.
각 플레이어 캐릭터가 가진 점수는 실제 게이머 데이터베이스에 저장이 됩니다. 그리고 그 점수는 게임 화면에 나타나는 플레이어 캐릭터 이름 옆의 [ ] 칸 안에 표시됩니다.
8.2캐주얼 게임 샘플2 프로그램의 서버 분산
분산로비시스템 예제 프로그램 의 서버는 실제 상용화하는 캐주얼 게임의 scalability를 고려해서 신중하게 설계되어 있습니다. 그 결과 다음과 같이 분산되어 있습니다.
그림 8-1캐주얼 게임 샘플2의 서버 구성도
팜 서버(Farm Server): 게이머가 실제로 접속할 수 없는 서버입니다.
팜 서버는 각 서버들의 중앙 관리 서버 역할을 합니다. 팜 서버가 다운되면 모든 게임 서버가 제 작동을 할 수 없으므로 매우 간단한 로직만을 가지는 것이 권장됩니다. 그리고 각 팜 서버에 접속되는 나머지 모든 서버들은 팜 클라이언트(Farm Client)가 됩니다.
팜 서버는 ProudDB Cache Server를 갖고 있습니다. 그리고 팜 클라이언트는 ProudDB Cache Client를 갖고 있습니다. (DB cache 클라이언트와 서버 참고)
팜 서버와 Entry 서버는 1대 1 관계입니다.
팜 서버와 로비 서버는 1대 다 관계입니다. 즉 게이머는 여러개의 로비 채널 중 하나를 선택할 수 있음을 의미합니다.
Entry 서버: Entry 서버는 게이머가 최초로 접속하는 서버입니다.
게이머가 ID와 비밀번호 입력을 받아 게이머 인증 과정을 수행합니다.
또한 게이머가 갖고 있는 플레이어 캐릭터 리스트를 Entry 클라이언트에 전송하는 역할을 합니다.
그리고 Entry 서버는 로비 서버와 로비 서버에 종속된 각 배틀 서버의 현황 정보를 받아 Entry 클라이언트에 전송하는 역할을 합니다.
로비 서버: 플레이어 캐릭터 선택과 로비 채널 선택이 완료된 Entry 클라이언트는 서버와의 연결을 끊고 나서, 로비 클라이언트를 준비합니다. 그리고 나서 로비 서버로 접속합니다.
로비 서버에서는 로비 채널 전체 채팅, 게임방 만들기, 게임방 들어가기를 수행합니다.
게임방 만들기나 게임방 들어가기 요청을 받은 로비 서버는 동시접속자가 가장 적은 배틀 서버를 찾아서 그 배틀 서버에게 게임방 만들기를 요청합니다. 그리고 게임방이 만들어지면 그 게임방의 정보를 로비 클라이언트에게 보냅니다. 로비 클라이언트는 그 정보에 기반해서 로비 서버와의 연결을 끊은 후 배틀 클라이언트를 준비한 다음에 배틀 서버에 접속합니다.
로비 서버와 배틀 서버는 1대 다 관계입니다. 즉 1개 로비 채널을 지원하는 배틀 서버는 여러개가 될 수 있습니다. 그렇기 때문에 로비 채널 한개가 감당하는 게이머 수는 확장성(scalability)를 가질 수 있습니다.
배틀 서버: 게임방 대기실과 실제 게임방의 게임 플레이를 관장하는 서버입니다. 배틀 서버는 0 개 이상의 게임방 인스턴스를 보유합니다.
본 샘플에서는 캐주얼 게임의 게임방 대기실을 로비 서버가 아닌 배틀 서버에서 관장합니다. 일반적으로 로비 서버는 고정된 한계의 게이머들이 들어올 수 있는 협소한 공간입니다. 따라서 서버의 scalability를 극대화하는 것이 중요합니다. 그래서 본 샘플에서는 게이머 대기실의 처리 부하까지 동적 확장이 용이한 배틀 서버에서 관리하는 형태로 만들어져 있습니다.
8.3게임 서버간의 서버-클라이언트 관계
분산로비시스템 예제 프로그램 에서는 각 게임 서버들끼리 기능별로 분산되어 서버간 통신을 수행합니다. 자세한 것은 캐주얼 게임 샘플2 프로그램의 서버 분산을 참고하십시오.
팜 클라이언트는 시작하면 팜 서버에 접속을 시도합니다.
•CFarmClient.Connect
•CFarmServer.OnClientJoin
•CFarmClient.OnJoinServerComplete
팜 서버로의 접속이 성공하면 팜 클라이언트는 팜 서버로의 인증 절차를 시작합니다.
•CFarmServer.RequestFarmLogon
•CFarmClient.NotifyFarmLogonSuccess
팜 서버와의 인증 절차가 성공적으로 끝나면 팜 클라이언트는 팜 서버와의 연결이 종료할 때까지 기다립니다.
•CFarmClient.OnLeaveServer
•CFarmServer.OnClientLeave
팜 클라이언트끼리는 서로가 P2P 통신이 엮여 있습니다. 따라서 팜 클라이언트끼리의 통신, 예를 들어 로비 서버와 배틀 서버간의 통신은 지속적으로 일어날 수 있습니다.
8.4Entry 서버와 클라이언트
분산로비시스템 예제 프로그램 의 게임 클라이언트가 처음 실행되면 entry client를 생성합니다. 그리고 entry client는 entry server에 접속합니다.
•CEntryClient::Connect
•CEntryClient::OnJoinServerComplete
접속 과정이 완료되면 entry client는 entry server에게 회원가입을 요청하거나 기존에 가입한 게이머ID와Password를 암호화된 메시지로 전송하여 로그인을 할수 있습니다. entry server는 받은 정보를 기반으로 ProudDB cache server에 보내서 정보로딩 및 게이머 인증을 요청합니다.
회원가입후 로그인하는 경우
•CEntryServer::RequestLoadNewGamer
•CEntryClient::NotifyLoadNewGamerSuccess
•CEntryServer::RequestFirstLogon
•CEntryClient::NotifyFirstLogonSuccess
기존에 가입한 아이디로 로그인하는 경우
•CEntryServer::RequestFirstLogon
•CEntryClient::NotifyFirstLogonSuccess
게이머 인증 및 정보 로딩이 성공하면 게임 클라이언트는 캐릭터 선택 폼으로 전환합니다. 그리고 entry server에게 로컬 플레이어 캐릭터 리스트를 요청 후 받습니다.
•CEntryServer::RequestHeroSlots
•CEntryClient::Hero_Begin
•CEntryClient::Hero_Add
•CEntryClient::Hero_End
게이머는 여기에서 로컬 플레이어 캐릭터를 생성하거나, 제거할 수 있습니다. 그리고 사용할
플레이어 캐릭터를 선택합니다. 그러면 entry server는 선택한 캐릭터 정보를 받아서 게이머 데이터베이스에 저장합니다.
•CEntryServer::RequestAddHero
•CEntryClient::NotifyAddHeroSuccess
•CEntryServer::RequestRemoveHero
•CEntryServer::NotifyRemoveHeroSuccess
•CEntryServer::RequestSelectHero
•CEntryServer::NotifySelectHeroSuccess
그리고 entry client는 서버에 요청해서 로비 채널 목록을 받습니다. entry server부터 받은 로비 채널 목록에는 각 로비 서버의 이름, 동시접속자수 및 로비 서버의 인터넷 주소를 받습니다. 게임 클라이언트는 게이머에게 목록을 보여주고, 게이머는 목록 중 하나를 선택할 수 있습니다.
•CEntryServer::RequestLobbyList
•CEntryClient::LobbyList_Begin
•CEntryClient::LobbyList_Add
•CEntryClient::LobbyList_End
이 과정에서 팜클라이언트(여기서는 로비서버)가 팜서버로 독점권을 요청하게 됩니다. entry client는 서버와의 연결을 끊고 제거됩니다. 게임 클라이언트는 로비 클라이언트를 준비한 후 로비서버로의 연결을 시작합니다. 로비 서버와 클라이언트 을 참고하십시오. 타 클라이언트에서 독점권 요청시 발생할수 있는 문제점 하나는 동일한 ID와 Password로 접속시 다른 entry client의 DBCache Client가 독점권을 요청하게 될때입니다. 이때는 이후 과정은 OnDataUnloadRequested 콜백시 넘어오는 파라미터내의 customfield로 구분하여 동시접속을 방지하게 됩니다. casual game sample2에서는 이러한 상황시 기존접속되어있던 팜클라이언트의 연결을 끊고 새로 독점권을 요청한 팜클라이언트에게 독점권을 넘겨주도록 구현 되어 있습니다.
8.5로비 서버와 클라이언트
Entry 서버와 클라이언트 에 의해 로비 클라이언트가 로비 서버와의 연결을 시작한 후 연결이 성공하면 게임 클라이언트는 로비 폼을 보여줍니다.
•CLobbyClient.Connect
•CLobbyServer.OnClientJoin
•CLobbyClient.OnJoinServerComplete
•CLobbyServer.RequestNextLogon
•CLobbyClient.NotifyNextLogonSuccess
로비폼을 위한 데이터 로딩이 완료된 후 로비 폼을 화면이 성공적으로 전환되면, 로비 내의 모든 플레이어 리스트와, 전체 채팅 창, 그리고 게임방 리스트가 나타납니다.
•CLobbyServer.NotifyChannelFormReady
로비 내의 모든 플레이어 리스트는 수시로 증가,수정,제거될 수 있습니다. 이러한 변경 정보는 지속적으로 로비 서버로부터 받게 됩니다.
•CLobbyClient.HeroSlot_Appear
•CLobbyClient.HeroSlot_Disappear
로비 내의 게임방 리스트는 수시로 증가, 수정, 제거될 수 있습니다. 이러한 변경 정보는 지속적으로 로비 서버로부터 받게 됩니다.
•CLobbyClient.GameRoom_Appear
•CLobbyClient.GameRoom_ShowState
•CLobbyClient.GameRoom_Disappear
실제 게임방은 배틀 서버에 존재합니다. 로비 서버는 배틀 서버로부터 동기화받은 게임방 리스트를 로비 클라이언트들에게 보내줄 뿐입니다.
•CFarmClient.GameRoom_Appear
•CLobbyServer.OnGameRoomAppear
•CFarmClient.GameRoom_ShowState
•CLobbyServer.OnGameRoomShowState
•CFarmClient.GameRoom_Disappear
•CLobbyServer.OnGameRoomDisappear
로비 클라이언트에서 채팅 메시지를 로비 서버에 보내면 로비 서버는 접속되어 있는 로비 클라이언트들에게 채팅 메시지를 멀티캐스트 합니다. 그리고 각 로비 클라이언트가 받은 채팅 메시지는 전체 채팅 창에 표시됩니다.
•CLobbyServer.Chat
•CLobbyClient.ShowChat
게임방 만들기
그림 8-2게임방 만들기 Sequence Diagram
게이머가 게임방 만들기를 요청하면 로비 서버에 요청이 전달됩니다. 그러면 로비 서버는 로비 서버에 종속되어 있는 각 배틀 서버의 리스트 중 가장 접속자가 적은 배틀 서버를 찾은 후 그 배틀 서버에게 게임방 생성을 요청합니다.
•CLobbyServer.RequestCreateGameRoom
•CFarmClient.RequestCreateGameRoom
•CBattleServer.CreateGameRoomByLobbyServer
배틀 서버는 게임방 생성 요청을 받아 처리한 후 결과를 로비 서버에게 보내줍니다. 로비서버는 이 정보를 받아 로비 클라이언트에게 보내줍니다.
•CFarmClient.NotifyCreateGameRoomResult
•CLobbyServer.OnCreateGameRoomResult
•CLobbyClient.NotifyCreateRoomSuccess
게임방 들어가기
그림 8-3게임방 들어가기 Sequence Diagram
이미 개설된 게임방 중 하나를 선택해서 들어가기를 요청하는 경우 로비 클라이언트는 요청 메시지를 로비 서버에 전송합니다. 로비 서버는 이를 받아 그 게임방을 관장하는 배틀 서버에게 진입 허가 요청을 보냅니다.
•CLobbyServer.RequestJoinGameRoom
•CFarmClient.RequestJoinGameRoom
배틀 서버는 진입 허가 요청을 받아 게임방의 진입 조건의 허락을 판단한 후 로비 서버에게 그 결과를 보냅니다. 로비 서버는 결과를 받은 후 로비 클라이언트에게 성공 또는 실패 여부를 전달합니다.
•CBattleServer.JoinGameRoomByLobbyServer
•CFarmClient.NotifyJoinGameRoomResult
•CLobbyClient.NotifyJoinRoomSuccess
로비 클라이언트가 게임방 생성 결과 혹은 게임방 진입 결과를 받되, 그것이 성공했음을 의미할 경우 게임방이 존재하는 배틀 서버의 주소 및 게임방의 guid를 받게 됩니다. 이 때 로비 클라이언트는 종료하며 배틀 클라이언트가 만들어진 후 배틀 서버로 접속하기 시작합니다. 이 과정은 battle_session2에 설명되어 있습니다. 이 과정에서도 타 팜클라이언트의 DBcache client의 독점권 요청시 문제가 될수 있는 상황이 있습니다. 동일한 ID와 password로 로그인할 때 새로운 entry client의 DBcache client가 독점권 요청이 발생하게 됩니다. 이때도 entry server에서 lobby server로 이동할 때 발생할 때 독점권 요청 루틴 처리 과정처럼 전의 연결을 끊고 새로운 연결을 시도한 팜 클라이언트에게 독점권을 넘겨줌으로써 중복접속을 방지합니다.
8.6배틀 서버와 클라이언트
로비 서버와 클라이언트 과정이 끝나고 배틀 서버로 접속을 시작한 배틀 클라이언트는 배틀 서버와의 연결이 성공할 때까지 기다립니다.
•CBattleClient.Connect
•CBattleServer.OnClientJoin
•CBattleClient.OnJoinServerComplete
연결이 성공하면 배틀 서버로의 로그온 절차를 진행합니다. 이때 진입할 게임방 정보를 같이 입력합니다. 즉, 로그온 과정에서 이미 생성된 게임방으로의 진입 절차도 같이 시행됩니다.
•CBattleServer.RequestNextLogon
•CBattleClient.NotifyNextLogonSuccess
배틀 서버로의 로그온(그리고 게임방 진입)이 성공하면 게임 클라이언트는 게임방 대기실 폼을 표시합니다. 게임방 대기실 폼을 위해 게임방 내의 게이머 리스트와 게임방 정보를 받습니다.
•CBattleClient.NotifyGameRoomInfo
•CBattleClient.HeroSlot_Appear
•CBattleClient.HeroSlot_Disappear
•CBattleClient.HeroSlot_ShowState
만약 로컬 플레이어가 방장이라면 게임 시작을 요청할 수 있습니다.
•CBattleServer.RequestStartPlayMode
방장이 아닌 경우(즉 방돌이)에는 게임 플레이를 시작할 수 있음에 동의하는 표시, 즉 'READY를 찍기'를 할 수 있습니다.
•CBattleServer.RequestToggleBattleReady
방돌이들이 전원 READY를 찍은 상태에서 방장이 게임 시작을 요청하면 게임이 본격적으로 시작될 수 있습니다. 하지만 게임 시작을 위해서는 각 게임 클라이언트가 플레이를 위한 데이터 파일 로딩이 모두 끝나야지만 시작할 수 있으며, 그때까지는 각 게이머들은 게임 플레이 시작을 대기하고 있어야 합니다.
•CBattleClient.GotoLoadingMode
•CBattleServer.NotifyLoadFinished
•CBattleClient.GotoPlayMode
게임 플레이를 하는 동안 플레이어 캐릭터의 점수 등이 변경되며, 이들은 DB 서버에 저장될 필요가 있을 것입니다. 본 샘플에서는 게임 플레이를 하는 동안 게이머가 자기의 점수를 쉽게 획득할 수 있게 만들어져 있습니다. 획득한 점수는 DB에 저장됩니다.
•CBattleServer.RequestGainScore
실제 게임 플레이를 하다가 중도 이탈하는 게이머는 접속을 끊고 로비 서버로 접속하는 과정으로 복귀하면 됩니다. 한편, 게임 플레이가 끝나면(예: 미션 클리어) 게임방은 대기실 모드로 전환하고, 각 게이머들은 게임방 대기실 화면으로 전환해야 합니다. 본 샘플 프로그램에서는 학습의 이해를 위해 게이머가 임의로 무조건 미션 클리어를 수행할 수 있도록 만들어져 있습니다. 여기서도 마찬가지로 다른 entry client의 DBcache client가 독점권 요청시 기존의 연결을 끊고 독점권을 넘겨주게 됩니다.
•CBattleServer.RequestMissionClear
•CBattleServer.GameRoom_GotoWaitingMode
•CBattleClient.GotoWaitingMode