;
;                      UDP Chat code V2.1   02/10/98
;
;    This code sends and receives UDP data packets like a
;  a simple IRC client, and  checks wether they have arrived
;  at their destination, and allows other copies of this
;  program to log in to this one (can act as a server or client).
;
;  Written by Anton Reinauer <anton@ww.co.nz>.
;  GUI: Alvaro Thompson <alvaro@enterprise.net> - And awful hacks
;  by me to his nice font sensitive code :-)
;        - typical bloody games programmer ;-)
;
;  Thanks to Paul Burkey for TCP_Funcs and to Dr. Ercole Spiteri
;  for TCP-to-Blitz.
;
;  ARexx portname: "UDP_Chat"
;
;  Turn overflow errors off in Debugger options,
;  and have amigalibs.res resident in compiler options.


WBStartup
NoCli
Hostname.s="localhost"  ; "localhost" default destination host address
#PORT=27272             ; 27272 default destination port to send data to
#LOCALPORT=27272        ; 27272
#NO_CONNECTION=1        ; 0 - are we connected, or have we been connected to the Internet since bootup?
                        ;   - 1 for not been connected, 0 for have/are connected.
#DEBUG=1                ; 1 - 1 for debug info and multiple local copies

#MAX_NUMBER_PLAYERS=8   ; 8
GAME_NAME.s="UDP_Chat"  ; "UDP_Chat"  Game name (for login)
PORT_NAME.s="UDP_Chat"  ; "UDP_Chat"  Rexx portname

INCLUDE "Net_Protocol_Header.bb2"  ; Net protocol constants
INCLUDE "UDPHeader.bb2"  ;Standard TCP/UDP library structures

DEFTYPE .w

packet_number.l=0
online=0
my_number=0
last_message_number.l=0
free_message.l=0
sock.l=-1
;********************************************************

NEWTYPE .message_status
  number.l     ; packet number
  ack.w        ; has it been received yet?
  timestamp.l  ; when was it sent?
  message.s    ; message
  resends.w    ; how many times has it been re-sent?
  player       ; which player was it sent too?
End NEWTYPE

NEWTYPE .player_info
status.w
ascii_host_string.s
player_colour.w
score.l
name.s
nick.s
End NEWTYPE

;*******************************************************
.Dims

Dim messages.message_status(100)
Dim host.sockaddrin(8), hostlen.l(8)
Dim players.player_info(8),pingprintx.w(8)

DEFTYPE .sockaddrin temphost

INCLUDE "UDPFuncs.bb2"  ; basic UDP socket functions

;*******************************************************

.Print_String                ; Print a string in window and scroll if necessary
Statement Print_String{text$}
  SHARED ypos
  If ypos<200
    ypos+10
  Else
    WScroll 10,60,590,220,0,10
  EndIf

  WLocate 10,ypos
  Print text$               ; print string received
End Function

.Get_Packet_Source            ; Check if packet has come from player already logged on.
Function.w Get_Packet_Source{}
 SHARED host(),temphost
 i=0
 Repeat                    ; check for each player (hostname and socket)
   i+1
   If host(i)\sin_addr\s_addr=temphost\sin_addr\s_addr AND  host(i)\sin_port=temphost\sin_port
     exit=1
   EndIf
   If i=8 AND exit=0       ; if at end of players and no match is found
     i=-1
     exit=1
   EndIf
 Until  exit=1

 Function Return i
End Function

.Get_Ascii_Address                     ; build a.b.c.d numerical address from long number
Function.s Get_Ascii_Address{address.l}
  string_address.l=Inet_NtoA_(address) ; get memory address of ASCII string version of host address (a.b.c.d)
  If string_address=0
    Function Return ""
  EndIf
  Repeat                               ; build ASCII host string
    letter.b=(Peek.b(string_address))
    If letter<>0
      temp$=temp$+Chr$(letter)
    EndIf
    string_address+1
  Until letter=0
  Function Return temp$
End Function

.Send_Reliable_Message         ; this message is checked to see if it has arrived, and resent if not
Statement Send_Reliable_Message{send_string.s,player}       ; if no confirmation can be made, link is considered lost
 SHARED messages(),free_message,last_message_number,packet_number

  messages(free_message)\number=packet_number,False,Ticks,send_string.s,0,player  ; log message in message array
  If messages(last_message_number)\ack=True
    last_message_number=free_message
  EndIf

  WriteUDP{&send_string.s,Len(send_string.s),player}   ; send message
  packet_number+1               ; set packet number to next free packet number (+1)
  free_message+1
  If free_message=101 Then free_message=0    ; if packet number >100 then wrap back to 0

End Statement

.Get_Host_By_Name        ; get a host structure by name (if exists), from a name server
Function Get_Host_By_Name{host$,port.w,player}
  SHARED host(),hostlen()

  *a.hostent=gethostbyname_(host$)     ; set up destination address to send packets to
  If *a.hostent=0
    WLocate 10,18
    Print "    No connection to host           "
    Function Return False
  Else
    WLocate 10,18
    Print "Type in data, and hit return to send"
  EndIf

  ;Copy Details to our Sockaddrin structure

  bb=CopyMem_(*a.hostent\h_addr_list\ItemA,&host(player)\sin_addr,*a.hostent\h_length)

  host(player)\sin_port=port       ;Set port number
  host(player)\sin_family=2        ;Set type to AT_INET
  hostlen(player)=SizeOf.sockaddrin        ;Get length of structure sockaddrin

  Function Return True
End Function

.Get_Host_By_Address      ; get a host structure by address (if exists), from a name server
Function.b Get_Host_By_Address{address.l,port,player}
  SHARED host(),hostlen()

  *a.hostent=gethostbyaddr_(address,4,2)  ; check wether host exists- we're not being shammed

  If *a.hostent=0
    Function Return False
  EndIf

  bb=CopyMem_(*a.hostent\h_addr_list\ItemA,&host(player)\sin_addr,*a.hostent\h_length) ; copy details to player's host
                                                                                       ; newtype to host
  host(player)\sin_port=port               ;Set port number
  host(player)\sin_family=2                ;Set type to AT_INET
  hostlen(player)=SizeOf.sockaddrin        ;Get length of structure sockaddrin
  Function Return True
End Function

.Localhost_Name       ; get our internet address- it changes each time we log in to our ISP
Function.s Localhost_Name{}
    If OpenFile (0,"ENV:HOSTNAME")
       FileInput 0
       While NOT Eof(0)
         a$=a$+Inkey$
       Wend
       CloseFile 0
       WindowInput 0
       Function Return a$
    EndIf
    Function Return "localhost"
End Function

.Connect_To_Server
Function Connect_to_Server{host.s,port.w}
  SHARED online, CP_REQ_CONNECT.s, GAME_NAME.s, NET_PROTOCOL_VERSION.s
  If online=0
    If Get_Host_By_Name{GTGetString(0,51), GTGetInteger(0,52),1}=True
      Print_String{"Attempting to connect to Server"}
      send$=CP_REQ_CONNECT.s+GAME_NAME.s+Chr$(0)+NET_PROTOCOL_VERSION.s
      WriteUDP{&send$,Len(send$),1}   ; send connection request
    Else
      Function Return False
    EndIf
  Else
    If online=2
      Print_String{"Already logged on to a Server!"}
    Else
      Print_String{"Already acting as a Server!"}
    EndIf
    Function Return False
  EndIf
End Function

.Resend_Message       ; resend a message
Function Resend_Message{message_number}
  SHARED messages()
  messages(message_number)\timestamp=Ticks
  messages(message_number)\resends+1
  If messages(message_number)\resends>5  ; is host not responding? After 5 retries consider link broken.
     Print_String{"Host not responding to Packet no: " + Str$(message_number)}
     Function Return False
  Else
     WriteUDP{&messages(message_number)\message,Len(messages(message_number)\message),messages(message_number)\player}
     Print_String{"Resent Packet no:" + Str$(message_number)}
     Function Return True
  EndIf
End Statement

