Silnik gry MMOG w Java EE - liczniki, architektura

0

Przystępuję do tworzenia silnika gry typu MMOG w technologii Java EE. Dla tych, którzy nie wiedzą, co to za gatunek, wyjaśniam. Na pewno znacie takie tytuły jak Travian, Plemiona, OGame, Duma Szlachecka. Chcę nieco zmienić koncepcję, ale o tym później. Dlaczego Java? Otóż aplikacja działa cały czas, jest obsługa wątków i liczników. Rozgrywka będzie toczyć się w czasie rzeczywistym.

Jak do tego podejść? Jak prawidłowo zaprojektować architekturę silnika?

Przykład 1. Kiedy stawiacie budynek, w przeglądarce trwa odliczanie czasu do końca. Kiedy rozbudujecie pola uprawne, zwiększacie produkcję, np. co 5,5 sekundy liczba jednostek pszenicy zwiększy się o 1. Docelowo zboże ma przyrastać nieliniowo (w zimie nie ma żniw). Czy dla każdego gracza tworzyć osobne liczniki? Będzie ich kilka razy więcej niż graczy (osobny licznik dla pszenicy, wojska, każdego budowanego budynku) o ile Java na to pozwoli. Wraz ze zmianą pory roku trzeba zmieniać interwały (znacie lepszy sposób?). Inna koncepcja to 1 wielki timer (ewentualnie kilka timerów), gdzie iterujemy po wszystkich graczach i ich dobytku. Taki timer musiałby taktować z dużą częstotliwością, co mocno obciąży procesor i pamięć. Trzecia koncepcja to obliczać stan surowców, kiedy takie informacje są potrzebne.

Przykład 2. Kiedy będę chciał osadzić grę w innej epoce, w całkiem innym świecie, mogą zmienić się surowce, może zmienić się ich ilość, mogą całkowicie zmienić się założenia. Myślę, że silnik powinien być uniwersalny, a wszystkie obiekty konfigurowalne. W praktyce takie założenie zwiększy złożoność kodu, a co za tym idzie, mocno obniży wydajność aplikacji. A może się mylę?

// Na przykładzie Traviana - podejście nr 1 - sztywna aplikacja
class Village {
    private int wood;
    private int clay;
    private int crop;
}

// Podejście uniwersalne (zakładamy, że jest jakieś globalne terytorium)
class TerritoryItem {
    private Position position;
    private TerritoryItemType type; // luźna fantazja
    private List<Item> items; // ma jakieś atrybuty
}

Do tego trzeba ustalić jakieś warunki typu „wioska może mieć max N atrybutu A”. Czy warto rozważyć zwiększenie skomplikowania aplikacji, aby nie pisać na sztywno w kodzie wszystkich reguł, zasad gry, surowców, jednostek, miejsc na mapie, a konfigurować je np. w bazie danych?

Serwer będzie odpowiadał wyłącznie za logikę biznesową. Komunikacja z klientem za pomocą REST lub WebSocket.

0

Moim zdaniem to powinien byc jakiś job który co jakiś czas 5 sekund, 30, minutę jak tam sobie chcesz, inkrementuje wartości surowców w każdej wiosce, na bieżąco wyliczając na podstawie jakichś tam ustalonych parametrów wartość o jaką powinien być powiększony dany surowiec.

gdzie iterujemy po wszystkich graczach i ich dobytku

Nie iterujemy, update w bazie danych dla wszystkich elementów.

0

To co mówisz da się osiągnąć bez wątków i Javy, a da się również w skryptowych językach takich jak Php czy Node.js.

Wystarczy że zapiszesz czas ostatniego requesta (gdziekolwiek, baza, plik, memcache) a potem aktualnego. Mija między nimi pewien czas (powiedzmy że 30 sekund). Przez ten czas powinna Ci się wytworzyć jakaś ilość zasobów (zależna od czasu). Z tego czasu możesz wyliczyć ile zasobów powinieneś dodać.

Jeżeli nikt np nie będzie grał w grę powiedzmy od 3:00 rano do 12:00 to różnica pomiędzy requestami to 9 godizny, czyli 32400 sekund. Na podstawie tego czasu możesz sobie policzyć ile zasobów dodać.

Nie widzę potrzeby żeby wyciągać armatę na komara.

2

ad. 1.

Temat rzeka - ale poczytaj sobie literaturę - trochę było ciekawych hintów architektonicznych w http://helion.pl/ksiazki/perelki-programowania-gier-vademecum-profesjonalisty-tom-1-mark-deloura,ppgvp.htm (jest 6 tomów - z tego mityczne 4 i 5 (nie czytałem).
(wiekszość rozdziałów dotyczy grafiki i Ci się nie przyda - ale w kazdym tomie jest kilka ciekawych architektonicznych pomysłów opisanych).

