Die perfekte C-API

Design Patterns, Erklärungen zu Algorithmen, Optimierung, Softwarearchitektur
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Antworten
Benutzeravatar
Krishty
Establishment
Beiträge: 7850
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Die perfekte C-API

Beitrag von Krishty »

Mein Spiel soll erweiterbar sein. Wir hatten mal hier auf ZFX mal drüber diskutiert, ob man sowas besser durch eine Skriptsprache löst oder durch eine API. Für mich ist es jetzt erstmal eine API geworden und hier ist mein kleines Post-Mortem.

Ziel

Ich habe einen Flugsimulator. Ich möchte gern, dass er durch unbeteiligte Dritte erweiterbar ist:
  • Man soll eigene Flugzeuge hinzufügen können (Flugphysik, Avionik, HUD, KI, etc.).
  • Man soll eigene Levels hinzufügen können (Landschaften, Gegnerkonstellationen).
  • Man soll seinen Flugzeugen und Levels eigenes Aussehen verleihen können.
  • Man soll seinen Flugzeugen und Levels eigenen Klang verleihen können.
Mittel zum Zweck

Diese Ziele versuche ich, durch eine C-API zu erreichen. Könnte vielleicht auch anders gehen – eine Skriptsprache wurde ja schon diskutiert. Oftmals wird sowas komplett Data-Driven gelöst, indem einfach alles komplett über Konfigurationsdateien und Formate einstellbar und bis zur Unkenntlichkeit verdrehbar ist. Ist aber aus anderen Gründen nicht mein Stil (Protip: Eine Code-API kann eine Daten-API implementieren, aber ohne die Komplexität von Dateiformaten in die Geschäftslogik zu ziehen).

Eine C-API also.

Philosophie

Mein Spiel soll gerade eben das Nötigste bereitstellen, was man braucht, um den Job zu erledigen. Viele [who?] mögen mächtige oder umfangreiche APIs, mit denen man jede Menge verrücktes Zeug machen kann [citation needed]. Ich nicht. Ich bilde mir ein, dass es mir die Wartung erschwert, und Optimierungen wie Multi-Threading. Dass mächtigere Schnittstellen zu höherer Komplexität führen und damit zu mehr Problemen. Vielleicht ist das aber auch bloß mein Charakter.

Ich traue meinen Moddern nicht. Ich sehe täglich, wie Schnittstellen missbraucht werden. Ich denke nicht einmal, dass andere schlechte Programmierer sind. Ich glaube schlicht, dass eine API eine Denkweise ausdrückt, und dass es nicht trivial ist, andere Denkweisen zu verstehen. Also ist es prinzipiell schwierig, andere APIs richtig zu benutzen, weil man die Denkweise des Verfassers verinnerlichen muss. Deshalb sollte man Schnittstellen so eindeutig und kohärent gestalten wie möglich. Niemals zwei Lösungswege anbieten, denn dann nehmen die meisten Modder die falsche – wer denkt schon wie ich!

Callbacks sind dabei ein gutes Mittel. Man überlässt nicht dem Modder die Kontrolle, was das Spiel tut, sondern lässt ihn nur anmelden, was er verändern möchte und mit welcher Funktion – wenn der perfekte Zeitpunkt gekommen ist, ruft ihn die Engine auf, nicht umgekehrt. So verhindere ich, dass zur falschen Zeit die falschen Dinge gemacht werden; und so muss sich kein Modder mit verrückten Abhängigkeiten und Implementierungsdetails der Engine befassen.
  • Objekte bekommen ein Callback für Physik hinterlegt
  • Objekte bekommen ein Callback für Grafik-Rendering hinterlegt
  • Objekte bekommen ein Callback für Sound-Rendering hinterlegt
  • Irgendwann werden auch Callbacks für KI kommen, aber so weit bin ich gerade noch nicht.
Auf die Levels gehe ich jetzt mal nicht ein, denn der Teil ist langweilig.

