Hallo,
ich habe nun schon einiges hier im Forum gelesen, mache mir aber immer noch Gedanken um die Umsetzung der Zeitlinie. Viele arbeiten mit Ticks, welche in konstanten Schritten berechnet werden. Andere wiederum schreiben von Realtime. Dabei werden meistens einfach alle aktuellen Werte beim Aufruf einer Aktion für den aktuellen Zeitpunkt berechnet. Meiner Meinung nach dürfte dies nur bis zu einer gewissen Komplexität funktionieren, will man den Server und dessen Antwortzeiten nicht in die Knie zwingen.
Bei meinem Spielentwurf habe ich das Problem, das es zu viele Komponenten gibt, um bei einem Seitenaufruf alles durchzurechnen. Meine Idee ist nun bei jeder Aktion, welche eine automatische Aktion nach sich zieht, einen Eintrag in einer Todoliste mit Zeitstempel zur Ausführung zu machen. Diese Todoliste wird durch einen Cronjob kontinuierlich abgearbeitet.
Im Endeffekt würde dann genau ein Cronjob laufen, welche jede Minute ausgeführt wird. Man holt also alle Aufträge aus der Todoliste, dessen Zeitstempel erreicht ist, und arbeitet die einzelnen Einträge nacheinander ab.
Was haltet ihr von der Idee, oder habt ihr bessere Vorschläge?
Fortschreiten der Zeit
gepostet vor 17 Jahre, 8 Monate von TBT
gepostet vor 17 Jahre, 8 Monate von Drezil
jup.. mache ich auch so ..
nur bei mir hab ich nen c++-daemon geschrieben, der sich dann darum kümmert und das idr. sekundengenau realisiert..
bei non-globalen änderungen, typischerweise ressourcenberechnung, gobäudebau + forschung kommt der nicht zum einsatz. das macht php noch allein (und das ziemlich fix)
alle ereignisse, die global einsehbar sind (flottenbewegungen, kämpfe, ...) werden vom daemon übernommen. der baut sich intern ne priority-queue zusammen und arbeitet die ab. wenn er mal nichts zu tun hat, macht er nen socket auf und hört, ob php ihm was zu sagen hat ("pass auf, da wurd ne flotte losgeschickt, nimm die in deine liste auf", etc.)
läuft im moment ganz rund. ich werde in ein paar wochen in den echten testbetrieb gehen und das auf einem server mit ein paar usern (<100) testen. mal sehen, wie stabil das alles im realen einsatz ist.
nur bei mir hab ich nen c++-daemon geschrieben, der sich dann darum kümmert und das idr. sekundengenau realisiert..
bei non-globalen änderungen, typischerweise ressourcenberechnung, gobäudebau + forschung kommt der nicht zum einsatz. das macht php noch allein (und das ziemlich fix)
alle ereignisse, die global einsehbar sind (flottenbewegungen, kämpfe, ...) werden vom daemon übernommen. der baut sich intern ne priority-queue zusammen und arbeitet die ab. wenn er mal nichts zu tun hat, macht er nen socket auf und hört, ob php ihm was zu sagen hat ("pass auf, da wurd ne flotte losgeschickt, nimm die in deine liste auf", etc.)
läuft im moment ganz rund. ich werde in ein paar wochen in den echten testbetrieb gehen und das auf einem server mit ein paar usern (<100) testen. mal sehen, wie stabil das alles im realen einsatz ist.
gepostet vor 17 Jahre, 8 Monate von Agmemon
Ich verfolge ein ähnliches Konzept, mit etwas anderen Ansätzen. Bei mir werden alle anstehenden Aktionen in Form von Tickets in der Datenbank gespeichert. Es gibt Bautickets, Bewegungstickets und so weiter.
Alle diese Tickets werden von der eigentlichen Webanwendung abgearbeitet, nur die Auslöser unterscheiden sich. Dabei unterscheide ich zwischen Aktionen, die nur den Spieler betreffen und Aktionen die mehrere Spieler oder die Spielwelt betreffen. Die erste Gruppe wird immer dann abgearbeitet, wenn der Spieler die entsprechenden Informationen braucht. Dies betrifft z.B. die Produktion von Rohstoffen und das Bauen von Gebäuden. Die Kontrolle darüber übernehmen meine Controller-Klassen. So sorgt z.B. der Basis-Controller dafür, dass die Datenbasis einer Basis neu berechnet wird, bevor irgendeine Funktion des Controllers durchgeführt wird. Dies sorgt zum einen dafür, dass die Datenbasis immer aktuell ist aber auch dafür, dass nur das Berechnet wird, was wirklich gebraucht wird.
Für die zweite Gruppe gibt es externe Prozesse. So habe ich einen Prozess, der für alles zuständig ist, was mit Bewegung zu tun, also z.B auch Angriffe. Diese Prozesse übernehmen aber nicht die Berechnung. Sie gucken nur in die Datenbank und holen sich die IDs der anstehenden Tickets und lösen dann die Berechnung im Spielsystem durch einen HTTP-Request aus.
Dieses Vorgehen hat in meinen Augen einige Vorteile. Unabhängige Tickettypen haben jeweils einen eigenen Prozess, der sich um sie kümmert, wodurch nicht alle anstehenden Berechnungen in einer Queue hängen. Es ist sicher gestellt, dass die richtige Reihenfolge in den Berechnungen eingehalten wird und keine Berechnung doppelt ausgelöst wird. Und das DRY-Prinzip wird eingehalten, da die Berechnungen auch oftmals auf andere Berechnungen zurückgreifen müssen. Zum Beispiel muss beim einem Angriff auf eine Basis auch der aktuelle Status der Basis berechnet werden.
Performance-Probleme im Request-Zyklus sollten damit nicht auftreten, da über FastCGI mehrere Instanzen der Anwendung laufen. Aufwändige Berechnungen blockieren damit immer nur eine Instanz. Und sollte sich trotzdem zeigen, dass die Performance leidet, ersetze ich die die entsprechende Berechnungen mit Inline-C. Dann ist es Drezils Ansatz etwas ähnlicher, aber die Vorteile bleiben erhalten.
Alle diese Tickets werden von der eigentlichen Webanwendung abgearbeitet, nur die Auslöser unterscheiden sich. Dabei unterscheide ich zwischen Aktionen, die nur den Spieler betreffen und Aktionen die mehrere Spieler oder die Spielwelt betreffen. Die erste Gruppe wird immer dann abgearbeitet, wenn der Spieler die entsprechenden Informationen braucht. Dies betrifft z.B. die Produktion von Rohstoffen und das Bauen von Gebäuden. Die Kontrolle darüber übernehmen meine Controller-Klassen. So sorgt z.B. der Basis-Controller dafür, dass die Datenbasis einer Basis neu berechnet wird, bevor irgendeine Funktion des Controllers durchgeführt wird. Dies sorgt zum einen dafür, dass die Datenbasis immer aktuell ist aber auch dafür, dass nur das Berechnet wird, was wirklich gebraucht wird.
Für die zweite Gruppe gibt es externe Prozesse. So habe ich einen Prozess, der für alles zuständig ist, was mit Bewegung zu tun, also z.B auch Angriffe. Diese Prozesse übernehmen aber nicht die Berechnung. Sie gucken nur in die Datenbank und holen sich die IDs der anstehenden Tickets und lösen dann die Berechnung im Spielsystem durch einen HTTP-Request aus.
Dieses Vorgehen hat in meinen Augen einige Vorteile. Unabhängige Tickettypen haben jeweils einen eigenen Prozess, der sich um sie kümmert, wodurch nicht alle anstehenden Berechnungen in einer Queue hängen. Es ist sicher gestellt, dass die richtige Reihenfolge in den Berechnungen eingehalten wird und keine Berechnung doppelt ausgelöst wird. Und das DRY-Prinzip wird eingehalten, da die Berechnungen auch oftmals auf andere Berechnungen zurückgreifen müssen. Zum Beispiel muss beim einem Angriff auf eine Basis auch der aktuelle Status der Basis berechnet werden.
Performance-Probleme im Request-Zyklus sollten damit nicht auftreten, da über FastCGI mehrere Instanzen der Anwendung laufen. Aufwändige Berechnungen blockieren damit immer nur eine Instanz. Und sollte sich trotzdem zeigen, dass die Performance leidet, ersetze ich die die entsprechende Berechnungen mit Inline-C. Dann ist es Drezils Ansatz etwas ähnlicher, aber die Vorteile bleiben erhalten.
gepostet vor 17 Jahre, 8 Monate von exe
Bei mir gibts da von Allem etwas. Grundsätzlich läuft das Spiel in Echtzeit. Anzeigen wie Rohstoffe werden nur beim Auslesen aus der Datenbank temporär berechnet. Das ist auch performanter als eine ständige Aktualisierung in der Datenbank, da ständige Updates gespart werden. Diese Anzeigen automatisch mitgespeichert, sobald der betreffende Datensatz aus einem anderen Grund aktualisiert wird (womit effektiv keine zusätzlichen Datenbankabfragen für die Aktualisierung der Anzeigen benötigt werden). Diese Aktualisierungen laufen bereits im Datenbanklayer ab bevor ein Ergebnisobjekt an die Engine ausgeliefert wird. Dadurch ist sichergestellt, dass egal wann und wo z.B. ein Gebäude abgerufen wird, die Rohstoffanzeigen immer aktuell sind.
Echte Spielevents wie Flottenbewegungen werden als Backup in der Datenbank notiert, laufen ansonsten aber in einer RAM-basierten Priorityqueue im Eventscheduler. Der hat für jeden Eventtyp (z.B. Bewegung, Konstruktion) einen Handler. Ist ein Event abgelaufen geht er sofort an den Handler. Ansonsten wartet der Scheduler darauf, dass ein Event abläuft oder ein anderer hinzugefügt wird.
Dabei gibt es jedoch auch Events, die mit gewissen Verzögerungen ausgeführt werden. Das betrifft momentan ausschliesslich Kämpfe. Diese werden vom Scheduler zwar sekundengenau getriggert, im Handler dann jedoch X Minuten lang verzögert. Der erste Kampfevent, der in einer Region stattfindet, führt eine Verzögerung ein. Sobald die abgelaufen ist wird der Kampf mit allen zwischenzeitlich eingetroffenen Einheiten ausgeführt. D.h. grundsätzlich wird in Echtzeit gerechnet, Kämpfe werden jedoch "tickbasierend" (oder beser gesagt in Runden mit zeitlichen Abständen) berechnet um mehrere Einheiten gemeinsam in einen Kampf stellen zu können.
Echte Spielevents wie Flottenbewegungen werden als Backup in der Datenbank notiert, laufen ansonsten aber in einer RAM-basierten Priorityqueue im Eventscheduler. Der hat für jeden Eventtyp (z.B. Bewegung, Konstruktion) einen Handler. Ist ein Event abgelaufen geht er sofort an den Handler. Ansonsten wartet der Scheduler darauf, dass ein Event abläuft oder ein anderer hinzugefügt wird.
Dabei gibt es jedoch auch Events, die mit gewissen Verzögerungen ausgeführt werden. Das betrifft momentan ausschliesslich Kämpfe. Diese werden vom Scheduler zwar sekundengenau getriggert, im Handler dann jedoch X Minuten lang verzögert. Der erste Kampfevent, der in einer Region stattfindet, führt eine Verzögerung ein. Sobald die abgelaufen ist wird der Kampf mit allen zwischenzeitlich eingetroffenen Einheiten ausgeführt. D.h. grundsätzlich wird in Echtzeit gerechnet, Kämpfe werden jedoch "tickbasierend" (oder beser gesagt in Runden mit zeitlichen Abständen) berechnet um mehrere Einheiten gemeinsam in einen Kampf stellen zu können.
gepostet vor 17 Jahre, 8 Monate von TBT
Daemon ist eine gute Idee, hab ich doch gleich mal eingebaut
Der Cronjob prüft jetzt nur noch ob der Daemon läuft. Der Daemon selber
prüft ob Aktionen anliegen, ansonsten macht er einfach nichts.
Der Cronjob prüft jetzt nur noch ob der Daemon läuft. Der Daemon selber
prüft ob Aktionen anliegen, ansonsten macht er einfach nichts.
gepostet vor 17 Jahre, 8 Monate von raufaser
Also ich hab das bei meinem Spiel so gemacht, dass in 3 Screen Session 3 verschiedene PHP Shellscripte laufen lasse, die Monster spawnen, Leben und Mana der Spieler wiederherstellen und die Monster auf der Karte bewegen.
Das ist sehr einfach zu warten, kann sehr leicht auf weitere Aufgaben ausgedehnt werden und ist für meine Zwecke vollkommen ausreichend.
Einfach, aber es funktioniert...
Gruß,
Marc
Das ist sehr einfach zu warten, kann sehr leicht auf weitere Aufgaben ausgedehnt werden und ist für meine Zwecke vollkommen ausreichend.
Einfach, aber es funktioniert...
Gruß,
Marc
gepostet vor 17 Jahre, 8 Monate von TheUndeadable
Im großen und ganzen läuft es auch bei mir so.
Es gibt einen Dienst (Daemon im Unix-Jargon), der rund um die Uhr im Hintergrund auf dem Server läuft. Er hält auch das Hauptsynchronisationsobjekt (Mutex).
Die ASP.Net-Seiten (Entspricht PHP-Seiten) arbeiten normal per MySQL auf der Datenbank. Werden kritische Operationen durchgeführt, so holt sich die ASP.Net-Seite den Mutex und der Dienst kann keine Aktionen durchführen.
Ich denke, dass ich dieses Konzept noch weiter fortführen werde, so dass die gesamte Arbeit nur im Dienst durchgeführt werden und die ASP.Net-Seiten die Kommandos über interne Kanäle (Remoting) an den Dienst leiten.
Es gibt einen Dienst (Daemon im Unix-Jargon), der rund um die Uhr im Hintergrund auf dem Server läuft. Er hält auch das Hauptsynchronisationsobjekt (Mutex).
Die ASP.Net-Seiten (Entspricht PHP-Seiten) arbeiten normal per MySQL auf der Datenbank. Werden kritische Operationen durchgeführt, so holt sich die ASP.Net-Seite den Mutex und der Dienst kann keine Aktionen durchführen.
Ich denke, dass ich dieses Konzept noch weiter fortführen werde, so dass die gesamte Arbeit nur im Dienst durchgeführt werden und die ASP.Net-Seiten die Kommandos über interne Kanäle (Remoting) an den Dienst leiten.
gepostet vor 17 Jahre, 8 Monate von TBT
So mein Daemon ist so gut wie fertig . Er ist komplett in PHP geschrieben, und stößt je nach Aufgabe bestimmte Klassen an. Die Ausführungszeit liegt pro Aufgabe aktuell bei 4-6 Millisekunden, das aber nur auf meinem häuslichen Entwicklunksrechner. Der richtige Server ist da doch noch ein Ende schneller
Da man bei PHP mit den Memoryleaks nicht so sicher sein kann, startet sich der Daemon in regelmäßigen Abständen selbst neu.
Da man bei PHP mit den Memoryleaks nicht so sicher sein kann, startet sich der Daemon in regelmäßigen Abständen selbst neu.
gepostet vor 17 Jahre, 8 Monate von Klaus
Dann ist es aber eher ein Cronjob in kurzen Abständen.
gepostet vor 17 Jahre, 8 Monate von r3venge
Bei mir läuft das ähnlich wie bei den meisten hier ab. Globale-Events erledigt der Eventhandler und nicht globale-Events werden bei Bedarf aktualisiert.
Der Eventhandler läuft als Hintergrundprozess und wird über einen Cronjob alle 15 Minuten überprüft. Sollte etwas nicht stimmen wird der Eventhandler neu gestartet und das Error-Log gespeichert. Funktioniert mit 20 Leuten ganz gut ;P
Der Eventhandler läuft als Hintergrundprozess und wird über einen Cronjob alle 15 Minuten überprüft. Sollte etwas nicht stimmen wird der Eventhandler neu gestartet und das Error-Log gespeichert. Funktioniert mit 20 Leuten ganz gut ;P
gepostet vor 17 Jahre, 8 Monate von TBT
Original von Klaus
Dann ist es aber eher ein Cronjob in kurzen Abständen.
muß nicht sein
Auch php kann threaded arbeiten und mehrere Dinge gleichzeitig ausführen.
Aktuell ist es aber wirklich nur ein sehr schneller cron, da gebe ich dir recht.
gepostet vor 17 Jahre, 8 Monate von Kaiser Nero
Ich habe mir auch ein PHP "Daemon" gebastelt, welches in einer Screen Session läuft und die gewünschten Aktionen ausführt.
Das Ganze funktioniert so, dass er wartet bis eine bestimmte Datei vorhanden ist oder ob ein Zeitstempel abgelaufen ist. Wenn die datei vorhanden ist wird der Zeitstempel aktualisiert (ToDo liste aus der DB geholt) und wenn der aktuelle Zeitstempel abgelaufen ist führt er die zu dem Zeitpunkt nötige Aktion aus und aktualisiert hinterher den Zeitstempel.
Es ist eigentlich einfach gestrickt. Warum PHP? Ganz einfach: Ich habe leider noch nicht die große Ahnung von C/C++ um den "Daemon" als richtigen Daemon zu schreiben.
Läuft aber ansonsten zuverlässig.
mfg
Das Ganze funktioniert so, dass er wartet bis eine bestimmte Datei vorhanden ist oder ob ein Zeitstempel abgelaufen ist. Wenn die datei vorhanden ist wird der Zeitstempel aktualisiert (ToDo liste aus der DB geholt) und wenn der aktuelle Zeitstempel abgelaufen ist führt er die zu dem Zeitpunkt nötige Aktion aus und aktualisiert hinterher den Zeitstempel.
Es ist eigentlich einfach gestrickt. Warum PHP? Ganz einfach: Ich habe leider noch nicht die große Ahnung von C/C++ um den "Daemon" als richtigen Daemon zu schreiben.
Läuft aber ansonsten zuverlässig.
mfg
gepostet vor 17 Jahre, 8 Monate von TheUndeadable
Bei meinem alten Spiel hatte ich auch einen Daemon in PHP geschrieben. Der lief über 30 Tage ohne Neustart des Skriptes. Auch habe ich kein Speicherloch oder ähnliches festgestellt.
Ich war damals recht erstaunt.
Habe dennoch auf C++ portieren müssen, da der C++-Daemon etwa 10fache Geschwindigkeit hatte und ich ein Tickspiel hatte, bei dem das komplette Spiel während der Berechnung der Ticks gestoppt war.
Ich war damals recht erstaunt.
Habe dennoch auf C++ portieren müssen, da der C++-Daemon etwa 10fache Geschwindigkeit hatte und ich ein Tickspiel hatte, bei dem das komplette Spiel während der Berechnung der Ticks gestoppt war.
gepostet vor 17 Jahre, 8 Monate von Toby
Ich hab mir auch einen PHP-Daemon geschrieben, der halt checkt obs Zeit für einen Tick ist und diesen ggf. auslöst. Er ruft dafür dann eine Task-Klasse auf, die ihm sagt, wieviele Tasks es gibt und dann entsprechend für jeden Task forkt, damit das parallel läuft. Funktioniert bisher (ohne echte Last, nur im Demobetrieb) recht gut...
gepostet vor 17 Jahre, 8 Monate von woodworker
Original von TBT
Auch php kann threaded arbeiten und mehrere Dinge gleichzeitig ausführen.
Aktuell ist es aber wirklich nur ein sehr schneller cron, da gebe ich dir recht.
threaded kann php 100% NICHT, und wird es laut aussage von rasmus auch nie können
du kannst forken aber mehr auch nicht