Architektura serwera gier online
Maciej Mróz
› Gry online
• Krótki czas od koncepcji do wyjścia na rynek (3-6 miesięcy)
• Rozwój w znacznej części po release
• Szybkie iteracje (czasem kilka update’ów dziennie)
• Ciągła analiza zachowania użytkowników
• A/B testing
• Wiele platform klienckich
• Desktopy, tablety, telefony
• Małe (5-10 osób), niezależne zespoły produktowe
• Wiele gier rozwijanych i utrzymywanych równolegle
• Złożona warstwa serwerowa
› Serwery – całkiem inne problemy
• System rozproszony
• Wiele równorzędnych instancji serwerów
• Asynchroniczna komunikacja
• Wielowątkowe „od zawsze”
• Programowanie współbieżne jest trudne (a mutexy to zło!)
• Nieprzewidywalne obciążenie
• Skalowalnośd trzeba brad pod uwagę od samego początku
• Znacznie mniejsza tolerancja na błędy w kodzie
• Jeśli coś się może zepsud, to w koocu się zepsuje
• Szansa 1 na milion w serwerze oznacza zdarzenie pewne 
• Infrastruktura jest zawodna
› Nasze rozwiązanie
• Platforma serwerowa rozwijana przez dedykowany zespół jako wewnętrzny
produkt
• Rozwiązuje problemy wymagające specjalistycznej wiedzy lub powtarzalne
• Load balancing, failover, komunikacja z bazami danych ...
• Usługi specjalne (leaderboardy, listy przyjaciół, autentykacja, systemy
płatności ...)
• Logika gry odseparowana od reszty serwera
• Rozwijana przez zespół tworzący grę jednocześnie z rozwojem klienta
• Aby zrobid działającą grę, nie trzeba znad detali serwera
• W dużym stopniu ustandaryzowane środowisko
› Elementy architektury
› Skalowalnośd
• Stawianie nowej instancji serwera
• Autokonfiguracja (bottom → up)
• Rejestracja w usłudze nodebalancer
• Gaszenie instancji
• Transfer stanów gry na inne aktywne instancje
• Z reguły niewidoczne dla użytkowników
• W 100% zautomatyzowane, także dla instancji web
• DynamoDB jako baza klucz -> wartośd
• Serwisy backendowe nie skalują się automatycznie
› Logika gry
• Uproszczony actor model (http://en.wikipedia.org/wiki/Actor_model)
• Serwer nie jest bezstanowy!
• Logika gry przetwarza i emituje wiadomości
• Komunikacja wyłącznie asynchroniczna
• Gwarantowana kolejnośd przesyłania komunikatów od A do B
• Brak bezpośredniej komunikacji z bazą danych
• Binarne bloby na poziomie bazy danych stanów gry
• Kompresja/dekompresja w locie
• Brak sztywnego protokołu sieciowego, można zakodowad co się chce jako tekst
• Format jest teoretycznie dowolny, ale używamy JSON
• Serwer nie rozumie logiki
• „czarna skrzynka” ładowania w czasie działania serwera przez dlopen()
• Może byd przeładowana w locie!
› API logiki (przykłady)
Interakcje z serwerem:
virtual bool on_init_global_config(DWORD64 timestamp, const string & global_config) = 0;
virtual void on_cleanup() = 0;
virtual bool on_put_gamestate(DWORD64 timestamp, INT32 gamestate_ID, const string & gamestate) = 0;
virtual void on_get_gamestate(INT32 gamestate_ID, string & gamestate, bool server_closing,
bool logic_reload) = 0;
virtual void on_erase_gamestate(INT32 gamestate_ID) = 0;
Przetwarzanie komunikatów:
virtual void handle_player_message(DWORD64 timestamp, INT32 user_id, INT32 gamestate_ID,
const std::string &message, const std::string &params) {};
virtual void handle_friend_message(DWORD64 timestamp, INT32 user_id, INT32 gamestate_ID,
const std::string &message, const std::string &params) {};
virtual void handle_system_message(DWORD64 timestamp, INT32 gamestate_ID,
const std::string &message, const std::string &params, std::string & output) {};
virtual void handle_gamestate_message(DWORD64 timestamp, INT32 gamestate_ID,
const std::string &message, const std::string &params) {};
› API serwera (przykłady)
Funkcjonalności udostępniane dla logiki:
virtual void SendMessage(int user_id,int gamestate_ID, const std::string &message,
const std::string &params) = 0;
virtual void SendBroadcastMessage(int exclude_ID, int gamestate_ID, const std::string &message,
const std::string &params) = 0;
virtual void RequestLeaderboard(int user_id, const string & leaderboard_key,
DWORD64 leaderboard_subkey) = 0;
virtual void UpdateLeaderboard(int user_id, const string & leaderboard_key,
DWORD64 leaderboard_subkey, vector<INT32> & scores) = 0;
virtual void SendMessageToGamestate(int gamestate_ID, const std::string & command,
const std::string & params) = 0;
virtual void SubmitAnalyticsEvent(const char * Game_ID, DWORD64 Player_ID,
const char * event_name_with_category,
const std::map<string, string> & options) = 0;
› Logika gry (przykłady)
Lua
C++
› Global Conflig
› Przykładowy stan gry
› Model developmentu
• Teamy są od siebie odizolowane, zarówno lokalnie jak i na produkcji
• Zależności zewnętrzne dostępne poprzez API
• Jeden „tech team” rozwijający platformę, działający jako grupa wsparcia dla
poszczególnych projektów
› Środowisko
• Lokalnie każdy zespół ma swój komplet wirtualnych serwerów
• Co najmniej instancja www, serwer baz danych i serwer gier.
• Ręcznie zarządzana wirtualizacja na kilku hostach KVM
• Planujemy wdrożenie OpenStacka
• Wystawianie nowej wersji na środowisko lokalne jest automatyczne
• Serwer Continuous Integration śledzi branch „dev”
• Nowe wersje budowane wielokrotnie w ciągu dnia
• Zepsute buildy mają priorytet 
• Wystawianie na produkcję jest w 100% pod kontrolą zespołu
• Serwer CI przygotowuje nową wersję na podstawie brancha
produkcyjnego
• Aktywacja zmian na produkcji poprzez dedykowany panel administracyjny
• Rollback pod jednym kliknięciem
› Panel administracyjny

Architektura serwera gier online

  • 1.
    Architektura serwera gieronline Maciej Mróz
  • 2.
    › Gry online •Krótki czas od koncepcji do wyjścia na rynek (3-6 miesięcy) • Rozwój w znacznej części po release • Szybkie iteracje (czasem kilka update’ów dziennie) • Ciągła analiza zachowania użytkowników • A/B testing • Wiele platform klienckich • Desktopy, tablety, telefony • Małe (5-10 osób), niezależne zespoły produktowe • Wiele gier rozwijanych i utrzymywanych równolegle • Złożona warstwa serwerowa
  • 3.
    › Serwery –całkiem inne problemy • System rozproszony • Wiele równorzędnych instancji serwerów • Asynchroniczna komunikacja • Wielowątkowe „od zawsze” • Programowanie współbieżne jest trudne (a mutexy to zło!) • Nieprzewidywalne obciążenie • Skalowalnośd trzeba brad pod uwagę od samego początku • Znacznie mniejsza tolerancja na błędy w kodzie • Jeśli coś się może zepsud, to w koocu się zepsuje • Szansa 1 na milion w serwerze oznacza zdarzenie pewne  • Infrastruktura jest zawodna
  • 4.
    › Nasze rozwiązanie •Platforma serwerowa rozwijana przez dedykowany zespół jako wewnętrzny produkt • Rozwiązuje problemy wymagające specjalistycznej wiedzy lub powtarzalne • Load balancing, failover, komunikacja z bazami danych ... • Usługi specjalne (leaderboardy, listy przyjaciół, autentykacja, systemy płatności ...) • Logika gry odseparowana od reszty serwera • Rozwijana przez zespół tworzący grę jednocześnie z rozwojem klienta • Aby zrobid działającą grę, nie trzeba znad detali serwera • W dużym stopniu ustandaryzowane środowisko
  • 5.
  • 6.
    › Skalowalnośd • Stawianienowej instancji serwera • Autokonfiguracja (bottom → up) • Rejestracja w usłudze nodebalancer • Gaszenie instancji • Transfer stanów gry na inne aktywne instancje • Z reguły niewidoczne dla użytkowników • W 100% zautomatyzowane, także dla instancji web • DynamoDB jako baza klucz -> wartośd • Serwisy backendowe nie skalują się automatycznie
  • 7.
    › Logika gry •Uproszczony actor model (http://en.wikipedia.org/wiki/Actor_model) • Serwer nie jest bezstanowy! • Logika gry przetwarza i emituje wiadomości • Komunikacja wyłącznie asynchroniczna • Gwarantowana kolejnośd przesyłania komunikatów od A do B • Brak bezpośredniej komunikacji z bazą danych • Binarne bloby na poziomie bazy danych stanów gry • Kompresja/dekompresja w locie • Brak sztywnego protokołu sieciowego, można zakodowad co się chce jako tekst • Format jest teoretycznie dowolny, ale używamy JSON • Serwer nie rozumie logiki • „czarna skrzynka” ładowania w czasie działania serwera przez dlopen() • Może byd przeładowana w locie!
  • 8.
    › API logiki(przykłady) Interakcje z serwerem: virtual bool on_init_global_config(DWORD64 timestamp, const string & global_config) = 0; virtual void on_cleanup() = 0; virtual bool on_put_gamestate(DWORD64 timestamp, INT32 gamestate_ID, const string & gamestate) = 0; virtual void on_get_gamestate(INT32 gamestate_ID, string & gamestate, bool server_closing, bool logic_reload) = 0; virtual void on_erase_gamestate(INT32 gamestate_ID) = 0; Przetwarzanie komunikatów: virtual void handle_player_message(DWORD64 timestamp, INT32 user_id, INT32 gamestate_ID, const std::string &message, const std::string &params) {}; virtual void handle_friend_message(DWORD64 timestamp, INT32 user_id, INT32 gamestate_ID, const std::string &message, const std::string &params) {}; virtual void handle_system_message(DWORD64 timestamp, INT32 gamestate_ID, const std::string &message, const std::string &params, std::string & output) {}; virtual void handle_gamestate_message(DWORD64 timestamp, INT32 gamestate_ID, const std::string &message, const std::string &params) {};
  • 9.
    › API serwera(przykłady) Funkcjonalności udostępniane dla logiki: virtual void SendMessage(int user_id,int gamestate_ID, const std::string &message, const std::string &params) = 0; virtual void SendBroadcastMessage(int exclude_ID, int gamestate_ID, const std::string &message, const std::string &params) = 0; virtual void RequestLeaderboard(int user_id, const string & leaderboard_key, DWORD64 leaderboard_subkey) = 0; virtual void UpdateLeaderboard(int user_id, const string & leaderboard_key, DWORD64 leaderboard_subkey, vector<INT32> & scores) = 0; virtual void SendMessageToGamestate(int gamestate_ID, const std::string & command, const std::string & params) = 0; virtual void SubmitAnalyticsEvent(const char * Game_ID, DWORD64 Player_ID, const char * event_name_with_category, const std::map<string, string> & options) = 0;
  • 10.
    › Logika gry(przykłady) Lua C++
  • 11.
  • 12.
  • 13.
    › Model developmentu •Teamy są od siebie odizolowane, zarówno lokalnie jak i na produkcji • Zależności zewnętrzne dostępne poprzez API • Jeden „tech team” rozwijający platformę, działający jako grupa wsparcia dla poszczególnych projektów
  • 14.
    › Środowisko • Lokalniekażdy zespół ma swój komplet wirtualnych serwerów • Co najmniej instancja www, serwer baz danych i serwer gier. • Ręcznie zarządzana wirtualizacja na kilku hostach KVM • Planujemy wdrożenie OpenStacka • Wystawianie nowej wersji na środowisko lokalne jest automatyczne • Serwer Continuous Integration śledzi branch „dev” • Nowe wersje budowane wielokrotnie w ciągu dnia • Zepsute buildy mają priorytet  • Wystawianie na produkcję jest w 100% pod kontrolą zespołu • Serwer CI przygotowuje nową wersję na podstawie brancha produkcyjnego • Aktywacja zmian na produkcji poprzez dedykowany panel administracyjny • Rollback pod jednym kliknięciem
  • 15.