Musisz sobie ustalić założenia - ile graczy ogólnie, ile to "zdarzeń" to ustrojstwo generuje. Z bazą danych i w JavaEE będziesz miał ciężko :-(, ale się da.
Ale jeśli będziesz potrzebował ponad 1000 updatów na sekundę - to odpuść - może i się da, ale zemrzesz.

  1. Liczenie tylko kiedy trzeba - po zalogowaniu, kiedy ktoś przegląda czyjeś konto, atakuje - ma sens.

  2. Liczenie okresowe też - i tu hint - możesz zrobić sobie np. w kazdym cyklu "mini tura gry" - przeliczanie np. tylko co 10tego gracza, czy obiektu. Będzie ułuda realtime. Całkiem zabawnie wypada tez pseudolowośc - wylosuj jeden obiekt i go "przelicz". Musisz mieć tylko algorytm pseudolosowy, który w jakimś rozsądnym czasie odwiedzi wszystkie obiekty.

  3. I teraz hint z gier3d - zrób płynne updaty:
    przyrost zboża = (przyrost zboża na sekundę) * (czas teraz - czas kiedy tu ostanio zaglądałem)
    i teraz możesz jak serwer Ci klęka - zmniejszać zęstotliwość updatów - będzie gra mniej "płynna" ale nie zemrze.
    (Tu tylko jest problem z twoim nieliniowym przysrostem - ale sobie wzory dopasujesz prosto).

  4. Serwer wcale nie musi być bardzo płynny! Ogólnie to dużo możesz załatwić klientem - klient może przeliczać sobie wszystko (np. w JavaScrypcie) płynnie. A na serwerze- aktualizujesz raz na minutę albo 10 minut. I serwer zadowolony - a u użytkownika wszystko płynnie sie zmienia w 20 fps :-) Co jakiś czas tylko sync - żeby się nie rozjechały liczniki :-).

Przykładowo server podaje Ci twoje zboże o 1757 to 135432,a przyrost 5.3 na sekundę. A ty widzisz - acha ale jest już 1717 więc mam tego zboża: (tu wstaw sobie wynik).

ad 2. Nie rób ogólnie - nie ogarniesz co robisz. Zrób pod jeden konkretny setting. (A potem zmieniaj tylko wartości współczynników z jakiegoś prostego pliku konf + skórki i nikt się nie zorentuje , że twoja wspaniały Starctaft to tak naprawdę Warcraft II :-) ).

To tylko dośc teoretycznie porady od laika z marnym doświadczeniem w temacie. ( Kiedyś zrobilem z kolegą kosmiczny mulitplayer, który w końcu nawet działał - ale miał graczy "prawie trzech" (spoko - to była nauka java3d :-), + kiedyś przez chwilę robiłem coś a la multiplayer simcity - i ta gra nawet nie doszła do beta testów. To było na JmonkeyEngine. Szybko się udało zrobić coś co działalo, ale po prawdzie, jakby klient nie ubił całej akcji (do której gra była tylko dodatkiem) - to umarlibyśmy na supporcie - problemy w tym czasie z obsługa kart 3d z javy były masakryczne (sterowniki). Teraz taki projekt robiłbym na webgl albo unity3d (i wiele firm z podobnymi projektami przemigrowało się z Jmonkey na webgl) )).

A poza tym uważam, że nie powinieneś robić tego na JavaEE i na pewno nie z bazą SQL

1

Można też pójść w innym kierunku i na początek proponuję zapoznać się z tym wykładem https://vimeo.com/122435924

Masz? Ok, Torben implementuje tu grę w życie w oparciu o system aktorowy, w Erlangu, ale to szczegół. Zatem można by mówiąc nieładnie zaiwanić ideę użycia aktorów do reprezentowania stanu poszczególnych graczy. Taki aktor-gracz będzie posiadał swoje „gruppies” w postaci aktorów-liczników, które będą przechowywać informacje o stanie poszczególnych elementów - kopalni, pól, hodowli bydła etc. Do tego potrzebujemy jeszcze jeden element w postaci metronomu, czyli obiektu, który będzie rozgłaszać powiadomienia do wszystkich aktorów w systemie w celu nadania grze tempa.
Aktorzy na podstawie informacji z metronomu będą wstanie wyliczyć swój aktualny stan i odpowiedzieć na żądania pytające o ten stan.

0

@jarekr000000: Java EE ma taką zaletę, że aplikacja działa cały czas. Bieżące informacje można przechowywać w obiektach, czyli w pamięci. Do bazy zapisujemy stan gry co pewien okres (np. 5 minut). Ale wciąż trzeba uwzględnić kilka sytuacji:

  1. Pada serwer lub aplikacja wywala się na jakimś błędzie. Timery przestają działać. Po starcie aplikacji trzeba je utworzyć ponownie. Produkcja zostanie wznowiona. Czy przez ten czas, kiedy serwer leżał, zboże powinno rosnąć? Niezależnie, jaką opcję wybierzemy, i tak musimy wczytać stan gry z bazy. Potem liczymy nowe wartości złóż lub kontynuujemy odliczanie ze stanu sprzed awarii.

  2. Liczenie tylko kiedy trzeba - wydaje się idealnym rozwiązaniem. Trzeba przeanalizować, jak często będziemy coś liczyć i na podstawie czego. Może się okazać, że mniejsze obciążenie spowodują timery. Przy liczeniu „na żądanie” nasze modele (wioski, budynki, wojsko) nie będą zawierać aktualnych danych. Przedtem trzeba je uaktualnić, nie zapominając o uaktualnieniu danych zależnych.

  3. Synchronizacja liczników - jeśli oprzemy przyrost zboża na timerach, paradoksalnie może dojść do rozbieżności. Nie da się odpalić wszystkich timerów jednocześnie. Czy w Javie jest jakiś mechanizm, aby zsynchronizować timery, odpalić je dopiero od następnego taktu (np. wyliczonego uniksowego znacznika czasu)? Ewentualnie można tworzyć zaplanowane zadania jednorazówki, ale tu też dojdzie do obsuw.

@Koziołek: Obejrzę filmik. Kwestia „metronomu” została omówiona. Sprzętowo mówi się „sygnał zegarowy”. Tu musimy przekazać wszystkim aktorom zdarzenie, więc serwer dostanie zadyszki. Może inaczej to wygląda w językach funkcyjnych.

1 użytkowników online, w tym zalogowanych: 0, gości: 1