Description: The Pegasus Network Library is C++ library for making networked games. Pegasus is based on Medusa and it is a part of PLIB. Pegasus, like Medusa, multiplexes I/O with its various client and server connections within a single process/thread. Pegasus is supported on many platforms, including Win32, Linux, and a majority of Unix implementations.
Keywords: NET, PLIB, Pegasus, network, library, portable, games, medusa, client, server, socket, TCP, IP, netAddress, netBuffer, netMessage, netGuid, netSocket, netChannel, netBufferChannel, netChat, netMessageChannel, netMonitorServer

# Pegasus Network Library

## Introduction

Pegasus is a C++ library for making networked games. Pegasus is based on Medusa and is part of PLIB. Pegasus, like Medusa, runs as a single process, multiplexing I/O with its various client and server connections within a single process/thread. Pegasus is supported on any platform that includes a socket implementation with the select() function. This includes Win32, Linux, and a majority of Unix implementations.

This document assumes a certain degree of knowledge of TCP/IP and socket programming.

To use the NET library, you'll need to '#include ' and link to libplibnet.a

## Symbol Conventions.

Pegasus follows the same conventions for symbols and tokens that are used by OpenGL and GLUT. All Pegasus symbols for classes and functions start with `net` and all `#define` tokens start with `NET`. Words within a class or function name are Capitalised and NOT separated with underscores. Words within `#define` tokens may be separated with underscores to make them readable.

## Initialisation.

The first NET call in any program must always be netInit().

## Classes

The following class hierarchy makes up the core package - which can be extended to add functionality or to change some underlying mechanisms.

       class netAddress

       class netBuffer
        |__ class netMessage

       class netGuid

       class netSocket
        |__ class netChannel
             |__ class netBufferChannel
             |    |__ class netChat
             |    |__ class netMessageChannel
             |
             |__ class netMonitorServer

## `class netBuffer`

A simple buffer class.

    class netBuffer
    {
    public:
      netBuffer ( int max_length ) ;
      ~netBuffer () ;

      int getLength() const ;
      int getMaxLength() const ;
      char* getData() ;

      void remove () ;
      void remove (int pos, int n) ;
      bool append (const char* s, int n) ;
      bool append (int n) ;
    } ;

## `class netMessage`

A message buffer used to transfer binary data and handle byte swapping.

    class netMessage : public netBuffer
    {
    public:

      netMessage ( const char* s, int n ) ;
      netMessage ( int type, int to_id, int from_id ) ;

      int getType () const ;
      int getToID () const ;
      int getFromID () const ;

      void geta ( void* a, int n ) const ;  // a=array; array should already be serialized
      void puta ( const void* a, int n ) ;

      int getch () const ;
      void putch ( int c ) ;

      bool getb () const ;
      void putb ( bool b ) ;

      int getw () const ;
      void putw ( int i ) ;

      int geti () const ;
      void puti ( int i ) ;

      void gets ( char* s, int n ) const ;
      void puts ( const char* s ) ;

      void print ( FILE *fd = stderr ) const ;
    };

## `class netAddress`

This is the representation of an Internet-style machine address.

    class netAddress
    {
    public:
      netAddress () ;
      netAddress ( const char* host, int port ) ;

      void set ( const char* host, int port ) ;

      const char* getHost () const ;
      int         getPort () const ;

      static const char* getLocalHost () ;

      bool getBroadcast () const ;
    } ;

When reading from the network using one of the higher level classes, you can use a netAddress with an empty string as the 'host' to mean "accept data from any host". When writing to the network, the reserved host name '<broadcast>' (the < and > are literally there) will cause the message to be broadcast to all machines on your subnet.

Otherwise, use either the machines' network name - or it's IP address (as an ASCII string).

## `class netSocket`

netSocket is the low-level socket class.

    class netSocket
    {
    public:
      netSocket () ;
      virtual ~netSocket () ;

      int getHandle () const ;
      void setHandle (int handle) ;

      bool  open ( bool stream=true ) ;
      int   bind ( cchar* host, int port ) ;
      int   listen ( int backlog ) ;
      int   accept ( netAddress* addr ) ;
      int   connect ( cchar* host, int port ) ;
      int   send ( const void * buffer, int size, int flags = 0 ) ;
      int   sendto ( const void * buffer, int size, int flags, const netAddress* to ) ;
      int   recv ( void * buffer, int size, int flags = 0 ) ;
      int   recvfrom ( void * buffer, int size, int flags, netAddress* from ) ;
      void  close ( void ) ;
      void  setBlocking ( bool blocking ) ;
    } ;

### Example:

This example is stripped of error checking for clarity:

#### Sender:
      netInit () ;

      netSocket *sock = new netSocket () ;
      sock -> open        ( false ) ;
      sock -> setBlocking ( false ) ;
      sock -> connect     ( host, port ) ;

      while ( !done )
        sock -> send ( msg, len, 0 );

      sock -> close () ;
| #### Reciever:
      netInit () ;

      netSocket *sock = new netSocket () ;
      sock -> open        ( false ) ;
      sock -> setBlocking ( false ) ;
      sock -> bind        ( host, port ) ;

      while ( !done )
        if ( (len = sock -> recv(msg, maxlen, 0)) >= 0 )
          ...use the data...

      sock -> close () ;

This code produces a 'Datagram' connection - which is fast but unreliable (using UDP protocol). Passing a 'true' to sock->open() would produce a 'Stream' connection (using TCP).

## `class netChannel`

netChannel adds event-handling to the low-level netSocket class. Otherwise, it can be treated as a normal non-blocking socket object. The direct interface between the poll loop and the channel object are the handleReadEvent and handleWriteEvent methods. These are called whenever a channel object 'fires' that event. The firing of these low-level events can tell us whether certain higher-level events have taken place, depending on the timing and state of the connection.

    class netChannel : public netSocket
    {
    public:

      netChannel () ;
      virtual ~netChannel () ;

      void setHandle (int s, bool is_connected = true);
      bool isConnected () const ;
      bool isClosed () const ;
      void shouldDelete () ;

      // --------------------------------------------------
      // socket methods
      // --------------------------------------------------

      bool  open        ( bool stream=true ) ;
      int   listen	    ( int backlog ) ;
      int   connect     ( cchar* host, int port ) ;
      int   send        ( const void * buf, int size, int flags = 0 ) ;
      int   recv        ( void * buf, int size, int flags = 0 ) ;
      void  close       ( void ) ;

      // poll() eligibility predicates
      virtual bool readable (void) ;
      virtual bool writable (void) ;

      // --------------------------------------------------
      // event handlers
      // --------------------------------------------------

      void handleReadEvent (void);
      void handleWriteEvent (void);

      // These are meant to be overridden.
      virtual void handleConnect (void) ;
      virtual void handleRead (void) ;
      virtual void handleWrite (void) ;
      virtual void handleClose (void) ;
      virtual void handleAccept (void) ;
      virtual void handleError (int error) ;

      static bool poll (u32 timeout = 0 ) ;
      static void loop (u32 timeout = 0 ) ;
    };

## `class netBufferChannel`

netBufferChannel is a netChannel with I/O buffering. Clients and servers built on top of netBufferChannel automatically support pipelining where you can send multiple commands without waiting for the response to each command before you send the next.

    class netBufferChannel : public netChannel
    {
    public:
      netBufferChannel (int in_buffer_size = 512, int out_buffer_size = 4096) ;
      void closeWhenDone (void) ;

      virtual bool bufferSend (const char* msg, int msg_len) ;
      virtual void handleBufferRead (netBuffer& buffer) ;
    };

## `class netChat`

netChat adds support for 'chat' style protocols - where one side sends a 'command', and the other sends a response (examples would be the common internet protocols - smtp, nntp, ftp, etc..).

The handle_buffer_read() method looks at the input stream for the current 'terminator' (usually '\r\n' for single-line responses, '\r\n.\r\n' for multi-line

    class netChat : public netBufferChannel
    {
    public:
      netChat () ;

      void setTerminator (const char* t);
      const char* getTerminator (void);

      bool push (const char* s) ;

      virtual void collectIncomingData (const char* s, int n) ;
      virtual void foundTerminator (void) ;
    };

## `class netMessageChannel`

A channel for binary messages. Takes care of packing and unpacking them in/out of the buffers.

    class netMessageChannel : public netBufferChannel
    {
    public:
      netMessageChannel () ;

      bool sendMessage ( const netMessage& msg ) ;
      virtual void handleMessage ( const netMessage& msg ) ;
    };

## `class netMonitorServer`

The monitor server gives you remote, 'back-door' access to your server while it is running. netMonitor is a telnet command port with password authorization. It can be paired with and used to remotely admin another server. Once connected via any telnet client to the monitor, you can issue commands. The monitor can be hooked up to a python interpreter for added power. Since an ordinary telnet session is not secure, the password could be intercepted, but that level of security is usually not needed for games.

    class netMonitorServer : public netChannel
    {
    public:
      netMonitorServer( cchar* _name, int port ) ;
      ~netMonitorServer() ;

      cchar* getPassword () const ;
      void setPassword ( cchar* string ) ;
      void setPrompt ( cchar* string ) ;
      void setCommandFunc ( void (*func)(cchar*, netMonitorChannel*) ) ;
    } ;