HowTos

Allgemeine Probleme (und Lösungsvorschläge) für Prolog und XPCE.

GUI-Programmierung
  • Scrollbalken am Fensterrand:
    Sobald grafische Elemente platziert werden, die nicht mehr im Sichtbarkeitsbereich des Fensters liegen, erscheinen am Rand Scrollbalken. Um dies zu unterbinden kann man folgendes Aufrufen: send(@window, scrollbars, none)

  • Textarea um größere Textmengen anzuzeigen:
    Wir haben keine Textarea für XPCE gefunden, jedoch kann man ein Objekt vom Typ picture erstellen und in diesem den Text anzeigen lassen:
    new(@textarea, picture('',size(150,100))),
    send(@window, display, @textarea)
    Vorteil ist, dass bei Bedarf automatisch Scrollbalken erstellt werden.
    Nachteil ist, dass auch wenn man Koordinaten für das Objekt angibt, diese ignoriert werden und das Objekt UNGEFAEHR bei point(20,20) angezeigt wird.
    Probleme gibt es, wenn man mehrere Textareas anzeigen möchte. Man kann man so vorgehen: Erste Textarea wie gewohnt erstellen. Um daneben eine weitere zu platzieren, muss diese so breit sein, wie es beide Textareas zusammen sein sollen. Außerdem muss sie (zeitlich) vor der anderen Textarea erstellt werden. Die kleinere Textarea wird dann über die größere gezeichnet und man kann den nicht über-gezeichnete Bereich als 2. Textarea benutzen.
    Leider ist die Lösung plattformabhängig: Unter Windows werden die zeitlich zuerst erstellten Komponenten zuoberst gemalt, unter Linux die zeitlich zuletzt erstellten.

  • Tastaturfokus automatisch setzen:
    Um den Tastaturfokus automatisch auf ein Textfeld zu setzen, gibt es den Befehl: =send(@textfield4, activate(true))=
    Leider scheint dies nicht richtig zu funktionieren. Interessanterweise klappt es aber, den Tasturfokus an das nächste Feld weiter zu geben: send(@textfield4, next).
    Wenn man also den Tastaturfokus auf das vorige Feld setzt und ihn dann an das nächste Feld weitergibt, hat man trotzdem den gewünschten Effekt.

  • Sechsecke in XPCE verwenden:
    Sechsecke sind als graphische Elemente in Prolog nicht darstellbar. Um Sechsecke anzuzeigen, kann man daher ein Bild von einem Sechseck in einem Bildbearbeitungsprogramm erstellen und mit transparentem Hintergrund als GIF- Datei speichern. Prolog erkennt transparenten Hintergrund dann zur Darstellung an. Intern ist das Bild jedoch weiterhin als Rechteck gespeichert. Wenn man das Bild anklickbar machen möchte, stößt man auf das Problem, dass das gesamte Rechteck klickbar ist, also auch der transpartente Bereich. Wie konnten dies nur so lösen, dass wir in das Sechseck ein Kreis gelegt haben. Dieser ist als graphisches Element in XPCE standardmäßig implementiert und deshalb auch darstellbar. Dieser kann nun hinter das Sechseck gelegt werden und ist damit unsichtbar. Durch Anpassung des Durchmessers kann man den Kreis so sehr vergrößern, dass er das Sechseck beinahe ausfüllt.

  • Neuere graphische Elemente überschreiben alte:
    Oft ist der Effekt erwünscht, dass man ein Grafikobjekt über ein anderes legen kann und das erste unter dem neuen Objekt nicht mehr zu sehen ist. Sollte man diesen Effekt nicht wünschen, muss man zunächst das neue Grafikobjekt auf das alte legen. Die Position des alten Objekts muss nun erneuert werden, damit es wieder als vorderstes angezeigt wird. Dazu setzt man das Objekt auf eine andere Koordinate und danach sofort zurück auf die alte. Für den Nutzer ist dieser Wechsel nicht zu sehen, trotzdem wird das alte Objekt nun über dem neuen angezeigt.

  • Dateien im laufenden Spiel öffnen:
    Dateien, die keine Prolog- Dateien sind, lassen sich nur über einen Shellbefehl öffnen. Hier kann man ausnutzen, dass man im Quelltext bestimmte Befehle an die Konsole schicken kann. Dies funktioniert in jedem Betriebssystem. Betriebssystem abhängig ist jedoch, wie man aus der Konsole eine Datei öffnen kann:
    • Linux: shell('xdg-open Dateiname &')
    • Windows: shell('start Dateiname')
    • Mac: shell('open Dateiname')
      In diesen Fällen öffnet das Betriebssystem die Datei mit der Standardsoftware, die für den Dateityp festgelegt wurde. Man kann stattdessen auch explizit einen Programmnamen angeben.

  • Abmessungen des Spielfelds:
    Schon vor Beginn der Arbeiten an der Grafik sollte man sich darüber Gedanken machen, auf was für einer Auflösung das Spiel später gespielt und präsentiert werden soll. Anhand dieser Entscheidung sollten alle Abmessungen ausgewählt werden.