(Nachteil von Callbacks: In nicht-C-Sprachen sind sie mitunter knifflig. In C# muss man bspw. Funktionen erst in den Speicher pinnen, bevor man sie an eine C-API übergeben kann. Die Dinger müssen halt fertig übersetzt sein, der Garbage Collector darf sie nicht wegräumen, und ein Thunk muss die Laufzeitumgebung wiederherstellen, und und und.)

Versuch 1: libc, Win32

Jeder C/C++-Programmierer kennt eine C-API: Die C-Laufzeitbibliothek. printf() und so. Sieht nach Pipi-Kram aus, tut aber was echt ernstes: Verbindet euer Programm mit einer dynamischen Bibliothek, die solche Dinge wie printf() implementiert. Ich benutze sie hier mal als nicht-Spieleprogrammierung-Beispiel, weil die Arbeit damit so verbreitet ist.

Uns fällt kaum auf, dass das eine echte API ist, weil sie auf die einfachste Art und Weise überhaupt implementiert wird: Als Sammlung dynamisch gelinkter Funktionen. printf() und Co sind vordeklariert. Startet das Programm, wird die nötige DLL gesucht, geladen, und die Sammlung Funktionszeiger gefüllt. Jeder Aufruf ist dann ein Sprung durch einen Funktionszeiger. Das merkt man nicht, weil sich der Compiler drum kümmert.

Win32 macht dasselbe, auch wenn es durch seitenlange Microsoft-Docs-Datenblätter sehr viel ernster aussieht. Kernel32.dll enthält die Win32-API für Low-Level-Systemzeug. CreateFile() ist eine exportierte Funktion aus dieser DLL. Wir rufen sie auf, als wäre sie eine von uns. Perfekte Welt.

Inflation

Der libc/Win32-Ansatz funktioniert nur so lange gut, bis Abwärtskompatibilität ins Spiel kommt: Wir merken, dass CreateFile() eben doch nicht alles tut, was wir brauchen, und dass wir es erweitern müssen. Wir wollen aber nichts kaputtmachen.

Solche APIs gehen dann immer den selben Weg: Inflation. Aus printf() werden sprintf(), fprintf(), vwprintf(), vfwprintf(), vswprintf(), vsnprintf()

Aus CreateFile() werden CreateFileA() und CreateFileW(), dann CreateFileTransactedA(), CreateFile2() …

Keine der „alten“ Funktionen kann geändert, abgeschaltet, oder entfernt werden, ohne existierende Programme kaputtzumachen. Wenn man das SDK kontrolliert, kann man sowas noch halbwegs mit dem Holzhammer durchdrücken. Wenn aber Drittparteien angefangen haben, die API in andere Sprachen zu übernehmen (Python, C#, …) – und das will ich ja, denn dafür halte ich das alles in C – ist alles verloren.

OMG Abwärtskompatibilität

Wenn man die alten Funktionen am Leben erhält, kommt man in eine kleine Zwickmühle: Früher oder später wird jemand anfangen, die neuen und die alten Funktionen zu vermischen. Schließlich ist der Sinn von Abwärtskompatibilität, dass alter Code nicht kaputtgeht. Unser ReadFile1() muss also mglw. erweitert werden um mit Dateien umzugehen, die via CreateFile2() erzeugt wurden. Oder zumindest um eine Prüfung, dann einen Fehler zu melden! Es gibt Wege drumherum (neue Typen für alles), aber die beschleunigen wiederum die Inflation. Man kommt da nicht mehr raus, und das Wachstum ist üblicherweise quadratisch.

OMFG Multi-Threading

Unsere CPUs haben seit einigen Jahren mehrere Rechenkerne, ne? Sogar unsere Handys. Wenn libc mit Multi-Threading konfrontiert wird (wir machen mal fröhlich fseek(), während im anderen Thread ein fwrite() im Gange ist), ist die Antwort fast immer: Implementation Defined. Schlimmstenfalls explodiert der Computer. May work until it doesn’t.

Bei Win32 wiederum ist die Antwort immer: „Läuft. Wir synchronisieren intern!“. Zu dem Preis, dass dann alles langsamer ist, weil sogar in Single-Threaded Programmen jeder Aufruf durch einen Mutex läuft.

Siehe auch The Rise of Worse is Better.

Beides lässt mir die Haare zu Berge stehen.

Zusammenfassung Versuch 1

+ einfach zu implementieren
+ einfach zu nutzen (landläufig bekannt)
- Schnittstelle wächst unaufhaltsam
- Implementierung wächst unaufhaltsam
- Konsistenzprüfung und Synchronisation schwierig
- Was passiert eigentlich, wenn eine abhängige DLL nicht gefunden wird? ;) Spoiler: Job wechseln


Nächstes Mal geht es mit dem zweiten Versuch weiter: Wie Vulkan und DirectX das Ganze lösen. Stay tuned.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4152
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Die perfekte C-API

Beitrag von Chromanoid »

Cool! Das finde ich ein echt spannendes Thema. Danke!

Insbesondere mit dynamischen Sprachen kann man da recht schlank abstrakte Ansätze fahren (mit den entsprechenden Kosten). Hier ist ein ganz netter Ansatz für Web-APIs zu finden: https://stripe.com/blog/api-versioning Das hat natürlich nur entfernt mit den Problemen hier zu tun, aber ähneln tut sich da doch einiges...
Benutzeravatar
Krishty
Establishment
Beiträge: 7850
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Die perfekte C-API

Beitrag von Krishty »

Ja, tut es. Gute Artikel über API-Design sind schwer zu finden – dafür umso mehr Plattitüden und „Ich hab’s so gelernt“-Antworten auf StackOverflow.

Bzgl. C-API-Versionierung fand ich den hier sehr gut: https://anteru.net/blog/2016/designing-c-apis-in-2016/
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Alexander Kornrumpf
Moderator
Beiträge: 1946
Registriert: 25.02.2009, 13:37

Re: Die perfekte C-API

Beitrag von Alexander Kornrumpf »

Krishty hat geschrieben: 27.04.2022, 08:24 Ja, tut es. Gute Artikel über API-Design sind schwer zu finden – dafür umso mehr Plattitüden und „Ich hab’s so gelernt“-Antworten auf StackOverflow.
Jupp. Ich bekam schon vom Lesen deines Artikels PTSD-Flashbacks (kidding). Manchmal fragt man sich echt ob wir nicht alles wegwerfen und nochmal neu anfangen wollen. Und dann braucht der Neuanfang doch wieder ein C-ABI damit überhaupt irgendwas geht. Wir besprachen es ja vor wenigen Tagen.
Benutzeravatar
Krishty
Establishment
Beiträge: 7850
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Die perfekte C-API

Beitrag von Krishty »

Versuch 2: DirectX, Vulkan

Das Versionierungsproblem wurde von unseren lieben Grafik-APIs folgendermaßen gelöst:
  1. Die Schnittstelle ist nicht global, sondern wird lokal instanziiert. Bei DirectX steckt die komplette Schnittstelle in einem COM-Objekt – also ungefähr in einer C++-Klasse mit virtuellen Methoden. Bei Vulkan steckt sie in einem Haufen Funktionszeiger.
     
  2. Wenn man eine Schnittstelle anfordert, gibt man an, welche Version man haben möchte.
Damit reduziert sich der Teil der API, der abwärtskompatibel bleiben muss, auf exakt eine Funktion: Auf die Initialisierungsfunktion. Wer in grober Vorzeit mal Direct3D 9 initialisiert hat, kennt noch diesen Aufruf:

  IDirect3D9 d3d9 = Direct3DCreate9(D3D_SDK_VERSION);

Hier gibt man die SDK-Version an und bekommt eine Schnittstelle zurück, die an diese Version angepasst ist. Mit Änderung der Schnittstelle hat Microsoft schlicht die SDK-Version hochgezogen. Alte Programme haben dann weiter die alte Schnittstelle zurückbekommen, neue Programme die neue.

Der wichtigste Vorteil: API-Versionen können nicht mehr vermischt werden, weil sie in völlig unterschiedlichen Objekten liegen! Das erleichtert die Validierung und Fehlerbehandlung erheblich.

Paranoia

Microsoft hat dieses Muster eingeschränkt für alle EXEs übernommen: Durch Manifest-Versionen. Jede EXE bringt ein Manifest mit, und darin steht, für welche Windows-Version die EXE kompiliert wurde. Die Win32-API, die Windows in den Prozess lädt, ist dann entsprechend konfiguriert.

Das ist an sich uninteressant, wäre da nicht das kleine Detail, wie sie die Versionen angeben: Nämlich durch Zufallszahlen.

  <!--The ID below indicates application support for Windows Vista -->
  <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
  <!--The ID below indicates application support for Windows 7 -->
  <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>


Warum? Schlicht, damit sich niemand als Windows-11-kompatibel deklarieren kann, bevor Windows 11 tatsächlich erschienen ist!

Davon kann man lernen, schon allein um Fehler zu vermeiden. API-Versionsnummern nicht inkrementieren, sondern in zufälligen Schritten hochzählen. Ein int hat viel Platz!

Beispiel

Ich habe meine ursprünglichen, globalen Funktionen aus Versuch 1 schlicht in ein C-struct voll Funktionszeiger gesteckt. Um die Physik eines Flugzeugs zu beeinflussen, es Geräusche machen zu lassen, und Flugdaten abzufragen, sah das dann so aus:

  struct API_v1 { // wäre in C++ eine Klasse mit virtuellen Methoden

    void setCallbacks(GraphicsCallback *, SoundCallback *, PhysicsCallback *);

    // graphics
    void (*drawTriangles)(int, Triangles const *);

    // sound
    Sound * (*createSound)(…);
    void (*playSound)(Plane *, Sound *, float loudness);

    // physics
    void (*setForce)(Plane *, Vector);
    float (*getAltitude)(Plane const *);
    void (*destroyPlane)(Plane *);

  };


Mods mussten bei mir während der Initialisierung die API-Version und ihre Callbacks hinterlegen. Die Callbacks bekamen dann diese API.

  void init() {
    api = createApi(Version);
    api->setCallbacks(doSound, doGraphics, doPhysics);
    engineSound = api->createSound(…); // we always need this
  }

  void doGraphics(Plane * me) {
    api->drawTriangles(…); // render plane
    api->drawTriangles(…); // update HUD
  }

  void doPhysics(Plane * me) {
    api->playSound(me, engineSound, engineRpm / 1000);
    api->setForce(me, complicatedFlightPhysics(…));
    if(api->getAltitude(me) < 0) // Crashed into the ground?
      api->destroyPlane(me);
  }


Mal alles kaputtmachen

Hier hören die meisten API-Artikel auf, weil wir nun eine heile, versionierte Welt haben. In Spielen ist die Sache aber ein kleines Bisschen komplizierter, denn das da oben ist immernoch ein Höllenloch.

Was passiert eigentlich, wenn jemand in doPhysics() anfängt, Dreiecke zu zeichnen? (Das ist gar nicht mal so abwegig, denn schließlich spielt unser Physik-Handler auch Sound ab!)

Oder wenn jemand in doGraphics() via api->destroyPlane(me); das Flugzeug zerstört, das wir gerade zeichnen?

Wenn die API das Zeichnen von Dreiecken anbietet … ist das nur für die Grafik-Callbacks gedacht? Oder kann ich das auch direkt nach der Initialisierung benutzen, um prozedurale Texturen ab Spielstart zu erzeugen?

Muss meine Implementierung von drawTriangles() nun jedes Mal checken, ob die Engine beim Rendern von Flugzeugen ist, und sonst einen Fehler zurückgeben? Und wie zum Teufel soll das mit Multi-Threaded Rendering koordiniert werden?!

Wer Direct3D 8/9 verwendet hat, kennt diese Situation wahrscheinlich sehr gut. Auch dort konnte man Render States zu jeder Zeit setzen, und jederzeit die Transformationsmatrix ändern, und jederzeit den Shader ändern. Das war mächtig und nützlich, bis man sich damit ins Bein schoss und zwei Abende lang ein fehlplatziertes SetRenderState() gesucht hat.

Das da oben ist keine API, sondern ein stinkendes Code-Moloch.

(Das Problem hat die libc/Win32-API übrigens genau so. Das weiß jeder, der unter Linux schonmal eine Funktion mit dem Postfix _r nutzen musste: Die meisten Linux-C-Funktionen sind nicht re-entrant, und wer sie trotzdem so nutzt, stirbt einen qualvollen Tod. Im ersten Versuch fiel es uns nur nicht auf, weil wir erst gar nicht so weit kamen. Unsere Ansprüche sind halt gestiegen!)

Zusammenfassung Versuch 2

+ löst die meisten Versionierungsprobleme
+ einfacher zu warten und zu optimieren (lokale Instanzen statt globaler Funktionen)
- mehr Aufwand
- schützt nicht vor groben Programmierfehlern

Aber die Erlösung ist nah. Alle guten Dinge sind drei!
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Alexander Kornrumpf
Moderator
Beiträge: 1946
Registriert: 25.02.2009, 13:37

Re: Die perfekte C-API

Beitrag von Alexander Kornrumpf »

Hmmm.

Mit den Callbacks und der damit einhergehenden Inversion handelst du dir aber doch noch ein konzeptuelles Problem ein: Versionierung funktioniert ja normalerweise so, dass der Anbieter der API mehrere Versionen bereitstellt aber der Konsument der API immer nur zu genau einer Version kompatibel sein muss. In deinem Fall ist es aber so dass dein Flugsimulator als Konsument der API (via Callbacks) zu _allen_ Versionen kompatibel bleiben muss, weil du es verschiedenen Anbietern (Moddern) erlauben willst dir das API in verschiedenen Versionen anzubieten und alle sollen funktionieren. Das ist ein viel viel schwereres Problem als das was man üblicherweise unter API-Versionierung versteht.

Ich hoffe es ist nachvollziehbar, dass das nicht symmetrisch ist. Etwas anschaulicher: Wenn ein Flugleug von mehreren Flugsimulatoren benuzt wird, und das Flugzeug bekommt einen neuen Parameter, dann funktionieren Flugsimulatoren, die den Parameter ignorieren vielleicht weiter, wenn man ees richtig macht. Wenn man aber wie du es glaube ich vorhast, einen Flugsimulator und mehrere Flugzeuge hat und der Simulator braucht einen neuen Parameter, dann kannst du die Flugzeuge, die den nicht anbieten, wegwerfen und wenn du das API noch so toll versionierst.

Es sei denn du versionierst eben auf der Konsumentenseite.

In dem DirectX Vergleich: Du kannst vielleicht mit den neuen DirectX12 dlls noch die alten DirectX9 Interfaces bekommen, aber du kannst nicht umgekehrt in deinem Spiel auswählen ob du einen DirectX9 oder einen DirectX 12 renderer willst. Benachbarte Versionen mit Architektursprüngen (9/10) (11/12) gibt es in manchen Spielen, ich denke auch wegen Windows Kompatibilität (XP, Win7) aber schon ab 2 Interationen wird es infeasible.
Benutzeravatar
x1m4
Establishment
Beiträge: 103
Registriert: 02.04.2022, 22:56
Kontaktdaten:

Re: Die perfekte C-API

Beitrag von x1m4 »

Sobald ich API Design höre, muss ich immer an das hier denken:
Bild
Benutzeravatar
Schrompf
Moderator
Beiträge: 4514
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Die perfekte C-API

Beitrag von Schrompf »

Schöne Zusammenstellung, Krishty. Ich bin nicht ganz sicher, ob wir beim selben Wunschzustand enden, aber ich bin gespannt.
Häuptling von Dreamworlds. Baut an was Neuem. Hilft nebenbei nur höchst selten an der Open Asset Import Library mit.
Benutzeravatar
Krishty
Establishment
Beiträge: 7850
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Die perfekte C-API

Beitrag von Krishty »

Alexander Kornrumpf hat geschrieben: 27.04.2022, 11:19In deinem Fall ist es aber so dass dein Flugsimulator als Konsument der API (via Callbacks) zu _allen_ Versionen kompatibel bleiben muss, weil du es verschiedenen Anbietern (Moddern) erlauben willst dir das API in verschiedenen Versionen anzubieten und alle sollen funktionieren. Das ist ein viel viel schwereres Problem als das was man üblicherweise unter API-Versionierung versteht.
Wir könnten hier ein Missverständnis haben, denn ich sehe mich eher als Anbieter der APIs und die Modder bauen ihre Flugzeuge darauf auf. Dass Flugzeuge mit mehreren Simulatoren kompatibel sind, ist so auch nicht geplant; eher, dass mein Simulator mehrere API-Versionen für verschiedene Flugzeuge anbietet.

Ich hatte das bewusst nicht genauer beschrieben, weil es einen eigenen Thread füllen könnte (und später auch wird), aber ganz kurz zusammengefasst: Mein Spiel sucht nach Mod-DLLs, ruft deren Initialisierung auf, und bittet sie dann, Callbacks für Physik/Rendering/etc. zur Verfügung zu stellen. Ich lasse die Simulation laufen und wenn ich ein Objekt simuliere, das nicht nativ einprogrammiert ist, greife ich auf die Callbacks zurück.

Ob mich das nun zum Anbieter einer API macht oder zum Benutzer, da hast du mich ehrlich gesagt verunsichert :D
x1m4 hat geschrieben: 27.04.2022, 12:32Sobald ich API Design höre, muss ich immer an das hier denken:
Nur, dass meine API keine Konkurrenz hat. Wäre so schön! :D
Schrompf hat geschrieben: 27.04.2022, 12:48 Schöne Zusammenstellung, Krishty. Ich bin nicht ganz sicher, ob wir beim selben Wunschzustand enden, aber ich bin gespannt.
Danke, ich auch ;)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
NytroX
Establishment
Beiträge: 264
Registriert: 03.10.2003, 12:47

