-
Notifications
You must be signed in to change notification settings - Fork 35
[AI] Initial Sketch of an async command queue #725
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
Ich muss mal schauen, wie wir das umsetzen. Dann waere das alles nach "Raeumen" und "base" (ueberall erreichbar) gruppiert. Ueber die "enums" wuerde man auch gut erkennen, welche Befehle "erlaubt" sind. Der erste Commit enthaelt nun erstmal - als Beispiel - eine Umsetzung fuer "SendToChat" |
|
Es müssen ja wahrscheinlich nicht sämtliche KI-Befehle umgeschrieben werden. Ich finde es schon sinnvoll, fachlich ähnliche Kommandos (Parameter sind gleich: eine Werbung, eine Lizenz,...) als gemeinsamen Typ zu behandeln und über action zu steuern, was damit gemacht werden soll. Ich wäre aber dafür, das Thema erst nach dem nächsten Release ernsthaft anzugehen, denn das wird wohl eine größere Baustelle. |
|
Du kannst gerne die richtigen Befehle raussuchen - ich denke vorrangig die "Setter" (doXYZ usw) sollten davon betroffen sein. der Code ist insofern vorbereitet, als das man auch bequem Dinge wie "onSentToChat" einbinden kann (also dann Aufruf nach "Lua", wenn das Ergebnis eines Funktionsaufrufs vorliegt). doBuyProgrammeLicence -> onBuyProgrammeLicence Das waere von Interesse, wenn die KI eben "nicht" auf das Ende eines Funktionsaufrufes warten soll - im Sinne von "dauert eventuell zu lange" oder "Daten werden nur aller X Sekunden berechnet". |
|
TODO: jede KI sollte eine eigene Queue bekommen, damit bei Bankrott und Co die Queue spezifisch geleert werden kann. |
|
Ich habe ueberlegt, hier gleich die Chance zu nutzen ... und die "TLuaFunctions" vielleicht ein wenig ... aufzuraeumen. Mein groesseres Ziel waere es ja, dass es "Aktionen" gibt, die von Spieler und auch KI ausgefuehrt werden koennen. Wenn wir also irgendwo die GUI benutzen, ist der Befehl am "Ende", eigentlich der gleiche, wie wenn die KI etwas macht. Kurz, ich wuerde gern aus dem "TVT.of_doBuyProgrammeLicence(id)" etwas machen, was auch der menschliche Spieler ausfuehrt. Dazu waere es aber wohl praktisch, wenn wir die Funktionen umlagern. In etwa ein In Lua wuerde das dann vielleicht so aufgerufen werden koennen: TVT.actions.office.BuyProgrammeLicence(licID)ohne die Unterteilung waere es: Natuerlich muesste man sich noch Gedanken machen, wie man das so aufgesplittet bekommt, dass die "actions"-Typen keine grossen Abhaengigkeiten mitschleppen (so dass sie sich bei den jeweiligen GUI-"on click" usw bequem verquicken lassen, ohne dass man alle moeglichen "actions" (und dazu zu kennende "Types") an der Backe hat. Ein "einfacher" Ansatz waere ... alle "actions" bekommen nur noch Primitive (oder "object") als Parameter/Result. Dann kann ein So aehnlich machen wir es glaube schon mit den GameModifiers usw - bei denen die run/undo nur durch die individuellen Parameter gesteuert werden. |
|
Ich finde den Ansatz für den Umbau grundsätzlich gut; das Aufräumen sowieso (Eliminieren nicht verwendeter Methoden). Die Unterteilung |
|
Ich denke wir wuerden generell eine "Indirektionsschicht" einbauen. Aehnlich der TGameModifier und anderer Klassen. Der Grundgedanke ist, dass die Wenn das soweit umgearbeitet wird - und man das auch fuer den menschlichen Spieler nutzt, dann ist das auch gleich ein riesen Grundstein fuer den Mehrspielermodus, da dann ebenfalls nur (synchronisiert - wie bei Lockstep) die "TCommand" umhergereicht werden muessen. Mein einfachster Ansatz waere der " |
Ich habe jetzt mal einen Vergleich "direkter Funktionsaufruf" und "indirekter Funktionsaufruf" gemacht ... wenn der GCC nicht fies optimiert hat, waren das sowieso vernachlaessigbar kleine Zeiten: 9 und 14ms. letzteres geht: Sprich ein wenig indirektion und am Ende lookup aus einer intMap (der auch auch 100.000 mal stattfand). Zeiten koennten bei einer groesseren Lookuptabelle ein wenig anwachsen. Denke aber, die Strafe fuer die Indirektion ist noch im Rahmen. |
|
Ich denke, ein initialer guter Testballon für die Aktionen sind das Gehen zum Ziel und das Verlassen eines Raums. Insb. bei letzterem gab es ja schon Exceptions. Interessant wäre dann die Frage, ob es spürbaren Einfluss auf die Geschwindigkeit hat, da ggf. mehr Update-Zyklen ins Land gehen, bevor eine Aktion abgeschlossen ist. |
|
Würden die Abfragen immer nur in einem Update-Zyklus beantwortet?
Das würde bedeuten, dass sämtliche Interaktion der KI mit den Hauptdaten wesentlich länger dauern würde. Hohe Vorspulgeschwindigkeiten würden sich nur erreichen lassen, wenn man die Anzahl der Update-Zyklen pro Sekunde auch hochdreht. Ein vollständiges Umstellen auf solche Queues kann ich mir dann nur vorstellen, wenn es einen separaten Update-Thread (keine GUI) mit einer massiv höheren Zahl von Zyklen gibt. |
|
Async heisst: du weisst nicht, wann es beantwortet wird.
Ein command pattern mit lockstep waere: alles was waehrend eines bestimmten Ticks an Commands reinkommt, wird ticks+x (das x bestimmt einen "lag", so dasse Spieler ihre Commands fuer den Zeitpunkt "ticks" an den Hauptclient schicken konnten) spaeter ausgefuehrt (in der Reihenfolge wie die Befehle reinkamen).
Ohne lockstep wuerde der x-Lag entfallen und alle angefallenen Befehle wuerden vom Mainthread abgearbeitet, sobald er wieder an der Codestelle vorbeikommt.
Beim Vorspulen wuerden also einfach mehr Befehle pro "tick" abgearbeitet.
|
|
Die Idee für die KI war doch aber, den asynchronen Teil in die AI-API auszulagern. D.h. Lua stellt eine Anfrage an ai.bmx, dort wird ein asynchroner Aufruf abgesetzt und gewartet bis die Antwort an Lua zurückgeschickt wird. Während eines "Lua"-Ticks wird die KI also geblockt bis die eine Anfrage abgearbeitet wurde. Das wird aber wahrscheinlich nicht die einzige asynchrone Anfrage während des Lua-Ticks sein. Deshalb wäre es sinnvoll, dass der Anfrage-Beantworter mit einer wesentlich höheren Frequenz läuft. |
|
Warum sollte er? 60 mal pro Sekunde werden alle jeweils dann vorhandenen Anfragen beantwortet.
Ich denke das ist schnell genug.
Die alternative waere ein Thread der per TCondVar schlaeft und auf neue Befehle wartet. Den muesste aber auch der Haupt/Gui-Thread benutzen.
|
|
Ja, das reicht für normales Spielen und vermutlich auch für kurzzeitiges Vorspulen (Tempo 1800). Beim den KI-Spielen ist bei mir aktuell bei Tempo 3600 Schluss. Bei Tempo 7200 bricht die KI-Performance ein, weil die Wartezeiten am Fahrstuhl verhältnismäßig länger werden (Warten auf einen Update-Zyklus) und auch die teuren Lua-Blitzmax-Anfragen dauern so viel Realzeit, dass wesentlich mehr Spielminuten vergehen. (z.B. Scheduling 50 min statt 25). (D.h. mindestens für den Entwicklungsmodus muss man das Thema im Hinterkopf behalten.) |
|
Ich muss nochmal korrigieren, ich hatte das falsch in Erinnerug. Die Spielminuten pro Task (insb. Scheduling) gehen bei 7200 zwar schon deutlich in die Höhe, wären meiner Ansicht nach aber noch vertretbar. Bei 14000 bekommt man definitiv kein brauchbares Ergebnis mehr (was die Spielweise der KI angeht). |
|
Fuer solche Sachen muss man dann ohne Threading und Sync arbeiten...alles andere macht nicht wirklich Sinn (theoretische Reproduzierbarkeit.
Und ohne obige Limitierung wird ja trotzdem alles verarbeitet..und beim Commandpattern waere es auch immer zu den gleichen GameTicks...auch wenn diese evtl mal laenger oder weniger lange dauern.
|
No description provided.