Sposoby zarządzania zmieniającymi się danymi projektantów wraz ze zmianami danych graczy

14

Mam grę online, w której gracze mogą w jakiś sposób kształtować świat - np. Obudowa Ultima Online, w której możesz budować domy bezpośrednio na określonych częściach mapy świata. Są to zmiany, które powinny trwać z czasem w ramach trwałego świata.

W tym samym czasie zespół projektowy dodaje nowe treści i modyfikuje stare, aby ulepszyć i rozszerzyć grę dla nowych graczy. Zrobią to najpierw na serwerze programistycznym podczas testowania, a następnie będą musieli połączyć swoją pracę z „pracą” graczy na serwerze na żywo.

Zakładając, że rozwiązujemy problemy z projektem gry - np. gracze mogą budować tylko w wyznaczonych obszarach, więc nigdy nie kolidują geograficznie z edycjami projektantów - jakie są dobre sposoby obsługi danych lub uporządkowania struktur danych, aby uniknąć konfliktów, gdy nowe dane projektanta zostaną połączone z danymi nowego gracza?

Przykład 1: gracz tworzy nowy typ przedmiotu, a gra przypisuje mu identyfikator 123456. Wszystkie instancje tego przedmiotu odnoszą się do 123456. Teraz wyobraź sobie, że projektanci gier mają podobny system, a projektant tworzy nowy przedmiot o numerze 123456. Jak tego uniknąć?

Przykład 2: ktoś tworzy popularny mod, który nadaje wszystkim twoim smokom francuski akcent. Zawiera skrypt z nowym obiektem o nazwie, assignFrenchAccentktórego używają do przypisania nowych zasobów głosu do każdego obiektu smoka. Ale masz zamiar wdrożyć swoje DLC „Napoleon vs Smaug”, które ma obiekt o tej samej nazwie - jak możesz to zrobić bez wielu problemów z obsługą klienta?

Myślałem o następujących strategiach:

  • Możesz użyć 2 oddzielnych plików / katalogów / baz danych, ale wtedy operacje odczytu są znacznie skomplikowane. „Pokaż wszystkie elementy” musi wykonać jeden odczyt na DB projektanta i jeden na DB odtwarzacza (i nadal musi jakoś rozróżniać 2).
  • Możesz użyć 2 różnych przestrzeni nazw w jednym sklepie, np. używanie ciągów jako klucza podstawowego i poprzedzanie ich słowami „DESIGN:” lub „PLAYER:”, ale tworzenie tych przestrzeni nazw może być nietrywialne, a zależności nie są jasne. (np. w RDBMS możesz nie być w stanie efektywnie używać ciągów jako kluczy podstawowych. Możesz użyć liczb całkowitych i przydzielić wszystkie klucze podstawowe poniżej pewnej liczby, np. 1 miliona, aby być danymi projektowymi i wszystko powyżej tego dane odtwarzacza. Ale te informacje są niewidoczne dla RDBMS, a linki klucza obcego przekroczą „podział”, co oznacza, że ​​wszystkie narzędzia i skrypty muszą jawnie je obejść).
  • Zawsze możesz pracować na tej samej współużytkowanej bazie danych w czasie rzeczywistym, ale wydajność może być niska, a ryzyko uszkodzenia danych odtwarzacza może zostać zwiększone. Nie obejmuje również gier, które działają na więcej niż 1 serwerze z różnymi danymi światowymi.
  • ... jakieś inne pomysły?

Przyszło mi do głowy, że chociaż jest to przede wszystkim problem w przypadku gier online, koncepcje mogą dotyczyć również modowania, w którym społeczność tworzy mody w tym samym czasie, gdy twórcy łatają swoją grę. Czy zastosowano tu jakieś strategie, aby zmniejszyć ryzyko złamania modów, gdy pojawią się nowe łatki?

Oznacziłem to również jako „kontrolę wersji”, ponieważ na jednym poziomie to jest to - 2 gałęzie rozwoju danych, które wymagają scalenia. Być może z tego kierunku mogą pochodzić pewne spostrzeżenia.

EDYCJA - kilka przykładów dodanych powyżej w celu wyjaśnienia problemu. Zaczynam myśleć, że problemem jest tak naprawdę przestrzeń nazw, którą można zaimplementować w sklepie za pomocą kluczy kompozytowych. To przynajmniej upraszcza strategię scalania. Ale mogą istnieć alternatywy, których nie widzę.

Kylotan
źródło
1
Wyobrażam sobie, że odpowiedź na to pytanie może przynajmniej częściowo zależeć od tego, jakie dane dodają projektanci i co dodają gracze.
lathomas64
Mógłby, ale bardziej interesują mnie ogólne rozwiązania dla 2 stron, które wnoszą wkład w jeden magazyn danych.
Kylotan
1
Wiele osób nie rozumie sedna tego pytania - nie są to sprzeczne zmiany graczy: raczej aktualizacje treści itp. Niszczące istniejące układy. @Kylotan mam rację?
Jonathan Dickinson
To tutaj aktualizacje treści dla programistów potencjalnie kolidują z aktualizacjami treści odtwarzacza. Naprawdę nie interesują mnie obejścia związane z projektowaniem gier (np. Pozwalanie graczom budować tylko w określonych miejscach), ale obejścia dotyczące struktury danych (np. Pozwalanie tylko graczom tworzyć rzeczy o identyfikatorach większych niż 1 milion).
Kylotan
2
Nie określiłeś, czy spodziewasz się aktualizacji w czasie rzeczywistym do świata na żywo? Dodatkowa uwaga: dwie strony przyczyniające się do jednego magazynu danych JEST bazą danych, to właśnie robią bazy danych, nie można tego obejść i głupotą byłoby ignorowanie dziesięcioleci wiedzy na temat unikania problemów z udostępnianymi danymi.
Patrick Hughes,

Odpowiedzi:

2

Myślę, że odpowiedzi proponujące rozwiązania DB przeskakują do konkretnej implementacji bez zrozumienia problemu. Bazy danych nie ułatwiają scalania, po prostu dają ramy do przechowywania danych. Konflikt jest nadal konfliktem, nawet jeśli znajduje się w DB. A sprawdzenie to rozwiązanie problemu biednego człowieka - zadziała, ale będzie to kosztem dla twojej użyteczności.

To, o czym tu mówisz, wpada w rozproszony model rozwoju problemu. Uważam, że pierwszym krokiem nie jest myślenie o graczach i projektantach jako o osobnych typach twórców treści. To usuwa sztuczny wymiar problemu, który nie wpływa na rozwiązanie.

Skutecznie masz swoją główną linię - kanoniczną wersję zatwierdzoną przez programistę. Możesz (prawdopodobnie) mieć także inne oddziały - serwery na żywo, na których ludzie aktywnie rozwijają i udostępniają mody. Treść można dodawać w dowolnym oddziale. Co najważniejsze, twoi projektanci nie są tutaj niczym specjalnym - są po prostu twórcami treści, którzy mieszkają w domu (i możesz je znaleźć i uderzyć, gdy coś spieprzą).

Zatem akceptowanie treści generowanych przez użytkowników jest standardowym problemem scalania. Musisz albo wyciągnąć ich zmiany z powrotem na linię główną, scalić, a następnie wypchnąć ponownie, lub przeciągnąć zmiany linii głównej na ich gałąź i scalić (pozostawiając linię główną „czystą” rzeczy generowanych przez użytkownika). Jak zwykle, przyciągnięcie do gałęzi i naprawienie jest bardziej przyjazne niż proszenie innych osób o wycofanie zmian, a następnie próba naprawienia ich zdalnie na ich końcu.

