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.
Programming - a skill for life!

by Max Foster: L6 Age ~16