]> git.mxchange.org Git - flightgear.git/commitdiff
Add multiplayer support from Duncan McCreanor and Diarmuid Tyson
authorehofman <ehofman>
Wed, 19 Mar 2003 20:45:09 +0000 (20:45 +0000)
committerehofman <ehofman>
Wed, 19 Mar 2003 20:45:09 +0000 (20:45 +0000)
23 files changed:
configure.ac
docs-mini/README.multiplayer [new file with mode: 0644]
src/Main/Makefile.am
src/Main/fg_init.cxx
src/Main/fg_io.cxx
src/Main/globals.hxx
src/Main/main.cxx
src/Main/options.cxx
src/Makefile.am
src/Model/model.cxx
src/Model/model.hxx
src/MultiPlayer/.cvsignore [new file with mode: 0644]
src/MultiPlayer/Makefile.am [new file with mode: 0644]
src/MultiPlayer/mpmessages.hxx [new file with mode: 0644]
src/MultiPlayer/mpplayer.cxx [new file with mode: 0644]
src/MultiPlayer/mpplayer.hxx [new file with mode: 0644]
src/MultiPlayer/multiplayrxmgr.cxx [new file with mode: 0644]
src/MultiPlayer/multiplayrxmgr.hxx [new file with mode: 0644]
src/MultiPlayer/multiplaytxmgr.cxx [new file with mode: 0644]
src/MultiPlayer/multiplaytxmgr.hxx [new file with mode: 0644]
src/Network/Makefile.am
src/Network/multiplay.cxx [new file with mode: 0644]
src/Network/multiplay.hxx [new file with mode: 0644]

index dd2a5b8977471ec7837ca374351b5572ee614897..7bbdbe8af8331fe7db3e81ef8e345f65c6f1b2ba 100644 (file)
@@ -57,16 +57,30 @@ if test "x$with_logging" = "xno" ; then
     AC_DEFINE([FG_NDEBUG], 1, [Define for no logging output])
 fi
 
+# Specify if we want to build with default Multiplayer support
+# default to with_network=yes
+AC_ARG_WITH(multiplayer, [  --with-multiplayer      Include default multiplayer support])
+if test "x$with_multiplayer" = "xno" ; then
+    echo "Building without default multiplayer support"
+else
+    echo "Building with default multiplayer support"
+    AC_DEFINE([FG_MPLAYER_AS], 1, [Define to build with default multiplayer support])
+fi
+AM_CONDITIONAL(ENABLE_MPLAYER_AS, test "x$with_multiplayer" != "xno")
+
+
 # Specify if we want to build with Oliver's networking support
 # default to with_network=yes
