AppNetworking Code
The code of the SuperMaximo SDL AppEngine
/////////////////////////////////////////////////////////////////////////////// /// /// /// SUPERMAXIMO APP ENGINE: AppNetworking Unit /// /// by Max Foster /// /// /// /////////////////////////////////////////////////////////////////////////////// { Copyright (c) 2011 Max Licensed under the Apache License, Version 2.0 (the 'License'); you may not use this file except in compliance with the License, as described at http://www.apache.org/licenses/ and http://www.pp4s.co.uk/licenses/ } { This unit is part of the SuperMaximo App Engine(c). It initialises and closes SDL_NET, without needing to actually include the SDL_NET unit itself in the main program code. It provides functions that allow the programmer to code networking into their program quickly and easily. Data can be sent by both TCP and UDP connections, which makes this unit ideal for all sorts of programs, from chat clients (which require the security of TCP) to games (which require the high speed of UDP). Please have a look at all of the functions to see just how flexible this unit is! } unit AppNetworking; {$mode objfpc}{$H+} interface uses Classes, SysUtils, SDL_NET, AppInput; //Initalises AppNetworking. Returns true on success function InitAppNetworking : boolean; //Quits AppNetworking procedure QuitAppNetworking; //Returns true if the server has been started. A server and a client can be run simultaneously function ServerStarted : boolean; //Returns true if the client has been started. A server and a client can be run simultaneously function ClientStarted : boolean; {Starts the server. The maximum amount of clients that can connect is specified by newmaxsockets. The port for the server to listen on is specified by newport. If the server is unable to start on the port specified, it attempts to start on the next port up from the port specified. It is a good idea to not use shownotices as it writelns any server success messages if true. (It is used by the function when attempting to restart the server after a failure). Returns true on success} function StartServer(newmaxsockets : integer; newport : longint; shownotices : boolean = true) : boolean; //Stops the server procedure CloseServer; //Restarts the server by stopping the server and starting it again with the same amount of sockets // specified when calling StartServer. Returns true on success. function RestartServer : boolean; //Returns the latest address of the server that can be used by clients to connect function GetNewLocalAddress : longword; //Returns the address of the server that can be used by clients to connect function GetLocalAddress : longword; {Checks if there is a new client, and if there is, accepts it if there is room. Returns the ID of the client so that the server can communicate with it. Give 'false' as an argument if you would not like to use UDP connections (so only TCP will be used).} function CheckForNewClient(useudp : boolean = true) : integer; //Checks if a client with the ID num exists. Returns true if there is a client with a matching ID function ClientExists(num : integer) : boolean; //Returns the amount of clients that are connected to the server function TotalClients : integer; //Kicks a client from the server procedure KickClient(num : integer); //Starts the client. The IP address to connect to is specified by newaddress. Use 'localhost' //(or '127.0.0.1') for LAN connections. Returns true on success. function StartClient(newaddress:pchar; newport:longint):boolean; //Stops the client procedure CloseClient; //Restarts the client by stopping the client and starting it again with the IP address specified //when calling StartClient. Returns true on success. function RestartClient:boolean; {Attempts to connect the client to a server on the IP address specified by StartClient. Returns true on success. Give 'false' as an argument if you would not like to use UDP connections (so only TCP will be used).} function ConnectToServer(useudp : boolean = true) : boolean; //Returns the ID of the client function ClientNumber : integer; {Sends a string of a maximum of 128 characters via TCP. Num is used by the server to specify what client to send the data to. Num does not need to be passed for a client. Returns true on success. Change the STR_BUFFER_SIZE constant in the AppNetworking unit to change the maximum string length. Set isserver to true if the server is sending the message. 'size' is the maximum size of the string being sent (but does not need to be set)} function SendStrTCP(data:string; num:integer = 0; isserver:boolean = false; size:integer = 0):boolean; {Returns a string received from client ID num. For a client, num does not need to be passed. 'DISCONNECTED' is returned if the connection was lost. In this circumstance, the server should kick the client, and the client should close itself. Set isserver to true if the server is receiving the message. 'size' is the maximum size of the string being received (but does not need to be set)} function RecvStrTCP(num : integer = 0; isserver : boolean = false; size : integer = 0) : string; {Sends an integer via TCP. Num is used by the server to specify what client to send the data to. Num does not need to be passed for a client. Returns true on success. Set isserver to true if the server is sending the message.} function SendIntTCP(data : integer; num : integer = 0; isserver : boolean = false) : boolean; {Returns an integer received from client ID num. For a client, num does not need to be passed. -1 is returned if the connection was lost. In this circumstance, the server should kick the client, and the client should close itself. Set isserver to true if the server is receiving the message.} function RecvIntTCP(num : integer = 0; isserver : boolean = false) : integer; {Sends an integer via UDP. Num is used by the server to specify what client to send the data to. Num does not need to be passed for a client. Returns true on success. NOTE: in a UDP connection, packets sent are not guaranteed to be received! Set isserver to true if the server is sending the message.} function SendIntUDP(data : integer; num : integer = 0; isserver : boolean = false) : boolean; {Returns an integer received by UDP. This function does not return the source of any packets received. If -1 is returned, it is not an indication that the connection has been lost. Use TCP to check whether the connection has been lost. Set isserver to true if the server is receiving the message.} function RecvIntUDP(isserver : boolean = false) : integer; {Puts an integer received by UDP and the ID of the sender into an array of length 2 passed as an argument. The data is put into the 0th index and the ID into the 1st index. If -1 is returned, it is not an indication that the connection has been lost. Use TCP to check whether the connection has been lost. Set isserver to true if the server is receiving the message.} procedure RecvIntIdUDP(var recvarray : array of integer; isserver : boolean = false); {Sends a character via UDP. Num is used by the server to specify what client to send the data to. Num does not need to be passed for a client. Returns true on success. NOTE: in a UDP connection, packets sent are not guaranteed to be received!} function SendChrUDP(data : char; num : integer = 0; isserver : boolean = false) : boolean; {Returns a character received by UDP. This function does not return the source of any packets received. If '#' is returned, it is not an indication that the connection has been lost. Use TCP to check whether the connection has been lost. Set isserver to true if the server is receiving the message.} function RecvChrUDP(isserver : boolean = false) : char; {Puts a character received by UDP and the ID of the sender into an array of length 2 passed as an argument. The data is put into the 0th index and the ID into the 1st index. If '#' is returned, it is not an indication that the connection has been lost. Use TCP to check whether the connection has been lost. Set isserver to true if the server is receiving the message.} procedure RecvChrIdUDP(var recvarray : array of char; isserver : boolean = false); {Sends a string via UDP, and returns true on success. Data is the string to send, and num is the client number, which does not need to be set for clients. Set isserver to true if the server is sending the message.} function SendStrUDP(data:string; num : integer = 0; isserver : boolean = false) : boolean; //Receives a string via UDP, and returns it. Set isserver to true if the server is sending the message. function RecvStrUDP(isserver : boolean = false) : string; {Receives a string via UDP. 'strtouse' is the string variable that the new string is assigned to, and 'idtouse' is the integer variable that the ID of the client that the message has been received from is assigned to. Set isserver to true if the server is sending the message.} procedure RecvStrIdUDP(var strtouse : string; var idtouse : integer; isserver : boolean = false); implementation const STR_BUFFER_SIZE = 128; //This is the absolute maximum length of a string being sent STR_SIZE = 32; INT_SIZE = 8; CHR_SIZE = 1; WAIT_TIME = 10; var serverstart, clientstart : boolean; serverport, clientport : longint; serverip, clientip : tIPADDRESS; clientaddress : pchar; servertcpsocketset, serverudpsocketset, clienttcpsocketset, clientudpsocketset : pSDLNET_SOCKETSET; servertcpsockets, clienttcpsockets : array of pTCPSOCKET; servertcpsocket : pTCPSOCKET; serverudpsocket, clientudpsocket : pUDPSOCKET; serverudpaddresses, clientudpaddresses : array of pIPADDRESS; recvstrbuffer, sendstrbuffer : string[STR_BUFFER_SIZE]; recvintbuffer, sendintbuffer, clientid, maxsockets : integer; recvintpacket, sendintpacket, recvchrpacket, sendchrpacket, recvstrpacket, sendstrpacket : pUDPPACKET; localresolveip : tIPADDRESS; localhostname : pchar; localaddress : longword; function InitAppNetworking : boolean; begin serverstart := false; clientstart := false; if SDLNET_INIT = 0 then Result := true else begin writeln(SDLNET_GETERROR()); Result := false; end; end; procedure QuitAppNetworking; begin SDLNET_QUIT; end; function ServerStarted : boolean; begin Result := serverstart; end; function ClientStarted : boolean; begin Result := clientstart; end; function StartServer(newmaxsockets : integer; newport : longint; shownotices : boolean = true) : boolean; var i:integer; begin Result := true; maxsockets := newmaxsockets; serverport := newport; SetLength(servertcpsockets, maxsockets); SetLength(serverudpaddresses, maxsockets); for i := 0 to maxsockets do begin servertcpsockets[i] := nil; serverudpaddresses[i] := nil; end; writeln('Attempting to start server on port ', serverport); servertcpsocketset := SDLNET_ALLOCSOCKETSET(maxsockets + 2); serverudpsocketset := SDLNET_ALLOCSOCKETSET(maxsockets + 1); if (servertcpsocketset = nil) or (serverudpsocketset = nil) then begin writeln('Socketsets could not be initiated'); writeln(SDLNET_GETERROR()); SDLNET_FREESOCKETSET(servertcpsocketset); SDLNET_FREESOCKETSET(serverudpsocketset); Result := false; end else begin if SDLNET_RESOLVEHOST(serverip, nil, serverport) = -1 then begin writeln('Host could not be resolved for initiating server'); writeln(SDLNET_GETERROR()); SDLNET_FREESOCKETSET(servertcpsocketset); SDLNET_FREESOCKETSET(serverudpsocketset); Result := false; end else begin servertcpsocket := SDLNET_TCP_OPEN(serverip); serverudpsocket := SDLNET_UDP_OPEN(serverport); if (servertcpsocket = nil) or (serverudpsocket = nil) then begin writeln('TCP or UDP sockets could not be opened (', SDLNET_GETERROR(), ')'); CloseServer; Result := StartServer(newmaxsockets, serverport + 1, false); end else begin if SDLNET_TCP_ADDSOCKET(servertcpsocketset, servertcpsocket) = -1 then begin writeln('Server TCP socket could not be added to TCP socketset'); writeln(SDLNET_GETERROR()); CloseServer; end else begin if SDLNET_UDP_ADDSOCKET(serverudpsocketset, serverudpsocket) = -1 then begin writeln('Server UDP socket could not be added to UDP socketset'); writeln(SDLNET_GETERROR()); CloseServer; end; end; end; end; end; if Result then begin if shownotices then begin writeln('Server started successfully'); writeln('Listening on port ', serverport); end; serverstart := true; end; end; procedure CloseServer; var i : integer; begin serverstart := false; for i := 0 to maxsockets do begin if ClientExists(i) then KickClient(i); end; if servertcpsocket <> nil then SDLNET_TCP_CLOSE(servertcpsocket); if serverudpsocket <> nil then SDLNET_UDP_CLOSE(serverudpsocket); if servertcpsocketset <> nil then SDLNET_FREESOCKETSET(servertcpsocketset); if serverudpsocketset <> nil then SDLNET_FREESOCKETSET(serverudpsocketset); writeln('Server shut down'); end; function RestartServer:boolean; begin CloseServer; Result := StartServer(maxsockets, serverport); end; function CheckForNewClient(useudp : boolean = true) : integer; var i : integer; udpintarray : array[0..1] of integer; quit : boolean; begin Result := -1; if SDLNET_CHECKSOCKETS(servertcpsocketset, 0) > 0 then begin if SDLNET_SOCKETREADY(pSDLNET_GENERICSOCKET(servertcpsocket)) then begin for i := 0 to maxsockets - 1 do begin if servertcpsockets[i] = nil then begin servertcpsockets[i] := SDLNET_TCP_ACCEPT(servertcpsocket); if servertcpsockets[i] <> nil then begin if SDLNET_TCP_ADDSOCKET(servertcpsocketset, servertcpsockets[i]) = -1 then begin writeln('Client TCP socket could not be added to TCP socketset.'); writeln('(The socket may be nil or the socketset may be full).'); SDLNET_TCP_CLOSE(servertcpsockets[i]); servertcpsockets[i] := nil; end else Result := i; end; break; end else begin if i = maxsockets - 1 then begin writeln('No free TCP sockets'); servertcpsockets[i + 1] := SDLNET_TCP_ACCEPT(servertcpsocket); SDLNET_TCP_ADDSOCKET(servertcpsocketset, servertcpsockets[i + 1]); repeat until SendStrTCP('DISCONNECTED', i + 1, true); KickClient(i + 1); end; end; end; end; end; if not useudp then exit; if Result > -1 then begin quit := false; repeat if RecvStrTCP(Result, true) = 'DISCONNECTED' then begin KickClient(Result); Result := -1; exit; end; writeln('Waiting for message via UDP'); RecvIntIdUDP(udpintarray, true); if AppKeyPressed(27) then quit := true; until ((udpintarray[0] = 123) and (udpintarray[1] < 0)) or quit; if quit then begin Result := -1; exit; end; NEW(serverudpaddresses[Result]); serverudpaddresses[Result]^ := recvintpacket^.address; repeat if AppKeyPressed(27) then quit := true; until SendStrTCP('CAPTURED', Result, true) or quit; if quit then begin Result := -1; exit; end; repeat if SendIntUDP(123, Result, true) then writeln('Sending message via UDP'); RecvStrTCP(Result, true); if AppKeyPressed(27) then recvstrbuffer := 'DISCONNECTED'; if recvstrbuffer = 'DISCONNECTED' then break; until recvstrbuffer = 'CONNECTED'; if recvstrbuffer = 'DISCONNECTED' then begin KickClient(Result); Result := -1; end else begin repeat until SendIntTCP(Result + 100, Result, true); writeln('Client ', Result, ' has connected!'); if SDLNET_UDP_BIND(serverudpsocket, Result, (serverudpaddresses[Result])^) < 0 then KickClient(Result); end; end; end; function GetNewLocalAddress : longword; begin localhostname := SDLNET_RESOLVEIP(localresolveip); SDLNET_RESOLVEHOST(localresolveip, localhostname, 1234); localaddress := localresolveip.host; Result := SDLNET_READ32(@localaddress); end; function GetLocalAddress : longword; begin if localaddress = 0 then GetNewLocalAddress; Result := SDLNET_READ32(@localaddress) end; function ClientExists(num : integer) : boolean; begin if servertcpsockets[num] = nil then Result := false else Result := true; end; function TotalClients : integer; var i : integer; begin Result := 0; for i := 0 to Length(servertcpsockets) - 1 do begin if servertcpsockets[i] <> nil then Result += 1; end; end; procedure KickClient(num : integer); var i : integer; sockpresent : boolean; begin sockpresent := false; if servertcpsockets[num] <> nil then begin SDLNET_TCP_DELSOCKET(servertcpsocketset, servertcpsockets[num]); sockpresent := true; end; if servertcpsockets[num] <> nil then begin SDLNET_TCP_CLOSE(servertcpsockets[num]); sockpresent := true; end; servertcpsockets[num] := nil; DISPOSE(serverudpaddresses[num]); SDLNET_UDP_UNBIND(serverudpsocket, num); if (num <> maxsockets) and sockpresent then writeln('Client ', num, ' disconnected'); end; function StartClient(newaddress : pchar; newport : longint) : boolean; begin Result := true; clientstart := true; clientid := -1; clientaddress := newaddress; clientport := newport; SetLength(clienttcpsockets, 1); SetLength(clientudpaddresses, 1); clienttcpsocketset := SDLNET_ALLOCSOCKETSET(1); clientudpsocketset := SDLNET_ALLOCSOCKETSET(1); if (clienttcpsocketset = nil) or (clientudpsocketset = nil) then begin Result := false; Writeln('Could not initialise socketsets'); CloseClient; end else begin if SDLNET_RESOLVEHOST(clientip, clientaddress, clientport) = -1 then begin Result := false; Writeln('Could not resolve host'); CloseClient; end; end; end; procedure CloseClient; begin clientstart := false; if clientudpaddresses[0] <> nil then DISPOSE(clientudpaddresses[0]); clientudpaddresses[0] := nil; if clienttcpsockets[0] <> nil then begin SDLNET_TCP_CLOSE(clienttcpsockets[0]); writeln('Closing TCP connection to server'); end; clienttcpsockets[0] := nil; if clientudpsocket <> nil then begin SDLNET_UDP_CLOSE(clientudpsocket); writeln('Closing UDP connections on port ', clientport); end; clientudpsocket := nil; if clienttcpsocketset <> nil then SDLNET_FREESOCKETSET(clienttcpsocketset); clienttcpsocketset := nil; if clientudpsocketset <> nil then SDLNET_FREESOCKETSET(clientudpsocketset); clientudpsocketset := nil; writeln('Client shut down'); end; function RestartClient : boolean; begin CloseClient; Result := StartClient(clientaddress, clientport); end; function ConnectToServer(useudp : boolean = true) : boolean; var udpintarray : array[0 .. 1] of integer; begin Result := false; clienttcpsockets[0] := SDLNET_TCP_OPEN(clientip); if clienttcpsockets[0] <> nil then begin clientudpsocket := SDLNET_UDP_OPEN(0); if clientudpsocket <> nil then begin SDLNET_TCP_ADDSOCKET(clienttcpsocketset, clienttcpsockets[0]); SDLNET_UDP_ADDSOCKET(clientudpsocketset, clientudpsocket); NEW(clientudpaddresses[0]); clientudpaddresses[0]^ := clientip; Result := true; end else writeln('UDP socket with server could not be opened'); end else writeln('TCP socket with server could not be opened'); if not useudp then exit; if Result then begin Result := false; repeat if SendIntUDP(123) then writeln('Sending message via UDP'); RecvStrTCP; if AppKeyPressed(27) then recvstrbuffer := 'DISCONNECTED'; if recvstrbuffer = 'DISCONNECTED' then break; until (recvstrbuffer = 'CAPTURED'); if recvstrbuffer = 'DISCONNECTED' then CloseClient else begin repeat writeln('Waiting...'); RecvStrTCP; if AppKeyPressed(27) then recvstrbuffer := 'DISCONNECTED'; if recvstrbuffer = 'DISCONNECTED' then break; RecvIntIdUDP(udpintarray); until (udpintarray[0] = 123) and (udpintarray[1] = -1); if recvstrbuffer <> 'DISCONNECTED' then begin repeat writeln('Sending CONNECTED via TCP'); if AppKeyPressed(27) then begin CloseClient; exit; end; until SendStrTCP('CONNECTED'); writeln('Waiting...'); repeat if AppKeyPressed(27) then begin CloseClient; exit; end; writeln('Waiting for client ID allocation via TCP'); clientid := RecvIntTCP; writeln('"', clientid, '" received from server'); if clientid < 100 then SendStrTCP('NEEDID'); until (clientid = -1) or (clientid > 0); if clientid > 0 then begin clientid -= 100; writeln('Connected to server!'); writeln('ID for this client is ', clientid); SDLNET_UDP_BIND(clientudpsocket, clientid, clientudpaddresses[0]^); Result := true; end else CloseClient; end else CloseClient; end; end; end; function ClientNumber : integer; begin Result := clientid; end; function SendStrTCP(data : string; num : integer = 0; isserver : boolean = false; size : integer = 0) : boolean; var sock : pTCPSOCKET; begin if size < 1 then size := STR_SIZE; Result := true; if isserver then SDLNET_CHECKSOCKETS(servertcpsocketset, WAIT_TIME) else SDLNET_CHECKSOCKETS(clienttcpsocketset, WAIT_TIME); if num >= 0 then begin if isserver then sock := servertcpsockets[num] else sock := clienttcpsockets[num]; sendstrbuffer := data; if sock <> nil then begin if not SDLNET_SOCKETREADY(pSDLNET_GENERICSOCKET(sock)) then begin if SDLNET_TCP_SEND(sock, @sendstrbuffer, size) < size then Result := false; end; end else Result := false; end else Result := false; end; function RecvStrTCP(num : integer = 0; isserver : boolean = false; size : integer = 0) : string; var i : integer; sock : pTCPSOCKET; begin if size < 1 then size := STR_SIZE; if isserver then i := SDLNET_CHECKSOCKETS(servertcpsocketset, WAIT_TIME) else i := SDLNET_CHECKSOCKETS(clienttcpsocketset, WAIT_TIME); if i > -1 then begin if isserver then sock := servertcpsockets[num] else sock := clienttcpsockets[num]; if sock <> nil then begin if SDLNET_SOCKETREADY(pSDLNET_GENERICSOCKET(sock)) then begin if SDLNET_TCP_RECV(sock, @recvstrbuffer, size) < 1 then Result := 'DISCONNECTED' else Result := recvstrbuffer; end; end; end; if Result = 'NEEDID' then begin if SendIntTCP(num + 100, num, true) then writeln('Sending ID to a client') else begin {BUGFIX: Old code was: if Result = 'NEEDID' then begin if SendIntTCP(num, num, true) then writeln('Sending ID to a client'); end; There was a problem that occasionally occurred when a client was trying to connect to a server. The client requires an ID sent by the server, but sometimes may not receive it, so sends the message 'NEEDID'. The server will then attempt to send the ID to the client. In order for the server to send data to the client, there must be no messages waiting to be received. This 'receive buffer' was not flushed by the server and the server would never be able to send a message. The fix attempts to send a message, and if it fails, it will flush the buffer by receiving all the incoming data and then send the message to the client. } repeat until RecvStrTCP(num, isserver, size) = ''; if SendIntTCP(num + 100, num, true) then writeln('Sending ID to a client'); end; Result := ''; end; end; function SendIntTCP(data : integer; num : integer = 0; isserver : boolean = false) : boolean; var sock : pTCPSOCKET; begin Result := true; if isserver then SDLNET_CHECKSOCKETS(servertcpsocketset, WAIT_TIME) else SDLNET_CHECKSOCKETS(clienttcpsocketset, WAIT_TIME); if num >= 0 then begin sendintbuffer := data; if isserver then sock := servertcpsockets[num] else sock := clienttcpsockets[num]; if sock <> nil then begin if not SDLNET_SOCKETREADY(pSDLNET_GENERICSOCKET(sock)) then begin if SDLNET_TCP_SEND(sock, @sendintbuffer, INT_SIZE) < INT_SIZE then Result := false; end; end else Result := false; end else Result := false; end; function RecvIntTCP(num : integer = 0; isserver : boolean = false) : integer; var i : integer; sock : pTCPSOCKET; begin if isserver then i := SDLNET_CHECKSOCKETS(servertcpsocketset, WAIT_TIME) else i := SDLNET_CHECKSOCKETS(clienttcpsocketset, WAIT_TIME); if i > -1 then begin if isserver then sock := servertcpsockets[num] else sock := clienttcpsockets[num]; if sock <> nil then begin if SDLNET_SOCKETREADY(pSDLNET_GENERICSOCKET(sock)) then begin if SDLNET_TCP_RECV(sock, @recvintbuffer, INT_SIZE) < 1 then Result := -1 else Result := recvintbuffer; end; end; end; end; function SendIntUDP(data : integer; num : integer = 0; isserver : boolean = false) : boolean; var sock : pUDPSOCKET; addr : ^tIPADDRESS; sendid : integer; begin Result := true; if isserver then SDLNET_CHECKSOCKETS(serverudpsocketset, WAIT_TIME) else SDLNET_CHECKSOCKETS(clientudpsocketset, WAIT_TIME); if num >= 0 then begin if isserver then sock := serverudpsocket else sock := clientudpsocket; if not SDLNET_SOCKETREADY(pSDLNET_GENERICSOCKET(sock)) then begin if isserver then addr := serverudpaddresses[num] else addr := clientudpaddresses[num]; sendintpacket := SDLNET_ALLOCPACKET(INT_SIZE); sendintpacket^.address := addr^; sendintpacket^.data := @data; sendintpacket^.len := INT_SIZE; sendintpacket^.maxlen := INT_SIZE; if isserver then sendid := -1 else sendid := clientid; if SDLNET_UDP_SEND(sock, sendid, sendintpacket) = 0 then Result := false; end else begin RecvIntUDP(isserver); Result := SendIntUDP(data, num, isserver); end; end else Result := false; end; function RecvIntUDP(isserver : boolean = false) : integer; var i : integer; sock : pUDPSOCKET; begin if isserver then i := SDLNET_CHECKSOCKETS(serverudpsocketset, WAIT_TIME) else i := SDLNET_CHECKSOCKETS(clientudpsocketset, WAIT_TIME); if i > -1 then begin if isserver then sock := serverudpsocket else sock := clientudpsocket; if SDLNET_SOCKETREADY(pSDLNET_GENERICSOCKET(sock)) then begin recvintpacket := SDLNET_ALLOCPACKET(INT_SIZE); if SDLNET_UDP_RECV(sock, recvintpacket) = -1 then Result := -1 else begin Result := Integer(recvintpacket^.data^); end; SDLNET_FREEPACKET(recvintpacket); end; end; end; procedure RecvIntIdUDP(var recvarray : array of integer; isserver : boolean = false); var i : integer; sock : pUDPSOCKET; begin recvarray[0] := -1; recvarray[1] := -2; //-1 is used for setting up a connection and >= 0 is for sending packets once connected, so < -1 is for an error if isserver then i := SDLNET_CHECKSOCKETS(serverudpsocketset, WAIT_TIME) else i := SDLNET_CHECKSOCKETS(clientudpsocketset, WAIT_TIME); if i > -1 then begin if isserver then sock := serverudpsocket else sock := clientudpsocket; if SDLNET_SOCKETREADY(pSDLNET_GENERICSOCKET(sock)) then begin recvintpacket := SDLNET_ALLOCPACKET(INT_SIZE); if SDLNET_UDP_RECV(sock, recvintpacket) <> -1 then begin recvarray[0] := Integer(recvintpacket^.data^); recvarray[1] := recvintpacket^.channel; end; SDLNET_FREEPACKET(recvintpacket); end; end; end; function SendChrUDP(data : char; num : integer = 0; isserver : boolean = false) : boolean; var sock : pUDPSOCKET; addr : ^tIPADDRESS; sendid : integer; begin Result := true; if isserver then SDLNET_CHECKSOCKETS(serverudpsocketset, WAIT_TIME) else SDLNET_CHECKSOCKETS(clientudpsocketset, WAIT_TIME); if num >= 0 then begin if isserver then sock := serverudpsocket else sock := clientudpsocket; if not SDLNET_SOCKETREADY(pSDLNET_GENERICSOCKET(sock)) then begin if isserver then addr := serverudpaddresses[num] else addr := clientudpaddresses[num]; sendchrpacket := SDLNET_ALLOCPACKET(CHR_SIZE); sendchrpacket^.address := addr^; sendchrpacket^.data := Pointer(String(data)); sendchrpacket^.len := CHR_SIZE; sendchrpacket^.maxlen := CHR_SIZE; if isserver then sendid := -1 else sendid := clientid; if SDLNET_UDP_SEND(sock, sendid, sendchrpacket) = 0 then Result := false; end else begin RecvChrUDP(isserver); Result := SendChrUDP(data, num); end; end else Result := false; end; function RecvChrUDP(isserver : boolean = false) : char; var i : integer; sock : pUDPSOCKET; begin if isserver then i := SDLNET_CHECKSOCKETS(serverudpsocketset, WAIT_TIME) else i := SDLNET_CHECKSOCKETS(clientudpsocketset, WAIT_TIME); if i > -1 then begin if isserver then sock := serverudpsocket else sock := clientudpsocket; if SDLNET_SOCKETREADY(pSDLNET_GENERICSOCKET(sock)) then begin recvchrpacket := SDLNET_ALLOCPACKET(CHR_SIZE); if SDLNET_UDP_RECV(sock, recvchrpacket) = -1 then Result := '#' else begin Result := Chr(recvchrpacket^.data^); end; SDLNET_FREEPACKET(recvchrpacket); end; end; end; procedure RecvChrIdUDP(var recvarray : array of char; isserver : boolean = false); var i : integer; sock : pUDPSOCKET; begin recvarray[0] := '#'; recvarray[1] := '#'; if isserver then i := SDLNET_CHECKSOCKETS(serverudpsocketset, WAIT_TIME) else i := SDLNET_CHECKSOCKETS(clientudpsocketset, WAIT_TIME); if i > -1 then begin if isserver then sock := serverudpsocket else sock := clientudpsocket; if SDLNET_SOCKETREADY(pSDLNET_GENERICSOCKET(sock)) then begin recvchrpacket := SDLNET_ALLOCPACKET(CHR_SIZE); if SDLNET_UDP_RECV(sock, recvchrpacket) <> -1 then begin recvarray[0] := Chr(recvchrpacket^.data^); recvarray[1] := Chr(recvchrpacket^.channel); //Need to convert back to integer unless you want to reference sender by a char end; SDLNET_FREEPACKET(recvchrpacket); end; end; end; function SendStrUDP(data : string; num : integer = 0; isserver : boolean = false) : boolean; var sock : pUDPSOCKET; addr : ^tIPADDRESS; sendid : integer; begin Result := true; if isserver then SDLNET_CHECKSOCKETS(serverudpsocketset, WAIT_TIME) else SDLNET_CHECKSOCKETS(clientudpsocketset, WAIT_TIME); if num >= 0 then begin if isserver then sock := serverudpsocket else sock := clientudpsocket; if not SDLNET_SOCKETREADY(pSDLNET_GENERICSOCKET(sock)) then begin if isserver then addr := serverudpaddresses[num] else addr := clientudpaddresses[num]; sendstrpacket := SDLNET_ALLOCPACKET(Length(data)); sendstrpacket^.address := addr^; sendstrpacket^.data := Pointer(data); sendstrpacket^.len := Length(data); sendstrpacket^.maxlen := Length(data); if isserver then sendid := -1 else sendid := clientid; if SDLNET_UDP_SEND(sock, sendid, sendstrpacket) = 0 then Result := false; end else begin RecvStrUDP(isserver); Result := SendStrUDP(data, num); end; end else Result := false; end; function RecvStrUDP(isserver : boolean = false) : string; var i : integer; sock : pUDPSOCKET; begin Result := ''; if isserver then i := SDLNET_CHECKSOCKETS(serverudpsocketset, WAIT_TIME) else i := SDLNET_CHECKSOCKETS(clientudpsocketset, WAIT_TIME); if i > -1 then begin if isserver then sock := serverudpsocket else sock := clientudpsocket; if SDLNET_SOCKETREADY(pSDLNET_GENERICSOCKET(sock)) then begin recvstrpacket := SDLNET_ALLOCPACKET(STR_SIZE); if SDLNET_UDP_RECV(sock, recvstrpacket) = -1 then Result := 'ERROR' else begin for i := 0 to recvstrpacket^.len - 1 do Result += char(recvstrpacket^.data[i]); end; SDLNET_FREEPACKET(recvstrpacket); end; end; end; procedure RecvStrIdUDP(var strtouse : string; var idtouse : integer; isserver : boolean = false); var i : integer; sock : pUDPSOCKET; begin strtouse := 'ERROR'; idtouse := -1; if isserver then i := SDLNET_CHECKSOCKETS(serverudpsocketset, WAIT_TIME) else i := SDLNET_CHECKSOCKETS(clientudpsocketset, WAIT_TIME); if i > -1 then begin if isserver then sock := serverudpsocket else sock := clientudpsocket; if SDLNET_SOCKETREADY(pSDLNET_GENERICSOCKET(sock)) then begin recvstrpacket := SDLNET_ALLOCPACKET(STR_SIZE); if SDLNET_UDP_RECV(sock, recvstrpacket) <> -1 then begin strtouse := ''; for i := 0 to recvstrpacket^.len-1 do strtouse += char(recvstrpacket^.data[i]); idtouse := recvstrpacket^.channel; end; SDLNET_FREEPACKET(recvstrpacket); end; end; end; end.