Netzwerk: Spielserver im LAN suchen und anmelden

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
Dostej
Beiträge: 529
Registriert: 01.10.2004 10:02
Kontaktdaten:

Netzwerk: Spielserver im LAN suchen und anmelden

Beitrag von Dostej »

Also, ich versuche mich gerade in Sachen Netzwerk. Dazu habe ich ein paar der Beispielcodes aus dem CodeArchiv durchforstet und etwas zusammengebaut, das alle Computer im LAN sucht und dort nach Spielservern sucht.
Es gibt dem Spieler die Auswahlliste zurück, welche Spiele im LAN angeboten werden. Der Spieler kann sich eins aussuchen und wird am dortigen Server angemeldet.

Client-Code:

Code: Alles auswählen

; Konstanten
#MAX_PREFERRED_LENGTH = -1 
#SV_TYPE_ALL          = $FFFFFFFF 
#NERR_SUCCESS         = 0 
#ERROR_MORE_DATA      = 234 
#MainWindow           = 100 
#MMTB                 = 200 

;- Window Constants
Enumeration
  #WND_Join_Game
EndEnumeration

;- Gadget Constants
Enumeration
  #WND_Join_Game_LICON
  #WND_Join_Game_BTN_OK
  #WND_Join_Game_BTN_Abbruch
EndEnumeration

; Strukturen und Vars
Structure HOSTENT 
  h_name.l 
  h_aliases.l 
  h_addrtype.w 
  h_length.w 
  h_addr_list.l 
EndStructure 
Structure SERVER_INFO_101 
  dwPlatformId.l 
  lpszServerName.l 
  dwVersionMajor.l 
  dwVersionMinor.l 
  dwType.l 
  lpszComment.l 
EndStructure 
Structure LAN_RESULT
  Name.s
  IP_Name.s
  Aktiv_Server.l ; 1, wenn dort ein aktiver Server gefunden werden konnte, dann sind auch  die weiteren Daten belegt
  Spiel_Name.s
  Spieler_bisher.l
EndStructure
NewList LAN_Client.LAN_RESULT()

; Initialisieren
If InitSprite() And InitKeyboard() And InitNetwork() = 0
  MessageRequester("Error", "Cant initialize Core")
  End
EndIf

; Proceduren
Procedure.s GetIPbyName (NameIP.s) 
  TheIPAdress.s 
  pHostinfo = gethostbyname_(NameIP) 
  If pHostinfo = 0 
    TheIPAdress = "Unable to resolve domain name" 
  Else 
    CopyMemory (pHostinfo, hostinfo.HOSTENT, SizeOf(HOSTENT)) 
    If hostinfo\h_addrtype <> #AF_INET 
      MessageRequester("Info","A non-IP address was returned",0) 
    Else 
      While PeekL(hostinfo\h_addr_list+AdressNumber*4) 
        ipAddress = PeekL(hostinfo\h_addr_list+AdressNumber*4) 
        TheIPAdress = StrU(PeekB(ipAddress),0)+"."+StrU(PeekB(ipAddress+1),0)+"."+StrU(PeekB(ipAddress+2),0)+"."+StrU(PeekB(ipAddress+3),0) 
        AdressNumber+1 
      Wend 
    EndIf 
  EndIf 
  ProcedureReturn TheIPAdress 
