home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 42 / Amiga Format AFCD42 (Issue 126, Aug 1999).iso / -serious- / programming / basic / udpchat / udp_chatv2.4.asc < prev    next >
Encoding:
Text File  |  1999-05-14  |  46.2 KB  |  1,261 lines

  1. ;
  2. ;                      UDP Chat code V2.4   26/04/99
  3. ;
  4. ;    This code sends and receives UDP data packets like a
  5. ;  a simple IRC client, and  checks wether they have arrived
  6. ;  at their destination, and allows other copies of this
  7. ;  program to log in to this one (can act as a server or client).
  8. ;
  9. ;  Written by Anton Reinauer <anton@ww.co.nz>.
  10. ;  GUI: Alvaro Thompson <alvaro@enterprise.net> - And awful hacks
  11. ;  by me to his nice font sensitive code :-)
  12. ;        - typical bloody games programmer ;-)
  13. ;
  14. ;  Thanks to Paul Burkey for TCP_Funcs and to Dr. Ercole Spiteri
  15. ;  for TCP-to-Blitz.
  16. ;
  17. ;  ARexx portname: "UDP_Chat"
  18. ;
  19. ;  Turn overflow errors off in Debugger options,
  20. ;  and have amigalibs.res resident in compiler options.
  21.  
  22. WBStartup
  23. NoCli
  24. DEFTYPE .w
  25. ResetTimer
  26.  
  27. ;****************    UDP Variables and Constants   **************
  28.  
  29. hostname.s="localhost"  ; "localhost" default destination host address
  30. #NO_CONNECTION=1        ; 0 - are we connected, or have we been connected to the Internet since bootup?  1- No
  31. #COMMS_DEBUG=1          ; 1 - 1 for debug info and multiple local copies
  32.  
  33. #MESSAGE_ARRAY_SIZE=256 ; 256
  34. GAME_NAME.s="UDP_Chat"  ; "UDP_Chat"  Game name (for login)
  35. max_wait=250            ; 250 Time to wait for an acknowledgement (frames)
  36. number_resends=5        ; 5 Number of resends to wait, before disconnecting player
  37. lag_allowance=5         ; 5 Number of frames to add to the current lag, when waiting for an out-of-order packet.
  38. max_number_players=8    ; 8
  39. #ACKNOWLEDGE_PACKET=3
  40. #OFFLINE=0
  41. #ONLINE=1
  42. #ATTEMPTING_TO_CONNECT=1
  43. #SERVER=2
  44. #CLIENT=3
  45.  
  46. #DISCONNECTED=1
  47. #CONNECTED=2
  48. #CONNECTION_REJECTED=3
  49. #SYSTEM_MESSAGE=4   ;  System message
  50. #HIGH=3             ;  High priority Security Message or Comms Debug info
  51. #MEDIUM=2           ;  Medium   "      "
  52. #LOW=1              ;  Low      "      "
  53. #SERVER_NUMBER=1
  54. #GAME_CLOSING_TIMEOUT=1500
  55. #DISCONNECT_TIMEOUT=1500
  56. #CLIENT_SERVER=0
  57. #PEER_TO_PEER=1
  58. packet_number.l=0
  59. online=#OFFLINE
  60. my_number=0
  61. last_message_number.l=0
  62. free_message.l=0
  63. sock.l=-1
  64. security=#LOW   ; level of security warnings (normally #LOW- show #LOW and higher ie: all)
  65. comms_debug_level=#LOW  ; level of debug info (normally #LOW- show #LOW and higher ie: all)
  66. tcp_stack=False
  67. disconnect_timeout=0
  68.  
  69. ;***************************  UDP Dims  *************************
  70.  
  71. .Dims
  72.  
  73. INCLUDE "Net_Protocol_Header.bb2"  ; Net protocol constants
  74. INCLUDE "UDPHeader.bb2"  ;Standard TCP/UDP library structures and UDP Funcs Newtypes
  75.  
  76. Dim messages.message_status(#MESSAGE_ARRAY_SIZE)
  77. Dim host.sockaddrin(max_number_players), hostlen.l(max_number_players)
  78. Dim players.player_info(max_number_players)
  79. Dim player_honesty(max_number_players,max_number_players)
  80.  
  81. DEFTYPE .sockaddrin temphost
  82.  
  83. For a=1 To max_number_players      ; this array dimension is for the player number who is claiming another is offline
  84.   For b=1 To max_number_players    ; this dimension is for the player number who is claimed to be offline
  85.     player_honesty(a,b)=False       ; initialise array
  86.   Next
  87. Next
  88. messages(free_message)\ack=True
  89.  
  90. ;*******************     Game Functions     ********************
  91.  
  92. ;  These Functions are specific to your program, the ones below
  93. ;  can be used as examples for your game- most of them are after
  94. ;  the UDP_Funcs, and this one will be able to be put there soon.
  95.  
  96. .Print_String                ; Print a string in window and scroll if necessary
  97. Statement Print_String{text$}
  98.   SHARED ypos
  99.   If ypos<200
  100.     ypos+10
  101.   Else
  102.     WScroll 10,60,590,220,0,10
  103.   EndIf
  104.  
  105.   WLocate 10,ypos
  106.   Print text$               ; print string received
  107. End Statement
  108.  
  109. ;************************    UDP Funcs    ***********************
  110.  
  111. ;  These are all specific functions for your game to communicate
  112. ;  over the internet or by modem/null-modem.
  113.  
  114. .Security_Warning
  115. Statement Security_Warning{text$,level}
  116.   SHARED security    ; security= 0 none, 1 all warnings, 2 medium and severe warnings and 3 only severe warnings
  117.   If level>=security  ; only print out security warnings if at set security level or higher
  118.     If level<#SYSTEM_MESSAGE
  119.       a$="Warning: "
  120.     EndIf
  121.     Print_String{a$ + text$}
  122.   EndIf
  123. End Statement
  124.  
  125. .Comms_Debug
  126. Statement Comms_Debug{text$,level}
  127.   SHARED comms_debug_level    ; comms_debug= 0 none, 1 all info, 2 medium and high priority info and 3 only high priority info
  128.   If level>=comms_debug_level   ; only print out debug info if at set debug level or higher
  129.     Print_String{text$}
  130.   EndIf
  131. End Statement
  132.  
  133. INCLUDE "UDPFuncs.bb2"        ; basic UDP socket functions
  134.  
  135. .Dim_Message_Arrays
  136. Statement Dim_Message_Arrays{new_max_number_players}
  137.   SHARED online,max_number_players
  138.   SHARED host(),hostlen(),players(),player_honesty()
  139.  
  140.   max_number_players=new_max_number_players
  141.   If online>#OFFLINE
  142.     Dim host.sockaddrin(max_number_players), hostlen.l(max_number_players)
  143.     Dim players.player_info(max_number_players)
  144.     Dim player_honesty(max_number_players,max_number_players)
  145.  
  146.     For a=1 To max_number_players      ; this array dimension is for the player number who is claiming another is offline
  147.       For b=1 To max_number_players    ; this dimension is for the player number who is claimed to be offline
  148.         player_honesty(a,b)=False       ; initialise array
  149.       Next
  150.     Next
  151.   EndIf
  152. End Statement
  153.  
  154. .Get_Packet_Source            ; Check if packet has come from player already logged on.
  155. Function.w Get_Packet_Source{}
  156.   SHARED host(),temphost,max_number_players
  157.   i=0
  158.   exit=0
  159.   Repeat                    ; check for each player (hostname and socket)
  160.     i+1
  161.     If host(i)\sin_addr\s_addr=temphost\sin_addr\s_addr AND  host(i)\sin_port=temphost\sin_port
  162.       exit=1
  163.     EndIf
  164.     If i=max_number_players AND exit=0; if at end of players and no match is found
  165.       i=-1
  166.       exit=1
  167.     EndIf
  168.   Until  exit=1
  169.  
  170.   Function Return i
  171. End Function
  172.  
  173. .Get_Ascii_Address                     ; build a.b.c.d numerical address from long number
  174. Function.s Get_Ascii_Address{address.l}
  175.   string_address.l=Inet_NtoA_(address) ; get memory address of ASCII string version of host address (a.b.c.d)
  176.   If string_address=0
  177.     Function Return ""
  178.   EndIf
  179.   Repeat                               ; build ASCII host string
  180.     letter.b=(Peek.b(string_address))
  181.     If letter<>0
  182.       temp$=temp$+Chr$(letter)
  183.     EndIf
  184.     string_address+1
  185.   Until letter=0
  186.   Function Return temp$
  187. End Function
  188.  
  189. .Send_Reliable_Message         ; this message is checked to see if it has arrived, and resent if not
  190. Statement Send_Reliable_Message{send_string.s,player,protocol}       ; if no confirmation can be made, link is considered lost
  191.  SHARED messages(),free_message,last_message_number,packet_number.l
  192.  
  193.   If protocol=#PEER_TO_PEER    ; Send message Peer-To-Peer
  194.     send_string.s=Mkl$(packet_number) + send_string.s
  195.     messages(free_message)\number=packet_number,False,Ticks,send_string.s,0,player  ; log message in message array
  196.     If messages(last_message_number)\ack=True
  197.      last_message_number=free_message
  198.     EndIf
  199.  
  200.     WriteUDP{&send_string.s,Len(send_string.s),player}   ; send message
  201.     packet_number+1               ; set packet number to next free packet number (+1)
  202.     free_message+1
  203.     If free_message=#MESSAGE_ARRAY_SIZE+1 Then free_message=0    ; if packet number >#MESSAGE_ARRAY_SIZE then wrap back to 0
  204.   Else     ; Send message Client-Server
  205.  
  206.   EndIf
  207.  
  208. End Statement
  209.  
  210. .Initialise_Server
  211. Function Initialise_Server{localport}
  212.   ; Set up the game so we can act as Server
  213.   SHARED online,port_used
  214.  
  215.   If online=#OFFLINE
  216.     If Initialise_UDP{localport}        ; bind socket to local port
  217.       online=#SERVER
  218.       Function Return True
  219.     Else
  220.       Security_Warning{"Error in setting up UDP at Port "+ Str$(port_used),#SYSTEM_MESSAGE}
  221.     EndIf
  222.   Else
  223.     Select online
  224.       Case #CLIENT
  225.         Security_Warning{"Already logged on to a Server!",#SYSTEM_MESSAGE}
  226.       Case #SERVER
  227.         Security_Warning{"Already acting as a Server!",#SYSTEM_MESSAGE}
  228.       Case #ATTEMPTING_TO_CONNECT
  229.         Security_Warning{"Already attempting to log on to a Server!",#SYSTEM_MESSAGE}
  230.     End Select
  231.   EndIf
  232.   Function Return False
  233. End Function
  234.  
  235. .Disconnect_From_Game
  236. Function Disconnect_From_Game{}
  237.   ; Remove us from the game, and if we're the Server- close down
  238.   ; the game and take away our ability to act as Server.
  239.   SHARED online,players(),max_number_players,my_number
  240.   SHARED disconnect_timeout
  241.   SHARED CP_END_GAME.s,CP_REQ_PLAYER_DISCONNECT.s
  242.  
  243.   Select online
  244.     Case #OFFLINE
  245.       Function Return True
  246.     Case #SERVER  ; if we are Server
  247.       dummy=0
  248.       For i=2 To max_number_players
  249.         If players(i)\status=#ONLINE  ; send to player if they're online
  250.           send$=CP_END_GAME.s
  251.           dummy=1
  252.           Send_Reliable_Message{send$,i,#PEER_TO_PEER}   ; Send string to connected players
  253.         EndIf
  254.       Next
  255.       If dummy=1
  256.         Security_Warning{"Telling players Game has ended.",#SYSTEM_MESSAGE}
  257.         disconnect_timeout=#DISCONNECT_TIMEOUT
  258.         Function Return False
  259.       Else
  260.         online=#OFFLINE
  261.         Function Return True
  262.       EndIf
  263.     Case #CLIENT  ;  we are client
  264.       send$=CP_REQ_PLAYER_DISCONNECT.s + Mki$(my_number)
  265.       Send_Reliable_Message{send$,#SERVER_NUMBER,#PEER_TO_PEER}
  266.       Security_Warning{"Telling Server we are disconnecting from Game.",#SYSTEM_MESSAGE}
  267.       disconnect_timeout=#DISCONNECT_TIMEOUT
  268.       Function Return False
  269.     Case #ATTEMPTING_TO_CONNECT  ;  we are client
  270.       Security_Warning{"Game Connection attempt aborted.",#SYSTEM_MESSAGE}
  271.       online=#OFFLINE
  272.       Function Return True
  273.    End Select
  274. End Function
  275.  
  276. .Connect_To_Server
  277. Function Connect_to_Server{host.s,port.w}
  278.   SHARED online, CP_REQ_CONNECT.s, GAME_NAME.s, NET_PROTOCOL_VERSION.s
  279.   If online=#OFFLINE
  280.     If Initialise_UDP{port}       ; bind socket to local port
  281.       If Get_Host_By_Name{GTGetString(0,51), GTGetInteger(0,52),1}=True
  282.         Security_Warning{"Attempting to connect to Server",#SYSTEM_MESSAGE}
  283.         send$=Mkl$(0) + CP_REQ_CONNECT.s + GAME_NAME.s + Chr$(0) + NET_PROTOCOL_VERSION.s  ; "0000" is a Pad for packet number
  284.         WriteUDP{&send$,Len(send$),1}   ; send connection request
  285.         online=#ATTEMPTING_TO_CONNECT
  286.         Function Return True
  287.       Else
  288.         Function Return False
  289.       EndIf
  290.     Else
  291.       Function Return False
  292.     EndIf
  293.   Else
  294.     Select online
  295.       Case #CLIENT
  296.         Security_Warning{"Already logged on to a Server!",#SYSTEM_MESSAGE}
  297.       Case #SERVER
  298.         Security_Warning{"Already acting as a Server!",#SYSTEM_MESSAGE}
  299.       Case #ATTEMPTING_TO_CONNECT
  300.         Security_Warning{"Already attempting to log on to a Server!",#SYSTEM_MESSAGE}
  301.     End Select
  302.     Function Return False
  303.   EndIf
  304. End Function
  305.  
  306. .Find_Next_Message
  307. Statement Find_Next_Message{}
  308. SHARED last_message_number,messages(),free_message
  309.  
  310.   exit=0
  311.   Repeat              ; find next unacknowledged message in array
  312.  
  313.     last_message_number+1
  314.     If last_message_number=#MESSAGE_ARRAY_SIZE+1 Then last_message_number=0
  315.  
  316.     If last_message_number=free_message  ; if have got to free_message then there are no messages waiting to be acknowledged
  317.       If last_message_number=0
  318.         last_message_number=#MESSAGE_ARRAY_SIZE
  319.         exit=1
  320.       Else
  321.         last_message_number-1
  322.         exit=1
  323.       EndIf
  324.     Else
  325.       If messages(last_message_number)\ack=False   ; we've found the next unacknowledged message in the array
  326.         exit=1
  327.       EndIf
  328.     EndIf
  329.  
  330.   Until exit=1
  331. End Statement
  332.  
  333. .Clear_Player_Arrays
  334. Statement Clear_Player_Arrays{player}    ; clear player's arrays
  335.   SHARED last_message_number,messages(),free_message,host()
  336.   SHARED player_honesty(),players(),max_number_players
  337.  
  338.   If messages(last_message_number)\player=player
  339.     find_next_message=True
  340.   Else
  341.     find_next_message=False
  342.   EndIf
  343.  
  344.   a=last_message_number
  345.   exit=False
  346.   Repeat                      ; clear message array
  347.     If messages(a)\player=player
  348.       messages(a)\ack=True    ; clear unacknowledged messages off message array
  349.     EndIf
  350.     If a=free_message Then exit=True
  351.     a+1
  352.     If a=#MESSAGE_ARRAY_SIZE+1 Then a=0    ; if at end of array, jump to beginning
  353.   Until exit=True
  354.  
  355.   If find_next_message=True
  356.     Find_Next_Message{}
  357.   EndIf
  358.  
  359.   host(player)\sin_addr\s_addr=0   ; clear host address array
  360.   host(player)\sin_port=0
  361.  
  362.   For a= 1 To max_number_players   ; clear player_honesty array
  363.     player_honesty(player,a)=False  ; clear disconnected player's array
  364.     player_honesty(a,player)=False  ; clear other player's arrays who have claimed the disconnected player was offline
  365.   Next
  366.  
  367.   players(player)\disconnect_requested=False ; reset disconnect request item
  368.   players(player)\status=#OFFLINE      ; player's status is offline
  369.  
  370. End Statement
  371.  
  372. .Disconnect_Player
  373. Statement Disconnect_Player{player}  ; Disconnect Player from game
  374.   SHARED players(),online,max_number_players
  375.   SHARED CP_REP_PLAYER_DISCONNECTED.s,CP_REQ_PLAYER_DISCONNECT.s
  376.  
  377.   If online=#SERVER   ;  If we are the Server
  378.     dummy=0
  379.     Security_Warning{"Player " +  Str$(player) + " has disconnected",#SYSTEM_MESSAGE}
  380.     For i=2 To max_number_players
  381.       If players(i)\status=#ONLINE      ; send to all players if they're online
  382.         If i <> player ; check to see if any players are left online
  383.           dummy=1
  384.         EndIf
  385.         send$=CP_REP_PLAYER_DISCONNECTED.s + Mki$(player) ; tell other players
  386.         Send_Reliable_Message{send$,i,#PEER_TO_PEER}   ; Send string to connected players that player is offline
  387.       EndIf
  388.     Next
  389.  
  390.     If dummy=0     ; if no players are left online
  391.       CNIF #COMMS_DEBUG=1
  392.         Comms_Debug{"All players have disconnected",#MEDIUM}
  393.       CEND
  394.     EndIf
  395.  
  396.     Clear_Player_Arrays{player}  ; clear message array of player's messages
  397.  
  398.   Else    ;  if we're a client, inform Server that a player can't be contacted
  399.     CNIF #COMMS_DEBUG=1
  400.       Comms_Debug{"Telling Server we can't contact player!",#MEDIUM}
  401.     CEND
  402.     send$=CP_REQ_PLAYER_DISCONNECT.s + Mki$(player)
  403.     Send_Reliable_Message{send$,#SERVER_NUMBER,#PEER_TO_PEER}   ; Send string to Server
  404.   EndIf
  405.  
  406. End Statement
  407.  
  408. .Resend_Message       ; resend a message
  409. Statement Resend_Message{message_number}
  410.   SHARED messages(),number_resends,online,players()
  411.   SHARED max_number_players
  412.  
  413.   messages(message_number)\timestamp=Ticks
  414.   messages(message_number)\resends+1
  415.   If messages(message_number)\resends>number_resends  ; is host not responding? After 5 retries consider link broken.
  416.      CNIF #COMMS_DEBUG=1
  417.        Comms_Debug{"Host not responding to Packet no: " + Str$(message_number),#HIGH}
  418.      CEND
  419.      If messages(message_number)\player<>#SERVER_NUMBER   ; We've lost contact with a Client
  420.  
  421.        If players(messages(message_number)\player)\disconnect_requested=False ; if haven't already sent a Disconnect req
  422.          Disconnect_Player{messages(message_number)\player}                   ; send disconnect request to Server
  423.          players(messages(message_number)\player)\disconnect_requested=True   ; don't send any more disconnect requests
  424.        EndIf                                                                       ; for that player
  425.      Else              ; we've lost contact with Server- game over!
  426.        online=#OFFLINE
  427.        For a= 1 To max_number_players
  428.          Clear_Player_Arrays{a}
  429.        Next
  430.      EndIf
  431.   Else
  432.      WriteUDP{&messages(message_number)\message,Len(messages(message_number)\message),messages(message_number)\player}
  433.      CNIF #COMMS_DEBUG=1
  434.        Comms_Debug{"Resent Packet no:" + Str$(message_number),#HIGH}
  435.      CEND
  436.   EndIf
  437. End Statement
  438.  
  439. .Acknowledge_Packet   ; mark message as been sucessfully sent
  440. Function Acknowledge_Packet{ack_packet_number,player}
  441.  
  442.   SHARED last_message_number,free_message,messages()
  443.   SHARED packet_number.l,lag_allowance,players()
  444.  
  445.   a=last_message_number
  446.   exit=-2
  447.   Repeat
  448.     If messages(a)\number=ack_packet_number   ; find message in sent messages array
  449.       messages(a)\ack=True         ; note it as being received
  450.       current_time.l=Ticks
  451.       players(player)\lag=current_time-messages(a)\timestamp ; calculate lag
  452.       exit=True
  453.     Else
  454.       If a=free_message  ; in case bad packet number sends it into a loop! It will only check around buffer once.
  455.         exit=False
  456.         a$="Warning- Invalid Packet number " + Str$(incoming_packet_number)
  457.         Security_Warning{ a$ + " from Player " + Str$(player),#MEDIUM}
  458.       EndIf
  459.       a+1
  460.     EndIf
  461.  
  462.     If a=#MESSAGE_ARRAY_SIZE+1 Then a=0    ; if at end of array, jump to beginning
  463.   Until exit>-2
  464.  
  465.   If exit=True     ; only do if packet has valid packet_number
  466.     If a=last_message_number OR (a=0 AND last_message_number=#MESSAGE_ARRAY_SIZE)   ; if last message unacknowledged is
  467.       Find_Next_Message{}                                             ; acknowledged then find next unacknowledged message
  468.     Else                ; we have lost a packet- resend last packet number!
  469.       allowable_lag=players(messages(last_message_number)\player)\lag + lag_allowance ; current lag of player
  470.       If messages(last_message_number)\timestamp-current_time > allowable_lag
  471.         Security_Warning{"Received packet ACK out of order, resending last unacknowledged packet.",#MEDIUM}      ; then resend m
  472.         Resend_Message{last_message_number}  ; if run out of resends- disconnect player
  473.       EndIf
  474.     EndIf
  475.   EndIf
  476.   Function Return exit
  477. End Function
  478.  
  479. .Requested_Connection      ; a player is trying to log in to us
  480. Statement Requested_Connection{incoming_string.s,player}
  481.   SHARED sock,players(),host(),hostlen(),GAME_NAME.s
  482.   SHARED temphost,temphostlen,online,my_number,max_number_players
  483.   SHARED CP_REP_ACCEPT.s,CP_REP_REJECT.s,CP_REP_PLAYER_INFO.s
  484.  
  485.   If player=-1 ; make sure player hasn't already logged in
  486.     If online=#OFFLINE OR online=#SERVER  ; If offline or Server
  487.       dummy=7
  488.       Repeat            ; build game name string from incoming request
  489.         b$=Mid$(incoming_string,dummy,1)
  490.         If b$<>Chr$(0)
  491.           c$=c$+b$
  492.         EndIf
  493.         dummy+1
  494.       Until b$=Chr$(0) OR dummy=(Len(incoming_string))
  495.  
  496.       If b$=Chr$(0)
  497.         If c$=GAME_NAME.s            ; if request is for correct game
  498.           protocol=Asc(Right$(incoming_string,1))
  499.           If protocol=#NET_PROTOCOL_VERSION  ; check wether using the same protocol
  500.             a$="Connection request from "
  501.             player=1
  502.             exit=0
  503.             Repeat      ;check if a spare player slot is availiable
  504.               player+1
  505.               If players(player)\status=#OFFLINE
  506.                 exit=1
  507.               Else
  508.                 If player=max_number_players
  509.                   exit=1
  510.                   player=-1
  511.                 EndIf
  512.               EndIf
  513.             Until exit=1
  514.  
  515.             If player>0     ; connection accepted
  516.               If Get_Host_By_Address{&temphost\sin_addr\s_addr,temphost\sin_port,player}=False
  517.                 Security_Warning{Str$(temphost\sin_addr\s_addr) + "- Host not found!",#SYSTEM_MESSAGE}
  518.                 Statement Return
  519.               EndIf
  520.  
  521.               players(player)\status=#ONLINE
  522.               my_number=#SERVER_NUMBER
  523.               players(#SERVER_NUMBER)\status=#ONLINE     ; We are now online as Server
  524.               If Get_Host_By_Address{&host(0)\sin_addr\s_addr,host(0)\sin_port,1}= False  ; put our details in Server Slot
  525.                 Security_Warning{Str$(host(0)\sin_addr\s_addr) + "- Host not found!",#SYSTEM_MESSAGE}
  526.                 Statement Return
  527.               EndIf
  528.  
  529.               temp$=Get_Ascii_Address{host(player)\sin_addr\s_addr}
  530.  
  531.               a$=a$+temp$ + "  Port: " + Str$(host(player)\sin_port)
  532.               players(player)\ascii_host_string=temp$   ; store it
  533.  
  534.               ; build player info string to tell other players of new player
  535.               send_others$=CP_REP_PLAYER_INFO.s + Mki$(player) + Mkl$(host(player)\sin_addr\s_addr)
  536.               send_others$=send_others$ + Mki$(host(player)\sin_port) + Chr$(players(player)\player_colour)
  537.               send_others$=send_others$ + Mkl$(players(player)\score)
  538.               send_others$=send_others$ + players(player)\name + Chr$(0) + players(player)\nick
  539.  
  540.               For i=1 To max_number_players
  541.                 If i<> player AND players(i)\status=#ONLINE
  542.                   If i>1
  543.                     Send_Reliable_Message{send_others$,i,#PEER_TO_PEER}    ; tell others players that new player is online
  544.                                                              ; and give them new players info.
  545.                   EndIf
  546.  
  547.                   send$=CP_REP_PLAYER_INFO.s + Mki$(i) + Mkl$(host(i)\sin_addr\s_addr)
  548.                   send$=send$ + Mki$(host(i)\sin_port) + Chr$(players(i)\player_colour)
  549.                   send$=send$ + Mkl$(players(i)\score)
  550.                   send$=send$ + players(i)\name + Chr$(0) + players(i)\nick
  551.  
  552.                   Send_Reliable_Message{send$,player,#PEER_TO_PEER}    ; tell new player of others online
  553.                                                          ; and give them their info.
  554.                 EndIf
  555.  
  556.               Next
  557.  
  558.               send$=CP_REP_ACCEPT.s
  559.               send$=send$ + Mki$(host(0)\sin_port)+ Mki$(player) + Mki$(map)  ; send connection accepted back to client
  560.               online=#SERVER
  561.               players(player)\disconnect_requested=False
  562.               Send_Reliable_Message{send$,player,#PEER_TO_PEER}
  563.  
  564.               Security_Warning{a$,#SYSTEM_MESSAGE}
  565.               Statement Return
  566.             Else
  567.               a$="Connection Request rejected- no spare player slot!"
  568.             EndIf
  569.           Else
  570.             a$="Connection Request rejected- Incorrect Protocol: "+Str$(protocol)
  571.           EndIf
  572.         Else
  573.           a$="Connection Request rejected- wrong game name: "+b$
  574.         EndIf
  575.       Else
  576.         a$="Connection Request rejected- Unknown error!"
  577.       EndIf
  578.     Else
  579.       a$="Connection Request rejected- host already logged on as Client"
  580.     EndIf
  581.   Else
  582.     a$="Connection Request rejected- host already logged on to Server."
  583.   EndIf
  584.  
  585.   send$=Mkl$(0) + CP_REP_REJECT.s + a$    ; send connection rejected back with reason
  586.   WriteUDP{&send$,Len(send$),0}  ; reply to sender
  587.   Security_Warning{a$,#MEDIUM}
  588.  
  589. End Statement
  590.  
  591. .Decode_Packet      ; Decode incoming packet and act accordingly
  592. Function Decode_Packet{}
  593.   SHARED players(),host(),temphost,online
  594.   SHARED my_number,player_honesty(),max_number_players
  595.   SHARED CP_REQ_PLAYER_DISCONNECT.s,CP_END_GAME_REC.s,REL_PACKET_ACK.s,CP_REP_ACCEPT.s
  596.   SHARED CP_GAME_END.s,CP_REP_PLAYER_DISCONNECTED.s,PING_REQUEST.s
  597.   SHARED PING_RESPONSE.s
  598.  
  599.   return_code=False
  600.   incoming_string.s=ReadUDP{}   ; get data from socket
  601.  
  602.   If incoming_string<>""      ; if there was some data waiting
  603.     packet_type=Asc(Mid$(incoming_string,5))
  604.     unknown_packet=0   ; check to see if valid packet is received
  605.     player=Get_Packet_Source{}  ; check to see if packet is from reliable online source
  606.     incoming_packet_number.l=Cvl(Left$(incoming_string,4))
  607.     return_message=0
  608.     player_known=True
  609.  
  610.     Select packet_type         ; as set in `Net_Protocol_Header.bb2'
  611.       Case  #CONTROL_PACKET    ; connection or control information
  612.         packet_type=Asc(Mid$(incoming_string,6))
  613.         Select packet_type
  614.           Case #CP_REQ_CONNECT      ; request to login to Server
  615.             Requested_Connection{incoming_string,player}
  616.             player_known=False
  617.           Case #CP_REP_ACCEPT       ; We Received confirmation of logon from Server
  618.             If online=#ATTEMPTING_TO_CONNECT
  619.               online=#CLIENT
  620.               my_number=Cvi(Mid$(incoming_string,9,2))  ; my player number
  621.               map.w=Cvi(Mid$(incoming_string,10,2))
  622.               players(#SERVER_NUMBER)\status=#ONLINE  ; Server is now online
  623.               Security_Warning{"Connection Accepted",#SYSTEM_MESSAGE}
  624.               return_message=#ACKNOWLEDGE_PACKET
  625.               return_code=#CONNECTED
  626.             Else
  627.               Security_Warning{"Illegal Reply Accept attempt by player: " + Str$(player),#MEDIUM}
  628.             EndIf
  629.             player_known=False
  630.  
  631.           Case #CP_REP_REJECT       ; Our logon request was rejected by host
  632.             If online=#ATTEMPTING_TO_CONNECT
  633.               Security_Warning{Mid$(incoming_string,7),#SYSTEM_MESSAGE}
  634.               online=#OFFLINE
  635.               return_code=#CONNECTION_REJECTED
  636.             Else
  637.               Security_Warning{"Illegal Reply Reject attempt by player: " + Str$(player),#MEDIUM}
  638.             EndIf
  639.             player_known=False
  640.  
  641.           Case #CP_REP_PLAYER_INFO  ; Information about new player that has just logged on
  642.           ;  If online=#CLIENT    ; if we are a client
  643.               newplayer=Cvi(Mid$(incoming_string,7,2))
  644.               address.l=Cvl(Mid$(incoming_string,9,4))
  645.               If Get_Host_By_Address{&address,Cvi(Mid$(incoming_string,13,2)),newplayer}=True
  646.                 ascii_address.s=Get_Ascii_Address{address}
  647.                 players(newplayer)\status=#ONLINE,ascii_address.s
  648.                 players(newplayer)\player_colour=Val(Mid$(incoming_string,15,1))
  649.                 players(newplayer)\score=Cvi(Mid$(incoming_string,16,4))
  650.                 players(newplayer)\disconnect_requested=False
  651.                 text$="New player at- " + ascii_address.s + " Port: " + Str$(host(newplayer)\sin_port)
  652.                 Security_Warning{text$,#SYSTEM_MESSAGE}
  653.               Else
  654.                 Security_Warning{"New player at unknown host!",#MEDIUM}
  655.               EndIf
  656.               return_message=#ACKNOWLEDGE_PACKET
  657.  
  658.               player_known=False
  659.           ;  Else
  660.           ;    Security_Warning{"Illegal Player Info Reply from player: " + Str$(player),#MEDIUM}
  661.           ;  EndIf
  662.  
  663.           Case #CP_REQ_PLAYER_DISCONNECT  ; Request from client to disconnect
  664.               ; or inform Server of disconnected player
  665.  
  666.             If online=#SERVER  AND player>0 ; only if Server, and from legal player
  667.                disconnecting_player=Cvi(Mid$(incoming_string,7,2))
  668.               If player=disconnecting_player  ; if player is leaving game
  669.                 Disconnect_Player{player}
  670.               Else    ; Ping suspect player to see if they're still online
  671.                 If players(disconnecting_player)\status=#ONLINE
  672.                   player_honesty(player,disconnecting_player)=True  ; record which player claims another is offline.
  673.                   Send_Reliable_Message{PING_REQUEST.s,disconnecting_player,#PEER_TO_PEER} ; so we can disconnect them if the cl
  674.                   CNIF #COMMS_DEBUG=1
  675.                     text$="R " + Str$(incoming_packet_number) + "," + Str$(player)  + ": "
  676.                     text$=text$ + "Player " + Str$(player) + " claims player " + Str$(disconnecting_player)
  677.                     Comms_Debug{text$ + " has disconnected",#HIGH}
  678.                   CEND
  679.                 Else
  680.                   CNIF #COMMS_DEBUG=1
  681.                     text$="Warning: Player " + Str$(player) + " says UNCONNECTED PLAYER " + Str$(disconnecting_player)
  682.                     text$=text$ + " is unreachable."
  683.                     Comms_Debug{text$,#HIGH}
  684.                   CEND
  685.                 EndIf
  686.                 return_message=#ACKNOWLEDGE_PACKET
  687.               EndIf
  688.             Else
  689.               Security_Warning{"Illegal Player Disconnect Request Packet from player: " + Str$(player), #MEDIUM}
  690.             EndIf
  691.  
  692.           Case #CP_REP_PLAYER_DISCONNECTED    ; Server has told us a player has disconnected from the game
  693.                                               ; - this can be us as well
  694.             If online=#CLIENT AND player=#SERVER_NUMBER  ; only if we're a client and message is from server
  695.               disconnected_player=Cvi(Mid$(incoming_string,7,2))
  696.               Clear_Player_Arrays{disconnected_player}     ; player is now offline
  697.               If disconnected_player=my_number ; my disconnection request has been confirmed by Server
  698.                 online=#OFFLINE          ; go offline
  699.                 For a= 1 To max_number_players
  700.                   If players(a)\status=#ONLINE
  701.                     Clear_Player_Arrays{a}
  702.                   EndIf
  703.                 Next
  704.                 Security_Warning{"We are disconnected from the game.",#SYSTEM_MESSAGE}
  705.                 return_code=#DISCONNECTED
  706.               Else
  707.                 Security_Warning{"Player " + Str$(disconnected_player) + " is disconnected from the game.",#SYSTEM_MESSAGE}
  708.               EndIf
  709.               return_message=#ACKNOWLEDGE_PACKET
  710.             Else
  711.               Security_Warning{"Illegal Player Disconnected Reply Packet from player: " + Str$(player),#MEDIUM}
  712.             EndIf
  713.  
  714.           Case #CP_END_GAME   ; Server has ended Game
  715.             If player=#SERVER_NUMBER AND online=#CLIENT; if message is from Server
  716.               send$=CP_END_GAME_REC.s
  717.               Send_Reliable_Message{send$,#SERVER_NUMBER,#PEER_TO_PEER}
  718.               online=#OFFLINE
  719.               return_message=#ACKNOWLEDGE_PACKET
  720.               Security_Warning{"Server has shut down game.",#SYSTEM_MESSAGE}
  721.               For a= 1 To max_number_players
  722.                 If players(a)\status=#ONLINE
  723.                   Clear_Player_Arrays{a}
  724.                 EndIf
  725.               Next
  726.               return_code=#DISCONNECTED
  727.             Else
  728.               Security_Warning{"Illegal End Game attempt by player: " + Str$(player),#MEDIUM}
  729.             EndIf
  730.  
  731.           Case #CP_END_GAME_REC  ; player has confirmed they received the Game End packet
  732.             If online=#SERVER AND player>0
  733.               Clear_Player_Arrays{player}      ; player is now offline
  734.               dummy=0
  735.               For i=2 To max_number_players
  736.                 If players(i)\status=#ONLINE   ; check to see if all players are offline
  737.                   dummy=1
  738.                 EndIf
  739.               Next
  740.               If dummy=0   ; if all players are offline
  741.                 online=#OFFLINE
  742.                 CNIF #COMMS_DEBUG=1
  743.                   text$="R " + Str$(incoming_packet_number) + "," + Str$(player)  + ": "
  744.                   Comms_Debug{text$ + "Player " + Str$(player) + " has confirmed game closure.",#HIGH}
  745.                 CEND
  746.                 Security_Warning{"All players have confirmed game has shut down.",#SYSTEM_MESSAGE}
  747.                 return_code=#DISCONNECTED
  748.  
  749.               CNIF #COMMS_DEBUG=1
  750.               Else
  751.                 text$="R " + Str$(incoming_packet_number) + "," + Str$(player)  + ": "
  752.                 Comms_Debug{text$ + "Player " + Str$(player) + " has confirmed game closure.",#HIGH}
  753.               CEND
  754.  
  755.               EndIf
  756.               return_message=#ACKNOWLEDGE_PACKET
  757.             Else
  758.               Security_Warning{"Illegal END_GAME_REC packet received from: " + Str$(player),#MEDIUM}
  759.             EndIf
  760.         Default
  761.           unknown_packet=1
  762.         End Select
  763.  
  764.       Case #REL_STRING_END     ; basic string message received from a player
  765.         If player>0 AND online>#ATTEMPTING_TO_CONNECT
  766.           text$="R " + Str$(incoming_packet_number) + "," + Str$(player)  + ": "
  767.           Security_Warning{text$ + Mid$(incoming_string,6,Len(incoming_string)-6),#SYSTEM_MESSAGE}
  768.           return_message=#ACKNOWLEDGE_PACKET
  769.         EndIf
  770.  
  771.       Case #REL_PACKET_ACK     ; a message has been received by a player
  772.         If player>0  AND online>#ATTEMPTING_TO_CONNECT
  773.           If Acknowledge_Packet{incoming_packet_number,player}=True
  774.             CNIF #COMMS_DEBUG=1
  775.               text$="R " + Str$(incoming_packet_number) + "," + Str$(player)  + ": "
  776.               Comms_Debug{text$ + ": Packet no. " + Str$(incoming_packet_number) + " arrived at destination.",#HIGH}
  777.             CEND
  778.           EndIf
  779.         EndIf
  780.  
  781.       Case #PING_REQUEST       ; a Ping request has been received
  782.         CNIF #COMMS_DEBUG=1
  783.           text$="R " + Str$(incoming_packet_number) + "," + Str$(player)  + ": "
  784.           Comms_Debug{text$ + "Player " + Str$(player) + " has sent a Ping request.",#HIGH}
  785.         CEND
  786.  
  787.         If player=#SERVER_NUMBER AND online=#CLIENT
  788.           return_message=#ACKNOWLEDGE_PACKET
  789.           Send_Reliable_Message{PING_RESPONSE.s,player,#PEER_TO_PEER} ; send Ping Response back
  790.         Else
  791.           Security_Warning{"Illegal PING_REQUEST received from player: " + Str$(player), #MEDIUM}
  792.         EndIf
  793.  
  794.       Case #PING_RESPONSE      ; Ping response received
  795.  
  796.         If online=#SERVER
  797.           CNIF #COMMS_DEBUG=1
  798.             text$="R " + Str$(incoming_packet_number) + "," + Str$(player)  + ": "
  799.             Comms_Debug{text$ + "Player " + Str$(player) + " has  sent a Ping response.",#HIGH}
  800.           CEND
  801.  
  802.           If player>0          ; if from online player
  803.             For a= 1 To max_number_players     ; this is to stop players from disconnecting other players illegally
  804.               If player_honesty(a,player)=True  ; if player loses connection to other player
  805.                 Disconnect_Player{a}       ; Disconnect player who has lost connection to another player
  806.                 CNIF #COMMS_DEBUG=1
  807.                   text$="R " + Str$(incoming_packet_number) + "," + Str$(player)  + ": "
  808.                   Comms_Debug{text$ + "Player " + Str$(a) + " has lost connection to player " + Str$(player),#HIGH}
  809.                 CEND
  810.               EndIf
  811.             Next
  812.           EndIf
  813.         Else
  814.           Security_Warning{"Illegal PING_RESPONSE received from player: " + Str$(player), #MEDIUM}
  815.         EndIf
  816.  
  817.     Default       ; unknown packet received
  818.       unknown_packet=1
  819.     End Select
  820.  
  821.     If unknown_packet=1
  822.       If player=-1                  ; if from unknown source
  823.         temp$=": " + Get_Ascii_Address{temphost\sin_addr\s_addr} + " Port: " + Str$(temphost\sin_port)
  824.       Else                         ; from known source
  825.         temp$=" Player: " + Str$(player)
  826.       EndIf
  827.       Security_Warning{"Warning- Unknown Packet from" + temp$,#MEDIUM}
  828.       Function Return False
  829.     EndIf
  830.  
  831.     If player_known=True AND player=-1  ; if we get sent a legal packet from unknown player
  832.       temp$=": " + Get_Ascii_Address{temphost\sin_addr\s_addr} + " Port: " + Str$(temphost\sin_port)
  833.       Security_Warning{"Warning- Packet from unknown Player at" + temp$,#MEDIUM}
  834.     EndIf
  835.  
  836.     If return_message>0
  837.       If return_message=#ACKNOWLEDGE_PACKET  ; send packet acknowledgment back
  838.         send$=Mkl$(incoming_packet_number)  + REL_PACKET_ACK.s  ;
  839.       EndIf
  840.       WriteUDP{&send$,Len(send$),player}  ; return a message back if return_message = 1 or 3
  841.     EndIf
  842.   EndIf
  843.  
  844.   Function Return return_code
  845. End Function
  846.  
  847. .Comms_Housekeeping
  848. Function Comms_Housekeeping{}
  849.   SHARED messages(),last_message_number,players(),online
  850.   SHARED max_wait,max_number_players,disconnect_timeout
  851.  
  852.   return_code=Decode_Packet{}    ; find out what was in the packet
  853.  
  854.   If messages(last_message_number)\ack=False  ; only check if there's messages that haven't been acknowledged yet.
  855.     If Ticks>messages(last_message_number)\timestamp+max_wait  ; has it been 5 secs since the packet was sent?
  856.       Resend_Message{last_message_number}  ; if run out of resends- disconnect player
  857.     EndIf
  858.   EndIf
  859.  
  860.   If disconnect_timeout>0   ; If disconnection timeout reaches
  861.     disconnect_timeout-1    ; zero then disconnect the program
  862.     If disconnect_timeout=0
  863.       online=#OFFLINE          ; go offline
  864.       For a= 1 To max_number_players
  865.         If players(a)\status=#ONLINE
  866.           Clear_Player_Arrays{a}
  867.         EndIf
  868.       Next
  869.       Security_Warning{"We are disconnected from the game.",#SYSTEM_MESSAGE}
  870.       return_code=#DISCONNECTED
  871.     EndIf
  872.   EndIf
  873.  
  874.   Function Return return_code
  875. End Function
  876.  
  877. ;******************* Game Variables and Constants **************
  878.  
  879. exit=False
  880. game_closing=0
  881. PORT_NAME.s="UDP_Chat"  ; "UDP_Chat"  Rexx portname
  882. #LOCALPORT=27272        ; 27272 default local port to send an receive from
  883. #PORT=27272     ; 27272 default destination port to send data to
  884. print_player.w=1
  885.  
  886. ;************************  Game Dims  **************************
  887.  
  888. Dim pingprintx(max_number_players)
  889.  
  890. ;*****************    Game Funcs Continued    ********************
  891.  
  892. .Window_Events
  893. Function Window_Events{}
  894.   SHARED pingwidth,hostnamey,online,players(),my_number,packet_number
  895.   SHARED game_closing.w,max_number_players,pingprintx(),hostname
  896.   SHARED port_used
  897.   SHARED REL_STRING_END.s
  898.  
  899.   exit=False
  900.   ev.l=Event    ; get window events
  901.   Select ev
  902.     Case $40   ; if a gadget has been hit
  903.       Select GadgetHit
  904.         Case51           ; Try to connect to a Server
  905.           If Connect_to_Server{GTGetString(0,51), GTGetInteger(0,52)}=True
  906.             WLocate 502,4
  907.             Print "Bound to: ",port_used
  908.           Else
  909.             Print_String{"Unable to Initialise UDP"}
  910.           EndIf
  911.  
  912.         Case52           ; Port to connect at above Server
  913.           If Get_Host_By_Name{GTGetString(0,51), GTGetInteger(0,52),1} : EndIf
  914.  
  915.         Case53    ; Server button- lets our program become the Server
  916.           If Initialise_Server{#LOCALPORT}
  917.             Print_String {"Bound to " + hostname + " "+ Str$(port_used)}
  918.             WLocate 502,4
  919.             Print "Bound to: ",port_used
  920.             WLocate pingprintx(#SERVER_NUMBER),32
  921.             Print "Serv"         ; show that we're Server
  922.             Print_String{"We are set up as Server."}
  923.           Else
  924.             Print_String{"Couldn't Initialise Server!"}
  925.           EndIf
  926.  
  927.         Case54           ; get name of localhost
  928.           a$=Localhost_Name {}   ;
  929.           WLocate 315+pingwidth+15,hostnamey+3
  930.           Print a$
  931.  
  932.         Case 55    ; button 1
  933.             Print_String{"Delay program for 1 second"}
  934.             VWait 50     ; put in artificial delay of 1 second For testing purposes
  935.             Print_String{"Delay Ended"}
  936.  
  937.         Case 56    ; button 2
  938.             Print_String{"Delay program for 80 seconds"}
  939.             VWait 4000   ;  put in artificial delay of 80 seconds for testing purposes- causes you to lose connection
  940.             Print_String{"Delay Ended"}
  941.  
  942.         Case62   ; button 8- Pause program
  943.           Print_String{"PAUSED"}
  944.           Repeat
  945.              ev.l=WaitEvent
  946.              If ev=$40
  947.                 Select GadgetHit
  948.                  Case 62
  949.                   unpause= 1
  950.                   Print_String{"UNPAUSED"}
  951.                 End Select
  952.              EndIf
  953.            Until unpause=1
  954.  
  955.         Case63    ; Disconnect button- disconnects us from the game
  956.           If online=#OFFLINE
  957.             Print_String{"We're already offline."}
  958.           Else
  959.             If Disconnect_From_Game{}
  960.               Print_String{"Game has ended."}
  961.               For i=1 To 8
  962.                 WLocate pingprintx(i),32
  963.                 Print "    "     ; Clear the lag times
  964.               Next
  965.             EndIf
  966.           EndIf
  967.         Case64         ; Send string message to connected players
  968.           If online>#ATTEMPTING_TO_CONNECT
  969.               temp$=GTGetString(0,64)
  970.               For i=1 To 8
  971.                 If players(i)\status=#ONLINE  AND i<>my_number   ; send to player if they're online and it's not my number :)
  972.                   Print_String{"S " + Str$(packet_number) + "," + Str$(i)  + ": "+ temp$}
  973.                   send$=REL_STRING_END.s + temp$ + Chr$(0)
  974.                   Send_Reliable_Message{send$,i,#PEER_TO_PEER}   ; Send string to connected players
  975.                 EndIf
  976.               Next
  977.           EndIf
  978.       End Select
  979.     Case $200    ; if close button pressed
  980.       If Disconnect_From_Game{}
  981.         exit=True
  982.         Print_String{"Program Closing."}
  983.       Else
  984.         game_closing=#GAME_CLOSING_TIMEOUT
  985.       EndIf
  986.   End Select
  987.  
  988.   Function Return exit
  989. End Function
  990.  
  991. ;     ARexx code
  992. ;  It seems replyrexxmsg doesn't actually work for sending result strings
  993. ;  back, use WRITERESULT, then replymsg for when you want to send
  994. ;  something back to rexx with result
  995.  
  996. Statement Result_Reply{*msg.RexxMsg,resulterror.l,resultstring$}
  997.   *msg\rm_Result1=result1
  998.   If ((*msg\rm_Action & #RXFF_RESULT)<>0)&(result1=0)
  999.     *msg\rm_Result2=CreateArgstring_(&resultstring$,Len(resultstring$))
  1000.   Else
  1001.     *msg\rm_Result2=0
  1002.   EndIf
  1003.   ReplyMsg_ *msg
  1004. End Statement
  1005.  
  1006. .Get_Rexx_Message
  1007. Function Get_Rexx_Message{}
  1008.   SHARED rexxport.l,online
  1009.   rmsg.l=RexxEvent(rexxport)
  1010.   If IsRexxMsg(rmsg)=1
  1011.     rexxline$=GetRexxCommand(rmsg,1)
  1012.     Print_String {rexxline$}
  1013.     linepos=Instr(rexxline$," ")
  1014.     If linepos=0
  1015.       command$=rexxline$
  1016.     Else
  1017.       command$=Left$(rexxline$,linepos-1)
  1018.     EndIf
  1019.     Select command$
  1020.       Case "QUIT"
  1021.           ReplyRexxMsg rmsg,0,0,""
  1022.           Function Return True
  1023.  
  1024.        Case "ISONLINE"
  1025.          Select online
  1026.             Case #OFFLINE
  1027.               Result_Reply{rmsg,0,"Offline"}
  1028.             Case #ATTEMPTING_TO_CONNECT
  1029.               Result_Reply{rmsg,0,"Attempting to Connect"}
  1030.             Case #SERVER
  1031.               Result_Reply{rmsg,0,"Server"}
  1032.             Case #CLIENT
  1033.               Result_Reply{rmsg,0,"Client"}
  1034.           End Select
  1035.  
  1036.       Case "CONNECTTOSERVER"
  1037.           hostname$=Right$(rexxline$,Len(rexxline$)-linepos)
  1038.           Print_String {hostname$}
  1039.           If Connect_to_Server{hostname$,#PORT} : EndIf
  1040.           ScreenToFront_ Peek.l(Addr Screen(0))
  1041.           WindowToFront_ Peek.l(Addr Window(0))
  1042.           ReplyRexxMsg rmsg,0,0,""
  1043.       Default
  1044.         ReplyRexxMsg rmsg,0,0,""
  1045.     End Select
  1046.   EndIf
  1047.   Function Return False
  1048. End Function
  1049.  
  1050. .General_Housekeeping
  1051. Function General_Housekeeping{}
  1052.   SHARED game_closing,online,print_player,pingprintx()
  1053.   SHARED players(),my_number,print_count,max_number_players
  1054.  
  1055.   exit=False
  1056.   If Get_Rexx_Message{}=True  ; check for any arexx messages
  1057.     exit=True                  ; exit if `Quit' received
  1058.   EndIf
  1059.  
  1060.   print_count+1
  1061.   If print_count=50
  1062.     print_count=1  ; do once per second
  1063.     print_player+1
  1064.     If print_player>max_number_players Then print_player=1
  1065.     If online>#ATTEMPTING_TO_CONNECT AND print_player<>my_number
  1066.  
  1067.       WLocate pingprintx(print_player),32
  1068.       If players(print_player)\status=#ONLINE
  1069.         Format "0000"
  1070.         Print Str$(players(print_player)\lag)   ; print lag onscreen
  1071.         Format""
  1072.       Else
  1073.         Print"    "
  1074.       EndIf
  1075.     EndIf
  1076.   EndIf
  1077.  
  1078.   If game_closing>0   ; if game is shutting down
  1079.     If online=#OFFLINE
  1080.       exit=True  ; if all players are offline then shutdown
  1081.       Print_String{"We are now Offline- Closing program."}
  1082.     Else
  1083.       If game_closing>=2    ; carry on countdown
  1084.         game_closing-1
  1085.       Else                  ; if countdown reaches 10 seconds then disconnect regardless
  1086.         exit=True
  1087.         Print_String{"Emergency Shutdown!- unable to disconnect from Server."}
  1088.       EndIf
  1089.     EndIf
  1090.   EndIf
  1091.   Function Return exit
  1092. End Function
  1093.  
  1094. .Exit
  1095. Statement Exit{text$}
  1096.   SHARED rexxport.l,rexxmsg.l
  1097.  
  1098.   Print_String{text$}    ; print error (if any)
  1099.  
  1100.   If rexxport>0              ; if we've successfully opened an Arexx port then close it
  1101.     DeleteRexxMsg rexxmsg.l
  1102.     DeleteMsgPort rexxport.l
  1103.     Print_String{"Rexxport closed"}
  1104.   EndIf
  1105.  
  1106.   Close_UDP{}     ; close all UDP stuff down
  1107.  
  1108.   Print_String{"Program exiting"}
  1109.  
  1110.   Delay_(50)
  1111.   End          ; close program
  1112.  
  1113. End Statement
  1114.  
  1115. ;*********************  End of Functions  ***********************
  1116.  
  1117. Gosub Init_Gui
  1118.  
  1119. ;************************  Main  Loop  **************************
  1120.  
  1121. .Main
  1122.  
  1123. Repeat
  1124.  
  1125.     WaitTOF_   ; pause a bit to allow prog to multitask nicely, and sync up display
  1126.  
  1127.     exit=Window_Events{}    ; check for input from user
  1128.  
  1129.     If online>#OFFLINE
  1130.       a=Comms_Housekeeping{}  ; Check for incoming data and do comms housekeeping work.
  1131.       Select a
  1132.         Case #CONNECTED       ; Server has told us we're online
  1133.           WLocate pingprintx(my_number)+5,32
  1134.           Print "Me"         ; print our player number - as a Client.
  1135.         Case #DISCONNECTED    ; We're now offline
  1136.           For i=1 To 8
  1137.             WLocate pingprintx(i),32
  1138.             Print "    "     ; Clear the lag times
  1139.           Next
  1140.           If game_closing>0 Then exit=1
  1141.         Case #CONNECTION_REJECTED ; Our connection attempt was rejected
  1142.       End Select
  1143.     EndIf
  1144.  
  1145.     If General_Housekeeping{}=True  ; Check for rexx messages
  1146.       exit=True                ; and do general housekeeping work.
  1147.     EndIf
  1148.  
  1149. Until exit=True    ; shut down if close gadget hit
  1150.  
  1151. Exit{""}   ; close UDP socket and Rexxport, and free read memory buffer
  1152.            ; END!
  1153.  
  1154. ;****************************************************************************
  1155.  
  1156.  
  1157. .Init_Gui            ; setup window and gadgets- note it's a bit hacked from Alvaro Thompson's original code
  1158.   ypos=40
  1159.   WBenchToFront_   ; and isn't proportional and completely font sensitive any more :-/
  1160.   WbToScreen0
  1161.   DefaultIDCMP $20|$40|$200
  1162.   *scr.Screen=Peek.l(Addr Screen(0))
  1163.   *myfont.TextAttr=*scr\Font
  1164.   fontheight=*myfont\ta_YSize
  1165.   ad1.l=*myfont\ta_Name
  1166.   fontname$=Peek$(ad1)
  1167.  
  1168.   LoadFont 1,fontname$,fontheight,0
  1169.  
  1170.   portwidth=TextLength_(*scr\RastPort,"Port:",5)+10
  1171.   numberswidth=TextLength_(*scr\RastPort,"99999",5)+14
  1172.   serverwidth=TextLength_(*scr\RastPort,"Send To:",8)+10
  1173.   connectwidth=TextLength_(*scr\RastPort,"Connect",7)+14
  1174.   disconnectwidth=TextLength_(*scr\RastPort,"Disconnect",10)+14
  1175.   pingwidth=TextLength_(*scr\RastPort,"Localhost:",11)
  1176.   delaywidth=TextLength_(*scr\RastPort,"SERVER",8)+10
  1177.   inputwidth=TextLength_(*scr\RastPort,"Input:",6)+10
  1178.   firstperson=TextLength_(*scr\RastPort,"1:",2)+10
  1179.  
  1180.   #SERVERBTN       =51
  1181.   #PORTBTN         =52
  1182.   #BUTTON          =53
  1183.  
  1184.   x=1
  1185.   y=1
  1186.  
  1187.   x1=x+(serverwidth*3)+portwidth+numberswidth+9+3+connectwidth+5
  1188.  
  1189.   If #NO_CONNECTION=0
  1190.     GTString  0,#SERVERBTN,x+serverwidth,y,serverwidth*2,fontheight+4,"Send To:",1,256,Localhost_Name {}
  1191.   Else
  1192.     GTString  0,#SERVERBTN,x+serverwidth,y,serverwidth*2,fontheight+4,"Send To:",1,256,"localhost"
  1193.   EndIf
  1194.   GTInteger 0,#PORTBTN,x+(serverwidth*3)+portwidth+3,y,numberswidth,fontheight+4,"Port:",1,#PORT
  1195.   GTButton  0,#BUTTON,331,y,delaywidth,fontheight+4,"Server",$10
  1196.   GTButton  0,63,333+delaywidth,y,disconnectwidth,fontheight+4,"Disconnect",$10
  1197.   y+fontheight+5
  1198.  
  1199.   hostnamey=y
  1200.  
  1201.   GTButton  0,54,315,y,pingwidth,fontheight+4,"Localhost:",$10
  1202.  
  1203.   WinWidth=x+(serverwidth*3)+portwidth+numberswidth+9+3+connectwidth+8+disconnectwidth+8+90
  1204.  
  1205.   y+fontheight+5
  1206.  
  1207.   GTButton  0,55,120,y,firstperson,fontheight+4,"1:",$10
  1208.   GTButton  0,56,180,y,firstperson,fontheight+4,"2:",$10
  1209.   GTButton  0,57,240,y,firstperson,fontheight+4,"3:",$10
  1210.   GTButton  0,58,300,y,firstperson,fontheight+4,"4:",$10
  1211.   GTButton  0,59,360,y,firstperson,fontheight+4,"5:",$10
  1212.   GTButton  0,60,420,y,firstperson,fontheight+4,"6:",$10
  1213.   GTButton  0,61,480,y,firstperson,fontheight+4,"7:",$10
  1214.   GTButton  0,62,540,y,firstperson,fontheight+4,"8:",$10
  1215.   For i= 1To8
  1216.     pingprintx(i)=86+ i*60
  1217.   Next
  1218.   pingprinty=y
  1219.  
  1220.   GTString  0,64,50,215,550,fontheight+4,"Send:",1,67,""
  1221.  
  1222.   WinHeight=y+fontheight+5
  1223.  
  1224.   ;WinX=WBWidth/2-(WinWidth/2)
  1225.   ;WinY=WBHeight/2-(WinHeight/2)
  1226.  
  1227.   WinTitle$="UDP Chat"
  1228.   ;ScreenTitle$="UDP Send "+Chr$(169)+"1997."
  1229.  
  1230.   Window 0,0,10,630,245,$0002|$0004|$0008,WinTitle$,1,2
  1231.   Use Window 0:Activate 0:AttachGTList 0,0:WTitle WinTitle$,ScreenTitle$:Menus Off
  1232.  
  1233.   WindowOutput0
  1234.   WindowInput 0
  1235.  
  1236.   a$=Localhost_Name {}   ;
  1237.   WLocate 315+pingwidth+15,hostnamey+3
  1238.   Print a$
  1239.  
  1240.   WLocate 10,y+3
  1241.   Print"Ping Time-VBL"
  1242.  
  1243.   CNIF #COMMS_DEBUG=0
  1244.     If FindPort_(PORT_NAME.s)
  1245.         Exit{"UDP_Chat already running!"}
  1246.     EndIf
  1247.   CEND
  1248.  
  1249.   rexxport.l=CreateMsgPort(PORT_NAME.s)
  1250.   If rexxport=0
  1251.     Exit{"Unable to open Rexxport!"}
  1252.   EndIf
  1253.   rexxmsg.l=CreateRexxMsg(rexxport,"rexx",PORT_NAME.s)
  1254.  
  1255.  WLocate 10,18
  1256.  Print "Type in data, and hit return to send"
  1257.  
  1258. Return
  1259.  
  1260.  
  1261.