Re: Die perfekte C-API

Beitrag von NytroX »

Super Beitrag.
Das mit den IDs kannte ich noch nicht... das werde ich demnächst mal auf der Arbeit versuchen und meine Kollegen so zum Wahnsinn treiben :-P
Aber es macht tatsächlich Sinn, man erinnere sich an den Windows 8 -> Windows 10 Sprung, oder den Versionssprung auf > 100, der demnächst bei Chrome*ium und Firefox ansteht. (Hm... "Gender"-Sternchen bei Derivaten... das könnte was werden. VSCode*ium).

Die "wichtigste" API hast du aber noch vergessen: Man nimmt eine Send(string) Funktion und einen Receive(string) Callback, und schickt sich Strings über einen (Unix-)Socket oder eine Pipe hin und her. VSCode/LanguageServer/Docker-Style. Dann hat man auch kein Problem mit Versionierung; da geht einfach alles ungeprüft den Bach runter :-) Und dann vermarktet man das als gRPC oder JSON-RPC.
Alexander Kornrumpf
Moderator
Beiträge: 1946
Registriert: 25.02.2009, 13:37

Re: Die perfekte C-API

Beitrag von Alexander Kornrumpf »

Krishty hat geschrieben: 27.04.2022, 21:14
Alexander Kornrumpf hat geschrieben: 27.04.2022, 11:19In deinem Fall ist es aber so dass dein Flugsimulator als Konsument der API (via Callbacks) zu _allen_ Versionen kompatibel bleiben muss, weil du es verschiedenen Anbietern (Moddern) erlauben willst dir das API in verschiedenen Versionen anzubieten und alle sollen funktionieren. Das ist ein viel viel schwereres Problem als das was man üblicherweise unter API-Versionierung versteht.
Wir könnten hier ein Missverständnis haben, denn ich sehe mich eher als Anbieter der APIs und die Modder bauen ihre Flugzeuge darauf auf. Dass Flugzeuge mit mehreren Simulatoren kompatibel sind, ist so auch nicht geplant; eher, dass mein Simulator mehrere API-Versionen für verschiedene Flugzeuge anbietet.

