home *** CD-ROM | disk | FTP | other *** search
- ;
- ; UDP Chat code V1.7 29/1/98
- ;
- ; This code sends and receives UDP data packets like a
- ; a simple IRC client, and checks wether they have arrived
- ; at their destination.
- ;
- ; 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.
- ;
- ; Turn overflow errors off in Debugger options
-
-
- WBStartup
- NoCli
- Hostname.s="localhost" ; default destination host address
- #PORT=3001 ; default destination port to send data to
- #LOCALPORT=3001
- #NO_CONNECTION=1 ; are we connected, or have we been connected to the Internet?
- ; 1 for not been connected, 0 for have/are connected.
- GAME_NAME.s="UDP_Test"
-
- INCLUDE "TCPFuncs.bb"
- INCLUDE "Net_Protocol_Header.bb2" ; Net protocol constants
- DEFTYPE .w
-
- packet_number.l=0
- online=0
- my_number=0
- ;********************************************************
-
- 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
- End NEWTYPE
-
- ;*******************************************************
- .Dims
-
- Dim messages.message_status(100)
- Dim host.sockaddrin(8), hostlen.l(8)
- Dim players.player_info(8)
-
- DEFTYPE .sockaddrin temphost
- ;*******************************************************
-
- .Print_String
- 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
- Function.w Get_Packet_Source{}
- SHARED host(),temphost
- i=0
- Repeat
- 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
- i=-1
- exit=1
- EndIf
- Until exit=1
-
- Function Return i
- End Function
-
- .WriteUDP
- Statement WriteUDP{ad.l,size.w,player}
- SHARED sock.l,host(),hostlen()
-
- ; This routine writes data via UDP
-
- sockwrite.l=0 ;Clear Writemask
- sockwrite.l BitSet sock.l ;set Writemask on our socket
- g=WaitSelect_(2,0,&sockwrite.l,0,0,0) ;Wait until server is ready to read our data
- c=sendto_(sock.l,ad,size,0,host(player),hostlen(player)) ;Send data to server
-
- End Statement
-
- .Send_String_Message
- Statement Send_String_Message{send_string.s,player}
- SHARED packet_number,messages(),free_message,last_message_number,REL_STRING_END.s
-
- send$=REL_STRING_END.s + Mkl$(packet_number) + send_string.s + Chr$(0)
- un
- messages(free_message)\number=packet_number,False,Ticks,send$,0,player
- If messages(last_message_number)\ack=True
- last_message_number=free_message
- EndIf
-
- WriteUDP{&send$,Len(send$),player}
- packet_number+1
- free_message+1
- If free_message=101 Then free_message=0
-
- End Statement
-
- .ReadUDP
- Function .s ReadUDP{}
- SHARED sock.l,TCPmem.l,temphost,temphostlen
-
- ; This Function reads data from the socket it is bound to.
- ; If there is no messages then it will return an empty string =""
-
- sockread.l=0 ;Clear Readmask
- sockread.l BitSet sock.l ;Set Readmask on our socket
- e=IoctlSocket_(sock.l,#FIONREAD,TCPmem.l) ;How much data is there?
- f.l=Peek.l(TCPmem.l) ;Place value in f
-
- If f>0 ; any data waiting
- temphostlen=SizeOf .sockaddrin
- c=recvfrom_(sock.l,TCPmem.l,f,0,temphost,temphostlen) ;Read all data waiting on socket
- ;and get host it was sent from in temphost
- If c>0 ; if there any data waiting
- a=0 : c$=""
- Repeat
- c$=c$+Chr$(Peek.b(TCPmem+a)) : a+1 ; build string
- Until a=c
- EndIf
-
- EndIf
-
- Function Return c$
- End Function
-
- .Get_Host
- Function Get_Host{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
-
- .Localhost_Name
- 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
-
- .ConnectUDP
- Function ConnectUDP{host$,port.w}
- SHARED sock.l,host(),hostlen(),port_used,bsd_library_address.l
- ;
- ; Open UDP socket and bind it to a port number
- ; Return true or False if successfull or not
- ;
-
- bsd_library_address.l=OpenLibrary_("bsdsocket.library",0)
- If bsd_library_address=0
- Function Return False
- Else
- sock.l=socket_(2,2,0) ; open UDP socket
- If sock=-1
- Function Return False
- Else
- *a.hostent=gethostbyname_(host$) ; set up destination address to send packets to
- If *a.hostent=0
- Function Return False
- EndIf
- inY=WBHeight
- ;Copy Details to our Sockaddrin structure
-
- bb=CopyMem_(*a.hostent\h_addr_list\ItemA,&host(0)\sin_addr,*a.hostent\h_length)
-
- host(0)\sin_port=port ;Set port number
- host(0)\sin_family=2 ;Set type to AT_INET
- hostlen(0)=SizeOf.sockaddrin ;Get length of structure sockaddrin
-
- temp=0
- port_used=port
- Repeat
- host(0)\sin_port=port_used ; for testing on localhost- keep trying one port at a time after #LOCALPORT for
- If bind_(sock,host(0),hostlen(0))=0 ; bind socket to port so we can free port
- Function Return True ; receive data on port
- Else
- port_used+1
- temp+1
- EndIf
- Until temp=10
- EndIf
- EndIf
- End Function
-
- .Resend_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?
- 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()
-
- 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 142,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
- Function.s Requested_Connection{incoming_string.s}
- SHARED sock,players(),host(),hostlen(),GAME_NAME,temphost,temphostlen,online,CP_REP_ACCEPT.s,CP_REP_REJECT.s
-
- 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 "
- dummy=1
- exit=0
- Repeat ;check if a spare player slot is availiable
- dummy+1
- If players(dummy)\status=0
- exit=1
- Else
- If dummy=8
- exit=1
- dummy=-1
- EndIf
- EndIf
- Until exit=1
-
- If dummy>0 ; connection accepted
-
- players(dummy)\status=1
- *a.hostent=gethostbyaddr_(&temphost\sin_addr\s_addr,4,2) ; check wether host exists- we're not being shammed
-
- If *a.hostent=0
- Function Return Str$(temphost\sin_addr\s_addr) + "Failed"
- EndIf
-
- bb=CopyMem_(*a.hostent\h_addr_list\ItemA,&host(dummy)\sin_addr,*a.hostent\h_length) ; copy details to player's host
- ; newtype, host()
- host(dummy)\sin_port=temphost\sin_port ;Set port number
- host(dummy)\sin_family=2 ;Set type to AT_INET
- hostlen(dummy)=SizeOf.sockaddrin ;Get length of structure sockaddrin
-
- string_address.l=Inet_NtoA_(host(dummy)\sin_addr\s_addr) ; get ASCII string version of host address (a.b.c.d)
- 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
- a$=a$+temp$ + " Port: " + Str$(host(dummy)\sin_port)
- players(dummy)\ascii_host_string=temp$ ; store it
-
- send$=CP_REP_ACCEPT.s + Mki$(host(0)\sin_port)+Chr$(dummy) ; send connection accepted back
- WriteUDP{&send$,Len(send$),dummy}
- 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 + a$ ; send connection rejected back with reason
- sockwrite.l=0 ;Clear Writemask
- sockwrite.l BitSet sock.l ;set Writemask on our socket
- g=WaitSelect_(2,0,&sockwrite.l,0,0,0) ;Wait until server is ready to read our data
- c=sendto_(sock.l,&send$,Len(send$),0,temphost,temphostlen) ;Send data to server
-
- Function Return a$
- End Function
-
- .Decode_Packet
- Statement Decode_Packet{incoming_string.s}
- SHARED players(),host(),GAME_NAME.s,temphost,temphostlen,online,REL_PACKET_ACK.s,CP_REP_ACCEPT.s
- 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
-
- Select packet_type
- Case #CONTROL_PACKET
- packet_type=Asc(Mid$(incoming_string,2))
- Select packet_type
- Case #CP_REQ_CONNECT
- a$=Requested_Connection{incoming_string}
- packet_not_connected=1
- Case #CP_REP_ACCEPT
- online=2
- my_number=Val(Left$(incoming_string,1))
- players(1)\status=1
- a$="Connection Accepted"
- Case #CP_REP_REJECT
- a$=Mid$(incoming_string,3)
- End Select
-
- Case #REL_STRING_END
- num$=Mid$(incoming_string,2,4)
- packet_number.l=Cvl(num$)
- a$=Str$(packet_number) + ": " + Mid$(incoming_string,6,Len(incoming_string)-6)
- send$=REL_PACKET_ACK.s + Mkl$(packet_number)
- WriteUDP{&send$,Len(send$),player} ; send packet acknowledgement back
-
- Case #REL_PACKET_ACK
- packet_number.l=Cvl(Mid$(incoming_string,2,4))
- a$=": Packet no. " + Str$(packet_number) + " arrived at destination."
- Acknowledge_Packet{packet_number}
-
- End Select
-
- If player<0 AND packet_not_connected=0
- Print_String{"Warning: Packet from unknown source!"}
- Statement Return
- EndIf
-
- a$="R "+a$
- Print_String{a$}
- End Statement
-
- ;****************************************
-
-
- Gosub Init_Gui
-
- ypos=40
- xpos=10
- free_message=0
- messages(free_message)\ack=True
- last_message_ack=0
-
- CNIF #NO_CONNECTION=1 ; if not connected to the `net
-
- If ConnectUDP {Hostname,#LOCALPORT} ; bind socket to local port
- Print_String {"Bound to "+Hostname+" "+ Str$(port_used)}
- WLocate 510,4
- Print "Bound to: ",port_used
- Else
- Print_String { "Error in setting up UDP at Port "+ Str$(port_used)}
- Goto Exit
- EndIf
-
- If Get_Host {Hostname,#PORT,0} :EndIf
-
- CELSE
- If ConnectUDP {Localhost_Name{},#LOCALPORT} ; bind socket to local port
- Print_String {"Bound to "+Hostname+" "+ Str$(port_used)}
- WLocate 510,4
- Print "Bound to: ",port_used
- Else
- Print_String { "Error in setting up UDP at Port "+ Str$(port_used)}
- Goto Exit
- EndIf
-
- If Get_Host {Localhost_Name{},#PORT,0} :EndIf
- CEND
-
- ResetTimer
-
- ;****************************************
-
- .Main
-
- Repeat
-
- VWait
-
- If ev.l=$40
- Select GadgetHit
- Case51 ; Server to connect to
- If online=0
- If Get_Host{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}
- EndIf
- Else
- If online=2
- Print_String{"Already logged on to a Server!"}
- Else
- Print_String{"Already acting as a Server!"}
- EndIf
- EndIf
- Case52 ; Port to connect at above Server
- If Get_Host{GTGetString(0,51), GTGetInteger(0,52),1} :EndIf
- Case53 ; Delay 1s
- VWait 50
- Case54 ; get name of localhost
- a$=Localhost_Name {} ;
- WLocate 315+pingwidth+15,hostnamey+3
- Print a$
- Case63 ; Delay 5s
- VWait 2000 ; delay 40s
- Case64 ; Send message to connected players
- If online>0
- send$=GTGetString(0,64)
- Print_String{"S " +Str$(packet_number) +": "+ send$}
- For i=1 To 8
- If players(i)\status=1 AND i<>my_number
- Send_String_Message{send$,i} ; Send string to connected players
- EndIf
- Next
- EndIf
- End Select
- EndIf
-
- a$=ReadUDP{} ; get data from socket
-
- If a$<>""
- Decode_Packet{a$}
- 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{message_number} ; wipe message off message acknowledge array
- EndIf
- EndIf
- EndIf
-
- ev=Event
-
- Until ev=$200 ; shut down if close gadget hit
-
- CloseTCP{} ; close connection
-
- dummy.l=CloseLibrary_(bsd_library_address) ; close bsdsocket.library
- ; or else Miami can't exit
- Exit:
- WLocate 10,18
- Print " "
- WLocate 10,18
- Print"Socket closed"
- Delay_(30)
-
- FreeMem TCPmem,$2000
-
- End
-
- ;****************************************
-
- .Init_Gui
- WBenchToFront_
- 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 5s",$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
-
- 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 Send"
- ;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"
-
- Return
-
-
-