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.3.asc < prev    next >
Encoding:
Text File  |  1999-05-14  |  38.8 KB  |  1,088 lines

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