Po pracy z tego rodzaju modelem obowiązują wszystkie normalne procesy dotyczące unikania konfliktów scalania. Niektóre z bardziej oczywistych:

  • Zachęcaj do swobodnego korzystania z przestrzeni nazw w celu umieszczenia zawartości ogrodzeniowej od konkretnego autora / mod / zespołu.
  • Tam, gdzie treść musi wchodzić w interakcje, ustal jasne konwencje wywoływania / używania, konwencje nazewnictwa i inne luźne „reguły”, które kierują rozwojem, aby łatwo było je połączyć ponownie. Zapewnij narzędzia, które pozwalają twórcom treści wiedzieć, czy przestrzegają tych zasad, idealnie zintegrowanymi z samym tworzeniem treści.
  • Zapewnij narzędzia do raportowania / analizy, aby wykryć prawdopodobne awarie scalania przed ich wystąpieniem. Naprawienie go po scaleniu jest prawdopodobnie bardzo bolesne. Zrób to, aby można było sprawdzić konkretną część treści i dać wszystko jasne jako gotowe do scalenia, aby scalenie było bezbolesne
  • Spraw, aby scalanie / integracja była niezawodna. Umożliwiają łatwe wycofywanie. Wykonaj rygorystyczne testy scalonych treści: jeśli test zakończy się niepowodzeniem, nie łącz go! Iteruj albo ich treść, albo twoją, aż scalenie przebiegnie czysto.
  • Unikaj używania przyrostowych identyfikatorów całkowitych do czegokolwiek (nie masz niezawodnego sposobu rozdzielenia ich na twórców). Działa to tylko w DB, ponieważ sama DB jest kanonicznym dostawcą identyfikatorów, więc nigdy nie dostajesz duplikatów; wprowadza jednak również jeden punkt awarii / obciążenia w systemie.
  • Zamiast tego używaj identyfikatorów GUID - ich przechowywanie kosztuje więcej, ale są specyficzne dla maszyny i dlatego nie powodują kolizji. Alternatywnie użyj identyfikatorów ciągów, jest to znacznie łatwiejsze do debugowania / rozwiązywania, ale droższe do przechowywania i porównywania.
MrCranky
źródło
Niestety niektóre z nich nie są pomocne w moim problemie (np. Zmuszanie graczy do przestrzegania pewnych zasad, ponieważ wszystko to musi być wykonywane automatycznie po stronie serwera) i nie sądzę, aby wsparcie stopnia zarządzania scalaniem i transakcji było praktyczne semantyka, o której wspominasz, ale ogólne podejście do przydzielania gwarantowanych unikalnych identyfikatorów, być może GUID, jest prawdopodobnie najbliższe temu, z czym pójdę.
Kylotan
O, rozumiem. Cóż, skoro kontrolujesz ich narzędzia do budowania, przynajmniej wymuszanie podejść przyjaznych dla scalania (przestrzeni nazw itp.) Jest czymś, co możesz zrobić bez konieczności zgody graczy.
MrCranky
Co robisz, jeśli dwóch graczy tworzy zduplikowane treści? Czy oddzielne instancje twojego świata gry są traktowane jako wyjątkowe? W takim przypadku być może pomocne będzie automatyczne sprawdzanie każdej unikalnej instancji, którą znasz, w stosunku do gałęzi głównej / głównej pod kątem konfliktów, które wystąpią, gdy wypchniesz te zmiany do instancji. Jeśli nie możesz kontrolować graczy, możesz przynajmniej ostrzec swój wewnętrzny zespół, że praca, którą wykonują, jest sprzeczna z instancją X świata, na wczesnym etapie rozwoju.
MrCranky,
Pojęcie przestrzeni nazw nie stanowi problemu - wybranie odpowiednich przestrzeni nazw z przestrzeni nazw wszystkich możliwych przestrzeni nazw jest! :) Duplikowanie treści nie stanowi dla mnie problemu - są to tylko 2 przypadki czegoś równoważnego. Ważne jest to, że nie występują szkodliwe scalenia lub nadpisywania. Jeśli chodzi o automatyczne kontrole kolizji, to zatrzymuje szkody wyrządzone podczas zapisu, ale nie rozwiązuje oryginalnego problemu z nazewnictwem. (Zmiana nazw rzeczy, aby uniknąć kolizji, może być trywialna, ze względu na odsyłacze).
Kylotan
Ach tak, rozumiem teraz, to nie same przestrzenie nazw, tylko wybór imienia. W takim przypadku identyfikatory GUID są prawdopodobnie odpowiedzią ponownie - mają treść skutecznie przechowywaną na swoim małym obszarze. Można podać dekoracyjną nazwę, ale gra użyłaby identyfikatora GUID.
MrCranky
1