Ich hatte das bewusst nicht genauer beschrieben, weil es einen eigenen Thread füllen könnte (und später auch wird), aber ganz kurz zusammengefasst: Mein Spiel sucht nach Mod-DLLs, ruft deren Initialisierung auf, und bittet sie dann, Callbacks für Physik/Rendering/etc. zur Verfügung zu stellen. Ich lasse die Simulation laufen und wenn ich ein Objekt simuliere, das nicht nativ einprogrammiert ist, greife ich auf die Callbacks zurück.

Ob mich das nun zum Anbieter einer API macht oder zum Benutzer, da hast du mich ehrlich gesagt verunsichert :D
Ich hatte das schon alles verstanden, bin aber nicht sicher wie ich meinen Einwand besser erklären soll, als ich es oben schon versucht hatte. Ich bin der Meinung, dass Callbacks das Anbieter - Konsumenten Verhältnis umdrehen, aber das ist am Ende nur Semantik. Wir müssen uns nicht darüber streiten, was die Wörter richtigerweise bedeuten, sondern das Problem existiert, egal wie man es nennt. Lass es stattdessen von Aufrufendem und Aufgerufenen reden, wenn es der guten Sache dient.

Der Punkt ist, dass es _viel_ einfacher ist als Aufgerufener alte Aufrufe weiter zu unterstützen, die man mal konnte, wenn man neue hinzufügt, als es als Aufrufender ist, damit zu leben dass man manche Aufrufe manchmal machen kann und manchmal nicht, je nachdem welches Modul gerade geladen ist. API-Versioning meint eigentlich immer (?) ersteres.

