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

  1. ;
  2. ;                      UDP Chat code V2.0   27/07/98
  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.  
  23. WBStartup
  24. NoCli
  25. Hostname.s="localhost"  ; "localhost" default destination host address
  26. #PORT=27272             ; 27272 default destination port to send data to
  27. #LOCALPORT=27272        ; 27272
  28. #NO_CONNECTION=1        ; 0     are we connected, or have we been connected to the Internet since bootup?
  29.                         ;       1 for not been connected, 0 for have/are connected.
  30. #DEBUG=1                ; 1     1 for debug info and multiple local copies
  31.  
  32. #MAX_NUMBER_PLAYERS=8   ; 8
  33. GAME_NAME.s="UDP_Chat"  ; "UDP_Chat"  Game name (for login)
  34. PORT_NAME.s="UDP_Chat"  ; "UDP_Chat"  Rexx portname
  35.  
  36. INCLUDE "Net_Protocol_Header.bb2"  ; Net protocol constants
  37. INCLUDE "UDPHeader.bb2"  ;Standard TCP/UDP library structures
  38.  
  39. DEFTYPE .w
  40.  
  41. packet_number.l=0
  42. online=0
  43. my_number=0
  44. last_message_number.l=0
  45. free_message.l=0
  46. sock.l=-1
  47. ;********************************************************
  48.  
  49. NEWTYPE .message_status
  50.   number.l     ; packet number
  51.   ack.w        ; has it been received yet?
  52.   timestamp.l  ; when was it sent?
  53.   message.s    ; message
  54.   resends.w    ; how many times has it been re-sent?
  55.   player       ; which player was it sent too?
  56. End NEWTYPE
  57.  
  58. NEWTYPE .player_info
  59. status.w
  60. ascii_host_string.s
  61. player_colour.w
  62. score.l
  63. name.s
  64. nick.s
  65. End NEWTYPE
  66.  
  67. ;*******************************************************
  68. .Dims
  69.  
  70. Dim messages.message_status(100)
  71. Dim host.sockaddrin(8), hostlen.l(8)
  72. Dim players.player_info(8),pingprintx.w(8)
  73.  
  74. DEFTYPE .sockaddrin temphost
  75.  
  76. INCLUDE "UDPFuncs.bb2"  ; basic UDP socket functions
  77.  
  78. ;*******************************************************
  79.  
  80. .Print_String                ; Print a string in window and scroll if necessary
  81. Statement Print_String{text$}
  82.   SHARED ypos
  83.   If ypos<200
  84.     ypos+10
  85.   Else
  86.     WScroll 10,60,590,220,0,10
  87.   EndIf
  88.  
  89.   WLocate 10,ypos
  90.   Print text$               ; print string received
  91. End Function
  92.  
  93. .Get_Packet_Source            ; Check if packet has come from player already logged on.
  94. Function.w Get_Packet_Source{}
  95.  SHARED host(),temphost
  96.  i=0
  97.  Repeat                    ; check for each player (hostname and socket)
  98.    i+1
  99.    If host(i)\sin_addr\s_addr=temphost\sin_addr\s_addr AND  host(i)\sin_port=temphost\sin_port
  100.      exit=1
  101.    EndIf
  102.    If i=8 AND exit=0       ; if at end of players and no match is found
  103.      i=-1
  104.      exit=1
  105.    EndIf
  106.  Until  exit=1
  107.  
  108.  Function Return i
  109. End Function
  110.  
  111. .Get_Ascii_Address                     ; build a.b.c.d numerical address from long number
  112. Function.s Get_Ascii_Address{address.l}
  113.   string_address.l=Inet_NtoA_(address) ; get memory address of ASCII string version of host address (a.b.c.d)
  114.   If string_address=0
  115.     Function Return ""
  116.   EndIf
  117.   Repeat                               ; build ASCII host string
  118.     letter.b=(Peek.b(string_address))
  119.     If letter<>0
  120.       temp$=temp$+Chr$(letter)
  121.     EndIf
  122.     string_address+1
  123.   Until letter=0
  124.   Function Return temp$
  125. End Function
  126.  
  127. .Send_Reliable_Message         ; this message is checked to see if it has arrived, and resent if not
  128. Statement Send_Reliable_Message{send_string.s,player}       ; if no confirmation can be made, link is considered lost
  129.  SHARED messages(),free_message,last_message_number,packet_number
  130.  
  131.   messages(free_message)\number=packet_number,False,Ticks,send_string.s,0,player  ; log message in message array
  132.   If messages(last_message_number)\ack=True
  133.     last_message_number=free_message
  134.   EndIf
  135.  
  136.   WriteUDP{&send_string.s,Len(send_string.s),player}   ; send message
  137.   packet_number+1               ; set packet number to next free packet number (+1)
  138.   free_message+1
  139.   If free_message=101 Then free_message=0    ; if packet number >100 then wrap back to 0
  140.  
  141. End Statement
  142.  
  143. .Get_Host_By_Name        ; get a host structure by name (if exists), from a name server
  144. Function Get_Host_By_Name{host$,port.w,player}
  145.   SHARED host(),hostlen()
  146.  
  147.   *a.hostent=gethostbyname_(host$)     ; set up destination address to send packets to
  148.   If *a.hostent=0
  149.     WLocate 10,18
  150.     Print "    No connection to host           "
  151.     Function Return False
  152.   Else
  153.     WLocate 10,18
  154.     Print "Type in data, and hit return to send"
  155.   EndIf
  156.  
  157.   ;Copy Details to our Sockaddrin structure
  158.  
  159.   bb=CopyMem_(*a.hostent\h_addr_list\ItemA,&host(player)\sin_addr,*a.hostent\h_length)
  160.  
  161.   host(player)\sin_port=port       ;Set port number
  162.   host(player)\sin_family=2        ;Set type to AT_INET
  163.   hostlen(player)=SizeOf.sockaddrin        ;Get length of structure sockaddrin
  164.  
  165.   Function Return True
  166. End Function
  167.  
  168. .Get_Host_By_Address      ; get a host structure by address (if exists), from a name server
  169. Function.b Get_Host_By_Address{address.l,port,player}
  170.   SHARED host(),hostlen()
  171.  
  172.   *a.hostent=gethostbyaddr_(address,4,2)  ; check wether host exists- we're not being shammed
  173.  
  174.   If *a.hostent=0
  175.     Function Return False
  176.   EndIf
  177.  
  178.   bb=CopyMem_(*a.hostent\h_addr_list\ItemA,&host(player)\sin_addr,*a.hostent\h_length) ; copy details to player's host
  179.                                                                                        ; newtype to host
  180.   host(player)\sin_port=port               ;Set port number
  181.   host(player)\sin_family=2                ;Set type to AT_INET
  182.   hostlen(player)=SizeOf.sockaddrin        ;Get length of structure sockaddrin
  183.   Function Return True
  184. End Function
  185.  
  186. .Localhost_Name       ; get our internet address- it changes each time we log in to our ISP
  187. Function.s Localhost_Name{}
  188.     If OpenFile (0,"ENV:HOSTNAME")
  189.        FileInput 0
  190.        While NOT Eof(0)
  191.          a$=a$+Inkey$
  192.        Wend
  193.        CloseFile 0
  194.        WindowInput 0
  195.        Function Return a$
  196.     EndIf
  197.     Function Return "localhost"
  198. End Function
  199.  
  200. .Connect_To_Server
  201. Function Connect_to_Server{host.s,port.w}
  202.   SHARED online, CP_REQ_CONNECT.s, GAME_NAME.s, NET_PROTOCOL_VERSION.s
  203.   If online=0
  204.     If Get_Host_By_Name{GTGetString(0,51), GTGetInteger(0,52),1}=True
  205.       Print_String{"Attempting to connect to Server"}
  206.       send$=CP_REQ_CONNECT.s+GAME_NAME.s+Chr$(0)+NET_PROTOCOL_VERSION.s
  207.       WriteUDP{&send$,Len(send$),1}   ; send connection request
  208.     Else
  209.       Function Return False
  210.     EndIf
  211.   Else
  212.     If online=2
  213.       Print_String{"Already logged on to a Server!"}
  214.     Else
  215.       Print_String{"Already acting as a Server!"}
  216.     EndIf
  217.     Function Return False
  218.   EndIf
  219. End Function
  220.  
  221. .Resend_Message       ; resend a message
  222. Function Resend_Message{message_number}
  223.   SHARED messages()
  224.   messages(message_number)\timestamp=Ticks
  225.   messages(message_number)\resends+1
  226.   If messages(message_number)\resends>5  ; is host not responding? After 5 retries consider link broken.
  227.      Print_String{"Host not responding to Packet no: " + Str$(message_number)}
  228.      Function Return False
  229.   Else
  230.      WriteUDP{&messages(message_number)\message,Len(messages(message_number)\message),messages(message_number)\player}
  231.      Print_String{"Resent Packet no:" + Str$(message_number)}
  232.      Function Return True
  233.   EndIf
  234. End Statement
  235.  
  236. .Find_Next_Message
  237. Statement Find_Next_Message{}
  238. SHARED last_message_number,messages(),free_message
  239.  
  240.   exit=0
  241.   Repeat              ; find next unacknowledged message in array
  242.  
  243.     last_message_number+1
  244.     If last_message_number=101 Then last_message_number=0
  245.  
  246.     If last_message_number=free_message  ; if have got to free_message then there are no messages waiting to be acknowledged
  247.       If last_message_number=0
  248.         last_message_number=100
  249.         exit=1
  250.       Else
  251.         last_message_number-1
  252.         exit=1
  253.       EndIf
  254.     Else
  255.       If messages(last_message_number)\ack=False   ; we've found the next unacknowledged message in the array
  256.         exit=1
  257.       EndIf
  258.     EndIf
  259.  
  260.   Until exit=1
  261. End Statement
  262.  
  263. .Acknowledge_Packet   ; mark message as been sucessfully sent
  264. Statement Acknowledge_Packet{ack_packet_number}
  265.   SHARED last_message_number,free_message,messages(),player,pingprintx()
  266.  
  267.   a=last_message_number
  268.   exit=0
  269.   Repeat
  270.     If messages(a)\number=ack_packet_number   ; find message in sent messages array
  271.       messages(a)\ack=True         ; note it as being received
  272.       Format "0000"
  273.       current_time.l=Ticks
  274.       current_lag=current_time-messages(a)\timestamp ; calculate lag
  275.       b$=Str$(current_lag)
  276.       WLocate pingprintx(player),32
  277.       Print b$         ; print lag onscreen
  278.       Format""
  279.       exit=1
  280.     EndIf
  281.     a+1
  282.     If a=101 Then a=0    ; if at end of array, jump to beginning
  283.   Until exit=1
  284.  
  285.   If a=last_message_number+1 OR (a=0 AND last_message_number=100)   ; if last message unacknowledged is acknowledged
  286.     Find_Next_Message{}                                             ; then find next unacknowledged message
  287.   Else                ; we have lost a packet- resend last packet number!
  288.     If messages(last_message_number)\timestamp-current_time > current_lag+5    ; if its behind the current lag resend it
  289.       Print_String{"Received packet ACK out of order, resending last unacknowledged packet."}
  290.       If Resend_Message{last_message_number}= False   ; if run out of resends- time out message
  291.         messages(last_message_number)\timestamp=current_time+500  ; stop it resending the packet infinitely
  292.         Acknowledge_Packet{last_message_number}       ; wipe message off message acknowledge array
  293.       EndIf
  294.     EndIf
  295.   EndIf
  296. End Statement
  297.  
  298. .Requested_Connection      ; a player is trying to log in to us
  299. Function.s Requested_Connection{incoming_string.s}
  300.   SHARED sock,players(),host(),hostlen(),GAME_NAME,CP_REP_ACCEPT.s,CP_REP_REJECT.s
  301.   SHARED send$,player,temphost,temphostlen,CP_REP_PLAYER_INFO.s
  302.   SHARED packet_number,online,my_number,pingprintx()
  303.  
  304.   If online<2
  305.     dummy=3
  306.     Repeat            ; build game name string from incoming request
  307.       b$=Mid$(incoming_string,dummy,1)
  308.       If b$<>Chr$(0)
  309.         c$=c$+b$
  310.       EndIf
  311.       dummy+1
  312.     Until b$=Chr$(0) OR dummy=(Len(incoming_string))
  313.  
  314.     If b$=Chr$(0)
  315.       If c$=GAME_NAME.s            ; if request is for correct game
  316.         protocol=Asc(Right$(incoming_string,1))
  317.         If protocol=#NET_PROTOCOL_VERSION  ; check wether using the same protocol
  318.           a$="Connection request from "
  319.           player=1
  320.           exit=0
  321.           Repeat      ;check if a spare player slot is availiable
  322.             player+1
  323.             If players(player)\status=0
  324.               exit=1
  325.             Else
  326.               If player=#MAX_NUMBER_PLAYERS-1
  327.                 exit=1
  328.                 player=-1
  329.               EndIf
  330.             EndIf
  331.           Until exit=1
  332.  
  333.           If player>0     ; connection accepted
  334.             If Get_Host_By_Address{&temphost\sin_addr\s_addr,temphost\sin_port,player}=False
  335.               Function Return Str$(temphost\sin_addr\s_addr) + "- Host not found!"
  336.             EndIf
  337.  
  338.             players(player)\status=1
  339.             my_number=1
  340.             players(1)\status=1       ; We are now online as Server
  341.             If Get_Host_By_Address{&host(0)\sin_addr\s_addr,host(0)\sin_port,1}= False  ; put our details in Server Slot
  342.               Function Return Str$(host(0)\sin_addr\s_addr) + "- Host not found!"
  343.             EndIf
  344.  
  345.             temp$=Get_Ascii_Address{host(player)\sin_addr\s_addr}
  346.  
  347.             a$=a$+temp$ + "  Port: " + Str$(host(player)\sin_port)
  348.             players(player)\ascii_host_string=temp$   ; store it
  349.             WLocate pingprintx(1)+5,32
  350.             Print "Me"         ; print our player number as 1- the Server.
  351.  
  352.             For i=1 To #MAX_NUMBER_PLAYERS
  353.               If i<> player AND players(i)\status=1
  354.                 If i>1
  355.                   ; build player info string
  356.                   send$=CP_REP_PLAYER_INFO.s + Mkl$(packet_number) + Mki$(player) + Mkl$(host(player)\sin_addr\s_addr)
  357.                   send$=send$ + Mki$(host(player)\sin_port) + Chr$(players(player)\player_colour)
  358.                   send$=send$ + Mkl$(players(player)\score)
  359.                   send$=send$ + players(player)\name + Chr$(0) + players(player)\nick
  360.  
  361.                   Send_Reliable_Message{send$,i}    ; tell others players that new player is online
  362.                                                     ; and give them new players info.
  363.                 EndIf
  364.  
  365.                 send$=CP_REP_PLAYER_INFO.s + Mkl$(packet_number) + Mki$(i) + Mkl$(host(i)\sin_addr\s_addr)
  366.                 send$=send$ + Mki$(host(i)\sin_port) + Chr$(players(i)\player_colour)
  367.                 send$=send$ + Mkl$(players(i)\score)
  368.                 send$=send$ + players(i)\name + Chr$(0) + players(i)\nick
  369.  
  370.                 Send_Reliable_Message{send$,player}    ; tell new player of others online
  371.                                                        ; and give them their info.
  372.               EndIf
  373.  
  374.             Next
  375.  
  376.             send$=CP_REP_ACCEPT.s + Mkl$(packet_number)
  377.             send$=send$ + Mki$(host(0)\sin_port)+ Mki$(player) + Mki$(map)  ; send connection accepted back to client
  378.             online=1
  379.             Function Return a$
  380.           Else
  381.             a$=a$+"- no spare player slot!"
  382.           EndIf
  383.         Else
  384.           a$="Connection request- Incorrect Protocol: "+Str$(protocol)
  385.         EndIf
  386.       Else
  387.         a$="Wrong game name connection request: "+b$
  388.       EndIf
  389.     Else
  390.       a$="Connection request- Unknown error!"
  391.     EndIf
  392.   Else
  393.     a$="Connection Request rejected- host already logged on as Client"
  394.   EndIf
  395.  
  396.   send$=CP_REP_REJECT.s +  Mkl$(packet_number) + a$  ; send connection rejected back with reason
  397.   player=0                    ; send packet back to temphost
  398.   Function Return a$
  399. End Function
  400.  
  401. .Decode_Packet      ; Decode incoming packet and act accordingly
  402. Statement Decode_Packet{incoming_string.s}
  403.   SHARED players(),host(),GAME_NAME.s,temphost,temphostlen,online,REL_PACKET_ACK.s,CP_REP_ACCEPT.s
  404.   SHARED send$,player,my_number,pingprintx()
  405.   packet_type=Asc(Left$(incoming_string,1))
  406.   packet_not_connected=0   ; check to see if packet should be from reliable online source
  407.   player=Get_Packet_Source{}  ; check to see if packet is from reliable online source
  408.   return_message=0
  409.  
  410.   Select packet_type         ; as set in `Net_Protocol_Header.bb2'
  411.     Case  #CONTROL_PACKET    ; connection or control information
  412.       packet_type=Asc(Mid$(incoming_string,2))
  413.       Select packet_type
  414.         Case #CP_REQ_CONNECT      ; request to login to Server
  415.           a$=Requested_Connection{incoming_string}
  416.           packet_not_connected=1
  417.           return_message=2
  418.         Case #CP_REP_ACCEPT       ; We Received confirmation of logon from Server
  419.           online=2
  420.           packet_number.l=Cvl(Mid$(incoming_string,3,4))
  421.           my_number=Cvi(Mid$(incoming_string,9,2))
  422.           map.w=Cvi(Mid$(incoming_string,10,2))
  423.           players(1)\status=1
  424.           a$="Connection Accepted"
  425.           WLocate pingprintx(my_number)+5,32
  426.           Print "Me"      ; print our player number - as a Client.
  427.           return_message=3
  428.         Case #CP_REP_REJECT       ; Our logon request was rejected by host
  429.           packet_number.l=Cvl(Mid$(incoming_string,3,4))
  430.           a$=Mid$(incoming_string,7)
  431.           return_message=3
  432.         Case #CP_REP_PLAYER_INFO  ; Information about new player that has just logged on
  433.           newplayer=Cvi(Mid$(incoming_string,7,2))
  434.           packet_number.l=Cvl(Mid$(incoming_string,3,4))
  435.           address.l=Cvl(Mid$(incoming_string,9,4))
  436.           If Get_Host_By_Address{&address,Cvi(Mid$(incoming_string,13,2)),newplayer}=True
  437.             ascii_address.s=Get_Ascii_Address{address}
  438.             players(newplayer)\status=1,ascii_address.s
  439.             players(newplayer)\player_colour=Val(Mid$(incoming_string,15,1))
  440.             players(newplayer)\score=Cvi(Mid$(incoming_string,16,4))
  441.             a$="New player at- " + ascii_address.s + " Port: " + Str$(host(newplayer)\sin_port)
  442.           Else
  443.             a$="New player at unknown host!"
  444.           EndIf
  445.           return_message=3
  446.        End Select
  447.  
  448.     Case #REL_STRING_END     ; basic string message received from a player
  449.       packet_number.l=Cvl(Mid$(incoming_string,2,4))
  450.       a$=Str$(packet_number) + "," + Str$(player)  + ": " + Mid$(incoming_string,6,Len(incoming_string)-6)
  451.       return_message=3
  452.  
  453.     Case #REL_PACKET_ACK     ; a message has been received by a player
  454.       packet_number.l=Cvl(Mid$(incoming_string,2,4))
  455.       a$=": Packet no. " + Str$(packet_number) + " arrived at destination."
  456.       Acknowledge_Packet{packet_number}
  457.  
  458.     Default                        ; unknown packet type received
  459.       If player<0                  ; if from unknown source
  460.         temp$=Get_Ascii_Address{temphost\sin_addr\s_addr} + "Port: " + Str$(temphost\sin_port)
  461.       Else                         ; from known source
  462.         temp$="Player: " + Str$(player)
  463.       EndIf
  464.       a$="Unknown Packet from: " + temp$
  465.   End Select
  466.  
  467.   If player<0 AND packet_not_connected=0
  468.     Print_String{"Warning: Packet from unknown source!"}
  469.     Statement Return
  470.   EndIf
  471.  
  472.   If return_message>0
  473.     If return_message=2
  474.       Send_Reliable_Message{send$,player}  ; send packet back (return_message=2)
  475.     Else
  476.       If return_message=3  ; send packet acknowledgment back
  477.         send$=REL_PACKET_ACK.s + Mkl$(packet_number)
  478.       EndIf
  479.       WriteUDP{&send$,Len(send$),player}  ; return a message back if return_message = 1 or 3
  480.     EndIf
  481.   EndIf
  482.  
  483.   a$="R "+a$
  484.   Print_String{a$}   ; print message in the window
  485. End Statement
  486.  
  487. ;****************************************
  488.  
  489. ;     ARexx code
  490. ;  It seems replyrexxmsg doesn't actually work for sending result strings
  491. ;  back, use WRITERESULT, then replymsg for when you want to send
  492. ;  something back to rexx with result
  493.  
  494. Statement Result_Reply{*msg.RexxMsg,resulterror.l,resultstring$}
  495.   *msg\rm_Result1=result1
  496.   If ((*msg\rm_Action & #RXFF_RESULT)<>0)&(result1=0)
  497.     *msg\rm_Result2=CreateArgstring_(&resultstring$,Len(resultstring$))
  498.   Else
  499.     *msg\rm_Result2=0
  500.   EndIf
  501.   ReplyMsg_ *msg
  502. End Statement
  503.  
  504. .Get_Rexx_Message
  505. Function Get_Rexx_Message{}
  506.   SHARED rexxport.l,online
  507.   rmsg.l=RexxEvent(rexxport)
  508.   If IsRexxMsg(rmsg)=1
  509.     rexxline$=GetRexxCommand(rmsg,1)
  510.     Print_String {rexxline$}
  511.     linepos=Instr(rexxline$," ")
  512.     If linepos=0
  513.       command$=rexxline$
  514.     Else
  515.       command$=Left$(rexxline$,linepos-1)
  516.     EndIf
  517.     Select command$
  518.       Case "QUIT"
  519.           ReplyRexxMsg rmsg,0,0,""
  520.           Function Return False
  521.  
  522.        Case "ISONLINE"
  523.          Select online
  524.             Case 0
  525.               Result_Reply{rmsg,0,"Offline"}
  526.             Case 1
  527.               Result_Reply{rmsg,0,"Server"}
  528.             Case 2
  529.               Result_Reply{rmsg,0,"Client"}
  530.           End Select
  531.  
  532.       Case "CONNECTTOSERVER"
  533.           hostname$=Right$(rexxline$,Len(rexxline$)-linepos)
  534.           Print_String {hostname$}
  535.           If Connect_to_Server{hostname$,#PORT} : EndIf
  536.           ScreenToFront_ Peek.l(Addr Screen(0))
  537.           WindowToFront_ Peek.l(Addr Window(0))
  538.           ReplyRexxMsg rmsg,0,0,""
  539.       Default
  540.         ReplyRexxMsg rmsg,0,0,""
  541.     End Select
  542.   EndIf
  543.   Function Return True
  544. End Function
  545.  
  546. .Exit
  547. Statement Exit{text$}
  548.   SHARED UDPmem.l,sock.l,rexxport.l,rexxmsg.l
  549.  
  550.   Print_String{text$}    ; print error (if any)
  551.   FreeMem UDPmem,$2000   ; free UDP read memory buffer
  552.  
  553.   If sock>=0      ; if we've successfully opened a socket then close it
  554.     CloseSocket_(sock)
  555.     Print_String {"Socket closed"}
  556.   EndIf
  557.  
  558.   If rexxport>0              ; if we've successfully opened an Arexx port then close it
  559.     DeleteRexxMsg rexxmsg.l
  560.     DeleteMsgPort rexxport.l
  561.     Print_String{"Rexxport closed"}
  562.   EndIf
  563.  
  564.   Print_String{"Program exiting"}
  565.  
  566.   Delay_(30)
  567.   End          ; close program
  568.  
  569. End Statement
  570.  
  571.  
  572. ;****************************************
  573.  
  574. ypos=40
  575. Gosub Init_Gui
  576. messages(free_message)\ack=True
  577.  
  578. CNIF #NO_CONNECTION=1    ; if not connected to the `net- just running locally
  579.   If Initialise_UDP {Hostname,#LOCALPORT}              ; bind socket to local port
  580.      Print_String {"Bound to "+Hostname+" "+ Str$(port_used)}
  581.      WLocate 502,4
  582.      Print "Bound to: ",port_used
  583.   Else
  584.      Exit{"Error in setting up UDP at Port "+ Str$(port_used)}
  585.   EndIf
  586.  
  587.   If Get_Host_By_Name {Hostname,#PORT,0}  :EndIf   ; Set up our host details
  588.  
  589. CELSE                   ; are connected to the net or have been since computer was booted up
  590.   If Initialise_UDP {Localhost_Name{},#LOCALPORT}              ; bind socket to local port
  591.      Print_String {"Bound to "+Hostname+" "+ Str$(port_used)}
  592.      WLocate 502,4
  593.      Print "Bound to: ",port_used
  594.   Else
  595.      Exit{"Error in setting up UDP at Port "+ Str$(port_used)}
  596.   EndIf
  597.  
  598.   If Get_Host_By_Name {Localhost_Name{},#PORT,0}   :EndIf     ; Set up our host details
  599. CEND
  600.  
  601. ResetTimer
  602. exit=False
  603.  
  604. ;****************************************
  605.  
  606. .Main
  607.  
  608. Repeat
  609.  
  610.     VWait   ; pause a bit to allow prog to multitask nicely.
  611.  
  612.     If ev.l=$40
  613.       Select GadgetHit
  614.         Case51           ; Try to connect to a Server
  615.           If Connect_to_Server{GTGetString(0,51), GTGetInteger(0,52)} : EndIf
  616.         Case52           ; Port to connect at above Server
  617.           If Get_Host_By_Name{GTGetString(0,51), GTGetInteger(0,52),1} : EndIf
  618.         Case53           ; put in artificial delay of 1 second for testing purposes
  619.           VWait 50
  620.         Case54           ; get name of localhost
  621.           a$=Localhost_Name {}   ;
  622.           WLocate 315+pingwidth+15,hostnamey+3
  623.           Print a$
  624.         Case63          ; put in artificial delay of 40 seconds for testing purposes- causes you to lose connection
  625.           VWait 3000    ; delay 60s
  626.         Case64         ; Send string message to connected players
  627.          If online>0
  628.              temp$=GTGetString(0,64)
  629.              For i=1 To 8
  630.                If players(i)\status=1  AND i<>my_number   ; send to player if they're online and it's not my number :)
  631.                  Print_String{"S " + Str$(packet_number) + "," + Str$(i)  + ": "+ temp$}
  632.                  send$=REL_STRING_END.s + Mkl$(packet_number) + temp$ + Chr$(0)
  633.                  Send_Reliable_Message{send$,i}   ; Send string to connected players
  634.                EndIf
  635.              Next
  636.          EndIf
  637.        End Select
  638.     EndIf
  639.  
  640.     a$=ReadUDP{}   ; get data from socket
  641.  
  642.     If a$<>""      ; if there was some data waiting
  643.        Decode_Packet{a$}    ; find out what was in the packet
  644.     EndIf
  645.  
  646.     If messages(last_message_number)\ack=False  ; only check if there's messages that haven't been acknowledged yet.
  647.       If Ticks>messages(last_message_number)\timestamp+250  ; has it been 5 secs since the packet was sent?
  648.         If Resend_Message{last_message_number}= False   ; if run out of resends- time out message
  649.           Acknowledge_Packet{message_number}            ; wipe message off message acknowledge array
  650.         EndIf
  651.       EndIf
  652.     EndIf
  653.  
  654.     If Get_Rexx_Message{}=False  ; check for any arexx messages
  655.       exit=True                  ; exit if `Quit' received
  656.     EndIf
  657.  
  658.     ev.l=Event     ; get window events
  659.     If ev=$200 Then exit=True
  660.  
  661. Until exit=True    ; shut down if close gadget hit
  662.  
  663. Exit{""}   ; close UDP socket and Rexxport, and free read memory buffer
  664.            ; END!
  665.  
  666. ;****************************************************************************
  667.  
  668. .Init_Gui            ; setup window and gadgets- note it's a bit hacked from Alvaro Thompson's original code
  669.     WBenchToFront_   ; and isn't proportional and completely font sensitive any more :-/
  670.     WbToScreen0
  671.     DefaultIDCMP $20|$40|$200
  672.     *scr.Screen=Peek.l(Addr Screen(0))
  673.     *myfont.TextAttr=*scr\Font
  674.     fontheight=*myfont\ta_YSize
  675.     ad1.l=*myfont\ta_Name
  676.     fontname$=Peek$(ad1)
  677.  
  678.     LoadFont 1,fontname$,fontheight,0
  679.  
  680.     portwidth=TextLength_(*scr\_RastPort,"Port:",5)+10
  681.     numberswidth=TextLength_(*scr\_RastPort,"99999",5)+14
  682.     serverwidth=TextLength_(*scr\_RastPort,"Send To:",8)+10
  683.     connectwidth=TextLength_(*scr\_RastPort,"Connect",7)+14
  684.     disconnectwidth=TextLength_(*scr\_RastPort,"Disconnect",10)+14
  685.     pingwidth=TextLength_(*scr\_RastPort,"Localhost:",11)
  686.     delaywidth=TextLength_(*scr\_RastPort,"Delay 1s",8)+10
  687.     inputwidth=TextLength_(*scr\_RastPort,"Input:",6)+10
  688.     firstperson=TextLength_(*scr\_RastPort,"1:",2)+10
  689.  
  690.     #SERVERBTN       =51
  691.     #PORTBTN         =52
  692.     #BUTTON          =53
  693.  
  694.     x=1
  695.     y=1
  696.  
  697.     x1=x+(serverwidth*3)+portwidth+numberswidth+9+3+connectwidth+5
  698.  
  699.     If #NO_CONNECTION=0
  700.       GTString  0,#SERVERBTN,x+serverwidth,y,serverwidth*2,fontheight+4,"Send To:",1,256,Localhost_Name {}
  701.     Else
  702.       GTString  0,#SERVERBTN,x+serverwidth,y,serverwidth*2,fontheight+4,"Send To:",1,256,"localhost"
  703.     EndIf
  704.     GTInteger 0,#PORTBTN,x+(serverwidth*3)+portwidth+3,y,numberswidth,fontheight+4,"Port:",1,#PORT
  705.     GTButton  0,#BUTTON,345,y,delaywidth,fontheight+4,"Delay 1s",$10
  706.     GTButton  0,63,340+delaywidth+10,y,delaywidth,fontheight+4,"Delay 1m",$10
  707.     y+fontheight+5
  708.  
  709.     hostnamey=y
  710.  
  711.     GTButton  0,54,315,y,pingwidth,fontheight+4,"Localhost:",$10
  712.  
  713.     WinWidth=x+(serverwidth*3)+portwidth+numberswidth+9+3+connectwidth+8+disconnectwidth+8+90
  714.  
  715.     y+fontheight+5
  716.  
  717.     GTButton  0,55,120,y,firstperson,fontheight+4,"1:",$10
  718.     GTButton  0,56,180,y,firstperson,fontheight+4,"2:",$10
  719.     GTButton  0,57,240,y,firstperson,fontheight+4,"3:",$10
  720.     GTButton  0,58,300,y,firstperson,fontheight+4,"4:",$10
  721.     GTButton  0,59,360,y,firstperson,fontheight+4,"5:",$10
  722.     GTButton  0,60,420,y,firstperson,fontheight+4,"6:",$10
  723.     GTButton  0,61,480,y,firstperson,fontheight+4,"7:",$10
  724.     GTButton  0,62,540,y,firstperson,fontheight+4,"8:",$10
  725.     For i= 1To8
  726.       pingprintx(i)=84+ i*60
  727.     Next
  728.     pingprinty=y
  729.  
  730.     GTString  0,64,50,215,550,fontheight+4,"Send:",1,67,""
  731.  
  732.     WinHeight=y+fontheight+5
  733.  
  734.     ;WinX=WBWidth/2-(WinWidth/2)
  735.     ;WinY=WBHeight/2-(WinHeight/2)
  736.  
  737.     WinTitle$="UDP Chat"
  738.     ;ScreenTitle$="UDP Send "+Chr$(169)+"1997."
  739.  
  740.     Window 0,0,10,WinWidth,245,$0002|$0004|$0008,WinTitle$,1,2
  741.     Use Window 0:Activate 0:AttachGTList 0,0:WTitle WinTitle$,ScreenTitle$:Menus Off
  742.  
  743.     WindowOutput0
  744.     WindowInput 0
  745.  
  746.     a$=Localhost_Name {}   ;
  747.     WLocate 315+pingwidth+15,hostnamey+3
  748.     Print a$
  749.  
  750.     WLocate 10,y+3
  751.     Print"Ping Time-VBL"
  752.  
  753.     CNIF #DEBUG=0
  754.       If FindPort_(PORT_NAME.s)
  755.           Exit{"UDP_Chat already running!"}
  756.       EndIf
  757.     CEND
  758.  
  759.     rexxport.l=CreateMsgPort(PORT_NAME.s)
  760.     If rexxport=0
  761.       Exit{"Unable to open Rexxport!"}
  762.     EndIf
  763.     rexxmsg.l=CreateRexxMsg(rexxport,"rexx",PORT_NAME.s)
  764.  
  765. Return
  766.  
  767.  
  768.