Przechowuj wszystko jako atrybut (lub dekorator) - z punktami montażu. Weźmy dom, który gracz zaprojektował jako przykład:

o House: { Type = 105 } // Simple square cottage.
 o Mount point: South Wall:
  o Doodad: Chair { Displacement = 10cm }
   o Mount point: Seat:
    o Doodad: Pot Plant { Displacement = 0cm, Flower = Posies } // Work with me here :)
 o Mount point: North Wall:
  o Doodad: Table { Displacement = 1m }
    o Mount point: Left Edge:
     o Doodad: Food Bowl { Displacement = 20cm, Food = Meatballs}

Dlatego każda jednostka może mieć jeden lub więcej punktów montowania - każdy punkt montowania może przyjmować zero lub więcej innych komponentów. Te dane będą przechowywane wraz z wersją , w której zostały zapisane, wraz z wszelkimi istotnymi właściwościami (takimi jak Przemieszczenie itp. W moim przykładzie) - NoSQL najprawdopodobniej byłby tutaj naprawdę fajny (klucz = identyfikator jednostki, wartość = szeregowy plik binarny Dane).

Każdy komponent musiałby wtedy być w stanie „uaktualnić” stare dane z poprzedniej wersji (nigdy nie usuwaj pól z danych serializowanych - po prostu „zeruj” je) - to uaktualnienie ma miejsce od momentu załadowania (wówczas zostanie natychmiast zapisane z powrotem w najnowsza dostępna wersja). Powiedzmy, że w naszym domu zmieniły się wymiary. Kod ulepszenia względnie obliczy odległość między północną a południową ścianą i proporcjonalnie zmieni przemieszczenia wszystkich zawartych bytów. Jako kolejny przykład w naszej misce z mięsem może zostać usunięte pole „Jedzenie”, a zamiast tego dostaniemy „Odmianę” (Mięso) i „Przepis” (Kulki). Skrypt aktualizacji zmieniłby „Kulki mięsne” w „Mięso”, „Kulki”. Każdy element powinien także wiedzieć, jak radzić sobie ze zmianami punktów montowania - np

To wszystko pozostawia dokładnie jedną kwestię otwartą: co się stanie, jeśli dwa obiekty zderzą się ze sobą (nie ich kontener - punkty montażu chronią cię przed tym)? Po aktualizacji powinieneś sprawdzić kolizje i spróbować je rozwiązać (rozsuwając różne elementy, trochę jak SAT). Jeśli nie możesz dowiedzieć się, jak rozwiązać kolizję, usuń jeden z przedmiotów i umieść go w skrytce - gdzie mogą kupić te usunięte przedmioty (za darmo) lub sprzedać je (za pełną cenę); i oczywiście powiadom gracza, że ​​uaktualnienie zepsuło część jego układu - być może z funkcją powiększania, aby mogli zobaczyć problem.

Ostatecznie powinieneś pozostawić złożone zmiany w rękach graczy (szybko zawieść), ponieważ żaden algorytm nie może uwzględnić estetyki - powinieneś być w stanie dać graczowi kontekst, w którym miejscu był kiedyś (aby mogli pamiętać, nie tylko wylądować z tymi wszystkimi przedmiotami w skrytce i nie wiedzieć, gdzie były).

Jonathan Dickinson
źródło
To trochę zbyt wąsko skupia się na pozycjonowaniu obiektów, co nie jest tak naprawdę kluczowym problemem, który próbuję rozwiązać. Bardziej chodzi o posiadanie unikalnych identyfikatorów w zbieżnych zestawach danych i konieczność łączenia ich bez jakiegokolwiek ryzyka konfliktu. Dodałem 2 przykłady do mojego postu innego dnia, aby spróbować wyjaśnić nieco dalej.
Kylotan
1