Normalerweise ist es so, das die aufgerufene Seite zwei Versionen anbietet und der Aufrufende benutzt dann Version 1 oder migriert irgendwann zu Version 2. Er benutzt nie beide gleichzeitig, oder sollte es zumindest nicht, sagst du ja selbst. Nach der Migration zu Version 2 sollte es für einen Aufrufenden keinen Grund mehr geben jemals Version 1 zu benutzen.

Was du aber vorhast ist manchmal Version 1 aufzurufen und manchmal Version 2. Das ändert sich ja nicht dadurch, dass du derjenige bist, der die Versionen definiert. Der ganze Sinn eines Callback ist ja, dass es dich zum Aufrufer macht. Ist der Dissenz, dass du nicht glaubst, dass das unüblich ist, ist der Dissenz, dass du nicht glaubst, dass das schwerer ist als Kompatibilität in die andere Richtung, oder war es dir einfach noch nicht als bemerkenswert aufgefallen?
Benutzeravatar
Krishty
Establishment
Beiträge: 7850
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Die perfekte C-API

Beitrag von Krishty »

NytroX hat geschrieben: 27.04.2022, 21:47Die "wichtigste" API hast du aber noch vergessen: Man nimmt eine Send(string) Funktion und einen Receive(string) Callback, und schickt sich Strings über einen (Unix-)Socket oder eine Pipe hin und her.
Fun fact: Ich schreibe den Artikel gerade nicht weiter, weil ich einem meiner Programme beibringen muss, einen String an ein Win32-Fenster in einem anderen Prozess zu schicken …
Alexander Kornrumpf hat geschrieben: 27.04.2022, 21:58Was du aber vorhast ist manchmal Version 1 aufzurufen und manchmal Version 2. Das ändert sich ja nicht dadurch, dass du derjenige bist, der die Versionen definiert. Der ganze Sinn eines Callback ist ja, dass es dich zum Aufrufer macht. Ist der Dissenz, dass du nicht glaubst, dass das unüblich ist, ist der Dissenz, dass du nicht glaubst, dass das schwerer ist als Kompatibilität in die andere Richtung, oder war es dir einfach noch nicht als bemerkenswert aufgefallen?
Ich rufe Version 1 auf, falls die Mod keine Version 2 unterstützt. Irgendwann wird ein Mischbetrieb einsetzen, in dem die gut gewarteten Mods auf API-Version 2 laufen, und der nicht gepflegte Rest auf Version 1.

In diesem Mischbetrieb kann ich Mod-Callbacks Version 2 aufrufen, die gegenüber Version 1 tolle neue Dinge ermöglichen (und biete dann natürlich auch V2 meiner API an). Aber nur, wenn die Mod auch V2 unterstützt. Sonst kann ich nur V1-Callbacks aufrufen, und muss die alte V1-API reinreichen.

Mir fällt das tatsächlich nicht als bemerkenswert oder besonders schwierig auf, denn meine Lösung wäre, das alte Standard-Verhalten mitzuschleppen.

Ein Kandidat für so eine Erweiterung in V2 wäre ja KI. Habe ich noch nicht drin … ich weiß noch nicht einmal, wie genau das aussehen soll. Aber irgendwann müssen Mods mal KI für ihre Flugzeuge mitbringen, damit man die auch bekämpfen kann, statt nur selber fliegen. Für mich wäre es nun offensichtlich, dass mein Spiel die KI schlicht überspringt, falls eine Mod nur V1 anbietet. Denn so war es vorher eben.

Was verpasse ich?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Alexander Kornrumpf
Moderator
Beiträge: 1946
Registriert: 25.02.2009, 13:37

Re: Die perfekte C-API

Beitrag von Alexander Kornrumpf »

Krishty hat geschrieben: 27.04.2022, 22:44 Ein Kandidat für so eine Erweiterung in V2 wäre ja KI. Habe ich noch nicht drin … ich weiß noch nicht einmal, wie genau das aussehen soll. Aber irgendwann müssen Mods mal KI für ihre Flugzeuge mitbringen, damit man die auch bekämpfen kann, statt nur selber fliegen. Für mich wäre es nun offensichtlich, dass mein Spiel die KI schlicht überspringt, falls eine Mod nur V1 anbietet. Denn so war es vorher eben.

Was verpasse ich?
Ich glaube aber dass es eben nicht symmetrisch ist. Ist natürlich keine exakte Wissenschaft, aber ich hatte oben das Beispiel DirectX gebracht: Es ist leichter für DirectX und oder den Grafiktreiber, weiter DirectX9 zu emulieren als es für ein Spiel wäre die Wahl zwischen einem DirectX9 und einem DirectX12 Renderer anzubieten, ohne dass das Gesamtergebnis nach DirectX9 aussieht.