-AC_ARG_WITH(network_olk, [  --with-network-olk      Include Oliver's multi-pilot networking support])
+NETWORK_DIRS=Network
+AC_ARG_WITH(network_olk, [  --with-network-olk      Include Oliver's multi-pilot networking support [default=no]])
 if test "x$with_network_olk" = "xno" ; then
     echo "Building without Oliver's multi-pilot network support"
 else
     echo "Building with Oliver's multi-pilot network support"
     AC_DEFINE([FG_NETWORK_OLK], 1, [Define to build with Oliver's networking])
 fi
-AM_CONDITIONAL(ENABLE_NETWORK_OLK, test "x$with_network_olk" != "xno")
+AM_CONDITIONAL(ENABLE_NETWORK_OLK, test "x$with_network_olk" != "xno" -a "x$with_multiplayer" = "xno")
+
 
 
 # Specify if we want to use WeatherCM instead of FGEnvironment.
@@ -82,7 +96,7 @@ fi
 AM_CONDITIONAL(ENABLE_WEATHERCM, test "x$with_weathercm" = "xyes")
 
 dnl Specify if we want the old menubar; default to the new one
-AC_ARG_WITH(old-menubar, [  --with-old-menubar        Use the old menu bar])
+AC_ARG_WITH(old-menubar, [  --with-old-menubar      Use the old menu bar])
 if test "x$with_old_menubar" = "xyes" ; then
     echo "Building with old menubar"
     AC_DEFINE([FG_OLD_MENUBAR], 1,
@@ -90,7 +104,6 @@ if test "x$with_old_menubar" = "xyes" ; then
 else
     echo "Building with new menubar"
 fi
-AM_CONDITIONAL(ENABLE_WEATHERCM, test "x$with_weathercm" = "xyes")
 
 dnl Thread related checks
 AC_ARG_WITH(threads, [  --with-threads          Include tile loading threads [default=no]])
@@ -590,6 +603,7 @@ AC_CONFIG_FILES([ \
        src/Main/runfgfs \
        src/Main/runfgfs.bat \
        src/Model/Makefile \
+       src/MultiPlayer/Makefile \
        src/Navaids/Makefile \
        src/Network/Makefile \
        src/NetworkOLK/Makefile \
@@ -639,6 +653,12 @@ else
     echo "Using FGEnvironment"
 fi
 
+if test "x$with_multiplayer" != "xno"; then
+    echo "Using default multiplayer support"
+elif test "x$with_network_olk" != "xno"; then
+    echo "Using Oliver's multi-pilot network support"
+fi
+
 if test "x$with_old_menubar" != "x"; then
     echo "Using old menubar"
 else
diff --git a/docs-mini/README.multiplayer b/docs-mini/README.multiplayer
new file mode 100644 (file)
index 0000000..ee081a1
--- /dev/null
@@ -0,0 +1,72 @@
+The commands are of the form:
+
+--multiplay=in | out,Hz,destination address,destination port
+--callsign=a_unique_name
+
+
+Below are some examples of startup commands that demonstrate the use of the 
+multiplayer facilities.
+
+For two players on a local network or across the internet:
+----------------------------------------------------------
+Player1:
+--multiplay=out,10,192.168.0.3,5500 --multiplay=in,10,192.168.0.2,5501 
+--callsign=player1
+
+Player2:
+--multiplay=out,10,192.168.0.2,5501 --multiplay=in,10,192.168.0.3,5500 
+--callsign=player2
+
+
+For multiple players on a local network:
+----------------------------------------
+Player1:
+--multiplay=out,10,255.255.255.255,5500 
+--multiplay=in,10,255.255.255.255,5500 --callsign=player1
+
+Playern:
+--multiplay=out,10,255.255.255.255,5500 
+--multiplay=in,10,255.255.255.255,5500 --callsign=playern
+
+Note that the callsign is used to identify each player in a multiplayer game 
+so the callsigns must be unique. The multiplayer code ignores packets that 
+are sent back to itself, as would occur with broadcasting when the rx and tx 
+ports are the same.
+
+
+Multiple players sending to a single player:
+--------------------------------------------
+Player1:
+--multiplay=out,10,192.168.0.2,5500 --callsign=player1
+
+Player2:
+--multiplay=out,10,192.168.0.2,5500 --callsign=player2
+
+Player3:
+--multiplay=out,10,192.168.0.2,5500 --callsign=player3
+
+Player4 (rx only):
+--multiplay=in,10,192.168.0.2,5500 --callsign=player4
+
+This demonstrates that it is possible to have multiple instances of 
+Flightgear that send to a single instance that displays all the traffic. This 
+is the sort of implementation that we are considering for use as a tower 
+visual simulator.
+
+
+For use with a server (when one is created):
+--------------------------------------------
+Player1:
+--multiplay=out,10,serveraddress,6000 --multiplay=in,10,myaddress,5500 
+--callsign=player1
+
+Player2:
+--multiplay=out,10,serveraddress,6000 --multiplay=in,10,myaddress,5501 
+--callsign=player2
+
+Playern:
+--multiplay=out,10,serveraddress,6000 --multiplay=in,10,myaddress,5502 
+--callsign=playern
+
+The server would simply act as a packet forwarding mechanism. When it 
+receives a packet, it sends it to all other active players.
index bdca16c8d97ffe3a74f40f2e72356e78a09b8d44..81e7d4623b4d0ccc3f55807ce478067ff316c92f 100644 (file)
@@ -73,6 +73,7 @@ fgfs_LDADD = \
        $(top_builddir)/src/Input/libInput.a \
        $(top_builddir)/src/Instrumentation/libInstrumentation.a \
        $(top_builddir)/src/Model/libModel.a \
+       $(top_builddir)/src/MultiPlayer/libMultiPlayer.a \
        $(top_builddir)/src/Navaids/libNavaids.a \
        $(top_builddir)/src/Scenery/libScenery.a \
         $(SCRIPTING_LIBS) \
index 363a0590a6cd388799be1b6675dbe190fcef5494..0731cc05c8435f92285d955589763f84a1f2cf54 100644 (file)
 #include <Time/moonpos.hxx>
 #include <Time/tmp.hxx>
 
+#ifdef FG_MPLAYER_AS
+#include <MultiPlayer/multiplaytxmgr.hxx>
+#include <MultiPlayer/multiplayrxmgr.hxx>
+#endif
+
 #ifdef FG_WEATHERCM
 #  include <WeatherCM/FGLocalWeatherDatabase.h>
 #else
@@ -249,7 +254,7 @@ bool fgInitFGRoot ( int argc, char **argv ) {
             root = fgScanForOption( "--fg-root=", config.str() );
         }
     }
-    
+
     // Next check if fg-root is set as an env variable
     if ( root.empty() ) {
         envp = ::getenv( "FG_ROOT" );
@@ -951,7 +956,7 @@ static void fgSetDistOrAltFromGlideSlope() {
         SG_LOG( SG_GENERAL, SG_ALERT, "Resetting glideslope to zero" );
         fgSetDouble("/sim/presets/glideslope-deg", 0);
         fgSetBool("/sim/presets/onground", true);
-    }                              
+    }
 }                       
 
 
@@ -1716,7 +1721,7 @@ bool fgInitSubsystems() {
         current_panel->bind();
     }
 
-    
+
     ////////////////////////////////////////////////////////////////////
     // Initialize the default (kludged) properties.
     ////////////////////////////////////////////////////////////////////
@@ -1747,6 +1752,18 @@ bool fgInitSubsystems() {
     globals->get_subsystem_mgr()->init();
 
 
+#ifdef FG_MPLAYER_AS
+    ////////////////////////////////////////////////////////////////////
+    // Initialize multiplayer subsystem
+    ////////////////////////////////////////////////////////////////////
+
+    globals->set_multiplayer_tx_mgr(new FGMultiplayTxMgr);
+    globals->get_multiplayer_tx_mgr()->init();
+
+    globals->set_multiplayer_rx_mgr(new FGMultiplayRxMgr);
+    globals->get_multiplayer_rx_mgr()->init();
+#endif
+
     ////////////////////////////////////////////////////////////////////////
     // End of subsystem initialization.
     ////////////////////////////////////////////////////////////////////
index 3dc66de136a608ec8b5356291178213d92409c40..9cac0c7d807c426e1bf179426a372da4854b4374 100644 (file)
 #include <Network/ray.hxx>
 #include <Network/rul.hxx>
 
+#ifdef FG_MPLAYER_AS
+#include <Network/multiplay.hxx>
+#endif
+
 #include "globals.hxx"
 #include "fg_io.hxx"
 
@@ -155,6 +159,17 @@ FGIO::parse_port_config( const string& config )
        } else if ( protocol == "rul" ) {
            FGRUL *rul = new FGRUL;
            io = rul;
+
+#ifdef FG_MPLAYER_AS
+       } else if ( protocol == "multiplay" ) {\
+           //Determine dir, rate, host & port
+           string dir = tokens[1];
+           string rate = tokens[2];
+           string host = tokens[3];
+           string port = tokens[4];
+           return new FGMultiplay(dir, atoi(rate.c_str()), host, atoi(port.c_str()));
+#endif
+
        } else {
            return NULL;
        }
@@ -204,7 +219,7 @@ FGIO::parse_port_config( const string& config )
        SG_LOG( SG_IO, SG_INFO, "  hostname = " << hostname );
        SG_LOG( SG_IO, SG_INFO, "  port = " << port );
        SG_LOG( SG_IO, SG_INFO, "  style = " << style );
-            
+
        io->set_io_channel( new SGSocket( hostname, port, style ) );
     }
 
@@ -217,7 +232,7 @@ FGIO::parse_port_config( const string& config )
 void
 FGIO::init()
 {
-    // SG_LOG( SG_IO, SG_INFO, "I/O Channel initialization, " << 
+    // SG_LOG( SG_IO, SG_INFO, "I/O Channel initialization, " <<
     //         globals->get_channel_options_list()->size() << " requests." );
 
     FGProtocol *p;
index 4c07ca2da51a34682cc4f5fa85a55afd4e36020e..a84d838352f51ae31ee8deb81982c0ff4efbc12c 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "fgfs.hxx"
 
+
 SG_USING_STD( vector );
 SG_USING_STD( string );
 
@@ -67,6 +68,10 @@ class FGIO;
 class FGModelLoader;
 class FGModelMgr;
 class FGScenery;
+#ifdef FG_MPLAYER_AS
+class FGMultiplayRxMgr;
+class FGMultiplayTxMgr;
+#endif
 class FGSoundMgr;
 class FGTextureLoader;
 class FGTileMgr;
@@ -170,6 +175,13 @@ private:
 
     FGIO* io;
 
+#ifdef FG_MPLAYER_AS
+    //Mulitplayer managers
+    FGMultiplayTxMgr *multiplayer_tx_mgr;
+
+    FGMultiplayRxMgr *multiplayer_rx_mgr;
+#endif
+
 public:
 
     FGGlobals();
@@ -236,8 +248,8 @@ public:
     inline void set_ATC_mgr( FGATCMgr *a ) {ATC_mgr = a; }
 
     inline FGATCDisplay *get_ATC_display() const { return ATC_display; }
-    inline void set_ATC_display( FGATCDisplay *d ) {ATC_display = d; } 
-    
+    inline void set_ATC_display( FGATCDisplay *d ) {ATC_display = d; }
+
     inline FGAIMgr *get_AI_mgr() const { return AI_mgr; }
     inline void set_AI_mgr( FGAIMgr *a ) {AI_mgr = a; }
 
@@ -285,6 +297,22 @@ public:
       model_mgr = mgr;
     }
 
+#ifdef FG_MPLAYER_AS
+    inline FGMultiplayTxMgr *get_multiplayer_tx_mgr () { return multiplayer_tx_mgr; }
+
+    inline void set_multiplayer_tx_mgr (FGMultiplayTxMgr * mgr)
+    {
+      multiplayer_tx_mgr = mgr;
+    }
+
+    inline FGMultiplayRxMgr *get_multiplayer_rx_mgr () { return multiplayer_rx_mgr; }
+
+    inline void set_multiplayer_rx_mgr (FGMultiplayRxMgr * mgr)
+    {
+      multiplayer_rx_mgr = mgr;
+    }
+#endif
+
     inline string_list *get_channel_options_list () {
        return channel_options_list;
     }
@@ -300,6 +328,7 @@ public:
 
     FGIO* get_io() const { return io; }
 
+
     /**
      * Save the current state as the initial state.
      */
index d154169beb7c96f809e96f89d79e470b65097cff..11c5c5a167703c0c60b732b16fcc4d3e4904ca40 100644 (file)
@@ -109,6 +109,12 @@ SG_USING_STD(endl);
 #ifdef FG_NETWORK_OLK
 #include <NetworkOLK/network.h>
 #endif
+
+#ifdef FG_MPLAYER_AS
+#include <MultiPlayer/multiplaytxmgr.hxx>
+#include <MultiPlayer/multiplayrxmgr.hxx>
+#endif
+
 #include <Objects/matlib.hxx>
 #include <Scenery/scenery.hxx>
 #include <Scenery/tilemgr.hxx>
@@ -433,7 +439,7 @@ void trRenderFrame( void ) {
 
 // Update all Visuals (redraws anything graphics related)
 void fgRenderFrame() {
-  
+
     GLfloat black[4] = { 0.0, 0.0, 0.0, 1.0 };
     GLfloat white[4] = { 1.0, 1.0, 1.0, 1.0 };
 
@@ -484,7 +490,7 @@ void fgRenderFrame() {
     // GLfloat terrain_color[4] = { 0.54, 0.44, 0.29, 1.0 };
     // GLfloat mat_shininess[] = { 10.0 };
     GLbitfield clear_mask;
-    
+
     if ( idle_state != 1000 ) {
        // still initializing, draw the splash screen
        if ( fgGetBool("/sim/startup/splash-screen") ) {
@@ -533,12 +539,12 @@ void fgRenderFrame() {
        if ( fgGetBool("/sim/rendering/skyblend") ) {
            if ( fgGetBool("/sim/rendering/textures") ) {
                // glClearColor(black[0], black[1], black[2], black[3]);
-               glClearColor(l->adj_fog_color[0], l->adj_fog_color[1], 
+               glClearColor(l->adj_fog_color[0], l->adj_fog_color[1],
                             l->adj_fog_color[2], l->adj_fog_color[3]);
                clear_mask |= GL_COLOR_BUFFER_BIT;
            }
        } else {
-           glClearColor(l->sky_color[0], l->sky_color[1], 
+           glClearColor(l->sky_color[0], l->sky_color[1],
                         l->sky_color[2], l->sky_color[3]);
            clear_mask |= GL_COLOR_BUFFER_BIT;
        }
@@ -591,7 +597,7 @@ void fgRenderFrame() {
                             globals->get_ephem()->getPlanets(),
                             globals->get_ephem()->getNumStars(),
                             globals->get_ephem()->getStars() );
+
            /* cout << "thesky->reposition( view_pos = " << view_pos[0] << " "
                 << view_pos[1] << " " << view_pos[2] << endl;
            cout << "    zero_elev = " << zero_elev[0] << " "
@@ -601,7 +607,7 @@ void fgRenderFrame() {
            cout << "    sun_rot = " << cur_light_params.sun_rotation
                 << " gst = " << SGTime::cur_time_params->getGst() << endl;
            cout << "    sun ra = " << globals->get_ephem()->getSunRightAscension()
-                << " sun dec = " << globals->get_ephem()->getSunDeclination() 
+                << " sun dec = " << globals->get_ephem()->getSunDeclination()
                 << " moon ra = " << globals->get_ephem()->getMoonRightAscension()
                 << " moon dec = " << globals->get_ephem()->getMoonDeclination() << endl; */
 
@@ -693,6 +699,11 @@ void fgRenderFrame() {
        }
 # endif
 
+#ifdef FG_MPLAYER_AS
+        // Update any multiplayer models
+        globals->get_multiplayer_rx_mgr()->Update();
+#endif
+
        if ( fgGetBool("/sim/rendering/skyblend") ) {
            // draw the sky backdrop
 
index c5a5b67ae3b40cf9b522a74e96e61f21ff1df0d4..379aef91949b2b35f29d879f84564f17e169de83 100644 (file)
@@ -82,7 +82,7 @@ static double
 atof( const string& str )
 {
 
-#ifdef __MWERKS__ 
+#ifdef __MWERKS__
     // -dw- if ::atof is called, then we get an infinite loop
     return std::atof( str.c_str() );
 #else
@@ -93,7 +93,7 @@ atof( const string& str )
 static int
 atoi( const string& str )
 {
-#ifdef __MWERKS__ 
+#ifdef __MWERKS__
     // -dw- if ::atoi is called, then we get an infinite loop
     return std::atoi( str.c_str() );
 #else
@@ -106,7 +106,7 @@ atoi( const string& str )
  * Set a few fail-safe default property values.
  *
  * These should all be set in $FG_ROOT/preferences.xml, but just
- * in case, we provide some initial sane values here. This method 
+ * in case, we provide some initial sane values here. This method
  * should be invoked *before* reading any init files.
  */
 void
@@ -224,6 +224,15 @@ fgSetDefaults ()
     fgSetBool("/sim/freeze/position", false);
     fgSetBool("/sim/freeze/clock", false);
     fgSetBool("/sim/freeze/fuel", false);
+
+#ifdef FG_MPLAYER_AS
+    fgSetString("/sim/multiplay/callsign", "callsign");
+    fgSetString("/sim/multiplay/rxhost", "0");
+    fgSetString("/sim/multiplay/txhost", "0");
+    fgSetInt("/sim/multiplay/rxport", 0);
+    fgSetInt("/sim/multiplay/txport", 0);
+#endif
+
 }
 
 
@@ -506,20 +515,22 @@ parse_fov( const string& arg ) {
 //  baud = {300, 1200, 2400, ..., 230400}
 //
 // Socket exacmple "--native=socket,dir,hz,machine,port,style" where
-// 
+//
 //  machine = machine name or ip address if client (leave empty if server)
 //  port = port, leave empty to let system choose
 //  style = tcp or udp
 //
 // File example "--garmin=file,dir,hz,filename" where
-// 
+//
 //  filename = file system file name
 
-static bool 
+static bool
 add_channel( const string& type, const string& channel_str ) {
-    // cout << "Channel string = " << channel_str << endl;
+    cout << "Channel string = " << channel_str << endl;
 
     globals->get_channel_options_list()->push_back( type + "," + channel_str );
+    
+    cout << "here" << endl;
 
     return true;
 }
@@ -1156,8 +1167,8 @@ struct OptionDesc {
 #endif
 
 // Parse a single option
-static int 
-parse_option (const string& arg) 
+static int
+parse_option (const string& arg)
 {
 #ifdef NEW_OPTION_PARSING
     if ( fgOptionMap.size() == 0 ) {
@@ -1287,7 +1298,7 @@ parse_option (const string& arg)
     } else if ( arg == "--enable-game-mode" ) {
        fgSetBool("/sim/startup/game-mode", true);
     } else if ( arg == "--disable-splash-screen" ) {
-       fgSetBool("/sim/startup/splash-screen", false); 
+       fgSetBool("/sim/startup/splash-screen", false);
     } else if ( arg == "--enable-splash-screen" ) {
         fgSetBool("/sim/startup/splash-screen", true);
     } else if ( arg == "--disable-intro-music" ) {
@@ -1559,6 +1570,10 @@ parse_option (const string& arg)
        add_channel( "atc610x", "dummy" );
     } else if ( arg.find( "--atlas=" ) == 0 ) {
        add_channel( "atlas", arg.substr(8) );
+
+    } else if ( arg.find( "--multiplay=" ) == 0 ) {
+       add_channel( "multiplay", arg.substr(12) );
+
     } else if ( arg.find( "--httpd=" ) == 0 ) {
        add_channel( "httpd", arg.substr(8) );
 #ifdef FG_JPEG_SERVER
@@ -1604,6 +1619,12 @@ parse_option (const string& arg)
     } else if ( arg.find( "--net-id=") == 0 ) {
         fgSetString("sim/networking/call-sign", arg.substr(9).c_str());
 #endif
+
+#ifdef FG_MPLAYER_AS
+    } else if ( arg.find( "--callsign=") == 0 ) {
+        fgSetString("sim/multiplay/callsign", arg.substr(11).c_str());
+#endif
+
     } else if ( arg.find( "--prop:" ) == 0 ) {
         string assign = arg.substr(7);
        string::size_type pos = assign.find('=');
@@ -1640,7 +1661,7 @@ parse_option (const string& arg)
        } else {
            default_view_offset = atof( woffset.c_str() ) * SGD_DEGREES_TO_RADIANS;
        }
-       /* apparently not used (CLO, 11 Jun 2002) 
+       /* apparently not used (CLO, 11 Jun 2002)
            FGViewer *pilot_view =
              (FGViewer *)globals->get_viewmgr()->get_view( 0 ); */
         // this will work without calls to the viewer...
index e820a20c0d7c7459018cd5cef52b4b34667b081d..ce31e6ebb9789f6ef74c93671d887becf4e05a27 100644 (file)
@@ -4,18 +4,24 @@ else
 WEATHER_DIR = Environment
 endif
 
-if ENABLE_NETWORK_OLK
-NETWORK_DIRS = Network NetworkOLK
-else
-NETWORK_DIRS = Network
-endif
-
 if HAVE_PLIB_PSL
 SCRIPTING_DIRS = Scripting
 else
 SCRIPTING_DIRS =
 endif
 
+if ENABLE_MPLAYER_AS
+MPLAYER_DIRS = MultiPlayer
+else
+MPLAYER_DIRS =
+endif
+
+if ENABLE_NETWORK_OLK
+NETWORK_DIRS = NetworkOLK
+else
+NETWORK_DIRS =
+endif
+
 SUBDIRS = \
         Include \
         Aircraft \
@@ -30,6 +36,8 @@ SUBDIRS = \
        Instrumentation \
         Model \
         Navaids \
+        Network \
+       $(MPLAYER_DIRS) \
         $(NETWORK_DIRS) \
         Objects \
         Scenery \
index 00773806e5d0ff90359812115550497734126954..8160ccf06fa6a64b820bb3bdd56797bd16d0ccc7 100644 (file)
@@ -649,9 +649,8 @@ FGModelPlacement::update ()
   _location->setPosition( _lon_deg, _lat_deg, _elev_ft );
   _location->setOrientation( _roll_deg, _pitch_deg, _heading_deg );
 
-  sgMat4 POS;
   sgCopyMat4(POS, _location->getTransformMatrix());
-  
+
   sgVec3 trans;
   sgCopyVec3(trans, _location->get_view_pos());
 
index 623d529f68121c9fe9847d340b7353e3e28fc452..313a5d9d3136fff7b9c473ba91f0d40a5bfa3a19 100644 (file)
@@ -228,7 +228,7 @@ private:
 };
 
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Model placement.
 ////////////////////////////////////////////////////////////////////////
@@ -271,9 +271,13 @@ public:
   virtual void setHeadingDeg (double heading_deg);
   virtual void setOrientation (double roll_deg, double pitch_deg,
                                double heading_deg);
+  
+  // Addition by Diarmuid Tyson for Multiplayer Support
+  // Allows multiplayer to get players position transform
+  virtual const sgVec4 *get_POS() { return POS; }
 
 private:
-  
+
                                 // Geodetic position
   double _lon_deg;
   double _lat_deg;
@@ -290,6 +294,12 @@ private:
                                 // Location
   FGLocation * _location;
 
+
+  // Addition by Diarmuid Tyson for Multiplayer Support
+  // Moved from update method
+  // POS for transformation Matrix
+  sgMat4 POS;
+
 };
 
 #endif // __MODEL_HXX
diff --git a/src/MultiPlayer/.cvsignore b/src/MultiPlayer/.cvsignore
new file mode 100644 (file)
index 0000000..4620540
--- /dev/null
@@ -0,0 +1,3 @@
+.deps
+Makefile.in
+Makefile
\ No newline at end of file
diff --git a/src/MultiPlayer/Makefile.am b/src/MultiPlayer/Makefile.am
new file mode 100644 (file)
index 0000000..a7b5a3e
--- /dev/null
@@ -0,0 +1,7 @@
+noinst_LIBRARIES = libMultiPlayer.a
+
+libMultiPlayer_a_SOURCES = multiplayrxmgr.cxx multiplayrxmgr.hxx multiplaytxmgr.cxx multiplaytxmgr.hxx mpplayer.cxx mpplayer.hxx mpmessages.hxx
+
+INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
+
+
diff --git a/src/MultiPlayer/mpmessages.hxx b/src/MultiPlayer/mpmessages.hxx
new file mode 100644 (file)
index 0000000..b29d2a9
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef MPMESSAGES_H
+#define MPMESSAGES_H
+
+#define MPMESSAGES_HID "$Id$"
+
+/****************************************************************
+* @version $Id$
+*
+* Description: Each message used for multiplayer communications
+* consists of a header and optionally a block of data. The combined
+* header and data is sent as one IP packet.
+*
+******************************************************************/
+
+#include <plib/sg.h>
+
+// Message identifiers
+#define CHAT_MSG_ID 1
+#define POS_DATA_ID 2
+
+#define MAX_CALLSIGN_LEN 10
+/** Header for use with all messages sent */
+typedef struct {
+
+    /** Message identifier */
+    char MsgId;
+
+    /** Length of the message inclusive of this header */
+    unsigned int iMsgLen;
+
+    /** IP address for reply to message (player's receiver address) */
+    unsigned long int lReplyAddress;
+
+    /** Port for replies (player's receiver port) */
+    unsigned int iReplyPort;
+
+    /** Callsign used by the player */
+    char sCallsign[MAX_CALLSIGN_LEN];
+
+} T_MsgHdr;
+
+#define MAX_CHAT_MSG_LEN 50
+/** Chat message */
+typedef struct {
+
+    /** Text of chat message */
+    char sText[MAX_CHAT_MSG_LEN];
+
+} T_ChatMsg;
+
+
+#define MAX_MODEL_NAME_LEN 50
+/** Aircraft position message */
+typedef struct {
+
+    /** Name of the aircraft model */
+    char sModel[MAX_MODEL_NAME_LEN];
+
+    /** Position data for the aircraft */
+    sgMat4 PlayerPos;
+
+} T_PositionMsg;
+
+
+
+#endif
+
+
diff --git a/src/MultiPlayer/mpplayer.cxx b/src/MultiPlayer/mpplayer.cxx
new file mode 100644 (file)
index 0000000..40ba5c5
--- /dev/null
@@ -0,0 +1,294 @@
+// mpplayer.cxx -- routines for a player within a multiplayer Flightgear
+//
+// Written by Duncan McCreanor, started February 2003.
+// duncan.mccreanor@airservicesaustralia.com
+//
+// Copyright (C) 2003  Airservices Australia
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+
+/******************************************************************
+* $Id$
+*
+* Description: Provides a container for a player in a multiplayer
+* game. The players network address, model, callsign and positoin
+* are held. When the player is created and open called, the player's
+* model is loaded onto the scene. The position transform matrix
+* is updated by calling SetPosition. When Draw is called the
+* elapsed time since the last update is checked. If the model
+* position information has been updated in the last TIME_TO_LIVE
+* seconds then the model position is updated on the scene.
+*
+******************************************************************/
+
+#include "mpplayer.hxx"
+
+#include <stdlib.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <plib/netSocket.h>
+
+#include <Main/globals.hxx>
+#include <Model/loader.hxx>
+#include <Scenery/scenery.hxx>
+
+
+// These constants are provided so that the ident command can list file versions.
+const char sMPPLAYER_BID[] = "$Id$";
+const char sMPPLAYER_HID[] = MPPLAYER_HID;
+
+
+/******************************************************************
+* Name: MPPlayer
+* Description: Constructor.
+******************************************************************/
+MPPlayer::MPPlayer() {
+
+    // Initialise private members
+    m_bInitialised = false;
+    m_LastUpdate = 0;
+    m_PlayerAddress.set("localhost", 0);
+
+
+}
+
+
+/******************************************************************
+* Name: ~MPPlayer
+* Description: Destructor.
+******************************************************************/
+MPPlayer::~MPPlayer() {
+
+    Close();
+
+}
+
+
+/******************************************************************
+* Name: Open
+* Description: Initialises class.
+******************************************************************/
+bool MPPlayer::Open(const string &sAddress, const int &iPort, const string &sCallsign, const string &sModelName, bool bLocalPlayer) {
+
+    bool bSuccess = true;
+
+    if (!m_bInitialised) {
+
+        m_PlayerAddress.set(sAddress.c_str(), iPort);
+        m_sCallsign = sCallsign;
+        m_sModelName = sModelName;
+        m_bLocalPlayer = bLocalPlayer;
+
+        // If the player is remote then load the model
+        if (!bLocalPlayer) {
+
+             LoadModel();
+
+        }
+
+        m_bInitialised = bSuccess;
+
+    } else {
+        cerr << "MPPlayer::Open - Attempt to open an already open player connection." << endl;
+        bSuccess = false;
+    }
+
+
+    /* Return true if open succeeds */
+    return bSuccess;
+
+}
+
+
+/******************************************************************
+* Name: Close
+* Description: Resets the object.
+******************************************************************/
+void MPPlayer::Close(void) {
+
+
+    // Remove the model from the game
+    if (!m_bLocalPlayer) {
+        globals->get_scenery()->get_scene_graph()->removeKid(m_ModelSel);
+    }
+
+    m_bInitialised = false;
+    m_bUpdated = false;
+    m_LastUpdate = 0;
+
+}
+
+
+/******************************************************************
+* Name: SetPosition
+* Description: Updates position data held for this player and resets
+* the last update time.
+******************************************************************/
+void MPPlayer::SetPosition(const sgMat4 PlayerPosMat4) {
+
+
+    // Save the position matrix and update time
+    if (m_bInitialised) {
+        memcpy(m_ModelPos, PlayerPosMat4, sizeof(sgMat4));
+        time(&m_LastUpdate);
+        m_bUpdated = true;
+    }
+
+
+}
+
+
+/******************************************************************
+* Name: Draw
+* Description: Updates the position for the player's model
+* The state of the player (old, initialised etc)
+* is returned.
+******************************************************************/
+int MPPlayer::Draw(void) {
+
+    int iResult = PLAYER_DATA_NOT_AVAILABLE;
+
+    sgCoord sgPlayerCoord;
+
+    if (m_bInitialised) {
+        if ((time(NULL) - m_LastUpdate < TIME_TO_LIVE)) {
+            // Peform an update if it has changed since the last update
+            if (m_bUpdated) {
+
+                // Transform and update player model
+                m_ModelSel->select(1);
+                sgSetCoord( &sgPlayerCoord, m_ModelPos);
+                m_ModelTrans->setTransform( &sgPlayerCoord );
+
+                iResult = PLAYER_DATA_AVAILABLE;
+
+                // Clear the updated flag so that the position data
+                // is only available if it has changed
+                m_bUpdated = false;
+            }
+
+        // Data has not been updated for some time.
+        } else {
+            iResult = PLAYER_DATA_EXPIRED;
+        }
+
+    }
+
+    return iResult;
+
+}
+
+
+/******************************************************************
+* Name: Callsign
+* Description: Returns the player's callsign.
+******************************************************************/
+string MPPlayer::Callsign(void) const {
+
+    return m_sCallsign;
+
+}
+
+
+/******************************************************************
+* Name: CompareCallsign
+* Description: Returns true if the player's callsign matches
+* the given callsign.
+******************************************************************/
+bool MPPlayer::CompareCallsign(const char *sCallsign) const {
+
+    return (m_sCallsign == sCallsign);
+
+}
+
+
+/******************************************************************
+* Name: LoadModel
+* Description: Loads the player's aircraft model.
+******************************************************************/
+void MPPlayer::LoadModel(void) {
+
+
+   m_ModelSel = new ssgSelector;
+   m_ModelTrans = new ssgTransform;
+
+   ssgEntity *Model = globals->get_model_loader()->load_model(m_sModelName);
+   Model->clrTraversalMaskBits( SSGTRAV_HOT );
+   m_ModelTrans->addKid( Model );
+   m_ModelSel->addKid( m_ModelTrans );
+   ssgFlatten( Model );
+   ssgStripify( m_ModelSel );
+
+   globals->get_scenery()->get_scene_graph()->addKid( m_ModelSel );
+   globals->get_scenery()->get_scene_graph()->addKid(  Model );
+
+
+}
+
+
+/******************************************************************
+* Name: FillPosMsg
+* Description: Populates the header and data for a position message.
+******************************************************************/
+void MPPlayer::FillPosMsg(T_MsgHdr *MsgHdr, T_PositionMsg *PosMsg) {
+
+    FillMsgHdr(MsgHdr, POS_DATA_ID);
+
+    strncpy(PosMsg->sModel, m_sModelName.c_str(), MAX_MODEL_NAME_LEN);
+    PosMsg->sModel[MAX_MODEL_NAME_LEN - 1] = '\0';
+
+    memcpy(PosMsg->PlayerPos, m_ModelPos, sizeof(sgMat4));
+
+
+}
+
+
+/******************************************************************
+* Name: FillMsgHdr
+* Description: Populates the header of a multiplayer message.
+******************************************************************/
+void MPPlayer::FillMsgHdr(T_MsgHdr *MsgHdr, const int iMsgId) {
+
+    struct in_addr address;
+
+    MsgHdr->MsgId = iMsgId;
+
+    switch (iMsgId) {
+        case CHAT_MSG_ID:
+            MsgHdr->iMsgLen = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
+            break;
+        case POS_DATA_ID:
+            MsgHdr->iMsgLen = sizeof(T_MsgHdr) + sizeof(T_PositionMsg);
+            break;
+        default:
+            MsgHdr->iMsgLen = sizeof(T_MsgHdr);
+            break;
+    }
+
+    inet_aton(m_PlayerAddress.getHost(), &address);
+    MsgHdr->lReplyAddress = address.s_addr;
+
+    MsgHdr->iReplyPort = m_PlayerAddress.getPort();
+
+    strncpy(MsgHdr->sCallsign, m_sCallsign.c_str(), MAX_CALLSIGN_LEN);
+    MsgHdr->sCallsign[MAX_CALLSIGN_LEN - 1] = '\0';
+
+
+}
+
diff --git a/src/MultiPlayer/mpplayer.hxx b/src/MultiPlayer/mpplayer.hxx
new file mode 100644 (file)
index 0000000..37a9d6e
--- /dev/null
@@ -0,0 +1,153 @@
+// mpplayer.hxx -- routines for a player within a multiplayer Flightgear
+//
+// Written by Duncan McCreanor, started February 2003.
+// duncan.mccreanor@airservicesaustralia.com
+//
+// Copyright (C) 2003  Airservices Australia
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+
+#ifndef MPPLAYER_H
+#define MPPLAYER_H
+
+#define MPPLAYER_HID "$Id$"
+
+/****************************************************************
+* @version $Id$
+*
+* Description:
+*
+******************************************************************/
+
+#include "mpmessages.hxx"
+
+#include <plib/ssg.h>
+#include <plib/sg.h>
+#include <plib/netSocket.h>
+#include <simgear/io/sg_socket_udp.hxx>
+#include <time.h>
+
+#include STL_STRING
+SG_USING_STD(string);
+
+// Number of seconds before a player is consider to be lost
+#define TIME_TO_LIVE 10
+
+#define PLAYER_DATA_NOT_AVAILABLE 0
+#define PLAYER_DATA_AVAILABLE 1
+#define PLAYER_DATA_EXPIRED 2
+
+class MPPlayer {
+public:
+
+    /** Constructor */
+    MPPlayer();
+
+    /** Destructor. */
+    ~MPPlayer();
+
+    /** Initialises the class.
+    * @param sIP IP address or host name for sending data to the player
+    * @param sPort Port number for sending data to the player
+    * @param sCallsign Callsign of the player (must be unique across all instances of MPPlayer).
+    * @param sModelName Path and name of the aircraft model file for the player
+    * @param bLocalPlayer True if this player is the local player, else false
+    * @return True if class opens successfully, else false
+    */
+    bool Open(const string &sIP, const int &iPort, const string &sCallsign,
+              const string &sModelName, const bool bLocalPlayer);
+
+    /** Initialises the player count for all instances of this object to zero. */
+    static void ResetPlayerCnt(void);
+
+    /** Closes the player connection */
+    void Close(void);
+
+    /** Sets the positioning matrix held for this player
+    * @param PlayerPosMat4 Matrix for positioning player's aircraft
+    */
+    void SetPosition(const sgMat4 PlayerPosMat4);
+
+    /** Transform and place model for player
+    */
+    int Draw(void);
+
+    /** Returns the callsign for the player
+    * @return Aircraft's callsign
+    */
+    string Callsign(void) const;
+
+    /** Compares the player's callsign with the given callsign
+    * @param sCallsign Callsign to compare
+    * @return True if callsign matches
+    */
+    bool CompareCallsign(const char *sCallsign) const;
+
+    /** Loads the model of the aircraft */
+    void LoadModel(void);
+
+    /** Populates a position message for the player
+    * @param MsgHdr Header to be populated
+    * @param PosMsg Position message to be populated
+    */
+    void FillPosMsg(T_MsgHdr *MsgHdr, T_PositionMsg *PosMsg);
+
+    /** Populates a mesage header with information for the player
+    * @param MsgHdr Header to be populated
+    * @param iMsgId Message type identifier to insert into header
+    */
+    void FillMsgHdr(T_MsgHdr *MsgHdr, const int iMsgId);
+
+
+private:
+
+    /** True if object is initialised */
+    bool m_bInitialised;
+
+    /** Position matrix for the player's aircraft */
+    sgMat4 m_ModelPos;
+
+    /** Used to remove player if no activity */
+    time_t m_LastUpdate;
+
+    /** Set when the player data is updated and cleared when read */
+    bool m_bUpdated;
+
+    /** Player's callsign */
+    string m_sCallsign;
+
+    /** Aircraft model for player */
+    string m_sModelName;
+
+    /** Simgear model selection */
+    ssgSelector *m_ModelSel;
+
+    /** Simgear model transform */
+    ssgTransform *m_ModelTrans;
+
+    /** True if this player is the local player */
+    bool m_bLocalPlayer;
+
+    /** Address information for the player */
+    netAddress m_PlayerAddress;
+
+};
+
+#endif
+
+
+
diff --git a/src/MultiPlayer/multiplayrxmgr.cxx b/src/MultiPlayer/multiplayrxmgr.cxx
new file mode 100644 (file)
index 0000000..7fc34a8
--- /dev/null
@@ -0,0 +1,346 @@
+// multiplayrxmgr.cxx -- routines for receiving multiplayer data
+//                       for Flightgear
+//
+// Written by Duncan McCreanor, started February 2003.
+// duncan.mccreanor@airservicesaustralia.com
+//
+// Copyright (C) 2003  Airservices Australia
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+
+/******************************************************************
+* $Id$
+*
+* Description: The multiplayer rx manager provides control over
+* multiplayer data reception and processing for an interactive
+* multiplayer FlightGear simulation.
+*
+* The objects that hold player information are accessed via
+* a fixed size array. A fixed array is used since it provides
+* speed benefits over working with linked lists and is easier
+* to code. Also, there is no point allowing for an unlimited
+* number of players as too many players will slow the game
+* down to the point where it is unplayable.
+*
+* When player position data is received, the callsign of
+* the player is checked against existing players. If the
+* player does not exist, a new player is created in the
+* next free slot of the player array. If the player does
+* exist, the player's positional matrix is updated.
+*
+* The Update method is used to move the players on the
+* scene. The return value from calling MPPlayer::Draw
+* indicates the state of the player. If data for a player
+* has not been received data for some time, the player object
+* is deleted and the array element freed.
+*
+******************************************************************/
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <plib/netSocket.h>
+#include <stdlib.h>
+#include <Main/fg_props.hxx>
+
+#include "multiplayrxmgr.hxx"
+#include "mpmessages.hxx"
+#include "mpplayer.hxx"
+
+#define MAX_PACKET_SIZE 1024
+
+// These constants are provided so that the ident command can list file versions.
+const char sMULTIPLAYTXMGR_BID[] = "$Id$";
+const char sMULTIPLAYRXMGR_HID[] = MULTIPLAYRXMGR_HID;
+
+
+
+/******************************************************************
+* Name: FGMultiplayRxMgr
+* Description: Constructor.
+******************************************************************/
+FGMultiplayRxMgr::FGMultiplayRxMgr() {
+
+    int iPlayerCnt;         // Count of players in player array
+
+    // Initialise private members
+    m_sRxAddress = "0";
+    m_iRxPort = 0;
+    m_bInitialised = false;
+
+    // Clear the player array
+    for (iPlayerCnt = 0; iPlayerCnt < MAX_PLAYERS; iPlayerCnt++) {
+        m_Player[iPlayerCnt] = NULL;
+    }
+
+}
+
+
+/******************************************************************
+* Name: ~FGMultiplayRxMgr
+* Description: Destructor. Closes and deletes objects owned by
+* this object.
+******************************************************************/
+FGMultiplayRxMgr::~FGMultiplayRxMgr() {
+
+    Close();
+
+}
+
+
+/******************************************************************
+* Name: init
+* Description: Initialises multiplayer receive.
+******************************************************************/
+bool FGMultiplayRxMgr::init(void) {
+
+    bool bSuccess = true;                       // Result of initialisation
+
+    // Initialise object if not already done
+    if (!m_bInitialised) {
+
+        // Set members from property values
+        m_sCallsign = fgGetString("/sim/multiplay/callsign");
+        m_sRxAddress = fgGetString("/sim/multiplay/rxhost");
+        m_iRxPort = fgGetInt("/sim/multiplay/rxport");
+
+        cout << "FGMultiplayRxMgr::init - rxaddress= " << m_sRxAddress << endl;
+        cout << "FGMultiplayRxMgr::init - rxport= " << m_iRxPort << endl;
+        cout << "FGMultiplayRxMgr::init - callsign= " << m_sCallsign << endl;
+
+        // Create and open rx socket
+        mDataRxSocket = new netSocket();
+        if (!mDataRxSocket->open(false)) {
+            // Failed to open rx socket
+            cerr << "FGMultiplayRxMgr::Open - Failed to create data receive socket" << endl;
+            bSuccess = false;
+        } else {
+
+            // Configure the socket
+            mDataRxSocket->setBlocking(false);
+            mDataRxSocket->setBroadcast(true);
+            if (mDataRxSocket->bind(m_sRxAddress.c_str(), m_iRxPort) != 0) {
+                perror("bind");
+                // Failed to bind
+                cerr << "FGMultiplayRxMgr::Open - Failed to bind receive socket" << endl;
+                bSuccess = false;
+            }
+
+        }
+
+        // Save manager state
+        m_bInitialised = bSuccess;
+
+    } else {
+        cerr << "FGMultiplayRxMgr::OpenRx - Receiver open requested when receiver is already open" << endl;
+        bSuccess = false;
+    }
+
+    /* Return true if open succeeds */
+    return bSuccess;
+
+}
+
+
+/******************************************************************
+* Name: Close
+* Description: Closes and deletes and player connections. Closes
+* and deletes the rx socket. Resets the object state
+* to unitialised.
+******************************************************************/
+void FGMultiplayRxMgr::Close(void) {
+
+    int iPlayerCnt;         // Count of players in player array
+
+    // Delete any existing players
+    for (iPlayerCnt = 0; iPlayerCnt < MAX_PLAYERS; iPlayerCnt++) {
+        if (m_Player[iPlayerCnt] != NULL) {
+            delete m_Player[iPlayerCnt];
+            m_Player[iPlayerCnt] = NULL;
+        }
+    }
+
+    // Delete socket
+    if (mDataRxSocket) {
+        mDataRxSocket->close();
+        delete mDataRxSocket;
+        mDataRxSocket = NULL;
+    }
+
+    m_bInitialised = false;
+
+}
+
+
+/******************************************************************
+* Name: ProcessData
+* Description: Processes data waiting at the receive socket. The
+* processing ends when there is no more data at the socket.
+******************************************************************/
+void FGMultiplayRxMgr::ProcessData(void) {
+
+    char sMsg[MAX_PACKET_SIZE];         // Buffer for received message
+    int iBytes;                         // Bytes received
+    T_MsgHdr *MsgHdr;                   // Pointer to header in received data
+    T_ChatMsg *ChatMsg;                 // Pointer to chat message in received data
+    T_PositionMsg *PosMsg;              // Pointer to position message in received data
+    char *sIpAddress;                   // Address information from header
+    char *sModelName;                   // Model that the remote player is using
+    char *sCallsign;                    // Callsign of the remote player
+    struct in_addr PlayerAddress;       // Used for converting the player's address into dot notation
+    int iPlayerCnt;                     // Count of players in player array
+    bool bActivePlayer = false;         // The state of the player that sent the data
+    int iPort;                          // Port that the remote player receives on
+
+
+    if (m_bInitialised) {
+
+        // Read the receive socket and process any data
+        do {
+
+            // Although the recv call asks for MAX_PACKET_SIZE of data,
+            // the number of bytes returned will only be that of the next
+            // packet waiting to be processed.
+            iBytes = mDataRxSocket->recv(sMsg, MAX_PACKET_SIZE, 0);
+
+            // Data received
+            if (iBytes > 0) {
+                if (iBytes >= sizeof(MsgHdr)) {
+
+                    // Read header
+                    MsgHdr = (T_MsgHdr *)sMsg;
+                    PlayerAddress.s_addr = MsgHdr->lReplyAddress;
+                    sIpAddress = inet_ntoa(PlayerAddress);
+                    iPort = MsgHdr->iReplyPort;
+                    sCallsign = MsgHdr->sCallsign;
+
+                    // Process the player data unless we generated it
+                    if (m_sCallsign != string(MsgHdr->sCallsign)) {
+
+
+                        // Process messages
+                        switch(MsgHdr->MsgId) {
+                            case CHAT_MSG_ID:
+                                if (MsgHdr->iMsgLen == sizeof(T_MsgHdr) + sizeof(T_ChatMsg)) {
+                                    ChatMsg = (T_ChatMsg *)(sMsg + sizeof(T_MsgHdr));
+                                    cout << "Chat [" << MsgHdr->sCallsign << "]" << " " << ChatMsg->sText << endl;
+                                } else {
+                                    cerr << "FGMultiplayRxMgr::MP_ProcessData - Chat message received with insufficient data" << endl;
+                                }
+                                break;
+
+                            case POS_DATA_ID:
+                                if (MsgHdr->iMsgLen == sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) {
+                                    PosMsg = (T_PositionMsg *)(sMsg + sizeof(T_MsgHdr));
+                                    sModelName = PosMsg->sModel;
+
+                                    // Check if the player is already in the game by using the Callsign.
+                                    bActivePlayer = false;
+                                    for (iPlayerCnt = 0; iPlayerCnt < MAX_PLAYERS; iPlayerCnt++) {
+                                        if (m_Player[iPlayerCnt] != NULL) {
+                                            if (m_Player[iPlayerCnt]->CompareCallsign(MsgHdr->sCallsign)) {
+
+                                                // Player found. Update the data for the player.
+                                                m_Player[iPlayerCnt]->SetPosition(PosMsg->PlayerPos);
+                                                bActivePlayer = true;
+
+                                            }
+                                        }
+                                    }
+
+                                    // Player not active, so add as new player
+                                    if (!bActivePlayer) {
+                                        iPlayerCnt = 0;
+                                        do {
+                                            if (m_Player[iPlayerCnt] == NULL) {
+                                                cout << "FGMultiplayRxMgr::ProcessRxData - Add new player. IP: " << sIpAddress
+                                                    << ", Call: " <<  sCallsign << ", model: " << sModelName << endl;
+                                                m_Player[iPlayerCnt] = new MPPlayer;
+                                                m_Player[iPlayerCnt]->Open(string(sIpAddress), iPort, string(sCallsign), string(sModelName), false);
+                                                m_Player[iPlayerCnt]->SetPosition(PosMsg->PlayerPos);
+                                                bActivePlayer = true;
+                                            }
+                                            iPlayerCnt++;
+                                        } while (iPlayerCnt < MAX_PLAYERS && !bActivePlayer);
+
+                                        // Check if the player was added
+                                        if (!bActivePlayer) {
+                                            if (iPlayerCnt == MAX_PLAYERS) {
+                                                cerr << "FGMultiplayRxMgr::MP_ProcessData - Unable to add new player (" << sCallsign << "). Too many players." << endl;
+                                            }
+                                        }
+                                    }
+
+                                } else {
+                                    cerr << "FGMultiplayRxMgr::MP_ProcessData - Position message received with insufficient data" << endl;
+                                }
+                                break;
+
+                            default:
+                                cerr << "FGMultiplayRxMgr::MP_ProcessData - Unknown message Id received: " << MsgHdr->MsgId << endl;
+                                break;
+
+
+                        }
+                    }
+                }
+
+
+            // Error or no data
+            } else if (iBytes == -1) {
+                if (errno != EAGAIN) {
+                    perror("FGMultiplayRxMgr::MP_ProcessData");
+                }
+            }
+
+        } while (iBytes > 0);
+
+    }
+
+
+}
+
+
+/******************************************************************
+* Name: Update
+* Description: For each active player, tell the player object
+* to update its position on the scene. If a player object
+* returns status information indicating that it has not
+* had an update for some time then the player is deleted.
+******************************************************************/
+void FGMultiplayRxMgr::Update(void) {
+
+    int iPlayerDataState;
+    int iPlayerId;
+
+    for (iPlayerId = 0; iPlayerId < MAX_PLAYERS; iPlayerId++) {
+        if (m_Player[iPlayerId] != NULL) {
+            iPlayerDataState = m_Player[iPlayerId]->Draw();
+
+            // If the player has not received an update for some
+            // time then assume that the player has quit.
+            if (iPlayerDataState == PLAYER_DATA_EXPIRED) {
+                delete m_Player[iPlayerId];
+                m_Player[iPlayerId] = NULL;
+            }
+
+        }
+    }
+
+}
+
+
diff --git a/src/MultiPlayer/multiplayrxmgr.hxx b/src/MultiPlayer/multiplayrxmgr.hxx
new file mode 100644 (file)
index 0000000..497bc63
--- /dev/null
@@ -0,0 +1,111 @@
+// multiplayrxmgr.hxx -- routines for receiving multiplayer data
+//                       for Flghtgear
+//
+// Written by Duncan McCreanor, started February 2003.
+// duncan.mccreanor@airservicesaustralia.com
+//
+// Copyright (C) 2003  Airservices Australia
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef MULTIPLAYRXMGR_H
+#define MULTIPLAYRXMGR_H
+
+#define MULTIPLAYRXMGR_HID "$Id$"
+
+
+#include "mpplayer.hxx"
+#include "mpmessages.hxx"
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include STL_STRING
+SG_USING_STD(string);
+
+#include <simgear/compiler.h>
+#include <plib/ssg.h>
+#include <plib/netSocket.h>
+
+// Maximum number of players that can exist at any time
+#define MAX_PLAYERS 10
+
+/****************************************************************
+* @version $Id$
+*
+* Description: The multiplay rx manager is responsible for
+* receiving and processing data from other players.
+
+* Data from remote players is read from the network and processed
+* via calling ProcessData. The models for the remote player are
+* positioned onto the scene by calling Update.
+*
+*******************************************************************/
+class FGMultiplayRxMgr {
+public:
+
+    /** Constructor */
+    FGMultiplayRxMgr();
+
+    /** Destructor. */
+    ~FGMultiplayRxMgr();
+
+    /** Initialises the multiplayer receiver.
+    * @return True if initialisation succeeds, else false
+    */
+    bool init(void);
+
+    /** Initiates processing of any data waiting at the rx socket.
+    */
+    void ProcessData(void);
+
+    /** Updates the model positions for the players
+    */
+    void Update(void);
+
+    /** Closes the multiplayer manager. Stops any further player packet processing.
+    */
+    void Close(void);
+
+
+private:
+
+
+    /** Holds the players that exist in the game */
+    MPPlayer *m_Player[MAX_PLAYERS];
+
+    /** Socket for receiving data from the server or another player */
+    netSocket *mDataRxSocket;
+
+    /** True if multiplay receive is initialised */
+    bool m_bInitialised;
+
+    /** Receive address for multiplayer messages */
+    string m_sRxAddress;
+
+    /** Receive port for multiplayer messages */
+    int m_iRxPort;
+
+    /** Local player's callsign */
+    string m_sCallsign;
+
+};
+
+#endif
+
+
+
diff --git a/src/MultiPlayer/multiplaytxmgr.cxx b/src/MultiPlayer/multiplaytxmgr.cxx
new file mode 100644 (file)
index 0000000..8a8256b
--- /dev/null
@@ -0,0 +1,229 @@
+// multiplaytxmgr.cxx -- routines for transmitting multiplayer data
+//                       for Flightgear
+//
+// Written by Duncan McCreanor, started February 2003.
+// duncan.mccreanor@airservicesaustralia.com
+//
+// Copyright (C) 2003  Airservices Australia
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+/******************************************************************
+* $Id$
+*
+* Description: The multiplayer tx manager provides is used
+* to send data to another player or a server for an
+* interactive multiplayer FlightGear simulation.
+*
+******************************************************************/
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <plib/netSocket.h>
+#include <stdlib.h>
+#include <Main/fg_props.hxx>
+#include "multiplaytxmgr.hxx"
+#include "mpmessages.hxx"
+#include "mpplayer.hxx"
+
+// These constants are provided so that the ident command can list file versions.
+const char sMULTIPLAYTXMGR_BID[] = "$Id$";
+const char sMULTIPLAYTXMGR_HID[] = MULTIPLAYTXMGR_HID;
+
+
+
+/******************************************************************
+* Name: FGMultiplayTxMgr
+* Description: Constructor.
+******************************************************************/
+FGMultiplayTxMgr::FGMultiplayTxMgr() {
+
+    int iPlayerCnt;         // Count of players in player array
+
+    // Initialise private members
+    m_bInitialised = false;
+    mLocalPlayer = NULL;
+
+}
+
+
+/******************************************************************
+* Name: ~FGMultiplayTxMgr
+* Description: Destructor. Closes and deletes objects owned by
+* this object.
+******************************************************************/
+FGMultiplayTxMgr::~FGMultiplayTxMgr() {
+
+    Close();
+
+}
+
+
+/******************************************************************
+* Name: init
+* Description: Initialises multiplayer transmit
+******************************************************************/
+bool FGMultiplayTxMgr::init(void) {
+
+
+    string sTxAddress;                          // Destination address
+    int iTxPort;
+    bool bSuccess = true;                       // Result of initialisation
+
+    // Initialise object if not already done
+    if (!m_bInitialised) {
+
+        // Set members from property values
+        string sTxAddress = fgGetString("/sim/multiplay/txhost");
+        iTxPort = fgGetInt("/sim/multiplay/txport");
+
+        cout << "FGMultiplayTxMgr::init - txaddress= " << sTxAddress << endl;
+        cout << "FGMultiplayTxMgr::init - txport= " << iTxPort << endl;
+        
+        if (iTxPort > 0) {
+
+
+            // Create and open tx socket
+            mDataTxSocket = new netSocket();
+            if (!mDataTxSocket->open(false)) {
+                // Failed to open tx socket
+                cerr << "FGMultiplayTxMgr::init - Failed to create data transmit socket" << endl;
+                bSuccess = false;
+            } else {
+                mDataTxSocket->setBroadcast(true);
+                if (mDataTxSocket->connect(sTxAddress.c_str(), iTxPort) != 0) {
+                    // Failed to connect tx socket
+                    cerr << "FGMultiplayTxMgr::init - Failed to connect data transmit socket" << endl;
+                    bSuccess = false;
+                }
+            }
+
+            // Create a player object for the local player
+            if (bSuccess) {
+                mLocalPlayer = new MPPlayer();
+                if (!mLocalPlayer->Open(fgGetString("/sim/multiplay/rxaddress"), fgGetInt("/sim/multiplay/rxport"),
+                                        fgGetString("/sim/multiplay/callsign"), fgGetString("/sim/model/path"), true)) {
+                    cerr << "FGMultiplayTxMgr::init - Failed to create player object for local player" << endl;
+                    bSuccess = false;
+                }
+            }
+
+        // If Tx port == zero then don't initialise
+        } else {
+
+            cout << "FGMultiplayTxMgr::init - Tx Port is zero. Multiplay out disabled." << endl;
+            bSuccess = false;
+
+        }
+
+        // Save manager state
+        m_bInitialised = bSuccess;
+
+    } else {
+        cerr << "FGMultiplayTxMgr::init - Attempt to init object that is already opened" << endl;
+        bSuccess = false;
+    }
+
+
+    /* Return true if init succeeds */
+    return bSuccess;
+
+}
+
+
+/******************************************************************
+* Name: Close
+* Description: Closes and deletes the local player object. Closes
+* and deletes the tx socket. Resets the object state to unitialised.
+******************************************************************/
+void FGMultiplayTxMgr::Close(void) {
+
+
+    // Delete local player
+    if (mLocalPlayer) {
+        delete mLocalPlayer;
+        mLocalPlayer = NULL;
+    }
+
+    // Delete socket
+    if (mDataTxSocket) {
+        mDataTxSocket->close();
+        delete mDataTxSocket;
+        mDataTxSocket = NULL;
+    }
+
+    m_bInitialised = false;
+
+}
+
+
+/******************************************************************
+* Name: SendMyPosition
+* Description: Sends the position data for the local position.
+******************************************************************/
+void FGMultiplayTxMgr::SendMyPosition(const sgMat4 PlayerPosMat4) {
+
+    T_MsgHdr MsgHdr;
+    T_PositionMsg PosMsg;
+    char sMsg[sizeof(T_MsgHdr) + sizeof(T_PositionMsg)];
+
+    if (m_bInitialised) {
+        mLocalPlayer->SetPosition(PlayerPosMat4);
+        mLocalPlayer->FillPosMsg(&MsgHdr, &PosMsg);
+        memcpy(sMsg, &MsgHdr, sizeof(T_MsgHdr));
+        memcpy(sMsg + sizeof(T_MsgHdr), &PosMsg, sizeof(T_PositionMsg));
+        mDataTxSocket->send(sMsg, sizeof(T_MsgHdr) + sizeof(T_PositionMsg), 0);
+    }
+
+
+}
+
+
+
+/******************************************************************
+* Name: SendTextMessage
+* Description: Sends a message to the player. The message must
+* contain a valid and correctly filled out header and optional
+* message body.
+******************************************************************/
+void FGMultiplayTxMgr::SendTextMessage(const string &sMsgText) const {
+
+    bool bResult = false;
+    T_MsgHdr MsgHdr;
+    T_ChatMsg ChatMsg;
+    int iNextBlockPosition = 0;
+    char sMsg[sizeof(T_MsgHdr) + sizeof(T_ChatMsg)];
+
+    if (m_bInitialised) {
+
+        mLocalPlayer->FillMsgHdr(&MsgHdr, CHAT_MSG_ID);
+
+        // Divide the text string into blocks that fit in the message
+        // and send the blocks.
+        while (iNextBlockPosition < sMsgText.length()) {
+            strncpy(ChatMsg.sText, sMsgText.substr(iNextBlockPosition, MAX_CHAT_MSG_LEN - 1).c_str(), MAX_CHAT_MSG_LEN);
+            ChatMsg.sText[MAX_CHAT_MSG_LEN - 1] = '\0';
+            memcpy(sMsg, &MsgHdr, sizeof(T_MsgHdr));
+            memcpy(sMsg + sizeof(T_MsgHdr), &ChatMsg, sizeof(T_ChatMsg));
+            mDataTxSocket->send(sMsg, sizeof(T_MsgHdr) + sizeof(T_ChatMsg), 0);
+            iNextBlockPosition += MAX_CHAT_MSG_LEN - 1;
+        }
+
+    }
+
+}
+
diff --git a/src/MultiPlayer/multiplaytxmgr.hxx b/src/MultiPlayer/multiplaytxmgr.hxx
new file mode 100644 (file)
index 0000000..99941fe
--- /dev/null
@@ -0,0 +1,102 @@
+// multiplaytxmgr.hxx -- routines for transmitting multiplayer data
+//                       for Flghtgear
+//
+// Written by Duncan McCreanor, started February 2003.
+// duncan.mccreanor@airservicesaustralia.com
+//
+// Copyright (C) 2003  Airservices Australia
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef MULTIPLAYTXMGR_H
+#define MULTIPLAYTXMGR_H
+
+#define MULTIPLAYTXMGR_HID "$Id$"
+
+
+#include "mpplayer.hxx"
+#include "mpmessages.hxx"
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include STL_STRING
+SG_USING_STD(string);
+
+#include <simgear/compiler.h>
+#include <plib/ssg.h>
+#include <plib/netSocket.h>
+
+// Maximum number of players that can exist at any time
+#define MAX_PLAYERS 10
+
+/****************************************************************
+* @version $Id$
+*
+* Description: The multiplay tx manager is responsible for
+* sending data to another player or a multiplayer server.
+*
+* The position information for the local player is transmitted
+* on each call to SendMyPosition.
+*
+*******************************************************************/
+class FGMultiplayTxMgr {
+public:
+
+    /** Constructor */
+    FGMultiplayTxMgr();
+
+    /** Destructor. */
+    ~FGMultiplayTxMgr();
+
+    /** Initialises the multiplayer transmitter.
+    * @return True if initialisation succeeds, else false
+    */
+    bool init(void);
+
+    /** Sends the position data for the local player
+    * @param PlayerPosMat4 Transformation matrix for the player's position
+    */
+    void SendMyPosition(const sgMat4 PlayerPosMat4);
+    
+    /** Sends a tex chat message.
+    * @param sMsgText Message text to send
+    */
+    void SendTextMessage(const string &sMsgText) const;
+
+    /** Closes the multiplayer manager. Stops any further player packet processing.
+    */
+    void Close(void);
+
+
+private:
+
+    /** The local player */
+    MPPlayer *mLocalPlayer;
+
+    /** Socket for sending to the server or another player if playing point to point */
+    netSocket *mDataTxSocket;
+
+    /** True if multiplay transmit is initialised */
+    bool m_bInitialised;
+
+};
+
+#endif
+
+
+
index ffe499c791de7677e29b21f8f18b7e8906987ab0..245bc4eef1908b262636d9ceda7f6966421f350e 100644 (file)
@@ -21,6 +21,7 @@ libNetwork_a_SOURCES = \
        net_ctrls.hxx net_fdm.hxx net_fdm_mini.hxx \
         nmea.cxx nmea.hxx \
         opengc.cxx opengc.hxx opengc_data.hxx \
+       multiplay.cxx multiplay.hxx \
        props.cxx props.hxx \
        pve.cxx pve.hxx \
        ray.cxx ray.hxx \
diff --git a/src/Network/multiplay.cxx b/src/Network/multiplay.cxx
new file mode 100644 (file)
index 0000000..b32d922
--- /dev/null
@@ -0,0 +1,135 @@
+// multiplay.cxx -- protocol object for multiplay in Flightgear
+//
+// Written by Diarmuid Tyson, started February 2003.
+// diarmuid.tyson@airservicesaustralia.com
+//
+// Copyright (C) 2003  Airservices Australia
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+
+#include <simgear/compiler.h>
+
+#include STL_STRING
+
+#include <iostream.h>
+
+#include <simgear/debug/logstream.hxx>
+
+#include "multiplay.hxx"
+
+SG_USING_STD(string);
+
+
+// These constants are provided so that the ident command can list file versions.
+const char sFG_MULTIPLAY_BID[] = "$Id$";
+const char sFG_MULTIPLAY_HID[] = FG_MULTIPLAY_HID;
+
+
+/******************************************************************
+* Name: FGMultiplay
+* Description: Constructor.  Initialises the protocol and stores
+* host and port information.
+******************************************************************/
+FGMultiplay::FGMultiplay (const string &dir, const int rate, const string &host, const int port) {
+
+  set_hz(rate);
+
+  set_direction(dir);
+
+  if (get_direction() == SG_IO_IN) {
+
+    fgSetInt("/sim/multiplay/rxport", port);
+    fgSetString("/sim/multiplay/rxhost", host.c_str());
+
+  } else if (get_direction() == SG_IO_OUT) {
+
+    fgSetInt("/sim/multiplay/txport", port);
+    fgSetString("/sim/multiplay/txhost", host.c_str());
+
+  }
+
+}
+
+
+/******************************************************************
+* Name: ~FGMultiplay
+* Description: Destructor.
+******************************************************************/
+FGMultiplay::~FGMultiplay () {
+}
+
+
+/******************************************************************
+* Name: open
+* Description: Enables the protocol.
+******************************************************************/
+bool FGMultiplay::open() {
+
+    if ( is_enabled() ) {
+       SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
+               << "is already in use, ignoring" );
+       return false;
+    }
+
+    set_enabled(true);
+
+    return is_enabled();
+}
+
+
+/******************************************************************
+* Name: process
+* Description: Prompts the multiplayer mgr to either send
+* or receive data over the network
+******************************************************************/
+bool FGMultiplay::process() {
+
+  if (get_direction() == SG_IO_IN) {
+
+    globals->get_multiplayer_rx_mgr()->ProcessData();
+
+  } else if (get_direction() == SG_IO_OUT) {
+
+    globals->get_multiplayer_tx_mgr()->
+    SendMyPosition(globals->get_aircraft_model()->get3DModel()->get_POS());
+
+  }
+
+    return true;
+}
+
+
+/******************************************************************
+* Name: close
+* Description:  Closes the multiplayer mgrs to stop any further
+* network processing
+******************************************************************/
+bool FGMultiplay::close() {
+
+  if (get_direction() == SG_IO_IN) {
+
+    globals->get_multiplayer_rx_mgr()->Close();
+
+  } else if (get_direction() == SG_IO_OUT) {
+
+    globals->get_multiplayer_tx_mgr()->Close();
+
+  }
+
+    return true;
+}
+
diff --git a/src/Network/multiplay.hxx b/src/Network/multiplay.hxx
new file mode 100644 (file)
index 0000000..10bea8a
--- /dev/null
@@ -0,0 +1,81 @@
+// multiplay.hxx -- protocol object for multiplay in Flightgear
+//
+// Written by Diarmuid Tyson, started February 2003.
+// diarmuid.tyson@airservicesaustralia.com
+//
+// Copyright (C) 2003  Airservices Australia
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+
+#ifndef _FG_MULTIPLAY_HXX
+#define _FG_MULTIPLAY_HXX
+
+#define FG_MULTIPLAY_HID "$Id$"
+
+#include STL_STRING
+SG_USING_STD(string);
+
+#include "protocol.hxx"
+#include <simgear/compiler.h>
+#include <Main/globals.hxx>
+#include <Main/fg_props.hxx>
+#include <Model/acmodel.hxx>
+#include <Model/model.hxx>
+#include <MultiPlayer/multiplaytxmgr.hxx>
+#include <MultiPlayer/multiplayrxmgr.hxx>
+
+/****************************************************************
+* @version $Id$
+*
+* Description: FGMultiplay is an FGProtocol object used as the basic
+* interface for the multiplayer code into FlightGears generic IO
+* subsystem.  It only implements the basic FGProtocol methods: open(),
+* process() and close().  It does not use Sim Gear's IO channels, as
+* the MultiplayMgrs creates their own sockets through plib.
+*
+* It will set up it's direction and rate protocol properties when
+* created.  Subsequent calls to process will prompt the
+* MultiplayMgr to either send or receive data over the network.
+*
+******************************************************************/
+
+class FGMultiplay : public FGProtocol {
+public:
+
+    /** Constructor */
+    FGMultiplay (const string &dir, const int rate, const string &host, const int port);
+
+    /** Destructor. */
+    ~FGMultiplay ();
+
+    /** Enables the FGMultiplay object
+    */
+    bool open();
+
+    /** Tells the multiplayer_mgr to send/receive data.
+    */
+    bool process();
+
+    /** Closes the multiplayer_mgr.
+    */
+    bool close();
+
+private:
+
+};
+
+
+#endif // _FG_MULTIPLAY_HXX