Wegen dem Crash-Log: Das kann schon sein, dass der Stacktrace nicht erzeugt werden kann. Du kannst dir den Code in SignalHandler.cc anschauen (in der util Library). Es wird versucht, eine GDB-Instanz an den laufenden Orxonox-Prozess zu attachen, um den Stacktrace zu erzeugen (unter Linux). Dabei kann natürlich einiges schief gehen.
Zu deinem eigentlichen Problem:
Ich habe das Spiel unter Windows mit Visual Studio debugged und ein paar Dinge gefixt und committed.
1) std::list statt std::vector
Das erste, worüber sich der Debugger beschwert hat, war ein kaputter Iterator in TowerDefense::tick und zwar in jenem Loop, in dem die Enemies zerstört werden, wenn sie am Ende des Pfades angelangt sind. Grundsätzlich war es dort schon einmal komisch, dass zuerst vector.erase(it) aufgerufen wurde und danach it->destroy(). Im Grunde wurde also auf ein ungültiger Iterator zugegriffen. Wenn man das gefixt hat, war der Debugger immer noch nicht glücklich damit, dass mitten im Loop Elemente aus dem Vector gelöscht wurden. Generell ist es etwas heikel, wenn man mit std::vector arbeitet und Elemente entfernt. Der vector ist vor allem dann geeignet, wenn man eine fixe Anzahl Elemente hat. Beispielsweise immer 20 Gegner, dann kann man von enemies[0] bis enemies[19] direkt darauf zugreifen. Wenn man aber einzelne Elemente hinzufügen oder entfernen muss, dann ist std::list besser geeignet.
Ich habe deshalb enemies_ so angepasst, dass es jetzt std::list statt std::vector ist. Damit war der Debugger erstmal zufrieden.
Übrigens: Es kann gut sein, dass das bei dir gar kein Problem war. Zum Beispiel dann, wenn der Iterator zwar eigentlich ungültig ist, aber in seinem Speicherbereich immer noch gültige Pointer existieren. Visual Studio hat im Debug-Modus aber ein paar zusätzliche Checks, die in solchen Fällen Alarm schlagen.
Commit:
http://www.orxonox.net/changeset/10587
2) Zerstörte Waypoints
Sobald ein Gegner am Ende des Pfades angelangt ist und zerstört wurde, trat der nächste Fehler auf. Der Grund ist, dass (vermutlich aufgrund eines Flüchtigkeitsfehlers) alle WaypointController dieselben Waypoints hatten. Da aber jeder WaypointController, wenn er zerstört wird, seine Waypoints ebenfalls zerstört, heisst das, dass alle anderen WaypointController plötzlich eine Liste von zerstörten Waypoints besassen, was unweigerlich zum Crash führt.
Ich habe dann gesehen, dass in TowerDefense::addTowerDefenseEnemy eigentlich bereits eigene Waypoints für jeden Controller erzeugt werden. Allerdings wurden diese gar nicht verwendet, die Funktion hat also bloss ein Memory-Leak erzeugt.
Ich habe den Code jetzt so angepasst, dass der neu erzeugte Waypoint auch tatsächlich verwendet wird. Somit funktioniert nun auch das Zerstören der Enemies am Ende des Pfades.
Commit:
http://www.orxonox.net/changeset/10588
3) Tower schiesst Enemy ab
Das war aber noch nicht alles, denn wenn ein Tower ein Enemy abgeschossen hat, gabs direkt den nächsten Crash. Der Grund dafür ist, dass TowerDefense eine Liste von allen Enemies hat (die Liste aus Punkt 1 oben). Wenn einer abgeschossen wird, ist der Pointer in der Liste nicht mehr gültig. Da (praktischerweise) eine Liste von WeakPtr verwendet wird, kann man mit einem NULL-Check prüfen, ob der Enemy noch existiert (und falls nicht, kann man ihn aus der Liste entfernen).
Ich habe diesen Check dementsprechend hinzugefügt.
Commit:
http://www.orxonox.net/changeset/10589
----
Und nun zum Problem mit dem Controller:
Lustigerweise funktioniert der Code, wenn man auf Zeile 196 anstelle von setController setXMLController aufruft. Der Unterschied liegt darin, dass setXMLController auch noch bHasLocalController_ auf true setzt. Und wenn du dir SpaceShip::tick anschaust, dann siehst du, dass sich ein SpaceShip nur dann bewegt, wenn diese Variable true ist (und die Towerdefense-Enemies erben von SpaceShip). Der Grund dafür ist die Netzwerktauglichkeit - ein Schiff ist zwar auf allen Clients sichtbar, aber nur ein Spieler (derjenige mit dem local controller) darf es steuern. (Oder, im Falle von Bots, darf es nur der Server steuern.)
Mein Vorschlag ist, dass du setXMLController aufrufst - oder einen public Setter für bHasLocalController_ machst (aber das ist vielleicht ein bisschen heikel).
Übrigens: Wegen dem Unterschied zwischen getController und getXMLController ist mir noch etwas eingefallen. Ein Controller ist in Orxonox normalerweise eine langlebige Sache. Beispielsweise hat der Spieler einen Controller (NewHumanController) oder jeder Bot (FormationController). Diese Controller bleiben das ganze Spiel über "am Leben". Der Controller steuert dabei manchmal ein Schiff, manchmal eine Specator-Kamera. Dann wird das Schiff zerstört, er kriegt ein neues, etc. Grundsätzlich kann man also sagen, dass der Controller unabhängig vom ControllableEntity existiert.
Beim XML-Controller ist das anders: Er wurde im Level-File erzeugt und hat nur eine Aufgabe, nämlich das ihm zugewiesene Schiff zu steuern. Wenn das Schiff zerstört wurde, kriegt der Controller kein neues Schiff mehr - er ist tot. Deshalb wird in diesem Fall der XML-Controller zerstört.
Du kannst dir das im Destructor von ControllableEntity anschauen: Der XML-Controller wird ebenfalls zerstört, der normale Controller hingegen nicht.
In meinen Augen ist das aber eine unglückliche Lösung. Es wäre trotz allem besser, nur einen Controller zu haben. Um den "XML-Controller" automatisch zu zerstören, wenn das Schiff zerstört wurde, gäbe es bessere Alternativen.