Am aufgerufeneren Ende bedeutet Abwärtkompatibilität, dass ich keine alten Features abschalten darf. Am aufrufenden Ende bedeutet Abwärtskompatibilität, dass ich mir nicht sicher sein kann, dass ein Feature dass ich benutzen will da ist. Das ist ein qualitativer Unterschied, oder nicht?
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4152
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Die perfekte C-API

Beitrag von Chromanoid »

@Alexander: Ich verstehe Deine Einwände nicht. Die Callbacks sind doch lediglich klar definierte Erweiterungspunkte. Ob ich ein Plugin-Struct mit Funktions-Pointern fülle, die dann diese Erweiterungspunkte darstellen und von der Engine aufgerufen werden, oder einfach Callbacks/Listener an die entsprechenden Stellen setze, ist doch eigentlich egal. In beiden Fällen brauche ich einen versionierten Kontext mit dem ich Spiel-Funktionen aufrufen kann.

Ich würde versuchen den Kontext als Parameter in den Callback reinzureichen. Dann hat die Engine alles unter Kontrolle - also:
void doGraphics(Plane * me, GraphicsContext * ctx) bzw. void doGraphics(Plane * me, ContextForGraphicsCallback * ctx) (letzteres, wenn da mehr Sachen verfügbar sein sollen als nur die Graphics-Funktionen). Wer sich dann den Kontext irgendwo hinspeichert, wird geteert und gefedert. Spannend wäre für mich, ob man auch One-time-Callbacks a'la setTimeout o.Ä. setzen kann, die dann auch weggeräumt werden müssen. edit: Ah da müsste man vermutlich ein struct statt einem Methoden-Pointer reingeben. In der struct könnte dann ein Pointer für die Callback-Methode, ein Pointer für die Aufräum-Methode und ein Pointer auf die "Closure-Struct" sein. Die Callback-Methode würde das "Closure-Struct" dann als einen der Parameter entgegen nehmen.
Benutzeravatar
Krishty
Establishment
Beiträge: 7850
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Die perfekte C-API

Beitrag von Krishty »

Chromanoid hat geschrieben: 28.04.2022, 01:49Ich würde versuchen den Kontext als Parameter in den Callback reinzureichen. Dann hat die Engine alles unter Kontrolle
Genau so mache ich’s auch :) Das erlaubt lustige Dinge im Closure, z. B. ein transaktionales Modell für Multi-Threading statt vieler kleiner Locks. Schreibe ich dann alles :)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Alexander Kornrumpf
Moderator
Beiträge: 1946
Registriert: 25.02.2009, 13:37

Re: Die perfekte C-API

Beitrag von Alexander Kornrumpf »

Ich habe mir mal ein Minimalbeispiel ausgedacht. Die meiner Meinung nach realistische Grundannahme ist, dass der Funktionsumfang eher wächst, also bei Versionssprüngen Sachen dazu kommen. Natürlich ist das Beispiel nicht an sich realistisch, welches Minimalbeispiel ist das schon. Ich behaupte aber dass es ein reales Problem illustriert:

Ich versuche jetzt nicht die ganzen C Signaturen richtig hinzubekommen, ihr denkt euch das richtige!

V1
setData(data)
getData()

V2
setData(data, data1)
getData(&data, &data1)

Für den Anbieter dieser API ist es trivial, beide Versionen zu unterstützen. Er kann V1 durch V2 emulieren, indem er data1 ignoriert. Auf der Seite des Anbieters gibt es mehr Information, als gebraucht wird, das ist nie ein Problem. Natürlich ist es nach der gleichen Logik auch kein Problem den Konsumenten pro Forma nach V2 zu portieren, aber der ganze Sinn der Übung ist es ja den Konsumenten nicht dazu zu zwingen.

Für den Konsumenten der API bedeutet beide Versionen zu unterstützen, dass er sinngemäß V2 durch V1 emulieren muss. Also das genaue Gegenteil. Der Konsument ist darauf ausgelegt, ein data1 zu verarbeiten zu können und zu wollen (sonst könnte er bei V1 bleiben), aber er muss die ganze Zeit den Sonderfall mitschleppen, dass er weniger Information hat als er eigentlich bräuchte. Ich behaupte das ist ein viel schwierigeres Problem.

Wenn ich irgendwo was über API-Versioning gelesen habe, dann war damit immer ersteres gemeint.

Ohne genaue Kenntnis von Krishtys Flugsimulator ist es letztlich Spekulation, in welchen der beiden Fälle er fällt. Es mag sein, dass ein KI Callback, wenn man es from scratch neu einführt, optional ist. Aber wenn die Physiksimulation einen Aspekt simuliert, den sie vorher nicht simuliert hat, stelle ich es mir (da in der Physik alles mit allem wechselwirkt) extrem nervig vor, das optional zu halten. Natürlich kann man das machen, mit entsprechendem Aufwand geht bestimmt alles, aber der tollste Versionierungsmechanismus nimmt einem diese Arbeit nicht ab.

Vielleicht ist das auch das Missverständnis oder das was meinen Einwand so schwer verständlich macht. Es ist wie ich am Anfang schon sagte ein konzeptuelles Problem, keine Kritik an der Implementierung der Versionierung. Die Lösung mit dem Kontext ist, gegeben die Anforderungen, absolut plausibel, darum geht es mir überhaupt nicht. Was ich sage ist: das ist nicht der schwere Teil des Problems.
Benutzeravatar
Krishty
Establishment
Beiträge: 7850
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Die perfekte C-API

Beitrag von Krishty »

Danke, es dämmert mir langsam!

Eine Mod soll niemals zwei API-Versionen unterstützen müssen. Bestenfalls wird sie mit dem neuen SDK neu kompiliert, an ein paar Stellen werden Aufrufe korrigiert/erweitert, und dann ab dafür. Schlimmstenfalls hat der Author keinen Bock und lässt sie auf Version Schlagmichtot vergammeln. Aber Mischimplementierung einer Mod ist – obwohl theoretisch möglich und tatsächlich so nervig, wie du zeigst – wirklich nicht vorgesehen.

