![]() |
Pokerprojekt realisierung
Also, hier ist shcon wieder ein Thread ovn mir, ich hoffe das ist nicht schlimm, dass ich jetzt soviele Threads öffne, aber hab halt ne Menge Fragen, damit ihc hinterher nicht wieder mein komplettes Projekt verwerfen darf...
Also wie der Titel schon sagt, programmier ich gerade ein Pokerspiel, bzw. fange gerade richtig an! Ich wollte es als Netzwerkspiel programmieren und dazu habe ich noch einige Fragen. Habe noch nicht soviel Erfahrung auf dem Gebiet und bisher nur ein kleines Tutorial zu der Programmierung eines kleinen Chats durchgearbeitet. Aufgrund dieser Kenntnisse habe ich nun folgendes überlegt. Ich habe das Pokerspiel als Programm, dort kann ich entweder einem Netzwerkspiel beitreten oder eines erstellen. Wenn ich dann zB eines erstelle, gebe ich zu Beginn einige Werte ein wie zB Blinds zu Beginn, Startgeld, maxSpielerzahl etc... Diese Werte würde ich dann an eine Form die als Server dient (mittels TServerSocket) weitergeben. Von da aus könnte ich dann immer wieder den aktuellen Stand (welcher Spieler dran ist etc.) abfragen. Was haltet ihr von der Idee? Realisierbar? Verbesserungsvorschläge? Werde mich da noch genauer informieren, aber ich wollte halt erstmal hier fragen ob das so realisierbar und sinnvoll ist! MfG und nochmal sorry für die vielen Threads im Moment! |
Re: Pokerprojekt realisierung
Zitat:
also ich bin jetzt kein Moderator o.Ä., aber ich denke viele Threads sind kein Problem (darüber hat sich afaik noch keiner beschwert). Wichtig ist halt nur, dass die Threads auch wirklich unterschiedliche Fragen beinhalten (da sollst Du sogar für jede Frage einen Thread eröffnen!). Wenn es aber 5x die Frage X von Dir gibt, dann weiß ja keiner wann die wo schon beantwortet wurde. Worauf Du mehr achten solltest ist die Rechtschreibung, Dinge wie shcon, ovn und ihc sollten nicht sein. Einfach vorm Abschicken oder während des Tippens nochmal überfliegen und korrigieren (kann ja mal passieren, häuft sich aber bei Dir auffällig). Zitat:
Was allerdings den momentanen Plan angeht, so denke ich kannst Du doch ein wenig etwas verbessern. Was hier ein wenig störrt ist das Formular. Ein Server nimmt Daten entgegen, kennt den Zustand usw., alles gut, aber die Darstellung hat damit nichts zu tun. Du kannst einfach eine Klasse verwenden, die das ganze realisiert. Damit schaffst Du eine gewisse Unabhängigkeit. Verbesserst Du irgendwann das Aussehen der Formulare/die Steuerung, so sollte das keinen Einfluss auf den Server haben. Die Spiellogik kann einfach gleich bleiben. Etwas allgemeiner lautet das Ziel, dass Du immer ein Problem in eine Einheit steckst. Eine solche Einheit kann dann z.B. eine Klasse sein (oder ein Modul, eine Komponente, etc.). Jedenfalls ist die Darstellung ein Problem, die Verwaltung ein anderes und als Drittes kannst Du noch den aktuellen Zustand abkapseln (was hat wer auf der Hand, wer ist am Zug, ...). Genaueres findest Du, wenn Du nach MVC (Modell, View, Controller) suchst. Das Modell sind dabei wirklich nur die Daten (ohne große Logik). Z.B. würde ein Spieler zum Modell gehören. Im Modell speicherst Du dabei nur was er auf der Hand hat, wie viel Geld er hat, wie er heißt,... Aber eben nur solche Informationen! Alle Methoden dienen dann nur dazu, dass Du den Zustand änderst, über Änderungen benachrichtigst und die Konsistens prüfst (z.B. darf kein Pokerspieler je mehr Geld setzen als er hat, solche Änderungen kannst Du dann verbieten). Das View erklärt sich von selbst. Dabei handelt es sich um ein Formular, dass einfach Datensätze aus dem Modell bekommt und anzeigt. Wo die Datensätze herkommen weiß es eigentlich gar nicht, es bekommt von irgendwo Daten und zeigt diese an. Zudem stellt das View die Schnittstelle zum Benuzer her, alle Aktionen die vom Benutzer ausgehen muss das View melden können (z.B. Stichwort Observer, geht aber auch ohne). Der Controller enthält dann die eigenltiche Logik. Alles was der Benutzer tut wird dem Controller gemeldet. Dieser reagiert darauf, in dem er das Modell verändert. Dieses benachrichtigt dann entweder direkt das View oder wieder den Controller, der dann als Vermittler zwischen View und Modell steht. Im Letzteren Fall wird das View dann vom Controller benachrichtigt. Wer das View benachrichtigt ist eigentlich auch egal, dass View bekommt einen neuen Zustand übergeben und zeigt diesen an (egal wo der her kommt). Der Vorteil dieser Trennung liegt (wie gesagt) in der Unabhängigkeit. Änderst Du z.B. beim View das Aussehen der Karten, so hat das keinen Einfluss auf die anderen Teile, da dies nur innerhalb des Views geschieht und nur die Darstellung betrifft. Zudem findest Du Dich in den einzelnen Klassen leichter zurecht, da sie nur genau eine Aufgabe beinhalten. Gruß Der Unwissende |
Re: Pokerprojekt realisierung
So, sorry erstmal für die Rechtschreibung, diese Flüchtigkeitsfehler passiern mir recht häufig, sollte ich mir mal abgewöhnen ;-)
Ich versuche drauf zu achten! Also so wie ich das verstanden habe, soll ich für alle "Oberbereiche" eine eigene Klasse schreiben? Hab da noch nicht so viel Erfahrung, das mach ich dann am besten jeweils iner eigenen Unit oder? Und den Server also ohne extra Form und auch ne eigene Klasse? Also habe ja vor längerem schonmal angefangen ein Pokerspiel zu programmieren, allerdings hatte ich da noch keine Netzwerkfunktion und so eingeplant, sondern mich vorerst nur mit der Darstellung der Karten und dem Abfragen der Hände beschäftigt. Deswegen bin ich nun etwas ratlos, wie ich das alles realisieren soll, is für mich schon ein etwas größeres Projekt :) Danke für die großartige Hilfe hier! :dp: |
Re: Pokerprojekt realisierung
Zitat:
Was die Oberbereiche angeht, so stimmt das, die kommen in eine Klasse, aber die können natürlich selbst wieder aus anderen (kleineren) Klassen zusammengesetzt werden. An sich ist die Idee der OOP (Objekt Orientierten Programmierung), dass eine Klasse ein bestimmte Problem löst. Man versucht dabei möglichst allgemeine Probleme zu lösen, nicht nur sehr spezielle Fälle. Das ganze realisiert man durch Abstraktion und kann dann eben die Lösung eines sehr abstrakten Problems für sehr viele unterschiedliche Fälle verwenden. Zitat:
Zitat:
Wie Du vielleicht bemerkt hast blieb das Modell wirklich völlig außen vor. Auch die Änderungen um etwas zu erweitern halten sich bei sauberer Modellierung immer in Grenzen. Alles was Du tun must ist dazu Dein Problem (das gesamte Pokerspiel) in kleinere Teilprobleme zu zerlegen (z.B. Darstellung der Karten, Spieler, Steuerung, ...). Unterprobleme kannst Du dann noch weiter zerlegen, so besteht ein Spieler aus verschiedenen Eigenschaften, kann aktiv sein oder warten, kann callen, erhöhen, ... Hier findest Du vielleicht wiederum Probleme, die unabhängig voneinander sind (z.B. das Speichern der Daten vom Spieler und die Anzeige Selbiger). Dein eigentliches Projekt baust Du dann einfach aus den Lösungen dieser kleinen Probleme zusammen. Das hat den Vorteil, dass Du sehr leichte/kleine Probleme hast, die kann man viel besser überschauen, testen und entwickeln. Das gibt dann weniger Fehler und ist leichter wartbar. Zudem kannst Du wenn Du das Programm erweiterst immer vieles wiederverwenden. So kann es reichen, dass Du einen solchen kleinen Teil austauscht oder erweiterst, manchmal sind es auch mehrere, aber selten oder nie alle. Hast Du hingegen eine große Unit, in der alles drin steht, dann musst Du natürlich direkt die Unit bearbeiten, die alles enthält. Gibt es hier Abhängigkeiten sind die schwerer zu berücksichtigen und Änderungen haben schnell viel größere Auswirkungen. Da ist es dann schwer den Überblick zu haben. Gruß Der Unwissende |
Re: Pokerprojekt realisierung
Suuuuuuper Tips! Danke, hast mich um einiges weitergebracht, wenn weitere Fragen gibt, melde ich mich!
|
Re: Pokerprojekt realisierung
Ok, jetzt nochmal meine genauere Planung.
Ich habe den Server, dem sind alle Klassen bekannt, das heisst zB die Spieler oder das Kartendeck. Die Clients senden dann Befehle an den Server, welcher dann die Werte aendert (zB Geld der Spieler oder so). Per Timer rufen die Clients dann jede Sekunde oder so, die Werte die sie zum Zeichnen brauchen ab. (Das würd ich mit Textnachrichten oder so realisiern!?) Hat zur Folge, dass die Clients die Klassen nicht direkt kennen, sondern nur die Infos über den Server abrufen können (Server hat die Klassen in uses und Clients den Server). Ist das richtig gedacht so? Wird das funktioniern oder ist es sinnvoller das anders zu machen? Will mich nochmal absichern, bevor ich die ganze Arbeit reinstecke und hinterher gehts net. |
Re: Pokerprojekt realisierung
Zitat:
Das häufige/ständige Abfragen von Werten wird als Polling bezeichnet, Du schaust ständig nach ob sich etwas geändert hat. Dabei gibt es zwei Probleme, ändert sich etwas, bekommst Du es vielleicht erst später mit (hier können 0.9999999 Sek. vergehen, bevor Du die Änderung bemerkst). Das andere Problem ist, dass Du häufig völlig umsonst nachfragst, weil sich noch überhaupt nichts geändert hat. Nicht zuletzt kann es natürlich auch zu einer gewissen Last führen, wenn alle gleichzeitig anfragen. Der deutlich intuitivere Weg ist es, dass Du die Clienten über Änderungen Informierst. Der Server erfährt immer von jeder Änderung und das sofort. Hat sich etwas geändert, kannst Du nun alle Clienten über die Änderung informieren und dabei selbst steuern, wer wann benachrichtigt wird (ob alle nacheinander oder ein paar/alle parallel). Somit erfahren alle Clienten schnellst möglich von Veränderungen und nur genau dann, wenn es wirklich Veränderungen gab. Ich denke Dein vorgeschlagener Weg ist einfacher zu programmieren, aber da solltest Du selbst den jeweiligen Aufwand abschätzen. An sich denke ich, dass Du den Weg wie gesagt selbst nicht 100%ig richtig findest (kann mich da aber auch irren). An sich wird die ereignisbasierte Benachrichtigung aber häufiger begegnen, da man hier viele Ressourcen spart und sich über Dinge wie Update-Intervalle keine Gedanken machen muss (reicht eine Sekunde, erzeugt eine halbe Sekunde zu viel Last, ..). Die Frage ist natürlich, wie benachrichtigt man alle Clienten? Dazu möchte ich einfach mal auf folgenden Thread verweisen ![]() Darin wird kurz das Prinzip von Ereignissen in einer Liste (auch als Observer-Pattern bekannt) behandelt. Dieser Weg beschreibt dabei, wie man ein Ereigniss beobachtet (sich über den Eintritt benachrichtigen lässt). Das ganze kannst Du dabei auch auf das Netzwerk übertragen. Schließt sich ein Client einem Netzwerk-Spiel an, verbindet er sich einfach mit dem Server. Dabei wird dem Server einfach mitgeteilt, dass ein neuer Spieler hinzu gekommen ist und über alle Änderungen benachrichtigt werden möchte. Beim Verbinden erhält der Server dabei automatisch alle Daten vom Clienten (IP-Adresse und Port). Der Rest (Kommunikation über Textnachrichten) kann dabei erhalten bleiben. Bekommt der Server eine Änderung mit, so schickt er einfach eine Nachricht an alle ihm bekannten Clienten. Hoffe das hilft weiter (und klingt nicht zu kompliziert, ist es nämlich gar nicht). Schau einfach mal, wie Du selbst den Aufwand einschätzt und frag ggf. natürlich gerne wieder nach! Gruß Der Unwissende |
Re: Pokerprojekt realisierung
Alsooo...das es sich so anhört als wär ich damit nicht zufrieden, lag denke ich eher an meiner Unwissenheit, aber ich sehe das Problem an meiner Lösung! ;-)
Daaaanke für deine wirklich sehr hilfreichen Lösungsansätze. Also den Link les ich mir denke ich mal morgen genauer durch, fehlt mir jetzt die Konzentration für ;-) Hab ja noch einen Thread eröffnet, um zu fragen ob ich ganze Klassen übers Netzwerk übertragen kann, da ist nun die Sache wie ich die Sachen übertrage auch schon wieder zum Hauptthema geworden ^^ Also du denkst immernoch das ist mit einfachen Textnachrichten möglich und am simpelsten? |
Re: Pokerprojekt realisierung
Zitat:
Die Frage wie Du hier die Informationen überträgst (quasi das gewählte Protokoll) sollten etwas mehr im Hintergrund stehen. Wichtiger ist, welche Informationen Du überträgst. Ganze Klassen zu übertragen ist häufig nicht nötig, da reicht häufig schon weniger Information aus. Der Vorteil an einfachen Textnachrichten liegt einfach darin, dass Du sehr einfach beobachten kannst, was übertragen wurde. Für binäre Formate wird das ein wenig schwieriger, ist aber natürlich ebenso möglich! Der klare Nachteil an Textnachrichten bestünde darin, dass Du die Informationen beim Sender in einen String umwandelst, den String überträgst und beim Empfänger wieder dekodierst, nicht unbedingt der schönste/nötigste Weg! An sich würde ich Dir jedenfalls vom Einsatz von komplett serialisierten Klassen abraten, die sind hier nicht nötig! Vielmehr läufst Du sonst Gefahr, dass Änderungen an den Klassen größere Konsequenzen haben könnten. Besser ist es, wenn Du nur die tatsächlichen Änderungen überträgst und der Empfänger dies geeignet weiter leitet (also das Update der Daten auf eigenen Klassen erfolgt). PS.: Sorry, bin zu faul nach dem anderen Thread zu suchen und dessen Fazit hier mit zu diskuttieren und/oder den Beitrag mit dort rein zu stellen. Falls sich also etwas wiederholt haben oder den dortigen Ansätzen wiedersprechen sollte, dann nicht wundern. PPS.: Eigentlich gehört ja auch jede Frage in einen eigenen Thread, also weitere Themen/Probleme lieber direkt in einem solchen eigenen Thread diskuttieren und auf den Thread verweisen (immerhin trifft das Thema Pokerprojekt realisierung ja nun auf alle Teilaspekte/Diskussionen zu). Gruß Der Unwissende |
Re: Pokerprojekt realisierung
Jo, ok super danke dann werd ichs mir Textnachrichten versuchen, hab auch festgestellt, das 50% der Informationen sowieso nur für den Server von Belang sind.
Also falls ich da jetzt keinen Denkfehler gemacht hab :D dann muss ich garnet soviele Werte übertragen! Nagut, danke! |
Re: Pokerprojekt realisierung
Boah jetzt hab ich grade extreme Probleme in der Umsetzung...
Also ich weiss nich ganz wie ich das nun alles Umsetzung soll mit dem Setzen und so...also die Runden zu "organisieren". Wer is wann dran und so... Dachte vielleicht an vier Phasen pro Runde, also vor dem Flop, vor dem Turn, vor dem River und nach dem River! Hat vielleicht er n paar Denkanstöße wie ich das gut umsetzen kann?? Wär sau nett, danke! |
Re: Pokerprojekt realisierung
Zitat:
woran genau hängst Du denn gerade? An sich klingt das doch schon gut! Allerdings glaube ich musst Du noch das Setzen der Blinds berücksichtigen (glaube mich zu erinnern, dass Du mit Blinds spielst?). Jedenfalls müssen erst die Blinds gesetzt werden, bevor es überhaupt Karten gibt, dann erst folgt die Phase vor dem Flop. Wie gesagt, an sich ist die Idee aber imho ganz gut. Was Du Dir noch überlegen solltest ist, was die Phasen gemeinsam haben und was sie wirklich unterscheidet. Das meiste erfolgt schließlich nach dem gleichen Prinzip, es gibt eine festgelegte Reihenfolge, jeder hat ein paar Optionen (hängen teilweise vom Vorgänger ab), z.B. ein Call, ein Bet/Raise, ein Pass, ... Zudem kann jeder immer wenn gesetzt wurde weiter erhöhen (abgesehen von Limits), jeder hat aber nur max. einmal die Chance darauf als erstes zu erhöhen (hoffe ist grob klar was ich meine, zeigt der erste in der Runde ein Check an und checken alle anderen, dann kann der erste nicht mehr in der gleichen Runde erhöhen, erhöht aber der zweite, so kann der erste noch ein reraise ansetzen, sobald er callen müsste um drin zu bleiben). Hier gilt es also, dass Du Dir die Regeln überlegst und das erstmal so einfach wie möglich (ohne Programmierung, nur verbal überlegen!). Wenn Dir klar ist, was Du gerade umsetzen möchtest, dann ist es sehr viel leichter das zu programmieren als einfach mal los zu legen und dann mal zu schauen. Für diesen Fall denke ich, dass das Setzen immer nach einem festen Prinzip abläuft. Dazu muss nur die Reihenfolge der Leute festgelegt werden, wer erster war und wer gerade dran ist, sowie die Höhe des aktuellen Einsatzes (in einer Runde in der keiner setzt null, zu Beginn der Big-Blind, ...). Aufgrund der Differenz von aktuellem Einsatz und dem, was ein Spieler bereits gesetzt hat, kannst Du die Optionen auswählen. So ist ein Check natürlich nur möglich, wenn man den aktuellen Einsatz schon gebracht hat (z.B. weil man den Big-Blind setzen musste), ein Raise nur wenn der aktuelle Einsatz kleiner als ein bestimmtes Limit (wenn vorhanden) und natürlich auch nur, wenn man selbst nicht schon für ein Call all-in ist. Eine Runde besteht also wieder aus mehr als einem Problem. Du musst schaun, was für eine Runde gerade gespielt wird. Je nachdem müssen nur zwei Spieler die Blinds setzen (da gibt es keine anderen Optionen) oder es läuft ein "normales" Setzen ab. Auch das Ende einer Runde läuft unterschiedlich ab. Nach dem ersten bekommt jeder Spieler zwei Karten (wenn ich Flop, River und Turn richtig deute soll es ein Texas-Hold'em werden?), nach der nächsten Runde gibt es den Flop, nach den zwei folgenden Runden gibt es je eine Karte und zu guter Letzt keine mehr (dafür folgt ggf. das Aufdecken). Auch das Geben von Karten ist dabei auch ein Problem, dass sich in mehrere Teile gliedern lässt. So ist einer der Schritte das Mischen der Karten (vor Beginn eines Spiels). Ein anderes Problem ist dann das abheben von Karten (die dann aufgedeckt oder gegeben werden). Das ganze kannst Du z.B. in einer Klasse Kartenstapel kapseln. Die Eigenschaften eines solchen Stapels sind dann ebenhalt, dass Du ihn erzeugen, mischen und Karten von oben (/unten) entnehmen kannst. Natürlich musst Du nicht wirklich etwas entnehmen oder ihn echt mischen, das sind nur die Eigenschaften, die er nach außen anbieten muss. So kannst Du auch ein festes Array erstellen, in dem die Karten immer an der gleichen Stelle stecken. Beim entnehmen greifst Du dann einfach auf eine Zufallszahl zurück und entnimmst die Karte indem Du Dir das im Array merkst (sonst wären auch 4 Herz-Asse möglich). Genauso kannst Du auf eine Liste zurückgreifen, die Du füllst, aus der Du ebenfalls per Zufall ein Element entnimmst (hier echt entnimmst) und die Zufallszahl eben den aktuellen Füllstand der Liste berücksichtigt (dort kann dann keine Karte doppelt entnommen werden). Natürlich kannst Du aber auch ein Array echt mischen und von oben/unten abheben! Ja, was noch ein wenig fehlt ist die "Spielsteuerung". Damit meine ich den Teil, der alles zusammen bringt. Diese sollte dann z.B. beim Start einer Runde die zwei Spieler auffordern die Blinds zu setzen. Danach greift sie einfach auf den Kartenstapel zurück, mischt ihn (soweit nötig) und entnimmt hier Karten und teilt diese aus. Nach dem Austeilen weiß die Spielsteuerung, wer gerade dran ist und was gesetzt wurde (der Big-Blind) und kann auf das Setzen zurückgreifen. Das Setzen ist wie gesagt ein eigenes Problem, dass Du unabhängig davon lösen kannst. Hier musst Du nur wissen, wann das setzen beendet ist. Ist dies der Fall, ändert die Spielsteuerung den Modus, verwendet den Kartenstapel um den Flop zu ziehen und startet ein neues Setzen (mit Startwert 0 und dem dann Ersten), usw. Ich denke das Prinzip sollte klar sein. Das was ich vorgeschlagen habe ist keineswegs der einzigste, beste oder schönste Weg, sondern nur ein möglicher. Es sollte auch nur nochmal gezeigt werden, dass Du hier mit möglichst einfachen Problemen arbeiten solltest, die Du alle einfach löst und dann zusammensetzt. Dabei kannst Du auch eine andere Partionierung der Probleme in Teilprobleme wählen und diese natürlich auch noch weiter zerlegen! Gruß Der Unwissende |
Re: Pokerprojekt realisierung
Ok, super für deine ausfürliche Antwort! Also ja, wird Texas Hold'em NoLimit!
Ja also die Sache mit dem Mischen und so hab ich ja schon alles, das stellt nicht mein Problem dar, sondern eher dieses rundenbasierte Setzen und alles aber ich versuch das nun mal umzusetzen! Werde die Blinds zB anfangs einfach automatisch setzen lassen, das vereinfacht die Sache und ich kann es hinterher ja immernoch ändern! Du bist echt ne große Hilfe!!! *Thumbs up* |
Re: Pokerprojekt realisierung
Also bin jetzt schon echt weiter gekommen.
Jetzt weiss ich nur nicht, wie ich den Server und Client zur Laufzeit erstellen soll? Also wollt dazu ja uach ne eigene Klasse machen, und dann die Indy-Komponenten verwenden! Wie erstell ich den jetzt zur Laufzeit? |
Re: Pokerprojekt realisierung
Zitat:
Delphi-Quellcode:
Was genau passiert, wenn ein solches Objekt erstellt wird, legt dann eben der Konstruktor Create fest. Wie man einen erzeugt wurde ja schon in einem anderen Thread behandelt (wenn ich mich richtig erinnere, sorry, link gerade nicht parat).
var server: TServer;
begin server := TServer.Create(...); ... end; Jedenfalls musst Du in diesem Konstruktor dann einfach die Initialisierung vornehmen. Diese kann dann z.B. darin bestehen, dass Du eine TIdTcpServer-Instanz erzeugst (ebenfalls durch Aufruf von Create) und diese in einer privaten Instanz-Variablen von TServer speicherst (von außen muss ja niemand sehen, dass TServer einen Indy-TCP-Server verwendet). Gruß Der Unwissende |
Re: Pokerprojekt realisierung
aso...stimmt ;-)
danke |
Re: Pokerprojekt realisierung
Ok, jetzt habe ich dazu aber nochmal ne Frage :D
Also wenn jetzt einer einen Server erstellt (dh er klickt zB auf Neues Spiel), wie mach ich das dann? Ruf ich dann als externes Programm den Server auf oder wie? Da Blick ich grad net ganz durch ^^ |
Re: Pokerprojekt realisierung
Zitat:
Die Funktionalität des Servers packst Du nun in eine eigene Klasse und gut ist. Was Du dann hast ist die Funktionalität in einer Klasse, mehr nicht. Das heißt, dass so noch kein Server läuft. Möchte jmd. ein neues (Netzwerk-)Spiel erstellen, so musst Du erst eine Instanz der Klasse erstellen und ggf. zusätzlich noch den Server aktivieren (erst dann lauscht der wirklich auf dem Kanal). Sehr direkt zu Deiner Frage, es macht hier mehr Sinn, dass Dein Programm gleichzeitig als Client und Server agieren kann. Ein Server kann aktiv oder inaktiv sein. Solange der Server inaktiv ist, ist der Kanal nicht belegt und keiner beantwortet Anfragen, die dort abgesetzt werden. Erst wenn Du denn aktivierst wird er Anfragen entgegen nehmen. Ähnlich sieht es mit der Clientfunktion aus, erst wenn Du ihm sagst, dass er sich mit einem bestimmten Server verbinden soll, wird der eine Nachricht abschicken. Deswegen kannst Du Beides ohne Probleme ins gleiche Programm packen und auf die entsprechende Funktionalität dort zurückgreifen, wo Du sie wirklich brauchst. |
Re: Pokerprojekt realisierung
Ok...versuch ich ma hinzukriegen aber im Moment hab ich eh keine kreative Phase :D
Dankeschön |
Re: Pokerprojekt realisierung
Ok, Fragen über Fragen und hier is schon wieder eine ;-)
Also wie schreibe ich ein Event für meinen Server, wenn ich ihn zur Laufzeit erstelle? Hab hier im Forum ganz viel zu TMyNotifyEvent gefunden aber daraus bin ich echt nicht schlau geworden! Also ich möchte das OnClientRead-Event definieren, aber ich weiss nicht wie ich das machen soll!? |
Re: Pokerprojekt realisierung
Zitat:
Ich werde im folgenden einfach mal auf Beides eingehen. Das Ereignis an sich ist nichts, was Du wirklich programmieren musst. Du legst eigentlich nur fest, wann die Ereignisbehandlung ausgelöst wird. Das ist schon alles. :wink: Bleibt das Problem der Ereignisbehandlung. Hier gibt es ganz verschiedene Ansätze. Der unter Delphi typische besteht in einem Callback. Callback kannst Du dabei wirklich als Rückruf übersetzen. Jmd. der sich für ein Ereignis interessiert hinterlässt seine "Nummer" (genau gesagt ist es eine Adresse) und wird "zurückgerufen", wenn das Ereignis eintritt. An sich ist dies natürlich (mehr oder weniger) immer der Weg, den man verwendet. Der (hauptsächliche) Unterschied liegt darin, wie man nun die Rückrufnummer hinterlässt. Dabei ist der Delphi-typische Weg die Verwendung eines Methodenzeigers. Ein Methodenzeiger speichert die Adresse einer Methode. Das ist ein eigener Datentyp, der einfach eine Adresse speichert. Du weißt dann, dass eine solche Adresse auf eine Methode zeigt und wie deren Signatur aussieht. Du weißt also welche Parameter Du übergeben musst und was für ein Ergebnis Du bekommst (keins wenn es eine Procedure ist). Ein Methodenzeiger wird wie folgt deklariert:
Delphi-Quellcode:
Wie Du hier siehst, werden Methodenzeiger als Datentyp deklariert. Du gibst einfach einen Namen an und die eigentliche Deklaration besteht aus dem Schlüsselwort procedure oder function gefolgt von den Argumenten (soweit vorhanden) und für Funktionen dem Rückgabetyp. Der deutliche Unterschied zu normalen Methoden besteht hier schon darin, dass Du keinen Namen der Methode angibst. Zudem wird immer das Suffix of object angehangen.
type
TMethodenZeiger = procedure(Argument1: Typ1; ... ArgumentN: TypN) of object; // oder auch TMethodenZeiger2 = function(Argument1: Typ1; ... ArgumentN: TypN): TRueckgabeTyp of object; Lässt Du Letzteres weg, so handelt es sich um einfach Funktionszeiger. Der Unterschied zwischen Funktions- und Methodenzeiger ist dabei recht einfach erklärt, Methoden gehören immer zu einem Objekt (Instanz einer Klasse), Funktionszeiger tun das nie! Beide sind in Delphi unterschiedlich aufgebaut und müssen deswegen unterschieden werden (Methodenzeiger verwenden ein verstecktes Argument, das Objekt). Der Unterschied besteht also darin, dass Du einem Methodenzeiger nur Funktionen und Prozeduren (deren Adresse) zuweisen kannst, die zu der Instanz einer Klasse gehören. Umgekehrt kannst Du Prozeduren und Funktionen, die zu einer Klasse gehören aber nie einem Funktionszeiger zuordnen. Wie gesagt, Delphi verwendet i.d.R. immer Methodenzeiger. Wie man die deklariert hat, hast Du schon gesehen. Nach der Deklaration kannst Du die wie jeden anderen Datentypen auch verwenden und Variablen von diesem Typ anlegen. Diese Variablen können natürlich einen Wert annehmen und wie gesagt, dieser muss eine Adresse auf eine Methode (Prozedur oder Funktion eines Objekts) sein. Zudem muss die Signatur der Methode mit der deklarierten übereinstimmen (also müssen gleichviele Parameter in gleicher Reihenfolge und mit gleichem Typ übergeben werden). Typischerweise legt man in Klassen alle Variablen mit geringster Sichtbarkeit (private) an (auf die Gründe gehe ich nicht näher ein, hier wäre ein Tutorial zur OOP nötig). Die eigentlichen Lese- und Schreibzugriffe delegiert man (Stichwort property). Ein Delphi property wirkt nach außen wie eine Variable, der Zugriff kann jedoch an eine Methode oder Variable delegiert werden.
Delphi-Quellcode:
So ungefähr könnte das dann aussehen. Hier wird eine private Variable FVariable vom Typ TTyp angelegt. Den gleichen Typ haben die beiden properties (aber andere Namen). Dem Schlüsselwort property folgt etwas, dass wie eine normale Variablendeklaration aussieht (property Variable: TTyp). Dem wiederum folgt die Delegation des Lesens und/oder Schreibens (mindestens eine Eigenschaft muss hier delegiert werden). Im ersten Fall würde beim Lesen von Variable direkt die Instanzvariable FVariable gelesen werden. Würdest Du Variable einen Wert zuweisen (schreiben), dann würde hier die Methode setVariable aufgerufen werden. Der Parameter hat dabei automatisch den Wert, den Du setzen wolltest.
type
TKlasse = class(TObject) private FVariable: TTyp; protected procedure setVariable(const FValue: TTyp); public property Variable: TTyp read FVariable write setVariable; property VariableRO: TTyp read FVariable; end; Hier siehst Du auch gleich warum man das macht, so ist VariableRO ReadOnly. Hier wurde nur das Lesen delegiert. Würdest Du versuchen diesen Wert für eine Instanz zu schreiben, wird Dir ein Fehler (schon zur Compilezeit) gemeldet. Aber auch das Delegieren macht Sinn. So kann setVariable prüfen, ob der übergebene Wert überhaupt gültig ist. Dies kann wichtig sein, wenn es Einschränkungen der gültigen Werte gibt. Auch das Lesen eines Wertes kann natürlich an eine Funktion delegiert werden. So wäre z.B. eine Eigenschaft Mittelwert denkbar, die Du natürlich leicht bei Bedarf berechnen kannst (statt sie in einer Variable zu speichern und bei allen Änderungen zu aktualisieren). Ja, das gleiche wird meist auch mit Methodenzeigern gemacht. Allerdings werden die Zugriffe hier meistens direkt an die Variable deklariert (nur damit Du dich im Folgenden nicht wunderst). So, alles zusammen ergibt jetzt die Ereignisbehandlung. Du legst einfach eine Klasse an, die ein property OnEventX besitzt. Dieses ist vom Typ ein Methodenzeiger und speichert die Rückrufnummer für die Ereignisbehandlung (in einer Variable). Tritt jetzt Dein Ereignis ein, so schaust Du nach, ob eine gültige Rückrufnummer hinterlegt wurde. Ist dies der Fall, rufst Du dort an und teilst das Ereignis mit. Ist dies nicht der Fall, so kannst Du niemanden rückrufen und musst entsprechend nichts machen. Das ganze mal komplett:
Delphi-Quellcode:
Das sind jetzt erstmal nur die Datentypen. Du hast einfach einen Datentypen als Methodenzeiger, eine Klasse, deren Instanzen zurückgerufen werden können und eine Klasse, die die Rückrufe durchführt, sobald etwas bestimmtes passiert. In der letzten ist die weitere Funktionalität durch eine Methode doFoo angedeutet. doFoo könnte z.B. Auslöser der Ereignisbehandlung sein.
type
TRückruf = procedure(const arg1: String) of object; TCallMe = class(TObject) public procedure callMe(const t: String); end; TKlasseMitEreignis = class(TObject) private FRückrufAdresse: TRückruf; ... public procedure doFoo; .... property OnEreignis: TRückruf read FRückrufAdresse write FRückrufAdresse; end; Die Implementierung von TCallMe ist hier völlig egal. Stell Dir einfach vor, dass die Klasse ein ShowMessage mit dem Argument t macht, sobald die Methode callMe aufgerufen wird. Bleibt noch der Aufruf der Ereignisbehandlung:
Delphi-Quellcode:
So sieht dann einfach der Rückruf aus. Du kannst auf die Variable, die vom Typ ein Methodenzeiger ist wie auf eine normale Methode zurückgreifen. Mit dem assigned wird überprüft, ob die Adresse <> nil ist. Dies ist der Fall, sobald einmal eine gültige Adresse zugewiesen wurde. Nil (0) ist die einzigste verbotene Adresse. Auf einem 32-Bit Rechner bleiben damit (2^32 - 1) Adressen übrig (so gute 4,2 Mrd.). Das heißt aber nicht, dass jede Adresse <> nil gültig ist. Weißt Du eine Adresse zu, die zu einem Objekt gehört, so ist die nur gültig, bis Du das Objekt löscht. Du solltest Dich dann vor dem Löschen des Objekts darum kümmern, dass Du dort, wo es zurückgerufen wird die Rückrufnummer wieder auf nil setzt. Ansonsten bleibt die alte Rückrufnummer gespeichert, aber der Anschluss, der zurückgerufen wird wurde schon entfernt. Das gibt dann ein Problem (eine Exception).
procedure TKlasseMitEreignis.doFoo;
begin ... // Benachrichtigung // prüfen ob eine gültige Rückrufadresse zugewiesen wurde if assigned(OnEreignis) then begin // Rückruf einer Prozedur, die ein Argument vom Typ String bekommt self.OnEreignis('WAS AUCH IMMER'); end; // if assigned(OnEreignis) end; Zu guter Letzt bleibt noch das setzen der Variable. Das schauen wir uns gleich am Beispiel an:
Delphi-Quellcode:
Natürlich solltest Du die Instanzen nicht in lokalen Variablen speichern, wie es hier der Fall ist, sonst wären die nach dem Verlassen der Methode eh unerreichbar und sollten hier schon wieder frei gegeben werden (anders gesagt, das Beispiel zeigt etwas unsauberes, nicht so nachmachen :wink:).
var klasseMitEreignis: TKlassesMitEreignis;
rückrufKlasse: TRückruf; begin // neue Instanzen erstellen klasseMitEreignis := TKlasseMitEreignis.Create; rückrufKlasse := TRückruf.Create; // eigentliches Registrieren der Rückruf-Adresse klasseMitEreignis.OnEreignis := rückrufKlasse.callMe; .... end; Das Prinzip sollte aber klar sein. Eine Sache ist hier jetzt aber ganz schlecht! Wie gesagt, es ist der Delphi-Standardweg. Trotzdem hast Du das Problem, dass Du nur eine Variable hast, also nur eine Rückrufadresse speichern kannst. Natürlich kannst Du für jede mögliche Rückrufadresse eine eigene Variable vorsehen, aber das verschiebt nur das Problem (man weiß nicht immer wie viele Spieler wirklich am Spiel teilnehmen wollen). Besser ist das Observer-Pattern zu verwenden. Dieses Entwurfsmuster beschreibt zwei Rollen, das Observable (beobachtbares Objekt) und die Observer (Beobachter). Ein Observable bietet dabei die Möglichkeit, dass sich Beobachter über eine bestimmte Schnittstelle registrieren und wieder deregistrieren können. Observer registrieren sich also beim Observable und alle registrierten Observer werden werden vom Observable zurückgerufen. Wie das genau geschieht (wie die registrierten Observer verwaltet und rückgerufen werden) ist dabei nicht durch das Pattern vorgegeben (Pattern definieren nur ein Problem und eine allgemeingültige Lösung, die unabhängig von der Implementierung ist!). Somit ist nur eindeutig bestimmt, was für eine Technik verwendet wird. Wie Du es z.B. in Delphi realisieren kannst, kannst Du dem einen Thread (hatte den Link denke ich schon gepostet) entnehmen. |
Re: Pokerprojekt realisierung
Oh mein Gott, wie kompliziert ist das denn...ich will doch nurn Ereignis erstellen o.O
Wo schreib ich denn jetzt zB rein was passiert wenn das Ereignis in Kraft tritt? Sorry aber das kapier ich grad ma garnich! Trotzdem danke fuer diene sehr ausfuehrliche Antwort! |
Re: Pokerprojekt realisierung
Zitat:
Um es hier nochmal einfach zu sagen (gelöst vom Code), Du hast zwei Rollen, Server und Client. Der Server kennt eintretende Ereignisse genau und benachrichtigt alle, von denen er weiß, dass sie sich dafür interessieren. Die Clienten wiederum registrieren/verbinden sich mit dem Server. Das heißt, dass sie sagen "Hallo, ich bin XYZ, bitte benachrichtige mich bei Ereignissen". Das ganze muss der Server sich merken (der muss ja wissen wer alles benachrichtigt werden möchte). Dazu schreibt er einfach alle Interessenten in eine Liste. Tritt jetzt das Ereingis ein, so schaut der Server in diese Liste. Ist die Leer, will's keiner wissen, er hat nichts zu tun. Sonst geht der einfach alle durch, die in der Liste stehen und benachrichtigt die. Beendet ein Client das Spiel, dann meldet der sich einfach beim Server ab und damit wird sein Name dann von der Liste genommen. Das ist die grobe Idee. An sich ist das aber ein sehr konkretes Beispiel (und natürlich auf dein Pokerspiel bezogen). Abstrakter würde man nicht von einem Server und Clienten sprechen, sondern davon, dass es einen gibt, der Benachrichtigt (Observable) und keinen, einen oder viele die benachritigt werden wollen (Observer). Die Anzahl der Observer ist nicht bekannt (Meldet sich ein Spieler, 10 oder 1000?), aber auch egal. Jeder der sich meldet kommt auf die Liste und jeder der sich abmeldet kommt wieder runter. Hat man also irgendeine Möglichkeit jmd. auf die Liste zu setzen und zu entfernen, kann man so beliebig viele (eben auch keinen) benachrichtigen, sobald ein Ereignis eintritt. Wer wen worüber benachrichtigt ist erstmal egal, das Prinzip ist immer das gleiche. Hast Du das Prinzip soweit verstanden? Wenn nicht nachfragen! Das ganze musst Du jetzt noch in Code umsetzen. Dabei gibt es verschiedene Probleme. Eine Trennung kann man hier sofort sehen, es muss Objekte geben, die sich benachrichtigen lassen wollen (die Observer) und etwas, dass die Benachrichtigung auslöst (Observable). Das Observable-Objekt muss zudem eine Möglichkeit haben, dass sich hier jmd. für eine Benachrichtigung melden/abmelden kann. Das Problem dabei ist, dass Du (wie gesagt) nicht weißt, wer jetzt eigentlich benachrichtigt werden möchte. Mal ist sind es Spieler A, B und C, dann wieder D, E und F und dann A und E. Das Problem, dass Du nicht weißt wieviele es sind lässt sich leicht über geeignete Strukturen lösen. Hier wäre es sicherlich die Liste. Eine Liste speichert einfach eine beliebige Anzahl von gleichartigen Elementen, entspricht ein wenig einem Array, nur ohne Größenbeschränkung. Problematischer ist, dass Du noch nicht weißt, wen Du benachrichtigen sollst. Hier musst Du Dir etwas überlegen, das man als Schnittstelle bezeichnen könnte/würde. Diese Schnittstelle ist verbindlich, sie legt fest, wie Du jmd. ansprechen kannst, der benachrichtigt werden möchte. Dabei musst Du alle Informationen speichern, die dazu nötig sind und wissen, wie Du über das Ereignis benachrichtigst. Stell Dir das einfach wie in einem Call-Center vor. Hier können sich Leute melden, die über den Verkaufstart der PS4 informiert werden wollen. Jetzt rufen die an und hinterlassen ihre Telefonnummer. Dazu müssen sie eine Möglichkeit haben, sich zu melden (die Telefonnummer für die Anmeldung). Das Call-Center speichert irgendwie die Nummern (wie ist egal). Natürlich muss es auch eine Möglichkeit geben, dass Du Dich wieder abmeldest, z.B. weil Du die X-Box 5D gekauft hast und Dich die PS4 nichtmehr interessiert. Startet der Verkauf, gehen die einfach die Liste durch und rufen jeden an der drauf steht. Jetzt müssen sie Dir aber noch mitteilen worum es geht. Da ist es dann gut, wenn Du auch verstehst was die meinen. Nach Möglichkeit solltest Du also jmd. am Telefon haben, der eine Sprache spricht, die Du verstehst. Kann auch sein, dass Du Dir noch denken kannst, dass der Verkaufsstart auf japanisch angekündigt wurde, ein Programm kann das nicht. Hier gibt es nur verstanden oder nicht verstanden. Am Besten überlegst Du Dir erstmal, was für Ereignisse Du im Moment berücksichtigen möchtest. Die Fragen die Du dabei stellen/beantworten solltes sind:
Poste erstmal die Ereignisse, dann kann man das leichter an einem Beispiel erklären. Und ansonsten frag einfach bei einzelnen Punkten, die Du nicht verstanden hast nach (ggf. halt immer nach dem nächsten Stück usw). |
Re: Pokerprojekt realisierung
Also ich hab vom Server noch nicht viel!
Delphi-Quellcode:
Ich dachte jetzt eigentlich, dass ich vielleicht irgendwie sowas schreiben kann:
unit Server;
interface uses Controller; type TServer = class(TServerSocket) Private Public constructor Create; end; var Controller: TController; Server: TServer; implementation constructor TServer.Create; begin inherited Create; Controller := TController.Create; end; end.
Delphi-Quellcode:
Sodass wenn eine Nachricht eingeht "blablabla" ausgeführt wird.
MyEvent: Procedure (Sender: TObject) of Object; //richtig so? :D
Server.onClientRead := MyEvent; procedure TForm1.MyEvent(Sender:TObject); begin blablabla; end; Wie realisier ich das nun? Weil eigentlich müsste TServerSocket doch onClientRead kennen oder nicht? Aber die Auto-Vervollständigung gibt da garnix aus.... Verstehste jetzt wo mein Problem is? Wie der Server arbeitet hab ich im groben schon verstanden! Danke für deine Hilfe! |
Re: Pokerprojekt realisierung
Zitat:
MyEvent: Procedure... ist ganz schlecht! Der Doppelpunkt legt den Typ einer Variable fest. Dieser kann nicht einfach vom Typ procedure... sein, das würde zu einem Problem führen, dass ich nicht näher erläutern möchte. Einfach gesagt würden zwei Variablen mit diesem Typ nie kompatibel sein, da Delphi keinen einheitlichen Typen annehmen würde (wie gesagt, egal). Du müsstest erst einen Datentypen erzeugen:
Delphi-Quellcode:
Dann kannst Du Variablen von diesem Typen erstellen:
type
TMyEvent = procedure(Sender: TObject) of Object;
Delphi-Quellcode:
Ich hoffe Du siehst den Unterschied, so wie die nötigen Schlüsselwörter. An sich sollte jeder Datentyp auch ein Präfix (T für Typen, P für Zeiger) enthalten. Methodenzeiger bekommen ein T, da es sich um Referenzen handelt. Nur explizite Zeiger (Datentyp Pointer oder Dinge mit ^ wie ^Integer, ^Byte, ...) bekommen ein P vorgesetzt!
var
variable: TMyEvent; Damit ist immer klar, wann Du einen Typen meinst und wann eine Methode/Variable oder sonst was. Wichtig ist aber auch hier, dass Du die zwei Seiten (Observable und Observer) unterscheidest. Die Benachrichtigung wird bereits durch TServerSocket ausgelöst. Das heißt, TServerSocket ist bereits ein fertiger Observer. Was noch fehlt ist die Methode, die benachrichtigt werden soll, also der Observer. Welche Signatur die Methode haben muss kannst Du der OH entnehmen, dort unter Ereignisse findest Du das OnClientRead-Event und dessen Datentyp. Folgst Du dem, so siehst Du welche Argumente (z.B. Sender: TObject bei einem TNotifyEvent) Du übergeben musst. Ich gehe mal davon aus, dass Du da schon nachgeschaut hast und es sich hier um eine Methode handelt, die ein Argument von TObject bekommt. Streich die beiden Zeilen, die ich oben gepostet habe und dann stimmt auch der Anfang
Delphi-Quellcode:
Das kannst Du so stehen lassen, da ist alles korrekt. Was Du nun hast sind beide Seiten, TServerSocket bietet eine Möglichkeit jmd. zu benachrichtigen und TForm1 hat eine Methode, die benachrichtigt werden kann (TForm1.MyEvent). Was noch fehlt ist das Verknüpfen/Registrieren, so dass TServerSocket weiß, dass TForm1.MyEvent zurück gerufen werden soll.
procedure TForm1.MyEvent(Sender:TObject);
begin blablabla; end; Ok, jetzt wird mir klar was Du mit der Zeile meintest:
Delphi-Quellcode:
Wobei Server eine Instanz von TServer sein muss und MyEvent eben die Methode von TForm1 (also die Zuweisung in einer Methode von TForm1 aufgerufen wird). Hier käme z.B. das OnFormCreate in Frage (wenn Server bereits erstellt wurde).
server.onClientRead := MyEvent;
Hier noch eine Bitte, bei solchen Zeilen wäre es immer schön, wenn Du ein wenig den Kontext angeben würdest. Wie Du gesehen hast ist (mir) hier nicht klar, was für einen Typ Server hat (usw.). |
Re: Pokerprojekt realisierung
Edit: Natürlich doof wenn man die uses Klausel vergisst -.-
Trotzdem funzt es noch nicht wie ich will, habs nun nochmal n bissl geändert:
Delphi-Quellcode:
So ich glaube dass es jetzt eigentlich klappt, aber beim constructor gibt er immer n Fehler aus! (siehe Kommentar)
unit Server;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, Spin, Controller, ScktComp; type TMyServer = class(TServerSocket) Private Public constructor Create; procedure MyReadEvent(Sender: TObject; Socket: TCustomWinSocket); end; var Controller: TController; Server1: TMyServer; //is das nicht n bisl komisch oder falsch? wenn man dann noch aufs oncreate ereignis guckt, wo dann der Server1.onClientRead zugewiesen wird...das is doch sicher nich ganz sauber oder? müsste ich nicht eigentlich im oncreate einfach TMyServer.onClientRead angeben? (das funzte aber nicht) implementation constructor TMyServer.Create; begin inherited Create; //Hier sagt er "Not enoough actual Parameters" Controller := TController.Create; Server1.onClientRead := MyReadEvent; end; procedure TMyServer.MyReadEvent(Sender: TObject; Socket: TCustomWinSocket); begin Showmessage('Klappt'); end; end. Außerdem gibt er ne komische Warnung aus: "Method Create hides virtual method of base type TServerSocket" Daraus werd ich noch nicht schlau! |
Re: Pokerprojekt realisierung
Liste der Anhänge anzeigen (Anzahl: 1)
Also, hab jetzt alles nochmal ueberarbeitet und auch einiges verbessern können!
Habe aber immernoch einen Fehler! Ich häng das Projekt einfach mal an! (Hab bisher eigentlich nur die 6Units "Server", "Client", "Controller", "Deck", "Karte", "Spieler" geschrieben aber hab die nun zum Testen und kompilieren mal in ein Projekt eingebunden!) Startet das Programm einfach mal und klickt auf Start Server. Dann kommtn Fehler und ich weiss nicht warum! Irgendwas mit Zugriffsverletzung aber was weiss ich nicht! |
Re: Pokerprojekt realisierung
Zitat:
Ich glaube Dir ist der Unterschied zwischen Klasse und Exemplar noch nicht ganz klar. Die Variable aServer in der Unit Main, speichert den Verweis auf ein Exemplar. Die Klasse TMyServer ist nur eine Klasse. Rufst Du den Konstruktor der Klasse TMyServer auf, so wird ein Exemplar erzeugt und der Verweis zurückgegeben (wie bei einer Funktion). aServer.Create deutet der Compiler jetzt so, dass Du für das Exemplar aServer die Funktion Create aufrufen möchtest. Das Problem ist aber, dass Du aServer natürlich noch gar kein Exemplar zugewiesen hast. Die korrekte Form wäre also
Delphi-Quellcode:
Danach ist in aServer ein Verweis auf dieses neue Objekt gespeichert. Das heißt, dass Du irgendwann, sobald Du aServer einen anderen Wert zuweist oder es nicht mehr brauchst, aServer.Free aufrufen musst (um den Speicher wieder zu säubern).
aServer := TMyServer.Create;
Wie Du damit schon siehst, sobald in aServer ein gültiger Verweis steht, kannst Du dann auch direkt auf die Methoden dieser Instanz zugreifen. Der Konstruktor wird aber immer direkt für eine Klasse aufgerufen (also TXYZ.Create) und das Ergebnis einer Variablen zugewiesen. Mit anderen Methoden solltest Du das nie tun! (es gibt statische Methoden, aber die sind ein anderes Thema). |
Re: Pokerprojekt realisierung
Ach scheiße :D Sowas passiert mir immer wieder! Ja mit dem Objekt-Orientierten-Programmieren is schon richtig! Bei vielen Dingen weiß ich immer nich ganz was gemeint ist und unser InfoLehrer hat eh keine Peilung was Erklären überhaupt bedeutet!
Er sagt immer nur: "Und eure Aufgabe ist jetzt euch zu dem Thema Infos aus dem Internet zu suchen! Das ist auch ein Teil der Informatik! Blabla..." Naja ich infomier mich da mal und wenn du ein gutes Tut kennst kannstes mir gerne mitteilen :) (Hoffe das ist nicht zu lang... :)) Edit: Ist der Code ansonsten denn auch so schlimm? :D |
Re: Pokerprojekt realisierung
Zitat:
Zitat:
|
Re: Pokerprojekt realisierung
Ok, aber er gibt immernoch ein Problem aus!
Und zwar: "Project LAN_Poker raised exception class EStackOverflow with message 'Stack overflow'" Ich werd daraus net schlau sorry (hoffe es ist nicht wieder son dummer Fehler)! |
Re: Pokerprojekt realisierung
Zitat:
Der Stack ist dabei ein Speicher mit einer max. Größe. Legst Du also nur lang genug was darauf ab, gibt es einen Stackoverflow. Die Grenze ist dabei sehr hoch gesetzt, kommt es zu einem Stackoverflow, liegt das i.d.R. wirklich am Programm! Schau einfach mal, wo sich eine Prozedur selbst aufruft (oder Du sonst einen Kreis hast). Als Hilfe kannst Du einfach einen Breakpoint (diese roten Punkte/ F5) setzen. Dort hält Delphi dann an, wenn Du das Programm ausführst. Hier kannst Du nun mit F7 in die Ausführung reinspringen und siehst, was Dein Code gerade macht. Bei größeren Schleifen kann das nerven, hier kannst Du auch einfach auf die Zeile hinter die Schleife einen neuen Breakpoint setzen und mit F9 die Ausführung fortsetzen. Die geht normal weiter, bis das Programm endet, ein Fehler auftritt oder ein Breakpoint den Ablauf erneut unterbricht. |
Re: Pokerprojekt realisierung
Yeeehaw soweit klappts :)
Super, vielen Dank und die Sache mit dem Breakpoint ist auch nicht schlecht zu wissen ;-) (Der Fehler war übrigens, dass ich den Konstruktor des Servers falsch deklariert hatte (ohne "AOwner: TComponent")) |
Re: Pokerprojekt realisierung
Liste der Anhänge anzeigen (Anzahl: 1)
Ok, also bisher geht alles ganz gut, bin grad bei dem Senden und Auswerten von Nachrichten!
Klappt soweit auch, nur weiss ich nicht, wie ich den Array der Spieler versenden soll. Alle anderen Werte sind ja nur Integer bzw Strings und auhc nur einzelne Werte!+ Das ist ja ein Array eines bestimmten Typs!
Delphi-Quellcode:
Also so sehen die Typen aus und ich muss jetzt vom ServerSocket irgendwie die Spieler auf dem ClientSocket aktualisiern lassen. Wie mache ich das am besten? Weil mit SendText wärn das ja dann doch ziemlich viele Nachrichten!
type
TKarte = class FBild, FFarbe: Integer; FVerdeckt: Boolean; Private Public property Bild: Integer read FBild write FBild; //Bild der Karte property Farbe: Integer read FFarbe write FFarbe; //Farbe der Karte property Verdeckt: Boolean read FVerdeckt write FVerdeckt; //Status der Karte end; TSpieler = class FHand: Array [1..2] of TKarte; FFolded: Boolean; FGeld, FBet: Integer; procedure SetHand(Index: Integer; Value: TKarte); function GetHand(Index: Integer): TKarte; Private Public property Hand[Index: Integer]: TKarte read GetHand write SetHand; property Folded: Boolean read FFolded write FFolded; property Geld: Integer read FGeld write FGeld; property Bet: Integer read FBet write FBet; end; var Spieler: Array off TSpieler; PS: Ich hab die Units nochmal angehängt! Edit: Sollt ich mich vielleicht doch in die IndyKomponenten einarbeiten? Ist das damit einfacher zu realisieren? |
Re: Pokerprojekt realisierung
Und ich hab noch eine Frage:
Wie kann ich alle aktiven Server im Netzwerk anzeigen lassen? Sodass die Clients dann auch darauf connecten koennen! |
Re: Pokerprojekt realisierung
Zitat:
Tritt ein neuer Spieler dem Spiel bei, so muss allen "alten" Spielern nur ein Datum (der neue Spieler) mitgeteilt werden. Umgekehrt muss dieser neue Spieler natürlich auch die Daten von allen "alten" Spielern bekommen. Das ganze machst Du einfach, in dem Du nacheinander die einzelnen Spieler an ihn schickst. Natürlich kannst Du auch direkt ein komplettes Array verschicken (minimiert den Overhead), aber das ist für Dein Spiel nicht zwingend nötig (werden ja kaum 1000e Spieler auf einem Server sein und kann man auch nachträglich ändern). Zitat:
Die Frage ist, ob Du wirklich so viele Nachrichten verschicken musst, wie Du fürchtest. Letztlich solltest Du immer nur die Information verschicken, die gerade benötigt wird. Dazu solltest Du Dir einfach mal anschauen, was alles passieren kann:
Ganz grob wären das die ersten Dinge, die mir einfallen. Das sind schon nicht so viele Nachrichten (imho). Jetzt wäre die Frage, welche Information zu welchem Ereignis gehört. Bei der Eröffnung ist nichts zu tun, da findet alles lokal statt und der Server kennt die Daten natürlich schon. Tritt nun ein Spieler dem Spiel bei, so interessieren nur die genannten Informationen, wo sitzt der Spieler, wieviel Geld hat er und wie heißt er. Im Gegenzug bekommt dieser Spieler die gleichen Daten aller anderen Spieler. Wird eine Runde begonnen, so erhält ein Spieler den Dealer-Button (es wird also die Reihenfolge festgelegt). Zudem müssen zwei Spieler den Big bzw. Small-Blind setzen. Das ist dann eine Aufforderung (Nachricht) an je einen Spieler. Im Gegenzug muss das setzen bestätigt werden. Dann werden die Hände gegeben und ein Spieler bekommt die Nachricht, dass er dran ist. Nun hat er die Auswahl, was genau er machen möchte (Setzen, Callen, Passen, ...). Was auch immer gemacht wird ist nur eine Nachricht von einem Spieler. Wurde die Aktion ausgeführt, so verliert der Spieler das Recht etwas zu machen und jmd. anderes bekommt die Nachricht, dass er dran ist. Der Server merkt sich dabei, wann eine Runde zu enden ist. Dabei kann es sein, dass sich der Flop ändert. Hier muss allen Spieler die Information der neuen Karten (und ggf. Position) mitgeteilt werden, also max. 3 Nachrichten auf einmal. Zu guter Letzt gibt es noch das Ende eines Spiels. Hier wird dann mitgeteilt, wer gewonnen hat, dieser Person wird sein Gewinn übermittelt und ggf. werden den Spielern die aufgedeckten Karten mitgeteilt. (ist nicht ganz vollständig, aber halt so grob der Ansatz). Der Letzte Punkt dürfe die meisten Nachrichten produzieren, hier kannst Du also gut abschätzen, wie groß die Menge der Nachrichten maximal wird. Dazu muss natürlich feststehen, in welcher Form Du Nachrichten überträgst. Wichtig ist, dass jede Nachricht eindeutig identifiziert werden kann (es macht einen Unteschied, ob man die Karten des Flops sieht oder die aufgedeckten des Gegners). Diese Identifikation kannst Du durch Konstanten festlegen. Da kämen Cardinal/Integer Werte (4 Byte) oder eindeutige Strings in Frage. Bei Strings hängt die Größe von der Länge des Strings ab. Sagen wir mal, deine Konstanten bestehen aus max. 50 Zeichen (sollten eigentlich deutlich weniger werden, oder?), dann wären das 50 Byte pro String. Dann haben wir noch die eigentlichen Werte. Auch hier können wir die größte Nachricht abschätzen. Das dürften solche sein, bei denen ein Spieler (Spielername dominiert mit seiner Größe), Karten aufdeckt. Sagen wir auch, dass Du für Spielernamen nur eine Länge von 50 Zeichen zulässt, also wieder 50 Byte. Eine Karte besteht aus einer Farbe und einem Wert. Sehr großzügig schätze ich auch diese Information mit 50 Byte ab. Insgesamt würde also das größte Datum hier 200 Byte enthalten. Das ganze wird jetzt für jeden Spieler verschickt und auch noch an jeden Spieler. Schätzen wir mal die Spielerzahl mit 20 ab, dann hätten wir also max. 20*20*200 Byte, also < 80 KByte. Dabei habe ich hier schon sehr sehr großzügig abgeschätzt und das ist der gesamte Traffic für diese Nachricht, wenn Du sie für jeden Spieler abschickst (also noch 20 Spieler im Spiel waren) und jeder sie empfängt. Pro Spieler kommen nur 4000 Byte (< 4 KByte) an, die meisten Webseiten dürften deutlich größer sein. An sich dürftest Du sogar sehr viel kleinere Werte erwarten, da kaum jmd. ernsthaft 50 Zeichen im Namen hat oder diese für die Identifikation einer Karte benötigt werden. Natürlich kannst Du auch Karten, Beträge und die Identifikation der Nachricht in einem Cardinal/Integer Wert speichern, dann nehmen diese konstant nur noch 4 Byte ein. Das gleiche geht sogar mit den Spielern, dabei musst Du nur einmal die Assoziation zwischen Spielername und Nummer mitteilen und bei jedem Spieler speichern, dann kannst Du auch anhand der Nummer den Spieler ermitteln, an sich sollte das aber einfach nicht nötig sein. Wie gesagt, ich denke so richtig viele Nachrichten sind es nicht und es ist der leichteste Weg. Natürlich kannst Du auch die Spieler serialisieren und die komplette Information verschicken. Dies bringt aber deutlich größere Strukturen mit sich und macht das Erweitern und die Fehlersuche imho etwas schwerer. Zitat:
Zitat:
Hier ist es wichtig zu unterscheiden, der Server, an dem Du arbeitest, den nenne ich mal A. A ist für ein Spiel zuständig. Der zentrale Server heißt jetzt B. Nur B muss unter einem festen Namen erreichbar sein. Startest Du ein neues Spiel (erstellst also einen A), dann meldest Du Dein Spiel (Dein A) bei dem zentralen Server B an. Starte ich jetzt das Programm und möchte mich einem Spiel anschließen, so verbinde ich mich mit B (der ist ja bekannt). B zeigt mir dann alle Server (As) an, die momentan verfügbar sind. Da würde dann Dein A auftauchen. Verbinde ich mich mit dem, so kennt B halt die Adresse von A, gibt die mir und ich verbinde mich direkt mit Deinem A. B vermittelt hier also nur und hostet nicht alle Spiele! Die Alternative ist der dezentrale Ansatz. Dabei gibt es einfach keinen bekannten Server, was die Sache deutlich schwieriger macht! Für ein "privates" Netzwerk kannst Du einfach einen Broadcast machen. Dabei sendest Du einfach eine Anfrage an jeden Rechner im Netzwerk. Soweit die Infrastruktur das unterstützt, geht dies über eine spezielle Broadcast-Adresse, z.B. der X.X.X.255. Alternativ kann man hier auch von einem Multicast sprechen, aber ich denke die Funktionalität bleibt eher teueren Switches vorenthalten (hier geht die Anfrage an mehrere Rechner, aber nicht zwangsweise an alle). Unterstützt der Switch keine Broadcasts, kannst Du natürlich einfach alle möglichen Adresesn durchgehen. Für große Netze führt das schnell zu einer Menge (unerwünschten) Traffic. Für das sehr große Netz Internet funktioniert der Ansatz natürlich gar nicht (4,2 Mrd. Rechner abfragen wäre aber auch nicht all zu klug). Da hast Du dann keine einfache Möglichkeit. Natürlich gibt es P2P Clients, die dezentral arbeiten, diese nutzen aber ihre Verbreitung aus! Dort versucht man einfach zufällig auf ein paar (recht vielen) Adressen und einem bestimmten Port eine Anfrage. Findet man jmd. der dort antwortet (also auch das Programm verwendet), so tauscht man einfach die schon bekannten anderen Teilnehmer aus. Je mehr Leute also mitmachen, desto größer wird die Liste. Bei Deinem Programm dürfte das schwerer werden, dass Du zufällig jmd. findest, der das Programm auch verwendet, nun ja, eher unwahrscheinlich. |
Re: Pokerprojekt realisierung
Ok, also erstmal ne Frage vorweg!
Hab im Inet was über ein Protokoll gelesen, dass die Kommunikation zwischen Server und Client regelt. Damit ist doch nichts anderes gemeint, als das auswerten von Textnachrichten, die zwischen Server und Clients hin und hergeschickt werden oder? Ok, dann zum Stand der Dinge ;-) Hab nun die Kommunikation zwischen Server und Client ziemlich fertig (das nötigste zumindest, hinterher wird das sicher noch erweitert). Jetzt habe ich aber ein neues Problem. In meiner Unit "Controller" habe ich die procedures "Call", "Bet", etc.! Die Unit "Server" hat den "Controller" in der uses Klausel! Jetzt muss aber der Server die Clients aktualisieren, wenn zB ein Call ausgeführt wird. Kann das dem Controller aber nicht sagen, weil der Server ja nicht in seinen uses steht! Gibts da ne Möglichkeit? "Über-Kreuz-uses" sollen (laut dem Forum) ja nicht gut sein (und funktioniern eh nicht!?). D.h. ich müsste Kommunikation zwischen Server und Controller umstrukturieren und den Server in die uses Klausel des Controllers schreiben, nicht umgekehrt? Oder geht das auch anders? Danke soweit! Edit: Ok, hab nun einfach den Controller ganz weggelassen und das ganze Zeugs direkt vom Server machen lassen! Klappt so super, ist natuerlich nurn bissl mehr Code im Server aber egal ;-) Wenn das Projekt fertig ist wirds eh nochmal komplett überarbeitet, wenn mir dann ne Lösung einfällt änder ichs vielleicht nochmal :) |
Re: Pokerprojekt realisierung
Zitat:
Um es einfach und kurz zu sagen, ein Protokoll legt nur fest, wie man kommuniziert. Es gibt zig Protokolle, die Du verwenden kannst, die Teilweise sogar auf einander aufbauen. Nimm einfach TCP/IP, dabei handelt es sich keineswegs um ein einziges Protokoll, vielmehr um TCP over IP. So steckst Du die Daten, die Du übertragen möchtest einfach in TCP-Pakete. Diese Pakete wiederum verpackst Du in IP-Pakete (dort stimmt der Begriff Paket nicht mehr ganz) und verschickst sie. IP stellt dabei nur sicher, dass irgendwelche Bytes an den Empfänger mit der angegebenen IP geht. Diese irgendwelchen Bytes werden auf der Empfängerseite dann weiter verwendet. Dort gibt es dann auch die IP Schicht, die diese Daten empfängt und an die darüberliegende Schicht weiterreicht. Für IP waren es nur irgendwelche Bytes, für die darüber liegende TCP Schicht ist es dann aber schon ein TCP-Paket. Das enthält ein paar Informationen (welches Paket um die Reihenfolge sicherzustellen, welche Checksumme um Übertragungsfehler zu erkennen...) und irgendwelche Nutzdaten (wieder nur Bytes). Für die darüber liegende Schicht gibt es z.B. auch wieder ein Protokoll, dass diese Nutzdaten bekommt und weiter verarbeitet. Deine Anwendung steht dabei auf der obersten Schicht (schau Dir einfach mal das OSI Modell an). Willst Du also Daten verschicken, läufst Du von oben nach unten und packst für jede Schicht (außer der physikalischen Übertragungschicht) einfach weitere Daten außen rum (die für die jeweilige Schicht benötigt werden). Auf jeder Schicht könntest Du dann von einem Server und einem Clienten sprechen (ziemlich allgemeine Begriffe). Natürlich kannst Du auch in Deiner Anwendung noch viele weitere Protokolle einbauen, die dann auf dem TCP/IP aufbauen. Auch auf jede dieser Schichten könnte dann zutreffen, dass die jeweils einen Client oder Server kennen. Wichtig ist eigentlich nur, dass für jede Schicht immer Metainformationen (nur für die Schicht wichtig!) und Nutzdaten übertragen werden. Die Nutzdaten interpretiert die Schicht dabei nicht weiter, sondern reicht diese an die nächst höhere Schicht weiter. Ganz oben steht immer etwas, dass nur noch Nutzdaten bekommt und mit denen Arbeitet. Bei einem Webserver hättest Du auch einen Interpreter, der eine HTML-Seite rendert. Die HTML-Seite wird dabei über das Hyper Text Transport Protocol (HTTP) übertragen. Dieses wird durch TCP-Pakete übertragen. Diese werden über IP übertragen.... Was für Nutzdaten in der obersten Schicht landen ist egal. Es sind einfach Bytes, die beliebig interpretiert werden können. Hast Du ein Datum von 4 Byte, kann das eine Zahl sein (z.B. ein Integer/Cardinal, ein Single, zwei Word, 4 Byte), es könnten Zeichen sein (4 Ascii Zeichen, 4 UTF-8 kodierte Zeichen, 2 UTF-16 kodierte Zeichen), aber auch alles andere, was in 4 Byte kodiert werden kann. Wie Du was überträgst kannst Du einfach in einem Protokoll festlegen (es sind also Nachrichten, aber nicht zwangsläufig Textnachrichten). Zitat:
So muss der eigentliche Auslöser den Server benachrichtigen, was getan wurde. Der Server wiederum muss alle Clients benachrichtigen. Bekommen die Clients eine Benachrichtigung vom Server, müssen sie diese darstellen. Das alles passiert, wenn ein Bet gemacht wurde, allerdings an drei völlig verschiedenen Stellen. Für alles könnte ein Controller in Frage kommen, der eine Methode "bet" hat (der Name "Bet" ist imho hier zu aussagelos). Zitat:
Es gibt immer saubere Alternativen (es geht also immer auch anders)! Der einfachste Weg ist es immer, dass Du die Units neu aufteilst. Häufig reicht es schon, dass man einen Teil in eine dritte Unit schiebt, auf die die beiden anderen zugreifen können. Diese dritte Unit sieht aber dafür die anderen zwei nicht. In diesem Fall musst Du nur schauen, was alles passieren kann und wer dazu welche Informationen benötigt. Betrachte ich die Methode Bet, so würde ich sagen, dass die immer dann aufgerufen wird, wenn jmd. etwas setzt. Da würde es mich dann aber stark wundern, dass der Server diese Methode / Unit kennen muss, denn der Server selbst wird nur benachrichtigt, wenn diese Methode von einem der Spieler aufgerufen wurde (wenn der selber setzt wäre das doch ungewöhlich). Aber auch den umgekehrten Weg würde ich nicht ganz verstehen, setzt ein Spieler, so würde hier die Methode Bet aufgerufen werden. Diese Methode wird also Clientseitig aufgerufen. Ein Client muss die Server-Unit gar nicht kennen, sondern sendet über einen TCP-Client eine Nachricht an den Server. Hier muss also die Unit Controller nur eine Möglichkeit haben, die Nachricht zu verschicken (oder jmd. zu informieren, was gemacht wurde, so dass dieser jmd. die Nachricht verschickt). Kommt die Nachricht beim Server an, so muss der alle Clienten darüber Informieren, was eine eigene Methode ist. Dabei muss der Server seine Liste der gemeldeten TCP-Clienten durchgehen und ihnen eine Nachricht zuschicken. Hier ist nur wichtig, dass über TCP (oder welche Protokoll auch immer) eine Nachricht versendet wird. Empfängt jetzt ein Client eine Nachricht, so muss er eine Ereignisbehandlung durchführen. Auch hier sehe ich nicht, dass ein Zugriff auf den Controller nötig wäre. Wo also siehst Du das Kreuz? Deutlicher gesagt würde ich folgenden Ablauf sehen/modellieren:
Die Namen sind jetzt nicht sonderlich gut gewählt, es soll nur anzeigen, dass hier nirgendwo ein Kreis entsteht, ein Überkreuzen ist also nicht nötig (soweit ich nicht irgendwas übersehe). Zitat:
Gehst Du aber immer den Weg, den Du gerade gegangen bist, dann führt das einfach zu riesigen Units. Die sind sehr viel schlechter wartbar und fehleranfälliger. Möchtest Du eine bestimmte Funktionalität wiederverwenden, so wird Dir schnell die gesamte Unit zu groß sein und man neigt zu copy&paste, bei so großen Units kannst Du dabei schnell mal eine wichtige Abhängigkeit (von etwas anderem in der Unit) übersehen und Du kopierst etwas, dass zwar kompiliert aber totaler Mist ist! Deswegen lieber kleine Units, die eine kleine Aufgabe lösen. Zitat:
Hast Du gleich sauber gearbeitet, stellt das spätere Anpassen (soweit es überhaupt nötig wird) kein Problem dar. Und glaub mir, dass ist kein erfundenes und übertriebenes Worst-Case-Szenario, sondern so ging es mir damals wirklich regelmässig. Da findet man nicht mal ebend eine Stelle, die man leicht ändern kann! Häufig passiert sowas zudem aus Zeitdruck heraus. Gibt man dem Druck nach, bekommt man vielleicht noch irgendwas zusammen, das läuft, aber der Nachfolgeauftrag ist dann schon nicht mehr wünschenswert! Da sollen dann eben kleine Änderungen vorgenommen werden, irgendwas Neues rein und Du stehst vor irgendeinem Chaos, dass Du irgendwann mal überarbeiten wolltest und weißt nicht wo anfangen. Damit erhöht sich nur der Aufwand, für das, was Du in der gleichen Zeit packen musst... Deshalb gilt wirklich, mache alles gleich richtig! Da gibt es diese bekannte Rechnung, dass eine Problem/Fehler während der Planung grob einen Dollar kostet (jeweils um behoben zu werden!), während der Implementierung der Funtion kostet der gleiche Fehler schon 10 Dollar, nach Fertigstellung/Zusammensetzen der Software kostet er 100 Dollar und wenn das Produkt ausgeliefert wurde > 1000 Dollar. Die Zahlen sind hier natürlich symbolisch zu verstehen, lieferst Du ein schlechtes Produkt aus, kann es Dich auch alles kosten (Imageschäden kann man nicht mit Geld beziffern). Trotzdem stimmen die Relationen der Kosten schon ganz gut, man hat echt schnell eine Verzehnfachung des Aufwands (gefühlt noch viel mehr), wenn man etwas vor sich her schiebt. Das gilt auch für Dinge, die man zu einem bestimmten Zeitpunkt noch nicht berücksichtigt. Je früher man etwas mit einbezieht, desto besser!!!! |
Re: Pokerprojekt realisierung
Ok, wahrscheinlich hast du Recht und ihc bin auch shcon ziemlich durcheinander im Projekt. Das liegt einfach daran, dass am Anfang viele Dinge nicht mit bedacht werden und die dann hinterher füchtig eingesetzt werden!
Ich denke ich schriebe die Units nochmal neu und ordentlich! Zu der Sache mit dem Controller. Also nur der Server kennt den Controller! Wenn ein Spieler setzt, schickt er eine Nachricht an den Server, zB: 'Bet[600]' ---> 600setzen Wenn der Server das empfängt, soll er den Controller benachrichtigen, dass er die procedure Bet aufruft! Dann kann der Controller aber nichtmehr den Server benachrichtigen, wenn er die procedure ausgeführt hat, weil er den Server ja nicht in den Uses hat! Ich weiss nun wie ich über-kreuz-uses machen kann, aber ich will es garnicht, will mein Projekt ja hinterher ordentlich haben! Was hälst du denn davon wie ich die Nachrichten schicke und auswerte? Ich schreib hier ma n paar Zeilen dazu:
Delphi-Quellcode:
Ansatz so richtig oder Verbesserungsvorschlag?
SendText('Call'); //wär zB ein einfacher Call
SendText('SGeld(1)[1500]'); //würde zB das Geld von Spieler 1 auf 1500 setzen Zur Auswertung hab ich mir ne Funktion geschrieben, der ich einen String und zwei Zeichen geben kann, und die dann den Substring zwischen den beiden Zeichen ausgibt. Sieht dann zB so aus:
Delphi-Quellcode:
Gibts da eigentlich ne schönere/bessere Möglichkeit als diese tausend if-Abfragen?
function TMyClient.Klammern(Value, Search1, Search2: String): String; //eben genannte Funktion
begin Result := Copy(Value, Pos(Search1, Value) + 1, Pos(Search2, Value) - Pos(Search1, Value) - 1); end; procedure TMyClient.Parse_Text(Value: String); //hier wird dann der Text ausgewertet, hoffe das ist einigermassen verständlich! var temp: String; begin temp := Copy(Value, 0, Pos('[', Value) - 1); if temp = 'CBet' then //CBet wird geändert CBet := StrtoInt(Klammern(Value, '[', ']')); if temp = 'Phase' then //Phase wird geändert Phase := StrtoInt(Klammern(Value, '[', ']')); if temp = 'SBlind' then //SBlind wird geändert SBlind := StrtoInt(Klammern(Value, '[', ']')); if temp = 'Dealer' then //Dealer wird geändert Dealer := StrtoInt(Klammern(Value, '[', ']')); if temp = 'AktiverSp' then //AktiverSp wird geändert AktiverSp := StrtoInt(Klammern(Value, '[', ']')); if temp = 'Pot' then //Pot wird geändert Pot := StrtoInt(Klammern(Value, '[', ']')); if temp = 'ClientNumber' then //ClientNr wird zugewiesen ClientNr := StrtoInt(Klammern(Value, '[', ']')); if temp = 'Start' then //Spielstatus if Klammern(Value, '[', ']') = 'True' then Start := True else Start := False; end; Und dann hab ich da noch ein Problem: Der Server schickt oft soviele Nachrichten nacheinander, dass der Server beim auswerten die Nachrichten durcheinander bringt, wie kann ich das verhindern? Danke für die Hilfe und hoffe ich hab nix vergessen, aber jetzt muss mein Hund raus ;-) |
Re: Pokerprojekt realisierung
Hallo,
ich würde das nicht per Text machen.
Delphi-Quellcode:
Das geht schneller auszuwerten und ist weniger tippfehleranfällig.
type
TPlayerActionType = (atBet, atCall, atCheck); TPlayerAction = packed record TimeStamp: TDateTime; ActionType: TActionType; Current: Cardinal; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:08 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz