Add multiplayer support from Duncan McCreanor and Diarmuid Tyson
This commit is contained in:
parent
01be2ed8e4
commit
e46c59d4d0
23 changed files with 1771 additions and 38 deletions
28
configure.ac
28
configure.ac
|
@ -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
|
||||
|
|
72
docs-mini/README.multiplayer
Normal file
72
docs-mini/README.multiplayer
Normal file
|
@ -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.
|
|
@ -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) \
|
||||
|
|
|
@ -118,6 +118,11 @@
|
|||
#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.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -57,6 +57,10 @@
|
|||
#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;
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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...
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -228,7 +228,7 @@ private:
|
|||
};
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
|
|
3
src/MultiPlayer/.cvsignore
Normal file
3
src/MultiPlayer/.cvsignore
Normal file
|
@ -0,0 +1,3 @@
|
|||
.deps
|
||||
Makefile.in
|
||||
Makefile
|
7
src/MultiPlayer/Makefile.am
Normal file
7
src/MultiPlayer/Makefile.am
Normal file
|
@ -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
|
||||
|
||||
|
68
src/MultiPlayer/mpmessages.hxx
Normal file
68
src/MultiPlayer/mpmessages.hxx
Normal file
|
@ -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
|
||||
|
||||
|
294
src/MultiPlayer/mpplayer.cxx
Normal file
294
src/MultiPlayer/mpplayer.cxx
Normal file
|
@ -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';
|
||||
|
||||
|
||||
}
|
||||
|
153
src/MultiPlayer/mpplayer.hxx
Normal file
153
src/MultiPlayer/mpplayer.hxx
Normal file
|
@ -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
|
||||
|
||||
|
||||
|
346
src/MultiPlayer/multiplayrxmgr.cxx
Normal file
346
src/MultiPlayer/multiplayrxmgr.cxx
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
111
src/MultiPlayer/multiplayrxmgr.hxx
Normal file
111
src/MultiPlayer/multiplayrxmgr.hxx
Normal file
|
@ -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
|
||||
|
||||
|
||||
|
229
src/MultiPlayer/multiplaytxmgr.cxx
Normal file
229
src/MultiPlayer/multiplaytxmgr.cxx
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
102
src/MultiPlayer/multiplaytxmgr.hxx
Normal file
102
src/MultiPlayer/multiplaytxmgr.hxx
Normal file
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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 \
|
||||
|
|
135
src/Network/multiplay.cxx
Normal file
135
src/Network/multiplay.cxx
Normal file
|
@ -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;
|
||||
}
|
||||
|
81
src/Network/multiplay.hxx
Normal file
81
src/Network/multiplay.hxx
Normal file
|
@ -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
|
Loading…
Reference in a new issue