EndProcedure 
Procedure GetLANList() 
  IPResult.s 
  se101.SERVER_INFO_101 
  nStructSize = SizeOf(SERVER_INFO_101) 
  RetCode = NetServerEnum_(0, 101, @bufptr, #MAX_PREFERRED_LENGTH, @dwEntriesread, @dwTotalentries, #SV_TYPE_ALL, 0, @dwResumehandle) 
  If RetCode = #NERR_SUCCESS And RetCode <> #ERROR_MORE_DATA 
    For i = 0 To dwEntriesread - 1 
      CopyMemory( bufptr + (nStructSize * i),@se101, nStructSize) 
      Buffer.s=Space(512) 
      result=WideCharToMultiByte_(#CP_ACP, 0, se101\lpszServerName, 255, @Buffer.s, 512, 0, 0) 
      IPResult = GetIPbyName (Buffer) 
      AddElement(LAN_Client())
      LAN_Client()\Name = Buffer
      LAN_Client()\IP_Name = IPResult
    Next 
  Else 
    MessageRequester("Info","Failed",0) 
  EndIf 
  NetApiBufferFree_(bufptr) 
  ; SendMessage_(MMTextBox,$00B6,0,30) 
EndProcedure 
Procedure.l Open_WND_Join_Game()
  ; wählt ein Spiel aus und schliesst die offenen Verbindungen zu anderen Spielservern
  ; Gibt den Listindex des gewählten Lan-Spiels zurück, oder -1, wenn keines gewählt wurde
  back.l = -1
  If OpenWindow(#WND_Join_Game, 216, 0, 313, 399,  #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_TitleBar , "Join Game")
    If CreateGadgetList(WindowID())
      
      ;-
      ListIconGadget(#WND_Join_Game_LICON, 10, 30, 290, 330, "Spiel Name", 200)
      AddGadgetColumn(#WND_Join_Game_LICON, 2, "Spieler bisher", 85)
      AddGadgetColumn(#WND_Join_Game_LICON, 3, "", 1)
      ButtonGadget(#WND_Join_Game_BTN_OK, 230, 370, 70, 20, "OK")
      ButtonGadget(#WND_Join_Game_BTN_Abbruch, 10, 370, 70, 20, "Abbruch")
      
      ; die Gadgets vorbereiten
      ResetList(LAN_Client())
      While NextElement(LAN_Client())
        If LAN_Client()\Aktiv_Server
          AddGadgetItem(#WND_Join_Game_LICON, -1, LAN_Client()\Name + " - " + LAN_Client()\Spiel_Name + Chr(10) + Str(LAN_Client()\Spieler_bisher) + Chr(10) + Str(ListIndex(LAN_Client())))
        EndIf
      Wend
      
      ; Aktionsschleife
      Repeat 
        Event = WaitWindowEvent() 
        If Event = #PB_EventGadget
          GadgetID = EventGadgetID() 
          If GadgetID = #WND_Join_Game_LICON
            back = Val(GetGadgetItemText(#WND_Join_Game_LICON, GetGadgetState(#WND_Join_Game_LICON), 2)) ; den index auslesen
            
          ElseIf GadgetID = #WND_Join_Game_BTN_OK
            Event = #PB_EventCloseWindow
            
          ElseIf GadgetID = #WND_Join_Game_BTN_Abbruch
            back = -1
            Event = #PB_EventCloseWindow
            
          EndIf 
        EndIf 
      Until Event = #PB_EventCloseWindow
      Event = 0 ; nur dieses Fenster schliessen
      ; die anderen offenen Serververbindungen schliessen
      ResetList(LAN_Client())
      While NextElement(LAN_Client())
        If LAN_Client()\Aktiv_Server And ListIndex(LAN_Client()) <> back ; wenn server, aber nicht der ausgewählte
          CloseNetworkConnection(LAN_Client()\Aktiv_Server)
        EndIf
      Wend
      CloseWindow(#WND_Join_Game)
    EndIf
  EndIf
  ProcedureReturn back
EndProcedure




; Welche Clients gibts im Lan
Debug "get lan list"
GetLANList()

;{ teste die Lan-Computer auf Spiel-Server
port.l = 1000
c.l = 0
ResetList(LAN_Client())
While NextElement(LAN_Client())
  Debug Str(ListIndex(LAN_Client())) + "- " + LAN_Client()\Name + " -> " + LAN_Client()\IP_Name
  ; auf welchen Clients läuft ein Server für das Spiel
  NetzwerkID = OpenNetworkConnection(LAN_Client()\IP_Name, port) 
  If NetzwerkID ; wenn die Verbindung aufgebaut werden konnte
    LAN_Client()\Aktiv_Server = NetzwerkID
    Debug "netzwerkID gefunden: " + Str(NetzwerkID)
    Msg.s = Space(20)
    If ReceiveNetworkData(NetzwerkID, @Msg, 20)
      Debug "received: " + Msg
      LAN_Client()\Spieler_bisher = Val(Left(Msg, 1))
      Msg = RTrim(Msg) 
      LAN_Client()\Spiel_Name = Right(Msg, Len(Msg)-1)
      Debug "Spiel gefunden: " + LAN_Client()\Spiel_Name
      c + 1
    EndIf
  EndIf 
Wend
Debug "alle server getestet"
ResetList(LAN_Client())
While NextElement(LAN_Client())
  ; zeigt die gefunden server, wenn die letzte Zahl <> 0, läuft dort ein Server
  Debug Str(ListIndex(LAN_Client())) + "- " + LAN_Client()\Name + " -> " + LAN_Client()\IP_Name + " - Erg: " + Str(LAN_Client()\Aktiv_Server)
Wend
;}
;{ Auswählen, welches Spiel mitgespielt werden soll
Anmeldung.l = 0 ; 1, wenn angelemdet
If c
  t = Open_WND_Join_Game() ; wählt ein Spiel aus und schliesst die offenen Verbindungen zu anderen Spielservern
  Debug "t: " + Str(t)
  If t > -1 ; Spiel wurde ausgewählt
    ; die Verbindungsdaten übertragen
    SelectElement(LAN_Client(), t)
    Debug "listindex: " + Str(ListIndex(LAN_Client()))
    NetzwerkID = LAN_Client()\Aktiv_Server
    SpielName.s = LAN_Client()\Spiel_Name
    ; die Anmeldung am Spielserver machen
    Debug "verbindungsdaten: ID: " + Str(NetzwerkID) + ". " + SpielName
    Msg = "1" ; Bitte um Anmeldung beim Spiel
    SendNetworkData(NetzwerkID, @Msg, 1) ; anmeldung für Spiel schicken
    
    Msg = "   !"
    Repeat ; warte auf die Anmeldebestätigung 
      If NetworkClientEvent(NetzwerkID) = 2
        ReceiveNetworkData(NetzwerkID, @Msg, 3)
      EndIf
      Debug "anmeldestring: " + Msg
      Anmeldung = Val(Left(Msg, 1))
      Debug "Anmeldung erhalten: " + Str(Anmeldung)
      Delay(100) ; 10 Abfragen pro Sek langen
    Until Anmeldung <> 0
  EndIf
Else
  MessageRequester("Problem", "Keine Spiele zum mitspielen vorhanden")
  End
EndIf
;}


If Anmeldung = 1 ; nur starten, wenn angemeldet
  
  MessageRequester("Angemeldet", Str(Anmeldung))
  
  ; hier beginnt nun das Spiel...
  
  
Else
  MessageRequester("Fehler", "Konnte nicht an den gewünschten Spiel teilnehmen")
  End
EndIf
und hier ist der Code für den Server:

Code: Alles auswählen

#MAX_CLIENTS = 8
Dim Client.l(#MAX_CLIENTS) 

;{ Den Server erstellen
OpenConsole() 
If InitNetwork() = 0 
  PrintN("Das Netzwerksystem konnte nicht gestartet werden") 
  Delay(3000) 
  End 
EndIf 


port = 1000 ; Alternativ dazu kann der Port auch eingegeben werden... (1000 wird von Clinet verwendet, muss also dort angepasst werden)
;PrintN("An welchem Port soll der Server lauschen?") 
;port=Val(Input()) 
PrintN("")

PrintN("Geben Sie den Spielnamen an (max. 19 Zeichen)?") 
SpielName.s =Input()
If Len(SpielName) > 19
  SpielName = Left(SpielName, 19)
EndIf
PrintN("")
PrintN("SpielName: " + SpielName) 
PrintN("")

If CreateNetworkServer(port) = 0 
  PrintN("Server konnte nicht erstellt werden") 
  Delay(3000) 
  End 
Else 
  PrintN("Server wurde erstellt")  
EndIf 
;}
;{ Die Aktionsschleife
Repeat 
  Select NetworkServerEvent() 
    Case 1 ;{ a new client has been connected to the server
      t = NetworkClientID()
      c.l = 0
      For x = 0 To #MAX_CLIENTS
        If Client(x) <> 0
          c + 1
        EndIf
      Next
      Msg.s = Str(c) + SpielName
      Msg = Msg + Space(20 - Len(Msg))
      SendNetworkData(t, @Msg, 20) ; sendet die bisherige Spieleranzahl und den SPielnamen
      PrintN("Serverabfrage - ID: " + Str(t))
      ;}
    Case 2  ;{ Raw Data has been received (To be Read with ReceiveNetworkData())
      t = NetworkClientID() ; sender speichern 
      ; testen, ob Sender in Liste eingetragen
      cnt.l = -1 ; nächster freier Platz
      x.l = -1 ; index des Spielers mit Daten
      For y = 0 To #MAX_CLIENTS
        If Client(y) = t
          x = y
        EndIf
        If Client(y) = 0 And cnt = -1
          cnt = y
        EndIf
      Next
      Debug "x: " + Str(x)
      If x > -1 ; wenn schon in der Clientliste
        ; spezifische Datenverarbeitung
        
        Debug "Erhalte daten von spieler (platz) " + Str(y) + " - verb: " + Str(t)
        ; Hier nun die Spielspezifische Datenverarbeitung
        
        
        
        
      Else
        Debug "neuer client sendet daten"
        ; Will sich der Client anmelden?
        Msg.s = " " ; erwarte einen Anmeldestring
        ReceiveNetworkData(t, @Msg, 1)
        Debug "gesendet: " + Msg
        If Msg = "1" ; Anmeldeversuch
          If cnt > -1 ; wenn noch Platz in der Clientliste war
            Client(cnt) = t ; in die Liste aufnehmen
            PrintN("Neuer Client auf platz "  + Str(cnt) + " - NR. " + Str(Client(cnt)))
            Msg.s = "1-" + Str(cnt) 
            SendNetworkData(t, @Msg, 3) ; sende dem Client - Bestätigung Warteliste
            Debug "bestätigung verschickt: " + Msg
          Else
            PrintN("Neuer Client versuchte sich einzuloggen - kein Platz auf Liste")
            Msg.s = "9-"
            SendNetworkData(t, @Msg, 2) ; sende dem Client - Absage wegen Voll
            Debug "absage geschickt: " + Msg
          EndIf
        EndIf
      EndIf
      ;}
      
    Case 3 ; a file has been received (To be Read with ReceiveNetworkFile())
      
    Case 4 ;{ a client has quit the server (deconnection)
      t = NetworkClientID() ; sender speichern 
      ; testen, ob Sender in Liste eingetragen
      c.l = 0
      For x = 0 To #MAX_CLIENTS
        If Client(x) = t
          Client(x) = 0 ; aus der Liste austragen
          PrintN("Client hat sich ausgeloggt - Nr: " + Str(x))
          c + 1
          ; evtl. Meldung an die anderen Spieler
        EndIf
      Next
      If c = 0
        PrintN("Ein Anfrager hat sich ausgeloggt - ID: " + Str(t))
      EndIf
      ;}
  EndSelect
  Delay(1)
ForEver 
;}
Ich hoffe ich habe keine Fehler mehr drin.

Zu guter letzt noch ne Verständnisfrage: Wenn ich das ganze übers Internet spielen will, müsste ich nur die IP-ADresse und den Port für den Server beim Client eingeben (und dafür die Suche weglassen) - Stimmt das?
Benutzeravatar
hardfalcon
Beiträge: 3447
Registriert: 29.08.2004 20:46

Beitrag von hardfalcon »

Ich brauchs zwar eigentlich nicht, habs aber mal getestet... Nicht schlecht! :D
Wenn du über Internet spielen willst, solltest du allerdings einen zentralen Server haben, bei dem sich alle "normalen Spieleserver" anmelden (müssen). Denn es wäre ziemlich nervig, jeden, gegen den man spielen will, zuerst anrufen zu müssen, um ihn nach seiner IP zu fragen... :wink: Du könntest es allerdings als Option einbauen...
Benutzeravatar
Dostej
Beiträge: 529
Registriert: 01.10.2004 10:02
Kontaktdaten:

Beitrag von Dostej »

Erst mal Danke :)

Wenn ich mal das Problem auf Seite lasse, wie ich an die IP komme, ist der Grundvorgang aber richtig, oder? Praktisch einfach eintippen.. (oder von Server laden oder oder oder)

Aber das mit dem Server - dafür bin ich vermutlich zu doof. Wenn ich das richtig verstanden habe, müsste auf dem "Grossen" Server dann so was wie ienphp-skript laufen oder ein weiteres "Spezielles" Serverprogramm (evtl. ne Abwandlung vom Atomic-server), der die eingetragenen Spielserver_ip´s speicehrt und bei Anfrage ne WEbsite generiert, die dann alle IP´s anzeigt.
(Falls ich das Prinzip vertsanden habe, ist mir dann die Arbeit immer noch zu gross - ganz von der Domain mal abgesehen...)
Benutzeravatar
hardfalcon
Beiträge: 3447
Registriert: 29.08.2004 20:46

Beitrag von hardfalcon »

Hmmm, sooo schwierig wäre es auch nicht, einen solchen Server zu machen (zumindest solange du bei der PB-Network-Library bleibst, denn Dinge wie z.B. ein HTTP-Get/-Post sind in der Tat schwieriger...). Du müsstest dem Server im Grunde nur die IP und den Port des vom spieler gestarteten servers mitteilen. Wie du die rauskriegst, das wurde hier im Forum auch schon mehrfach diskutiert... Alles andere (auf dem Serer vorhandene Spieler, Port, passwort-geschützter Server, etc...) ist nicht zwingend erforderlich, sondern hängt nur davon ab, wie du dein Spiel programmierst. Aber am Besten wre es natürlich, wenn der Server diese Daten alle verwaltet. (wenn ein Server sehr "interessant" zum mitspielen ist, z.B. tolle Map und viele Spieler, aber niedriger Ping, o.ä., dann wird der Spieleserver unnötig belastet, weil er ständig auch noch die Requests von anderen Servern bearbeiten muss. Wenn er allerdings nur dann zum Zentralserver senden muss, wenn sich etwas an den eigentlichen Informationen über das laufende Spiel geändert hat (Server (nicht mehr) voll, neue Spieleranzahl, neue Map, etc...), wird er entlastet, und das Spiel hat weniger Lags.) Wenn du dein Spiel OpenSource machst, könntest du z.B. über SourceForge eventuell einen (Sponsor für einen) Server finden... Aber das ist noch eine ganz andere Sache, erstmal muss ja das Spiel fertig sein... :wink:

cya
Pascal
Antworten