Das Ziel ist, dass der Flugsimulator sich schneller entwickelt als die Mods, und dass Benutzer regelmäßig auf die neueste Version aktualisieren können, ohne dass ihre alten Mods kaputtgehen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Alexander Kornrumpf
Moderator
Beiträge: 1946
Registriert: 25.02.2009, 13:37

Re: Die perfekte C-API

Beitrag von Alexander Kornrumpf »

Krishty hat geschrieben: 28.04.2022, 08:57 Eine Mod soll niemals zwei API-Versionen unterstützen müssen.
Das hatte ich von Anfang an verstanden. Desweegen war von Anfang an die Frage ob deine Anwendung (die Seite die mehrere Versionen unterstützen muss) der Anbieter oder der Konsument in der nunmehr aufgezeigten Assymmetrie ist. Durch die Callbacks ist das für einen Außenstehenden jedenfalls nicht sofort offensichtlich.

Da du weiterhin überzeugt bist, kein Problem zu haben, vermute ich dass die Aufteilung der Programmlogik zwischen Anwendung und Mod so ist, dass du tatsächlich kein Problem hast. Ist ja klar, dass du deine Pläne besser kennst, als ich es tue. Oder du erlebst noch eine Überraschung. Berichte dann gerne, wie es ausgegangen ist!
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4152
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Die perfekte C-API

Beitrag von Chromanoid »

@Alexander: Ich checke es immer noch nicht so richtig, wo das Problem ist, wäre aber sehr daran interessiert es zu verstehen. Erweiterbarkeit ist praktisch meine Profession :D...

Hier mal Pseudo-Code wie ich mir den zweiten Vorschlag als Konsument vorstelle:

Code: Alles auswählen

// MyMod
#include "api-v1.h"

Api* api;

void levelStart() {
  api->createEnemy( ... );
}

export void mod() {
  api = createApi();
  api->onLevelStart( &levelStart );
}
Wie würdest Du das jetzt ohne Callback lösen (also insbesondere "onLevelStart" oder so)? Und warum hat das Einfluss auf die Komplexität?
Alexander Kornrumpf
Moderator
Beiträge: 1946
Registriert: 25.02.2009, 13:37

Re: Die perfekte C-API

Beitrag von Alexander Kornrumpf »

Es geht nicht darum, dass ich das "ohne Callback lösen" will.

Es geht darum, dass es (manchmal? immer?) eine Seite gibt die leichter versionieren kann als die andere (falls wir uns so weit denn einig sind) und dass Callbacks es schwerer machen, zu erkennen, welche Seite welche ist. Das ganz unabhängig davon, welche Seite das API definiert und technisch zur Verfügung stellt (ich denke das ist der Punkt der Verwirrung).

In meinem Minimalbeispiel, das hatte ich leider nicht geschrieben, weil ich es für offensichtlich bzw. irrelevant hielt, hatte ich die Idee, dass ein Konsument das API benutzen kann um data und data1 wegzuspeichern und wiederzuholen. Also natürlich würde man dieses konkrete Problem in der Realität nicht so lösen, aber dafür ist es eben ein Beispiel.

Es gibt also eine Seite, ich nenne sie "Anbieter" die die Funktion "speichere Daten" anbietet und eine Seite, ich nenne sie Konsument, die die Funktion "speichere Daten" konsumiert.

Jetzt kommt der Konsument daher und sagt, "ich würde den Anbieter gerne austauschbar machen und ich habe soviel Marktmacht, dass ich das API definieren kann". Also geht der Konsument her und macht die Krishty Sache wo er den Anbieter nach Callbacks für getData und setData fragt. Einfach nur eine zusätzliche Indirektion, sonst bleibt alles gleich.

Eure Intuition scheint zu sein, jetzt den frührern Konsumenten "Anbieter" zu nennen und den früheren Anbieter "Konsumenten", weil ihr danach zu gehen scheint, wer das API definiert (="anbietet").

Mein Einwand ist, dass der Umstand wer das API definiert eben gerade nicht ändert welche Seite die Funktionalität anbietet und welche Seite die Funktionalität konsumiert und somit auch nicht für welche Seite es einfach (einfachER?) ist mehrere Versionen gleichzeitig zu unterstützen.

Als Konsument in diesem Sinne musst du dir, wenn du das API warum auch immer diktieren kannst, sehr genau überlegen, ob du überhaupt davon profitierst, abwärtskompatibel zu Anbietern zu sein, die Features, die du brauchst, eben nicht anbieten.

Ein Flugsimulator ist dummerweise kein Minimalbeispiel, und deswegen ist es da eben nicht so übersichtlich, welche Seite welche ist. Natürlich ist Krishty da der Experte.
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 455
Registriert: 05.07.2003, 11:17

Re: Die perfekte C-API

Beitrag von Lord Delvin »

Chromanoid hat geschrieben: 28.04.2022, 01:49 Ich würde versuchen den Kontext als Parameter in den Callback reinzureichen.
Mir ist nicht ganz klar wie es gemeint war, aber ich würde in diesem "Kontext" die Funktionszeiger auf die in der Situation erlaubten Funktionen unterbringen und dann sollte der verbleibende Kritikpunkt gelöst sein.

Bei der Versionierung vermisse ich irgendwie den Prozess alte APIs wieder zu löschen. Wenn es den nicht gibt hat man die Inflation ja nur versteckt. APIs unbegrenzt zu unterstützen halte ich für nicht tragbar, da daran auch Architekturentscheidungen hängen können, die erhebliche Kosten aller Art verursachen können. Das muss ja nicht mal bedeuten, dass es damals nicht richtig war, was man gemacht hat. Man braucht halt einen gangbaren Prozess mit Vorwarnzeit und nicht dieses llvm-style "ist jetzt halt weg". Vielleicht geben die das auch durch Aushang im Apple HQ vorher bekannt ;)

Parallelisierung würde ich lösen, indem man erklärt, dass Zugriff auf globalen State verboten ist und sonst nichts weiter dazu sagen.

Was mich insgesamt etwas verwundert ist, dass man man bei dem Beispiel nicht über Datenformate geht. Ich habe hier vor vielen Jahren bestimmt selbst auch was Richtung Lua-API vorgeschlagen. Aber letztlich war das für mich zu teuer und hat das Projekt in den Abgrund getrieben. Mir sind die Grenzen klar, aber ich wüsste nicht, warum man ein Flugzeug nicht statisch realisieren kann.
XML/JSON in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
Benutzeravatar
Krishty
Establishment
Beiträge: 7850
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Die perfekte C-API

Beitrag von Krishty »

Lord Delvin hat geschrieben: 28.04.2022, 17:01Was mich insgesamt etwas verwundert ist, dass man man bei dem Beispiel nicht über Datenformate geht. Ich habe hier vor vielen Jahren bestimmt selbst auch was Richtung Lua-API vorgeschlagen. Aber letztlich war das für mich zu teuer und hat das Projekt in den Abgrund getrieben. Mir sind die Grenzen klar, aber ich wüsste nicht, warum man ein Flugzeug nicht statisch realisieren kann.
Antworten auf unterschiedlichen Ebenen:
  1. Modder können wilde Sachen machen, auf die ich nicht gekommen bin oder für die ich keine Zeit hatte. Und ich kann in der Simulation Abkürzungen nehmen (soll ja immernoch Spaß machen), ohne dass die sich sofort auf alles auswirken.
     
  2. Unterschätz nicht die Komplexität von Flugzeugsimulationen – der UH-60 in DCS zählt rund ein Megabyte Lua-Quelltext, und das ist „nur“ ein Hubschrauber. In Flugzeugen steckt deutlich mehr Code als in Autos, und den kannst du schlecht statisch darstellen, weil es halt ein Computerprogramm ist, das du simulieren musst. (Moderne Strahlflugzeuge können ohne Computer keine Sekunde in der Luft bleiben.)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Alexander Kornrumpf
Moderator
Beiträge: 1946
Registriert: 25.02.2009, 13:37

Re: Die perfekte C-API

Beitrag von Alexander Kornrumpf »

Krishty hat geschrieben: 28.04.2022, 18:00
Lord Delvin hat geschrieben: 28.04.2022, 17:01Was mich insgesamt etwas verwundert ist, dass man man bei dem Beispiel nicht über Datenformate geht. Ich habe hier vor vielen Jahren bestimmt selbst auch was Richtung Lua-API vorgeschlagen. Aber letztlich war das für mich zu teuer und hat das Projekt in den Abgrund getrieben. Mir sind die Grenzen klar, aber ich wüsste nicht, warum man ein Flugzeug nicht statisch realisieren kann.
Antworten auf unterschiedlichen Ebenen:
  1. Modder können wilde Sachen machen, auf die ich nicht gekommen bin oder für die ich keine Zeit hatte. Und ich kann in der Simulation Abkürzungen nehmen (soll ja immernoch Spaß machen), ohne dass die sich sofort auf alles auswirken.
     
  2. Unterschätz nicht die Komplexität von Flugzeugsimulationen – der UH-60 in DCS zählt rund ein Megabyte Lua-Quelltext, und das ist „nur“ ein Hubschrauber. In Flugzeugen steckt deutlich mehr Code als in Autos, und den kannst du schlecht statisch darstellen, weil es halt ein Computerprogramm ist, das du simulieren musst. (Moderne Strahlflugzeuge können ohne Computer keine Sekunde in der Luft bleiben.)
Ich denke einer der Gründe, warum ich ein Problem sah, wo du keinst sahst, ist dass man als Außenstehender leicht vergisst, wie crazy die Flightsim-Community ist. Deine Erwartung ist wahrscheinlich, dass die Menge an Code in der Mod ("Flugzeug") die Menge an Code im Spiel ("Simulator") bei weitem in den Schatten stellen wird, und dann macht alles was Du gesagt hast auch Sinn. Das wäre bei anderen Genres aber eher nicht so.
Benutzeravatar
Krishty
Establishment
Beiträge: 7850
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Die perfekte C-API

Beitrag von Krishty »

Alexander Kornrumpf hat geschrieben: 28.04.2022, 18:29Deine Erwartung ist wahrscheinlich, dass die Menge an Code in der Mod ("Flugzeug") die Menge an Code im Spiel ("Simulator") bei weitem in den Schatten stellen wird
This! Ist möglicherweise schon jetzt so; ich müsste nachsehen.

Fun fact: Valves Arbeit am neuen Counter-Strike verschleppt sich endlos, weil sie es nicht schaffen, die ganzen Community-Mods kompatibel zu halten. Dabei dürfte es sich aber in erster Linie um Daten-Mods handeln (Maps, Texturen, etc.) (um das nicht wie Widerspruch aussehen zu lassen).
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Alexander Kornrumpf
Moderator
Beiträge: 1946
Registriert: 25.02.2009, 13:37

Re: Die perfekte C-API

Beitrag von Alexander Kornrumpf »

Krishty hat geschrieben: 28.04.2022, 18:32Dabei dürfte es sich aber in erster Linie um Daten-Mods handeln (Maps, Texturen, etc.) (um das nicht wie Widerspruch aussehen zu lassen).
Es ist dann nicht nur kein Widerspruch sondern sogar Bestätigung. Ich glaube dass sie das Problem mit einem versionierten API genauso hätten. Wenn die Daten, die du brauchst, nicht da sind, sind sie nicht da, egal wie schön das Interface ist.
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 455
Registriert: 05.07.2003, 11:17

Re: Die perfekte C-API

Beitrag von Lord Delvin »

OK sehe ich ein. In dem Detailgrad braucht man natürlich das C API.
XML/JSON in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
Benutzeravatar
Krishty
Establishment
Beiträge: 7850
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Die perfekte C-API

Beitrag von Krishty »

Kurzer Nachtrag dazu: Einige Dinge habe ich komplett statisch definiert, bspw. Räder. Kanonen stelle ich gerade auf eine statische Beschreibung um. Die Triebwerke sind 90% statisch definiert, brauchen aber ein Callback weil die Berechnung des Schubs in Abhängigkeit von Temperatur/Luftdichte/Geschwindigkeit/Feuchtigkeit echt komplex ist :(
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten