home *** CD-ROM | disk | FTP | other *** search
Text File | 1999-05-14 | 46.2 KB | 1,261 lines |
- ;
- ; UDP Chat code V2.4 26/04/99
- ;
- ; 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
- DEFTYPE .w
- ResetTimer
-
- ;**************** UDP Variables and Constants **************
-
- hostname.s="localhost" ; "localhost" default destination host address
- #NO_CONNECTION=1 ; 0 - are we connected, or have we been connected to the Internet since bootup? 1- No
- #COMMS_DEBUG=1 ; 1 - 1 for debug info and multiple local copies
-
- #MESSAGE_ARRAY_SIZE=256 ; 256
- GAME_NAME.s="UDP_Chat" ; "UDP_Chat" Game name (for login)
- max_wait=250 ; 250 Time to wait for an acknowledgement (frames)
- number_resends=5 ; 5 Number of resends to wait, before disconnecting player
- lag_allowance=5 ; 5 Number of frames to add to the current lag, when waiting for an out-of-order packet.
- max_number_players=8 ; 8
- #ACKNOWLEDGE_PACKET=3
- #OFFLINE=0
- #ONLINE=1
- #ATTEMPTING_TO_CONNECT=1
- #SERVER=2
- #CLIENT=3
-
- #DISCONNECTED=1
- #CONNECTED=2
- #CONNECTION_REJECTED=3
- #SYSTEM_MESSAGE=4 ; System message
- #HIGH=3 ; High priority Security Message or Comms Debug info
- #MEDIUM=2 ; Medium " "
- #LOW=1 ; Low " "
- #SERVER_NUMBER=1
- #GAME_CLOSING_TIMEOUT=1500
- #DISCONNECT_TIMEOUT=1500
- #CLIENT_SERVER=0
- #PEER_TO_PEER=1
- packet_number.l=0
- online=#OFFLINE
- my_number=0
- last_message_number.l=0
- free_message.l=0
- sock.l=-1
- security=#LOW ; level of security warnings (normally #LOW- show #LOW and higher ie: all)
- comms_debug_level=#LOW ; level of debug info (normally #LOW- show #LOW and higher ie: all)
- tcp_stack=False
- disconnect_timeout=0
-
- ;*************************** UDP Dims *************************
-
- .Dims
-
- INCLUDE "Net_Protocol_Header.bb2" ; Net protocol constants
- INCLUDE "UDPHeader.bb2" ;Standard TCP/UDP library structures and UDP Funcs Newtypes
-
- Dim messages.message_status(#MESSAGE_ARRAY_SIZE)
- Dim host.sockaddrin(max_number_players), hostlen.l(max_number_players)
- Dim players.player_info(max_number_players)
- Dim player_honesty(max_number_players,max_number_players)
-
- DEFTYPE .sockaddrin temphost
-
- For a=1 To max_number_players ; this array dimension is for the player number who is claiming another is offline
- For b=1 To max_number_players ; this dimension is for the player number who is claimed to be offline
- player_honesty(a,b)=False ; initialise array
- Next
- Next
- messages(free_message)\ack=True
-
- ;******************* Game Functions ********************
-
- ; These Functions are specific to your program, the ones below
- ; can be used as examples for your game- most of them are after
- ; the UDP_Funcs, and this one will be able to be put there soon.
-
- .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 Statement
-
- ;************************ UDP Funcs ***********************
-
- ; These are all specific functions for your game to communicate
- ; over the internet or by modem/null-modem.
-
- .Security_Warning
- Statement Security_Warning{text$,level}
- SHARED security ; security= 0 none, 1 all warnings, 2 medium and severe warnings and 3 only severe warnings
- If level>=security ; only print out security warnings if at set security level or higher
- If level<#SYSTEM_MESSAGE
- a$="Warning: "
- EndIf
- Print_String{a$ + text$}
- EndIf
- End Statement
-
- .Comms_Debug
- Statement Comms_Debug{text$,level}
- SHARED comms_debug_level ; comms_debug= 0 none, 1 all info, 2 medium and high priority info and 3 only high priority info
- If level>=comms_debug_level ; only print out debug info if at set debug level or higher
- Print_String{text$}
- EndIf
- End Statement
-
- INCLUDE "UDPFuncs.bb2" ; basic UDP socket functions
-
- .Dim_Message_Arrays
- Statement Dim_Message_Arrays{new_max_number_players}
- SHARED online,max_number_players
- SHARED host(),hostlen(),players(),player_honesty()
-
- max_number_players=new_max_number_players
- If online>#OFFLINE
- Dim host.sockaddrin(max_number_players), hostlen.l(max_number_players)
- Dim players.player_info(max_number_players)
- Dim player_honesty(max_number_players,max_number_players)
-
- For a=1 To max_number_players ; this array dimension is for the player number who is claiming another is offline
- For b=1 To max_number_players ; this dimension is for the player number who is claimed to be offline
- player_honesty(a,b)=False ; initialise array
- Next
- Next
- EndIf
- End Statement
-
- .Get_Packet_Source ; Check if packet has come from player already logged on.
- Function.w Get_Packet_Source{}
- SHARED host(),temphost,max_number_players
- i=0
- exit=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=max_number_players 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,protocol} ; if no confirmation can be made, link is considered lost
- SHARED messages(),free_message,last_message_number,packet_number.l
-
- If protocol=#PEER_TO_PEER ; Send message Peer-To-Peer
- send_string.s=Mkl$(packet_number) + send_string.s
- 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=#MESSAGE_ARRAY_SIZE+1 Then free_message=0 ; if packet number >#MESSAGE_ARRAY_SIZE then wrap back to 0
- Else ; Send message Client-Server
-
- EndIf
-
- End Statement
-
- .Initialise_Server
- Function Initialise_Server{localport}
- ; Set up the game so we can act as Server
- SHARED online,port_used
-
- If online=#OFFLINE
- If Initialise_UDP{localport} ; bind socket to local port
- online=#SERVER
- Function Return True
- Else
- Security_Warning{"Error in setting up UDP at Port "+ Str$(port_used),#SYSTEM_MESSAGE}
- EndIf
- Else
- Select online
- Case #CLIENT
- Security_Warning{"Already logged on to a Server!",#SYSTEM_MESSAGE}
- Case #SERVER
- Security_Warning{"Already acting as a Server!",#SYSTEM_MESSAGE}
- Case #ATTEMPTING_TO_CONNECT
- Security_Warning{"Already attempting to log on to a Server!",#SYSTEM_MESSAGE}
- End Select
- EndIf
- Function Return False
- End Function
-
- .Disconnect_From_Game
- Function Disconnect_From_Game{}
- ; Remove us from the game, and if we're the Server- close down
- ; the game and take away our ability to act as Server.
- SHARED online,players(),max_number_players,my_number
- SHARED disconnect_timeout
- SHARED CP_END_GAME.s,CP_REQ_PLAYER_DISCONNECT.s
-
- Select online
- Case #OFFLINE
- Function Return True
- Case #SERVER ; if we are Server
- dummy=0
- For i=2 To max_number_players
- If players(i)\status=#ONLINE ; send to player if they're online
- send$=CP_END_GAME.s
- dummy=1
- Send_Reliable_Message{send$,i,#PEER_TO_PEER} ; Send string to connected players
- EndIf
- Next
- If dummy=1
- Security_Warning{"Telling players Game has ended.",#SYSTEM_MESSAGE}
- disconnect_timeout=#DISCONNECT_TIMEOUT
- Function Return False
- Else
- online=#OFFLINE
- Function Return True
- EndIf
- Case #CLIENT ; we are client
- send$=CP_REQ_PLAYER_DISCONNECT.s + Mki$(my_number)
- Send_Reliable_Message{send$,#SERVER_NUMBER,#PEER_TO_PEER}
- Security_Warning{"Telling Server we are disconnecting from Game.",#SYSTEM_MESSAGE}
- disconnect_timeout=#DISCONNECT_TIMEOUT
- Function Return False
- Case #ATTEMPTING_TO_CONNECT ; we are client
- Security_Warning{"Game Connection attempt aborted.",#SYSTEM_MESSAGE}
- online=#OFFLINE
- Function Return True
- End Select
- 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=#OFFLINE
- If Initialise_UDP{port} ; bind socket to local port
- If Get_Host_By_Name{GTGetString(0,51), GTGetInteger(0,52),1}=True
- Security_Warning{"Attempting to connect to Server",#SYSTEM_MESSAGE}
- send$=Mkl$(0) + CP_REQ_CONNECT.s + GAME_NAME.s + Chr$(0) + NET_PROTOCOL_VERSION.s ; "0000" is a Pad for packet number
- WriteUDP{&send$,Len(send$),1} ; send connection request
- online=#ATTEMPTING_TO_CONNECT
- Function Return True
- Else
- Function Return False
- EndIf
- Else
- Function Return False
- EndIf
- Else
- Select online
- Case #CLIENT
- Security_Warning{"Already logged on to a Server!",#SYSTEM_MESSAGE}
- Case #SERVER
- Security_Warning{"Already acting as a Server!",#SYSTEM_MESSAGE}
- Case #ATTEMPTING_TO_CONNECT
- Security_Warning{"Already attempting to log on to a Server!",#SYSTEM_MESSAGE}
- End Select
- Function Return False
- EndIf
- End Function
-
- .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=#MESSAGE_ARRAY_SIZE+1 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=#MESSAGE_ARRAY_SIZE
- 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
-
- .Clear_Player_Arrays
- Statement Clear_Player_Arrays{player} ; clear player's arrays
- SHARED last_message_number,messages(),free_message,host()
- SHARED player_honesty(),players(),max_number_players
-
- If messages(last_message_number)\player=player
- find_next_message=True
- Else
- find_next_message=False
- EndIf
-
- a=last_message_number
- exit=False
- Repeat ; clear message array
- If messages(a)\player=player
- messages(a)\ack=True ; clear unacknowledged messages off message array
- EndIf
- If a=free_message Then exit=True
- a+1
- If a=#MESSAGE_ARRAY_SIZE+1 Then a=0 ; if at end of array, jump to beginning
- Until exit=True
-
- If find_next_message=True
- Find_Next_Message{}
- EndIf
-
- host(player)\sin_addr\s_addr=0 ; clear host address array
- host(player)\sin_port=0
-
- For a= 1 To max_number_players ; clear player_honesty array
- player_honesty(player,a)=False ; clear disconnected player's array
- player_honesty(a,player)=False ; clear other player's arrays who have claimed the disconnected player was offline
- Next
-
- players(player)\disconnect_requested=False ; reset disconnect request item
- players(player)\status=#OFFLINE ; player's status is offline
-
- End Statement
-
- .Disconnect_Player
- Statement Disconnect_Player{player} ; Disconnect Player from game
- SHARED players(),online,max_number_players
- SHARED CP_REP_PLAYER_DISCONNECTED.s,CP_REQ_PLAYER_DISCONNECT.s
-
- If online=#SERVER ; If we are the Server
- dummy=0
- Security_Warning{"Player " + Str$(player) + " has disconnected",#SYSTEM_MESSAGE}
- For i=2 To max_number_players
- If players(i)\status=#ONLINE ; send to all players if they're online
- If i <> player ; check to see if any players are left online
- dummy=1
- EndIf
- send$=CP_REP_PLAYER_DISCONNECTED.s + Mki$(player) ; tell other players
- Send_Reliable_Message{send$,i,#PEER_TO_PEER} ; Send string to connected players that player is offline
- EndIf
- Next
-
- If dummy=0 ; if no players are left online
- CNIF #COMMS_DEBUG=1
- Comms_Debug{"All players have disconnected",#MEDIUM}
- CEND
- EndIf
-
- Clear_Player_Arrays{player} ; clear message array of player's messages
-
- Else ; if we're a client, inform Server that a player can't be contacted
- CNIF #COMMS_DEBUG=1
- Comms_Debug{"Telling Server we can't contact player!",#MEDIUM}
- CEND
- send$=CP_REQ_PLAYER_DISCONNECT.s + Mki$(player)
- Send_Reliable_Message{send$,#SERVER_NUMBER,#PEER_TO_PEER} ; Send string to Server
- EndIf
-
- End Statement
-
- .Resend_Message ; resend a message
- Statement Resend_Message{message_number}
- SHARED messages(),number_resends,online,players()
- SHARED max_number_players
-
- messages(message_number)\timestamp=Ticks
- messages(message_number)\resends+1
- If messages(message_number)\resends>number_resends ; is host not responding? After 5 retries consider link broken.
- CNIF #COMMS_DEBUG=1
- Comms_Debug{"Host not responding to Packet no: " + Str$(message_number),#HIGH}
- CEND
- If messages(message_number)\player<>#SERVER_NUMBER ; We've lost contact with a Client
-
- If players(messages(message_number)\player)\disconnect_requested=False ; if haven't already sent a Disconnect req
- Disconnect_Player{messages(message_number)\player} ; send disconnect request to Server
- players(messages(message_number)\player)\disconnect_requested=True ; don't send any more disconnect requests
- EndIf ; for that player
- Else ; we've lost contact with Server- game over!
- online=#OFFLINE
- For a= 1 To max_number_players
- Clear_Player_Arrays{a}
- Next
- EndIf
- Else
- WriteUDP{&messages(message_number)\message,Len(messages(message_number)\message),messages(message_number)\player}
- CNIF #COMMS_DEBUG=1
- Comms_Debug{"Resent Packet no:" + Str$(message_number),#HIGH}
- CEND
- EndIf
- End Statement
-
- .Acknowledge_Packet ; mark message as been sucessfully sent
- Function Acknowledge_Packet{ack_packet_number,player}
-
- SHARED last_message_number,free_message,messages()
- SHARED packet_number.l,lag_allowance,players()
-
- a=last_message_number
- exit=-2
- Repeat
- If messages(a)\number=ack_packet_number ; find message in sent messages array
- messages(a)\ack=True ; note it as being received
- current_time.l=Ticks
- players(player)\lag=current_time-messages(a)\timestamp ; calculate lag
- exit=True
- Else
- If a=free_message ; in case bad packet number sends it into a loop! It will only check around buffer once.
- exit=False
- a$="Warning- Invalid Packet number " + Str$(incoming_packet_number)
- Security_Warning{ a$ + " from Player " + Str$(player),#MEDIUM}
- EndIf
- a+1
- EndIf
-
- If a=#MESSAGE_ARRAY_SIZE+1 Then a=0 ; if at end of array, jump to beginning
- Until exit>-2
-
- If exit=True ; only do if packet has valid packet_number
- If a=last_message_number OR (a=0 AND last_message_number=#MESSAGE_ARRAY_SIZE) ; if last message unacknowledged is
- Find_Next_Message{} ; acknowledged then find next unacknowledged message
- Else ; we have lost a packet- resend last packet number!
- allowable_lag=players(messages(last_message_number)\player)\lag + lag_allowance ; current lag of player
- If messages(last_message_number)\timestamp-current_time > allowable_lag
- Security_Warning{"Received packet ACK out of order, resending last unacknowledged packet.",#MEDIUM} ; then resend m
- Resend_Message{last_message_number} ; if run out of resends- disconnect player
- EndIf
- EndIf
- EndIf
- Function Return exit
- End Function
-
- .Requested_Connection ; a player is trying to log in to us
- Statement Requested_Connection{incoming_string.s,player}
- SHARED sock,players(),host(),hostlen(),GAME_NAME.s
- SHARED temphost,temphostlen,online,my_number,max_number_players
- SHARED CP_REP_ACCEPT.s,CP_REP_REJECT.s,CP_REP_PLAYER_INFO.s
-
- If player=-1 ; make sure player hasn't already logged in
- If online=#OFFLINE OR online=#SERVER ; If offline or Server
- dummy=7
- 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=#OFFLINE
- exit=1
- Else
- If player=max_number_players
- 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
- Security_Warning{Str$(temphost\sin_addr\s_addr) + "- Host not found!",#SYSTEM_MESSAGE}
- Statement Return
- EndIf
-
- players(player)\status=#ONLINE
- my_number=#SERVER_NUMBER
- players(#SERVER_NUMBER)\status=#ONLINE ; 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
- Security_Warning{Str$(host(0)\sin_addr\s_addr) + "- Host not found!",#SYSTEM_MESSAGE}
- Statement Return
- 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
-
- ; build player info string to tell other players of new player
- send_others$=CP_REP_PLAYER_INFO.s + Mki$(player) + Mkl$(host(player)\sin_addr\s_addr)
- send_others$=send_others$ + Mki$(host(player)\sin_port) + Chr$(players(player)\player_colour)
- send_others$=send_others$ + Mkl$(players(player)\score)
- send_others$=send_others$ + players(player)\name + Chr$(0) + players(player)\nick
-
- For i=1 To max_number_players
- If i<> player AND players(i)\status=#ONLINE
- If i>1
- Send_Reliable_Message{send_others$,i,#PEER_TO_PEER} ; tell others players that new player is online
- ; and give them new players info.
- EndIf
-
- send$=CP_REP_PLAYER_INFO.s + 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,#PEER_TO_PEER} ; tell new player of others online
- ; and give them their info.
- EndIf
-
- Next
-
- send$=CP_REP_ACCEPT.s
- send$=send$ + Mki$(host(0)\sin_port)+ Mki$(player) + Mki$(map) ; send connection accepted back to client
- online=#SERVER
- players(player)\disconnect_requested=False
- Send_Reliable_Message{send$,player,#PEER_TO_PEER}
-
- Security_Warning{a$,#SYSTEM_MESSAGE}
- Statement Return
- Else
- a$="Connection Request rejected- no spare player slot!"
- EndIf
- Else
- a$="Connection Request rejected- Incorrect Protocol: "+Str$(protocol)
- EndIf
- Else
- a$="Connection Request rejected- wrong game name: "+b$
- EndIf
- Else
- a$="Connection Request rejected- Unknown error!"
- EndIf
- Else
- a$="Connection Request rejected- host already logged on as Client"
- EndIf
- Else
- a$="Connection Request rejected- host already logged on to Server."
- EndIf
-
- send$=Mkl$(0) + CP_REP_REJECT.s + a$ ; send connection rejected back with reason
- WriteUDP{&send$,Len(send$),0} ; reply to sender
- Security_Warning{a$,#MEDIUM}
-
- End Statement
-
- .Decode_Packet ; Decode incoming packet and act accordingly
- Function Decode_Packet{}
- SHARED players(),host(),temphost,online
- SHARED my_number,player_honesty(),max_number_players
- SHARED CP_REQ_PLAYER_DISCONNECT.s,CP_END_GAME_REC.s,REL_PACKET_ACK.s,CP_REP_ACCEPT.s
- SHARED CP_GAME_END.s,CP_REP_PLAYER_DISCONNECTED.s,PING_REQUEST.s
- SHARED PING_RESPONSE.s
-
- return_code=False
- incoming_string.s=ReadUDP{} ; get data from socket
-
- If incoming_string<>"" ; if there was some data waiting
- packet_type=Asc(Mid$(incoming_string,5))
- unknown_packet=0 ; check to see if valid packet is received
- player=Get_Packet_Source{} ; check to see if packet is from reliable online source
- incoming_packet_number.l=Cvl(Left$(incoming_string,4))
- return_message=0
- player_known=True
-
- Select packet_type ; as set in `Net_Protocol_Header.bb2'
- Case #CONTROL_PACKET ; connection or control information
- packet_type=Asc(Mid$(incoming_string,6))
- Select packet_type
- Case #CP_REQ_CONNECT ; request to login to Server
- Requested_Connection{incoming_string,player}
- player_known=False
- Case #CP_REP_ACCEPT ; We Received confirmation of logon from Server
- If online=#ATTEMPTING_TO_CONNECT
- online=#CLIENT
- my_number=Cvi(Mid$(incoming_string,9,2)) ; my player number
- map.w=Cvi(Mid$(incoming_string,10,2))
- players(#SERVER_NUMBER)\status=#ONLINE ; Server is now online
- Security_Warning{"Connection Accepted",#SYSTEM_MESSAGE}
- return_message=#ACKNOWLEDGE_PACKET
- return_code=#CONNECTED
- Else
- Security_Warning{"Illegal Reply Accept attempt by player: " + Str$(player),#MEDIUM}
- EndIf
- player_known=False
-
- Case #CP_REP_REJECT ; Our logon request was rejected by host
- If online=#ATTEMPTING_TO_CONNECT
- Security_Warning{Mid$(incoming_string,7),#SYSTEM_MESSAGE}
- online=#OFFLINE
- return_code=#CONNECTION_REJECTED
- Else
- Security_Warning{"Illegal Reply Reject attempt by player: " + Str$(player),#MEDIUM}
- EndIf
- player_known=False
-
- Case #CP_REP_PLAYER_INFO ; Information about new player that has just logged on
- ; If online=#CLIENT ; if we are a client
- newplayer=Cvi(Mid$(incoming_string,7,2))
- 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=#ONLINE,ascii_address.s
- players(newplayer)\player_colour=Val(Mid$(incoming_string,15,1))
- players(newplayer)\score=Cvi(Mid$(incoming_string,16,4))
- players(newplayer)\disconnect_requested=False
- text$="New player at- " + ascii_address.s + " Port: " + Str$(host(newplayer)\sin_port)
- Security_Warning{text$,#SYSTEM_MESSAGE}
- Else
- Security_Warning{"New player at unknown host!",#MEDIUM}
- EndIf
- return_message=#ACKNOWLEDGE_PACKET
-
- player_known=False
- ; Else
- ; Security_Warning{"Illegal Player Info Reply from player: " + Str$(player),#MEDIUM}
- ; EndIf
-
- Case #CP_REQ_PLAYER_DISCONNECT ; Request from client to disconnect
- ; or inform Server of disconnected player
-
- If online=#SERVER AND player>0 ; only if Server, and from legal player
- disconnecting_player=Cvi(Mid$(incoming_string,7,2))
- If player=disconnecting_player ; if player is leaving game
- Disconnect_Player{player}
- Else ; Ping suspect player to see if they're still online
- If players(disconnecting_player)\status=#ONLINE
- player_honesty(player,disconnecting_player)=True ; record which player claims another is offline.
- Send_Reliable_Message{PING_REQUEST.s,disconnecting_player,#PEER_TO_PEER} ; so we can disconnect them if the cl
- CNIF #COMMS_DEBUG=1
- text$="R " + Str$(incoming_packet_number) + "," + Str$(player) + ": "
- text$=text$ + "Player " + Str$(player) + " claims player " + Str$(disconnecting_player)
- Comms_Debug{text$ + " has disconnected",#HIGH}
- CEND
- Else
- CNIF #COMMS_DEBUG=1
- text$="Warning: Player " + Str$(player) + " says UNCONNECTED PLAYER " + Str$(disconnecting_player)
- text$=text$ + " is unreachable."
- Comms_Debug{text$,#HIGH}
- CEND
- EndIf
- return_message=#ACKNOWLEDGE_PACKET
- EndIf
- Else
- Security_Warning{"Illegal Player Disconnect Request Packet from player: " + Str$(player), #MEDIUM}
- EndIf
-
- Case #CP_REP_PLAYER_DISCONNECTED ; Server has told us a player has disconnected from the game
- ; - this can be us as well
- If online=#CLIENT AND player=#SERVER_NUMBER ; only if we're a client and message is from server
- disconnected_player=Cvi(Mid$(incoming_string,7,2))
- Clear_Player_Arrays{disconnected_player} ; player is now offline
- If disconnected_player=my_number ; my disconnection request has been confirmed by Server
- online=#OFFLINE ; go offline
- For a= 1 To max_number_players
- If players(a)\status=#ONLINE
- Clear_Player_Arrays{a}
- EndIf
- Next
- Security_Warning{"We are disconnected from the game.",#SYSTEM_MESSAGE}
- return_code=#DISCONNECTED
- Else
- Security_Warning{"Player " + Str$(disconnected_player) + " is disconnected from the game.",#SYSTEM_MESSAGE}
- EndIf
- return_message=#ACKNOWLEDGE_PACKET
- Else
- Security_Warning{"Illegal Player Disconnected Reply Packet from player: " + Str$(player),#MEDIUM}
- EndIf
-
- Case #CP_END_GAME ; Server has ended Game
- If player=#SERVER_NUMBER AND online=#CLIENT; if message is from Server
- send$=CP_END_GAME_REC.s
- Send_Reliable_Message{send$,#SERVER_NUMBER,#PEER_TO_PEER}
- online=#OFFLINE
- return_message=#ACKNOWLEDGE_PACKET
- Security_Warning{"Server has shut down game.",#SYSTEM_MESSAGE}
- For a= 1 To max_number_players
- If players(a)\status=#ONLINE
- Clear_Player_Arrays{a}
- EndIf
- Next
- return_code=#DISCONNECTED
- Else
- Security_Warning{"Illegal End Game attempt by player: " + Str$(player),#MEDIUM}
- EndIf
-
- Case #CP_END_GAME_REC ; player has confirmed they received the Game End packet
- If online=#SERVER AND player>0
- Clear_Player_Arrays{player} ; player is now offline
- dummy=0
- For i=2 To max_number_players
- If players(i)\status=#ONLINE ; check to see if all players are offline
- dummy=1
- EndIf
- Next
- If dummy=0 ; if all players are offline
- online=#OFFLINE
- CNIF #COMMS_DEBUG=1
- text$="R " + Str$(incoming_packet_number) + "," + Str$(player) + ": "
- Comms_Debug{text$ + "Player " + Str$(player) + " has confirmed game closure.",#HIGH}
- CEND
- Security_Warning{"All players have confirmed game has shut down.",#SYSTEM_MESSAGE}
- return_code=#DISCONNECTED
-
- CNIF #COMMS_DEBUG=1
- Else
- text$="R " + Str$(incoming_packet_number) + "," + Str$(player) + ": "
- Comms_Debug{text$ + "Player " + Str$(player) + " has confirmed game closure.",#HIGH}
- CEND
-
- EndIf
- return_message=#ACKNOWLEDGE_PACKET
- Else
- Security_Warning{"Illegal END_GAME_REC packet received from: " + Str$(player),#MEDIUM}
- EndIf
- Default
- unknown_packet=1
- End Select
-
- Case #REL_STRING_END ; basic string message received from a player
- If player>0 AND online>#ATTEMPTING_TO_CONNECT
- text$="R " + Str$(incoming_packet_number) + "," + Str$(player) + ": "
- Security_Warning{text$ + Mid$(incoming_string,6,Len(incoming_string)-6),#SYSTEM_MESSAGE}
- return_message=#ACKNOWLEDGE_PACKET
- EndIf
-
- Case #REL_PACKET_ACK ; a message has been received by a player
- If player>0 AND online>#ATTEMPTING_TO_CONNECT
- If Acknowledge_Packet{incoming_packet_number,player}=True
- CNIF #COMMS_DEBUG=1
- text$="R " + Str$(incoming_packet_number) + "," + Str$(player) + ": "
- Comms_Debug{text$ + ": Packet no. " + Str$(incoming_packet_number) + " arrived at destination.",#HIGH}
- CEND
- EndIf
- EndIf
-
- Case #PING_REQUEST ; a Ping request has been received
- CNIF #COMMS_DEBUG=1
- text$="R " + Str$(incoming_packet_number) + "," + Str$(player) + ": "
- Comms_Debug{text$ + "Player " + Str$(player) + " has sent a Ping request.",#HIGH}
- CEND
-
- If player=#SERVER_NUMBER AND online=#CLIENT
- return_message=#ACKNOWLEDGE_PACKET
- Send_Reliable_Message{PING_RESPONSE.s,player,#PEER_TO_PEER} ; send Ping Response back
- Else
- Security_Warning{"Illegal PING_REQUEST received from player: " + Str$(player), #MEDIUM}
- EndIf
-
- Case #PING_RESPONSE ; Ping response received
-
- If online=#SERVER
- CNIF #COMMS_DEBUG=1
- text$="R " + Str$(incoming_packet_number) + "," + Str$(player) + ": "
- Comms_Debug{text$ + "Player " + Str$(player) + " has sent a Ping response.",#HIGH}
- CEND
-
- If player>0 ; if from online player
- For a= 1 To max_number_players ; this is to stop players from disconnecting other players illegally
- If player_honesty(a,player)=True ; if player loses connection to other player
- Disconnect_Player{a} ; Disconnect player who has lost connection to another player
- CNIF #COMMS_DEBUG=1
- text$="R " + Str$(incoming_packet_number) + "," + Str$(player) + ": "
- Comms_Debug{text$ + "Player " + Str$(a) + " has lost connection to player " + Str$(player),#HIGH}
- CEND
- EndIf
- Next
- EndIf
- Else
- Security_Warning{"Illegal PING_RESPONSE received from player: " + Str$(player), #MEDIUM}
- EndIf
-
- Default ; unknown packet received
- unknown_packet=1
- End Select
-
- If unknown_packet=1
- If player=-1 ; 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
- Security_Warning{"Warning- Unknown Packet from" + temp$,#MEDIUM}
- Function Return False
- EndIf
-
- If player_known=True AND player=-1 ; if we get sent a legal packet from unknown player
- temp$=": " + Get_Ascii_Address{temphost\sin_addr\s_addr} + " Port: " + Str$(temphost\sin_port)
- Security_Warning{"Warning- Packet from unknown Player at" + temp$,#MEDIUM}
- EndIf
-
- If return_message>0
- If return_message=#ACKNOWLEDGE_PACKET ; send packet acknowledgment back
- send$=Mkl$(incoming_packet_number) + REL_PACKET_ACK.s ;
- EndIf
- WriteUDP{&send$,Len(send$),player} ; return a message back if return_message = 1 or 3
- EndIf
- EndIf
-
- Function Return return_code
- End Function
-
- .Comms_Housekeeping
- Function Comms_Housekeeping{}
- SHARED messages(),last_message_number,players(),online
- SHARED max_wait,max_number_players,disconnect_timeout
-
- return_code=Decode_Packet{} ; find out what was in the packet
-
- 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+max_wait ; has it been 5 secs since the packet was sent?
- Resend_Message{last_message_number} ; if run out of resends- disconnect player
- EndIf
- EndIf
-
- If disconnect_timeout>0 ; If disconnection timeout reaches
- disconnect_timeout-1 ; zero then disconnect the program
- If disconnect_timeout=0
- online=#OFFLINE ; go offline
- For a= 1 To max_number_players
- If players(a)\status=#ONLINE
- Clear_Player_Arrays{a}
- EndIf
- Next
- Security_Warning{"We are disconnected from the game.",#SYSTEM_MESSAGE}
- return_code=#DISCONNECTED
- EndIf
- EndIf
-
- Function Return return_code
- End Function
-
- ;******************* Game Variables and Constants **************
-
- exit=False
- game_closing=0
- PORT_NAME.s="UDP_Chat" ; "UDP_Chat" Rexx portname
- #LOCALPORT=27272 ; 27272 default local port to send an receive from
- #PORT=27272 ; 27272 default destination port to send data to
- print_player.w=1
-
- ;************************ Game Dims **************************
-
- Dim pingprintx(max_number_players)
-
- ;***************** Game Funcs Continued ********************
-
- .Window_Events
- Function Window_Events{}
- SHARED pingwidth,hostnamey,online,players(),my_number,packet_number
- SHARED game_closing.w,max_number_players,pingprintx(),hostname
- SHARED port_used
- SHARED REL_STRING_END.s
-
- exit=False
- ev.l=Event ; get window events
- Select ev
- Case $40 ; if a gadget has been hit
- Select GadgetHit
- Case51 ; Try to connect to a Server
- If Connect_to_Server{GTGetString(0,51), GTGetInteger(0,52)}=True
- WLocate 502,4
- Print "Bound to: ",port_used
- Else
- Print_String{"Unable to Initialise UDP"}
- EndIf
-
- Case52 ; Port to connect at above Server
- If Get_Host_By_Name{GTGetString(0,51), GTGetInteger(0,52),1} : EndIf
-
- Case53 ; Server button- lets our program become the Server
- If Initialise_Server{#LOCALPORT}
- Print_String {"Bound to " + hostname + " "+ Str$(port_used)}
- WLocate 502,4
- Print "Bound to: ",port_used
- WLocate pingprintx(#SERVER_NUMBER),32
- Print "Serv" ; show that we're Server
- Print_String{"We are set up as Server."}
- Else
- Print_String{"Couldn't Initialise Server!"}
- EndIf
-
- Case54 ; get name of localhost
- a$=Localhost_Name {} ;
- WLocate 315+pingwidth+15,hostnamey+3
- Print a$
-
- Case 55 ; button 1
- Print_String{"Delay program for 1 second"}
- VWait 50 ; put in artificial delay of 1 second For testing purposes
- Print_String{"Delay Ended"}
-
- Case 56 ; button 2
- Print_String{"Delay program for 80 seconds"}
- VWait 4000 ; put in artificial delay of 80 seconds for testing purposes- causes you to lose connection
- Print_String{"Delay Ended"}
-
- Case62 ; button 8- Pause program
- Print_String{"PAUSED"}
- Repeat
- ev.l=WaitEvent
- If ev=$40
- Select GadgetHit
- Case 62
- unpause= 1
- Print_String{"UNPAUSED"}
- End Select
- EndIf
- Until unpause=1
-
- Case63 ; Disconnect button- disconnects us from the game
- If online=#OFFLINE
- Print_String{"We're already offline."}
- Else
- If Disconnect_From_Game{}
- Print_String{"Game has ended."}
- For i=1 To 8
- WLocate pingprintx(i),32
- Print " " ; Clear the lag times
- Next
- EndIf
- EndIf
- Case64 ; Send string message to connected players
- If online>#ATTEMPTING_TO_CONNECT
- temp$=GTGetString(0,64)
- For i=1 To 8
- If players(i)\status=#ONLINE 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 + temp$ + Chr$(0)
- Send_Reliable_Message{send$,i,#PEER_TO_PEER} ; Send string to connected players
- EndIf
- Next
- EndIf
- End Select
- Case $200 ; if close button pressed
- If Disconnect_From_Game{}
- exit=True
- Print_String{"Program Closing."}
- Else
- game_closing=#GAME_CLOSING_TIMEOUT
- EndIf
- End Select
-
- Function Return exit
- End Function
-
- ; 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 True
-
- Case "ISONLINE"
- Select online
- Case #OFFLINE
- Result_Reply{rmsg,0,"Offline"}
- Case #ATTEMPTING_TO_CONNECT
- Result_Reply{rmsg,0,"Attempting to Connect"}
- Case #SERVER
- Result_Reply{rmsg,0,"Server"}
- Case #CLIENT
- 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 False
- End Function
-
- .General_Housekeeping
- Function General_Housekeeping{}
- SHARED game_closing,online,print_player,pingprintx()
- SHARED players(),my_number,print_count,max_number_players
-
- exit=False
- If Get_Rexx_Message{}=True ; check for any arexx messages
- exit=True ; exit if `Quit' received
- EndIf
-
- print_count+1
- If print_count=50
- print_count=1 ; do once per second
- print_player+1
- If print_player>max_number_players Then print_player=1
- If online>#ATTEMPTING_TO_CONNECT AND print_player<>my_number
-
- WLocate pingprintx(print_player),32
- If players(print_player)\status=#ONLINE
- Format "0000"
- Print Str$(players(print_player)\lag) ; print lag onscreen
- Format""
- Else
- Print" "
- EndIf
- EndIf
- EndIf
-
- If game_closing>0 ; if game is shutting down
- If online=#OFFLINE
- exit=True ; if all players are offline then shutdown
- Print_String{"We are now Offline- Closing program."}
- Else
- If game_closing>=2 ; carry on countdown
- game_closing-1
- Else ; if countdown reaches 10 seconds then disconnect regardless
- exit=True
- Print_String{"Emergency Shutdown!- unable to disconnect from Server."}
- EndIf
- EndIf
- EndIf
- Function Return exit
- End Function
-
- .Exit
- Statement Exit{text$}
- SHARED rexxport.l,rexxmsg.l
-
- Print_String{text$} ; print error (if any)
-
- 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
-
- Close_UDP{} ; close all UDP stuff down
-
- Print_String{"Program exiting"}
-
- Delay_(50)
- End ; close program
-
- End Statement
-
- ;********************* End of Functions ***********************
-
- Gosub Init_Gui
-
- ;************************ Main Loop **************************
-
- .Main
-
- Repeat
-
- WaitTOF_ ; pause a bit to allow prog to multitask nicely, and sync up display
-
- exit=Window_Events{} ; check for input from user
-
- If online>#OFFLINE
- a=Comms_Housekeeping{} ; Check for incoming data and do comms housekeeping work.
- Select a
- Case #CONNECTED ; Server has told us we're online
- WLocate pingprintx(my_number)+5,32
- Print "Me" ; print our player number - as a Client.
- Case #DISCONNECTED ; We're now offline
- For i=1 To 8
- WLocate pingprintx(i),32
- Print " " ; Clear the lag times
- Next
- If game_closing>0 Then exit=1
- Case #CONNECTION_REJECTED ; Our connection attempt was rejected
- End Select
- EndIf
-
- If General_Housekeeping{}=True ; Check for rexx messages
- exit=True ; and do general housekeeping work.
- EndIf
-
- 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
- ypos=40
- 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,"SERVER",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,331,y,delaywidth,fontheight+4,"Server",$10
- GTButton 0,63,333+delaywidth,y,disconnectwidth,fontheight+4,"Disconnect",$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)=86+ 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,630,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 #COMMS_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)
-
- WLocate 10,18
- Print "Type in data, and hit return to send"
-
- Return
-
-
-