.Find_Next_Message
Statement Find_Next_Message{}
SHARED last_message_number,messages(),free_message

  exit=0
  Repeat              ; find next unacknowledged message in array

    last_message_number+1
    If last_message_number=101 Then last_message_number=0

    If last_message_number=free_message  ; if have got to free_message then there are no messages waiting to be acknowledged
      If last_message_number=0
        last_message_number=100
        exit=1
      Else
        last_message_number-1
        exit=1
      EndIf
    Else
      If messages(last_message_number)\ack=False   ; we've found the next unacknowledged message in the array
        exit=1
      EndIf
    EndIf

  Until exit=1
End Statement

.Acknowledge_Packet   ; mark message as been sucessfully sent
Statement Acknowledge_Packet{ack_packet_number}
  SHARED last_message_number,free_message,messages(),player,pingprintx()

  a=last_message_number
  exit=0
  Repeat
    If messages(a)\number=ack_packet_number   ; find message in sent messages array
      messages(a)\ack=True         ; note it as being received
      Format "0000"
      current_time.l=Ticks
      current_lag=current_time-messages(a)\timestamp ; calculate lag
      b$=Str$(current_lag)
      WLocate pingprintx(player),32
      Print b$         ; print lag onscreen
      Format""
      exit=1
    EndIf
    a+1
    If a=101 Then a=0    ; if at end of array, jump to beginning
  Until exit=1

  If a=last_message_number+1 OR (a=0 AND last_message_number=100)   ; if last message unacknowledged is acknowledged
    Find_Next_Message{}                                             ; then find next unacknowledged message
  Else                ; we have lost a packet- resend last packet number!
    If messages(last_message_number)\timestamp-current_time > current_lag+5    ; if its behind the current lag resend it
      Print_String{"Received packet ACK out of order, resending last unacknowledged packet."}
      If Resend_Message{last_message_number}= False   ; if run out of resends- time out message
        messages(last_message_number)\timestamp=current_time+500  ; stop it resending the packet infinitely
        Acknowledge_Packet{last_message_number}       ; wipe message off message acknowledge array
      EndIf
    EndIf
  EndIf
End Statement

.Requested_Connection      ; a player is trying to log in to us
Function.s Requested_Connection{incoming_string.s}
  SHARED sock,players(),host(),hostlen(),GAME_NAME,CP_REP_ACCEPT.s,CP_REP_REJECT.s
  SHARED send$,player,temphost,temphostlen,CP_REP_PLAYER_INFO.s
  SHARED packet_number,online,my_number,pingprintx()

  If online<2
    dummy=3
    Repeat            ; build game name string from incoming request
      b$=Mid$(incoming_string,dummy,1)
      If b$<>Chr$(0)
        c$=c$+b$
      EndIf
      dummy+1
    Until b$=Chr$(0) OR dummy=(Len(incoming_string))

    If b$=Chr$(0)
      If c$=GAME_NAME.s            ; if request is for correct game
        protocol=Asc(Right$(incoming_string,1))
        If protocol=#NET_PROTOCOL_VERSION  ; check wether using the same protocol
          a$="Connection request from "
          player=1
          exit=0
          Repeat      ;check if a spare player slot is availiable
            player+1
            If players(player)\status=0
              exit=1
            Else
              If player=#MAX_NUMBER_PLAYERS-1
                exit=1
                player=-1
              EndIf
            EndIf
          Until exit=1

          If player>0     ; connection accepted
            If Get_Host_By_Address{&temphost\sin_addr\s_addr,temphost\sin_port,player}=False
              Function Return Str$(temphost\sin_addr\s_addr) + "- Host not found!"
            EndIf

            players(player)\status=1
            my_number=1
            players(1)\status=1       ; We are now online as Server
            If Get_Host_By_Address{&host(0)\sin_addr\s_addr,host(0)\sin_port,1}= False  ; put our details in Server Slot
              Function Return Str$(host(0)\sin_addr\s_addr) + "- Host not found!"
            EndIf

            temp$=Get_Ascii_Address{host(player)\sin_addr\s_addr}

            a$=a$+temp$ + "  Port: " + Str$(host(player)\sin_port)
            players(player)\ascii_host_string=temp$   ; store it
            WLocate pingprintx(1)+5,32
            Print "Me"         ; print our player number as 1- the Server.

            For i=1 To #MAX_NUMBER_PLAYERS
              If i<> player AND players(i)\status=1
                If i>1
                  ; build player info string
                  send$=CP_REP_PLAYER_INFO.s + Mkl$(packet_number) + Mki$(player) + Mkl$(host(player)\sin_addr\s_addr)
                  send$=send$ + Mki$(host(player)\sin_port) + Chr$(players(player)\player_colour)
                  send$=send$ + Mkl$(players(player)\score)
                  send$=send$ + players(player)\name + Chr$(0) + players(player)\nick

                  Send_Reliable_Message{send$,i}    ; tell others players that new player is online
                                                    ; and give them new players info.
                EndIf

                send$=CP_REP_PLAYER_INFO.s + Mkl$(packet_number) + Mki$(i) + Mkl$(host(i)\sin_addr\s_addr)
                send$=send$ + Mki$(host(i)\sin_port) + Chr$(players(i)\player_colour)
                send$=send$ + Mkl$(players(i)\score)
                send$=send$ + players(i)\name + Chr$(0) + players(i)\nick

                Send_Reliable_Message{send$,player}    ; tell new player of others online
                                                       ; and give them their info.
              EndIf

            Next

            send$=CP_REP_ACCEPT.s + Mkl$(packet_number)
            send$=send$ + Mki$(host(0)\sin_port)+ Mki$(player) + Mki$(map)  ; send connection accepted back to client
            online=1
            Function Return a$
          Else
            a$=a$+"- no spare player slot!"
          EndIf
        Else
          a$="Connection request- Incorrect Protocol: "+Str$(protocol)
        EndIf
      Else
        a$="Wrong game name connection request: "+b$
      EndIf
    Else
      a$="Connection request- Unknown error!"
    EndIf
  Else
    a$="Connection Request rejected- host already logged on as Client"
  EndIf

  send$=CP_REP_REJECT.s +  Mkl$(packet_number) + a$  ; send connection rejected back with reason
  player=0                    ; send packet back to temphost
  Function Return a$
End Function

.Decode_Packet      ; Decode incoming packet and act accordingly
Statement Decode_Packet{incoming_string.s}
  SHARED players(),host(),GAME_NAME.s,temphost,temphostlen,online,REL_PACKET_ACK.s,CP_REP_ACCEPT.s
  SHARED send$,player,my_number,pingprintx()
  packet_type=Asc(Left$(incoming_string,1))
  packet_not_connected=0   ; check to see if packet should be from reliable online source
  player=Get_Packet_Source{}  ; check to see if packet is from reliable online source
  return_message=0

  Select packet_type         ; as set in `Net_Protocol_Header.bb2'
    Case  #CONTROL_PACKET    ; connection or control information
      packet_type=Asc(Mid$(incoming_string,2))
      Select packet_type
        Case #CP_REQ_CONNECT      ; request to login to Server
          a$=Requested_Connection{incoming_string}
          packet_not_connected=1
          return_message=2
        Case #CP_REP_ACCEPT       ; We Received confirmation of logon from Server
          online=2
          packet_number.l=Cvl(Mid$(incoming_string,3,4))
          my_number=Cvi(Mid$(incoming_string,9,2))
          map.w=Cvi(Mid$(incoming_string,10,2))
          players(1)\status=1
          a$="Connection Accepted"
          WLocate pingprintx(my_number)+5,32
          Print "Me"      ; print our player number - as a Client.
          return_message=3
        Case #CP_REP_REJECT       ; Our logon request was rejected by host
          packet_number.l=Cvl(Mid$(incoming_string,3,4))
          a$=Mid$(incoming_string,7)
          return_message=3
        Case #CP_REP_PLAYER_INFO  ; Information about new player that has just logged on
          newplayer=Cvi(Mid$(incoming_string,7,2))
          packet_number.l=Cvl(Mid$(incoming_string,3,4))
          address.l=Cvl(Mid$(incoming_string,9,4))
          If Get_Host_By_Address{&address,Cvi(Mid$(incoming_string,13,2)),newplayer}=True
            ascii_address.s=Get_Ascii_Address{address}
            players(newplayer)\status=1,ascii_address.s
            players(newplayer)\player_colour=Val(Mid$(incoming_string,15,1))
            players(newplayer)\score=Cvi(Mid$(incoming_string,16,4))
            a$="New player at- " + ascii_address.s + " Port: " + Str$(host(newplayer)\sin_port)
          Else
            a$="New player at unknown host!"
          EndIf
          return_message=3
       End Select

    Case #REL_STRING_END     ; basic string message received from a player
      packet_number.l=Cvl(Mid$(incoming_string,2,4))
      a$=Str$(packet_number) + "," + Str$(player)  + ": " + Mid$(incoming_string,6,Len(incoming_string)-6)
      return_message=3

    Case #REL_PACKET_ACK     ; a message has been received by a player
      packet_number.l=Cvl(Mid$(incoming_string,2,4))
      a$=": Packet no. " + Str$(packet_number) + " arrived at destination."
      Acknowledge_Packet{packet_number}

    Default                        ; unknown packet type received
      If player<0                  ; if from unknown source
        temp$=Get_Ascii_Address{temphost\sin_addr\s_addr} + "Port: " + Str$(temphost\sin_port)
      Else                         ; from known source
        temp$="Player: " + Str$(player)
      EndIf
      a$="Unknown Packet from: " + temp$
  End Select

  If player<0 AND packet_not_connected=0
    Print_String{"Warning: Packet from unknown source!"}
    Statement Return
  EndIf

  If return_message>0
    If return_message=2
      Send_Reliable_Message{send$,player}  ; send packet back (return_message=2)
    Else
      If return_message=3  ; send packet acknowledgment back
        send$=REL_PACKET_ACK.s + Mkl$(packet_number)
      EndIf
      WriteUDP{&send$,Len(send$),player}  ; return a message back if return_message = 1 or 3
    EndIf
  EndIf

  a$="R "+a$
  Print_String{a$}   ; print message in the window
End Statement

;****************************************

;     ARexx code
;  It seems replyrexxmsg doesn't actually work for sending result strings
;  back, use WRITERESULT, then replymsg for when you want to send
;  something back to rexx with result

Statement Result_Reply{*msg.RexxMsg,resulterror.l,resultstring$}
  *msg\rm_Result1=result1
  If ((*msg\rm_Action & #RXFF_RESULT)<>0)&(result1=0)
    *msg\rm_Result2=CreateArgstring_(&resultstring$,Len(resultstring$))
  Else
    *msg\rm_Result2=0
  EndIf
  ReplyMsg_ *msg
End Statement

.Get_Rexx_Message
Function Get_Rexx_Message{}
  SHARED rexxport.l,online
  rmsg.l=RexxEvent(rexxport)
  If IsRexxMsg(rmsg)=1
    rexxline$=GetRexxCommand(rmsg,1)
    Print_String {rexxline$}
    linepos=Instr(rexxline$," ")
    If linepos=0
      command$=rexxline$
    Else
      command$=Left$(rexxline$,linepos-1)
    EndIf
    Select command$
      Case "QUIT"
          ReplyRexxMsg rmsg,0,0,""
          Function Return False

       Case "ISONLINE"
         Select online
            Case 0
              Result_Reply{rmsg,0,"Offline"}
            Case 1
              Result_Reply{rmsg,0,"Server"}
            Case 2
              Result_Reply{rmsg,0,"Client"}
          End Select

      Case "CONNECTTOSERVER"
          hostname$=Right$(rexxline$,Len(rexxline$)-linepos)
          Print_String {hostname$}
          If Connect_to_Server{hostname$,#PORT} : EndIf
          ScreenToFront_ Peek.l(Addr Screen(0))
          WindowToFront_ Peek.l(Addr Window(0))
          ReplyRexxMsg rmsg,0,0,""
      Default
        ReplyRexxMsg rmsg,0,0,""
    End Select
  EndIf
  Function Return True
End Function

.Exit
Statement Exit{text$}
  SHARED UDPmem.l,sock.l,rexxport.l,rexxmsg.l

  Print_String{text$}    ; print error (if any)
  FreeMem UDPmem,$2000   ; free UDP read memory buffer

  If sock>=0      ; if we've successfully opened a socket then close it
    CloseSocket_(sock)
    Print_String {"Socket closed"}
  EndIf

  If rexxport>0              ; if we've successfully opened an Arexx port then close it
    DeleteRexxMsg rexxmsg.l
    DeleteMsgPort rexxport.l
    Print_String{"Rexxport closed"}
  EndIf

  Print_String{"Program exiting"}

  Delay_(30)
  End          ; close program

End Statement


;****************************************

ypos=40
Gosub Init_Gui
messages(free_message)\ack=True

CNIF #NO_CONNECTION=1    ; if not connected to the `net- just running locally
  If Initialise_UDP {Hostname,#LOCALPORT}              ; bind socket to local port
     Print_String {"Bound to "+Hostname+" "+ Str$(port_used)}
     WLocate 502,4
     Print "Bound to: ",port_used
  Else
     Exit{"Error in setting up UDP at Port "+ Str$(port_used)}
  EndIf

  If Get_Host_By_Name {Hostname,#PORT,0}  :EndIf   ; Set up our host details

CELSE                   ; are connected to the net or have been since computer was booted up
  If Initialise_UDP {Localhost_Name{},#LOCALPORT}              ; bind socket to local port
     Print_String {"Bound to "+Hostname+" "+ Str$(port_used)}
     WLocate 502,4
     Print "Bound to: ",port_used
  Else
     Exit{"Error in setting up UDP at Port "+ Str$(port_used)}
  EndIf

  If Get_Host_By_Name {Localhost_Name{},#PORT,0}   :EndIf     ; Set up our host details
CEND

ResetTimer
exit=False

;****************************************

.Main

Repeat

    VWait   ; pause a bit to allow prog to multitask nicely.

    If ev.l=$40
      Select GadgetHit
        Case51           ; Try to connect to a Server
          If Connect_to_Server{GTGetString(0,51), GTGetInteger(0,52)} : EndIf
        Case52           ; Port to connect at above Server
          If Get_Host_By_Name{GTGetString(0,51), GTGetInteger(0,52),1} : EndIf
        Case53           ; put in artificial delay of 1 second for testing purposes
          VWait 50
        Case54           ; get name of localhost
          a$=Localhost_Name {}   ;
          WLocate 315+pingwidth+15,hostnamey+3
          Print a$
        Case63          ; put in artificial delay of 40 seconds for testing purposes- causes you to lose connection
          VWait 3000    ; delay 60s
        Case64         ; Send string message to connected players
         If online>0
             temp$=GTGetString(0,64)
             For i=1 To 8
               If players(i)\status=1  AND i<>my_number   ; send to player if they're online and it's not my number :)
                 Print_String{"S " + Str$(packet_number) + "," + Str$(i)  + ": "+ temp$}
                 send$=REL_STRING_END.s + Mkl$(packet_number) + temp$ + Chr$(0)
                 Send_Reliable_Message{send$,i}   ; Send string to connected players
               EndIf
             Next
         EndIf
       End Select
    EndIf

    a$=ReadUDP{}   ; get data from socket

    If a$<>""      ; if there was some data waiting
       Decode_Packet{a$}    ; find out what was in the packet
    EndIf

    If messages(last_message_number)\ack=False  ; only check if there's messages that haven't been acknowledged yet.
      If Ticks>messages(last_message_number)\timestamp+250  ; has it been 5 secs since the packet was sent?
        If Resend_Message{last_message_number}= False   ; if run out of resends- time out message
          Acknowledge_Packet{last_message_number}           ; wipe message off message acknowledge array
        EndIf
      EndIf
    EndIf

    If Get_Rexx_Message{}=False  ; check for any arexx messages
      exit=True                  ; exit if `Quit' received
    EndIf

    ev.l=Event     ; get window events
    If ev=$200 Then exit=True

Until exit=True    ; shut down if close gadget hit

Exit{""}   ; close UDP socket and Rexxport, and free read memory buffer
           ; END!

;****************************************************************************

.Init_Gui            ; setup window and gadgets- note it's a bit hacked from Alvaro Thompson's original code
    WBenchToFront_   ; and isn't proportional and completely font sensitive any more :-/
    WbToScreen0
    DefaultIDCMP $20|$40|$200
    *scr.Screen=Peek.l(Addr Screen(0))
    *myfont.TextAttr=*scr\Font
    fontheight=*myfont\ta_YSize
    ad1.l=*myfont\ta_Name
    fontname$=Peek$(ad1)

    LoadFont 1,fontname$,fontheight,0

    portwidth=TextLength_(*scr\_RastPort,"Port:",5)+10
    numberswidth=TextLength_(*scr\_RastPort,"99999",5)+14
    serverwidth=TextLength_(*scr\_RastPort,"Send To:",8)+10
    connectwidth=TextLength_(*scr\_RastPort,"Connect",7)+14
    disconnectwidth=TextLength_(*scr\_RastPort,"Disconnect",10)+14
    pingwidth=TextLength_(*scr\_RastPort,"Localhost:",11)
    delaywidth=TextLength_(*scr\_RastPort,"Delay 1s",8)+10
    inputwidth=TextLength_(*scr\_RastPort,"Input:",6)+10
    firstperson=TextLength_(*scr\_RastPort,"1:",2)+10

    #SERVERBTN       =51
    #PORTBTN         =52
    #BUTTON          =53

    x=1
    y=1

    x1=x+(serverwidth*3)+portwidth+numberswidth+9+3+connectwidth+5

    If #NO_CONNECTION=0
      GTString  0,#SERVERBTN,x+serverwidth,y,serverwidth*2,fontheight+4,"Send To:",1,256,Localhost_Name {}
    Else
      GTString  0,#SERVERBTN,x+serverwidth,y,serverwidth*2,fontheight+4,"Send To:",1,256,"localhost"
    EndIf
    GTInteger 0,#PORTBTN,x+(serverwidth*3)+portwidth+3,y,numberswidth,fontheight+4,"Port:",1,#PORT
    GTButton  0,#BUTTON,345,y,delaywidth,fontheight+4,"Delay 1s",$10
    GTButton  0,63,340+delaywidth+10,y,delaywidth,fontheight+4,"Delay 1m",$10
    y+fontheight+5

    hostnamey=y

    GTButton  0,54,315,y,pingwidth,fontheight+4,"Localhost:",$10

    WinWidth=x+(serverwidth*3)+portwidth+numberswidth+9+3+connectwidth+8+disconnectwidth+8+90

    y+fontheight+5

    GTButton  0,55,120,y,firstperson,fontheight+4,"1:",$10
    GTButton  0,56,180,y,firstperson,fontheight+4,"2:",$10
    GTButton  0,57,240,y,firstperson,fontheight+4,"3:",$10
    GTButton  0,58,300,y,firstperson,fontheight+4,"4:",$10
    GTButton  0,59,360,y,firstperson,fontheight+4,"5:",$10
    GTButton  0,60,420,y,firstperson,fontheight+4,"6:",$10
    GTButton  0,61,480,y,firstperson,fontheight+4,"7:",$10
    GTButton  0,62,540,y,firstperson,fontheight+4,"8:",$10
    For i= 1To8
      pingprintx(i)=84+ i*60
    Next
    pingprinty=y

    GTString  0,64,50,215,550,fontheight+4,"Send:",1,67,""

    WinHeight=y+fontheight+5

    ;WinX=WBWidth/2-(WinWidth/2)
    ;WinY=WBHeight/2-(WinHeight/2)

    WinTitle$="UDP Chat"
    ;ScreenTitle$="UDP Send "+Chr$(169)+"1997."

    Window 0,0,10,WinWidth,245,$0002|$0004|$0008,WinTitle$,1,2
    Use Window 0:Activate 0:AttachGTList 0,0:WTitle WinTitle$,ScreenTitle$:Menus Off

    WindowOutput0
    WindowInput 0

    a$=Localhost_Name {}   ;
    WLocate 315+pingwidth+15,hostnamey+3
    Print a$

    WLocate 10,y+3
    Print"Ping Time-VBL"

    CNIF #DEBUG=0
      If FindPort_(PORT_NAME.s)
          Exit{"UDP_Chat already running!"}
      EndIf
    CEND

    rexxport.l=CreateMsgPort(PORT_NAME.s)
    If rexxport=0
      Exit{"Unable to open Rexxport!"}
    EndIf
    rexxmsg.l=CreateRexxMsg(rexxport,"rexx",PORT_NAME.s)

Return