Netzwerkkommunikation und Sockets
  • Nach dem Senden einer Nachricht reagiert Prolog beim Empfänger nicht mehr:
    Es wurde kein Zeilenumbruch (am Ende des Atoms) mitgesendet! einfach '\n' hinterhersenden.
  • Kein Ereignis, wenn ein Klient die Verbindung beendet / verliert:
    Es gibt ein Ereignis, wenn ein Kleint etwas an den Server schickt. Dieses Ereignis kann man dann an ein Prädikat binden, welches dann mit den ankommenden Text etwas machen kann. Ebenfalls gibt es ein Ereignis, wenn ein Klient sich zu dem Server verbindet - jedoch gibt es keins, wenn jemand den Server verlässt. Wir haben uns die eingehenden Sockets vom Server in einer Datenbasis gespeichert und wenn jemand den Server verlässt ist dieses "Objekt" (bzw. der Name des Objekts) noch in der Datenbasis. Wenn sich nun ein neuer Klient verbindet wird wieder das "alte Objekt" vergeben.
Beispiel
A verbindet sich mi Server und verlässt diesen dann wieder. B verbindet sich nach Verlassen von A zum Server und nimmt somit die Identität von A an - was total unpraktisch ist.
Lösung
Jeder Klient sollte ordentlich mit einen bestimmten Befehl an den Server (zB quitt) diesen verlassen können (so, dass der Server den Verbindungsabbruch verwalten kann). Ferner ist es hilfreich ein sogenanntes Pingpong-Event einzuführen um sogenannte Timeouts festzustellen (Verlust der Verbindung durch Absturz des PCs oder schlechter WLAN-Empfang etc.)
(Ich empfehle für das Pingpong-Event auch einen eigenen Thread zu verwenden, dann kann man die Überprüfung periodisch durchführen und die Zeit zwischen den Perioden durch das wait_for… steuern.)

Multithreading

Es wurde eine Client-Server-Architektur gewählt und damit mehrere Spiele gleichzeitig auf dem Server stattfinden können wurden die einzelnen Spiele in eigenständige Threads ausgelagert. Dabei sind wir über einige Probleme gestolpert, die wir -mehr oder weniger- gelöst haben.

  • Globale Datenbasis
    Das erste Problem war, dass jeder Thread nicht automatisch eine eigene Datenbasis verwaltet und dies natürlich zu ungewollten Seiteneffekten kommt. Bei unseren ersten Tests mussten wir schon feststellen, dass irgendetwas nicht stimmte, wenn der "Bösewicht" in allen Spielen die gleiche Spielfigur war - und dieses Problem nicht durch den Zufallsgenerator kam.
    Die Lösung wurde schnell gefunden: Das Prädikat thread_local/1, was wie dynamic/1 oder multifile/1 zu benutzen ist. Alle Datenbestände der einzelnen Spiele müssen von "der Aussenwelt abgeschnitten" werden - das Management der Klienten (also wer an welchem Spiel teilnimmt / ob man an mehreren Spielen teil nimmt etc.) übernimmt der Server, der dann die Nachrichten an die einzelnen Threads schickt.

  • Threads terminieren vorzeitig / garnicht
    Ein Problem für das wir leider keine ideale Lösung gefunden hatten war, dass die einzelnen Threads sich einfach beenden, nachdem sie die erste Klausel erfolgreich abgearbeitet haben. Da man ja aber noch Nachrichten der Spiele an das Spiel schicken will sollte der Thread "am Leben gehalten" werden.
    Die Lösung, die wir gefunden haben ist leider nicht Plattformunabhängig. So hat es auf den Suse-Rechnern im Labor des Informatikums mit einer Endlosschleife und einen kleinen performance-steigernden Trick ohne Probleme geklappt - jedoch hängt sich ein Thread mit diesem Trick unter Windows oder anderen Linux-Systemen (??) auf und unter Mac OS X wird der CPU fast komplett ausgelastet, auch wenn der Thread beendet wurde (Funktionalität bleibt unter Mac trotzdem erhalten). Die Zeile wait_for… muss ggf. entfernt werden um Funktionalität in Windows oder anderen Systemen zu behalten - dadurch steigt allerdings die Prozessorlast (da ja endlos Alternativen produziert werden durch repeat/0 und mit fail/0 fehlschlagen und er zurück ins Backtracking springt). Wir haben ein Prädikat keep_alive/0 definiert, was wie folgt aussieht:

keep_alive :- repeat, wait_for_input([],_,0), fail.

  • Plattformabhängigkeit
    Multi-Threading kann die Plattformunabhängigkeit beschränken. Unter Windows sind wir auf Probleme gestoßen, die wir unter Linux nicht hatten:
    • Verschiedene Nachrichten, die wir über die Sockets verschicken wollten, plötzlich in einer anderen Reihenfolge versendet, als wir sie notiert hatten!
    • Der Befehl wait_for_input/3 hat unter Windows für unerklärliche Probleme gesorgt (vielleicht ist er auch für das obige Problem verantwortlich)

-- LidiaKhmylko -- 07 Mar 2011
 
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Foswiki? Send feedback