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.
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,
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]])
src/Main/runfgfs \
src/Main/runfgfs.bat \
src/Model/Makefile \
+ src/MultiPlayer/Makefile \
src/Navaids/Makefile \
src/Network/Makefile \
src/NetworkOLK/Makefile \
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
--- /dev/null
+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.
$(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) \
#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
root = fgScanForOption( "--fg-root=", config.str() );
}
}
-
+
// Next check if fg-root is set as an env variable
if ( root.empty() ) {
envp = ::getenv( "FG_ROOT" );
SG_LOG( SG_GENERAL, SG_ALERT, "Resetting glideslope to zero" );
fgSetDouble("/sim/presets/glideslope-deg", 0);
fgSetBool("/sim/presets/onground", true);
- }
+ }
}
current_panel->bind();
}
-
+
////////////////////////////////////////////////////////////////////
// Initialize the default (kludged) properties.
////////////////////////////////////////////////////////////////////
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.
////////////////////////////////////////////////////////////////////
#include <Network/ray.hxx>
#include <Network/rul.hxx>
+#ifdef FG_MPLAYER_AS
+#include <Network/multiplay.hxx>
+#endif
+
#include "globals.hxx"
#include "fg_io.hxx"
} 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;
}
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 ) );
}
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;
#include "fgfs.hxx"
+
SG_USING_STD( vector );
SG_USING_STD( string );
class FGModelLoader;
class FGModelMgr;
class FGScenery;
+#ifdef FG_MPLAYER_AS
+class FGMultiplayRxMgr;
+class FGMultiplayTxMgr;
+#endif
class FGSoundMgr;
class FGTextureLoader;
class FGTileMgr;
FGIO* io;
+#ifdef FG_MPLAYER_AS
+ //Mulitplayer managers
+ FGMultiplayTxMgr *multiplayer_tx_mgr;
+
+ FGMultiplayRxMgr *multiplayer_rx_mgr;
+#endif
+
public:
FGGlobals();
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; }
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;
}
FGIO* get_io() const { return io; }
+
/**
* Save the current state as the initial state.
*/
#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>
// 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 };
// 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") ) {
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;
}
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] << " "
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; */
}
# 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
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
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
* 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
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
+
}
// 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;
}
#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 ) {
} 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" ) {
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
} 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('=');
} 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...
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 \
Instrumentation \
Model \
Navaids \
+ Network \
+ $(MPLAYER_DIRS) \
$(NETWORK_DIRS) \
Objects \
Scenery \
_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());
};
-\f
+
////////////////////////////////////////////////////////////////////////
// Model placement.
////////////////////////////////////////////////////////////////////////
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;
// Location
FGLocation * _location;
+
+ // Addition by Diarmuid Tyson for Multiplayer Support
+ // Moved from update method
+ // POS for transformation Matrix
+ sgMat4 POS;
+
};
#endif // __MODEL_HXX
--- /dev/null
+.deps
+Makefile.in
+Makefile
\ No newline at end of file
--- /dev/null
+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
+
+
--- /dev/null
+#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
+
+
--- /dev/null
+// 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';
+
+
+}
+
--- /dev/null
+// 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
+
+
+
--- /dev/null
+// 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;
+ }
+
+ }
+ }
+
+}
+
+
--- /dev/null
+// 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
+
+
+
--- /dev/null
+// 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;
+ }
+
+ }
+
+}
+
--- /dev/null
+// 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
+
+
+
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 \
--- /dev/null
+// 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;
+}
+
--- /dev/null
+// 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