Próbuję powiązać to z czymś, co rozumiem, więc teraz myślę w kategoriach Minecraft. Wyobrażam sobie serwer na żywo, w którym gracze wprowadzają zmiany w czasie rzeczywistym, podczas gdy programiści działają na serwerze testowym, który naprawia / tworzy nowe treści.

Twoje pytanie wydaje się prawie 2 wyjątkowymi pytaniami:

  1. Jak zapewnić unikalność identyfikatorów obiektów
  2. Jak zapewnić, aby przestrzenie nazw skryptów nie kolidowały

Spróbowałbym rozwiązać numer 1 za pomocą tymczasowego systemu odwołań. Na przykład, gdy ktoś tworzy nowy obiekt, może być oznaczony jako niestabilny lub tymczasowy. Wyobrażam sobie, że wszystkie nowe treści tworzone na serwerze testowym będą oznaczone jako niestabilne (chociaż mogą również odnosić się do nietrwałych treści).

Gdy będziesz gotowy przenieść nową zawartość do serwera na żywo, proces importowania znajdzie obiekty lotne i przypisze im identyfikatory obiektów serwera na żywo, które są ustawione w kamieniu. Różni się to od zwykłego importu / scalania, ponieważ musisz być w stanie odwoływać się do istniejących nieulotnych obiektów, jeśli musisz je naprawić lub zaktualizować.

W przypadku # 2 wydaje się, że naprawdę potrzebujesz pewnego poziomu pośredniej transmutacji skryptu, który może mieszać nazwę funkcji do unikalnej przestrzeni nazw. to znaczy

assignFrenchAccent

Staje się

_Z11assignFrenchAccent
Błąd 454
źródło
0

Jeśli pliki danych mają format tekstowy, a nie binarny, a projektanci i odtwarzacze modyfikują różne obszary, możesz spróbować scalenia SVN.

lathomas64
źródło
0

Myślę, że baza danych / system plików replikowany w różnych środowiskach przy użyciu procedury „wyewidencjonowywania” byłby najlepszy.

Tak więc, ilekroć projektant chce wprowadzić jakieś zmiany w świecie, wyewidencjonuje / zablokuje wszystkie zasoby, które chce utworzyć / zmodyfikować na wszystkich kopiach bazy danych (rozwój i produkcja), więc żaden inny gracz lub projektant nie może go zmodyfikować . Następnie pracowałby nad bazą danych programowania do czasu zakończenia nowego projektu, a wtedy zmiany zostałyby scalone z produkcyjną bazą danych, a te zasoby byłyby rejestrowane / odblokowywane we wszystkich środowiskach.

Edycja odtwarzacza działałaby w ten sam sposób, z tym wyjątkiem, że role bazy danych / systemu plików zostałyby odwrócone - działają one na produkcyjnej bazie danych, a wszystkie aktualizacje są przesyłane do dev po zakończeniu.

Blokowanie zasobów może być ograniczone do właściwości, w których nie chcesz gwarantować konfliktów: w przykładzie 1 blokujesz, ID 123456gdy tylko gracz zacznie tworzyć, więc programiści nie będą mieli przypisanego tego identyfikatora. W przykładzie 2 Twoi programiści zablokowaliby nazwę skryptu assignFrenchAccentpodczas programowania, więc gracz musiałby wybrać inną nazwę podczas opracowywania swojej modyfikacji (to małe uciążliwości można zmniejszyć przez przestrzeń nazw, ale to samo w sobie nie pozwoli uniknąć konfliktów, chyba że podasz każdą z nich użytkownik / programista określonej przestrzeni nazw, a wtedy będziesz miał ten sam problem z zarządzaniem przestrzeniami nazw). Oznacza to, że cały programista musiałby czytać z pojedynczej bazy danych on-line, ale wszystko, czego potrzebujesz w tej bazie danych w tych przykładach, to nazwy obiektów, więc wydajność nie powinna stanowić problemu.

Jeśli chodzi o implementację, wystarczy jedna tabela ze wszystkimi kluczami i stanem zasobów (dostępna, zablokowana od programisty, zablokowana od prod) zsynchronizowana / dostępna w czasie rzeczywistym w różnych środowiskach. Bardziej złożone rozwiązanie zaimplementowałoby kompletny system kontroli wersji - możesz użyć istniejącego systemu, takiego jak CVS lub SVN, jeśli wszystkie zasoby są w systemie plików.

SkimFlux
źródło
Zazwyczaj globalne blokowanie jakichkolwiek danych nie jest praktyczne - gracze mogą edytować świat tylko podczas normalnej gry i nie chciałbyś, aby ta gra powstrzymywała projektantów od pracy. Jeśli zezwolisz na globalne blokady, operacja scalania jest w zasadzie operacją nadpisywania, co jest łatwe - ale jeśli nie masz globalnych blokad, co wtedy?
Kylotan
Jak wspomniano lathomas64, odpowiedź zależy od rodzaju danych, o których mówisz. Bez globalnych bloków uważam, że musiałbyś mieć system kontroli wersji i zestaw reguł, które rozwiązywałyby wszelkie konflikty - reguły te zależałyby od wymagań dotyczących danych i rozgrywki. Kiedy już je masz, myślę, że każde scalenie sprowadza się do prostej operacji nadpisywania.
SkimFlux
0

Myślę, że chodzi tutaj o to, aby czysto przyjąć na siebie odpowiedzialność. 1) Serwer mówi, co jest obecnie akceptowalne, oraz interfejs API, za pomocą którego można uzyskać dostęp. Baza danych jest modyfikowana zgodnie z pewnymi zasadami. 2) Twórcy mogą tworzyć treści, ale po aktualizacji muszą być dostępne. Jest to wyłącznie twoja odpowiedzialność: każda aktualizacja musi być w stanie przeanalizować stare struktury danych, najlepiej tak czyste i łatwe, jak to możliwe.

Pomysł na punkt montowania ma swoje zalety, jeśli chcesz śledzić unikalne przedmioty i pozycje wewnątrz plastycznej struktury, szczególnie jeśli przyjmiemy, że cała „domowa” struktura gracza ulegnie dramatycznej zmianie i chcesz to zachować małe rzeczy dekoracyjne w odpowiednich szafkach.

To bardzo skomplikowana sprawa, powodzenia! Prawdopodobnie nie ma jednej odpowiedzi.

karmington
źródło
0

Nie wydaje mi się, żeby to stanowiło duży problem, kiedy to robicie.

Po prostu nadpisałbym mody stworzone przez użytkownika, z ostrzeżeniem, że „Mod X może nie działać poprawnie z tą wersją”, pozostawiam twórcom modów zmianę pracy. Nie sądzę, że jest to nierealne oczekiwanie, że aktualizacje mogą wyłączyć niektóre mody.

To samo dotyczy treści utworzonych przez użytkownika, wystarczy wykonać kopię zapasową i zastąpić.

Nie mam w tym żadnego doświadczenia, tylko sugeruję.

Woody Zantzinger
źródło
Myślę, że gdyby to było wyłącznie dla modów dostarczonych przez użytkownika, miałbyś rację. Ale niektóre gry wyraźnie dotyczą treści tworzonych przez użytkowników, więc nie można ich po prostu zniszczyć.
Kylotan
Następnie zostaw w systemie miejsce na treści, które możesz dodać później. Jeśli używasz numerów identyfikacyjnych, zarezerwuj 1-1000. Lub jeśli użytkownicy mogą nazwać swoje zasoby, nie pozwól użytkownikom rozpocząć nazwy od „FINAL-” lub coś w tym rodzaju (zarezerwuj ją dla własnych zasobów). EDYCJA: lub jeszcze lepiej, zrób to w odwrotnej kolejności, zmuszając treść użytkownika do zakresu lub prefiksu
Woody Zantzinger