1
0
Fork 0

Add multiplayer support from Duncan McCreanor and Diarmuid Tyson

This commit is contained in:
ehofman 2003-03-19 20:45:09 +00:00
parent 01be2ed8e4
commit e46c59d4d0
23 changed files with 1771 additions and 38 deletions

View file

@ -57,16 +57,30 @@ if test "x$with_logging" = "xno" ; then
AC_DEFINE([FG_NDEBUG], 1, [Define for no logging output])
fi
# Specify if we want to build with default Multiplayer support
# default to with_network=yes
AC_ARG_WITH(multiplayer, [ --with-multiplayer Include default multiplayer support])
if test "x$with_multiplayer" = "xno" ; then
echo "Building without default multiplayer support"
else
echo "Building with default multiplayer support"
AC_DEFINE([FG_MPLAYER_AS], 1, [Define to build with default multiplayer support])
fi
AM_CONDITIONAL(ENABLE_MPLAYER_AS, test "x$with_multiplayer" != "xno")
# Specify if we want to build with Oliver's networking support
# default to with_network=yes
AC_ARG_WITH(network_olk, [ --with-network-olk Include Oliver's multi-pilot networking support])
NETWORK_DIRS=Network
AC_ARG_WITH(network_olk, [ --with-network-olk Include Oliver's multi-pilot networking support [default=no]])
if test "x$with_network_olk" = "xno" ; then
echo "Building without Oliver's multi-pilot network support"
else
echo "Building with Oliver's multi-pilot network support"
AC_DEFINE([FG_NETWORK_OLK], 1, [Define to build with Oliver's networking])
fi
AM_CONDITIONAL(ENABLE_NETWORK_OLK, test "x$with_network_olk" != "xno")
AM_CONDITIONAL(ENABLE_NETWORK_OLK, test "x$with_network_olk" != "xno" -a "x$with_multiplayer" = "xno")
# Specify if we want to use WeatherCM instead of FGEnvironment.
@ -82,7 +96,7 @@ fi
AM_CONDITIONAL(ENABLE_WEATHERCM, test "x$with_weathercm" = "xyes")
dnl Specify if we want the old menubar; default to the new one
AC_ARG_WITH(old-menubar, [ --with-old-menubar Use the old menu bar])
AC_ARG_WITH(old-menubar, [ --with-old-menubar Use the old menu bar])
if test "x$with_old_menubar" = "xyes" ; then
echo "Building with old menubar"
AC_DEFINE([FG_OLD_MENUBAR], 1,
@ -90,7 +104,6 @@ if test "x$with_old_menubar" = "xyes" ; then
else
echo "Building with new menubar"
fi
AM_CONDITIONAL(ENABLE_WEATHERCM, test "x$with_weathercm" = "xyes")
dnl Thread related checks
AC_ARG_WITH(threads, [ --with-threads Include tile loading threads [default=no]])
@ -590,6 +603,7 @@ AC_CONFIG_FILES([ \
src/Main/runfgfs \
src/Main/runfgfs.bat \
src/Model/Makefile \
src/MultiPlayer/Makefile \
src/Navaids/Makefile \
src/Network/Makefile \
src/NetworkOLK/Makefile \
@ -639,6 +653,12 @@ else
echo "Using FGEnvironment"
fi
if test "x$with_multiplayer" != "xno"; then
echo "Using default multiplayer support"
elif test "x$with_network_olk" != "xno"; then
echo "Using Oliver's multi-pilot network support"
fi
if test "x$with_old_menubar" != "x"; then
echo "Using old menubar"
else

View 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.

View file

@ -73,6 +73,7 @@ fgfs_LDADD = \
$(top_builddir)/src/Input/libInput.a \
$(top_builddir)/src/Instrumentation/libInstrumentation.a \
$(top_builddir)/src/Model/libModel.a \
$(top_builddir)/src/MultiPlayer/libMultiPlayer.a \
$(top_builddir)/src/Navaids/libNavaids.a \
$(top_builddir)/src/Scenery/libScenery.a \
$(SCRIPTING_LIBS) \

View file

@ -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.
////////////////////////////////////////////////////////////////////

View file

@ -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;

View file

@ -31,6 +31,7 @@
#include "fgfs.hxx"
SG_USING_STD( vector );
SG_USING_STD( string );
@ -67,6 +68,10 @@ class FGIO;
class FGModelLoader;
class FGModelMgr;
class FGScenery;
#ifdef FG_MPLAYER_AS
class FGMultiplayRxMgr;
class FGMultiplayTxMgr;
#endif
class FGSoundMgr;
class FGTextureLoader;
class FGTileMgr;
@ -170,6 +175,13 @@ private:
FGIO* io;
#ifdef FG_MPLAYER_AS
//Mulitplayer managers
FGMultiplayTxMgr *multiplayer_tx_mgr;
FGMultiplayRxMgr *multiplayer_rx_mgr;
#endif
public:
FGGlobals();
@ -236,8 +248,8 @@ public:
inline void set_ATC_mgr( FGATCMgr *a ) {ATC_mgr = a; }
inline FGATCDisplay *get_ATC_display() const { return ATC_display; }
inline void set_ATC_display( FGATCDisplay *d ) {ATC_display = d; }
inline void set_ATC_display( FGATCDisplay *d ) {ATC_display = d; }
inline FGAIMgr *get_AI_mgr() const { return AI_mgr; }
inline void set_AI_mgr( FGAIMgr *a ) {AI_mgr = a; }
@ -285,6 +297,22 @@ public:
model_mgr = mgr;
}
#ifdef FG_MPLAYER_AS
inline FGMultiplayTxMgr *get_multiplayer_tx_mgr () { return multiplayer_tx_mgr; }
inline void set_multiplayer_tx_mgr (FGMultiplayTxMgr * mgr)
{
multiplayer_tx_mgr = mgr;
}
inline FGMultiplayRxMgr *get_multiplayer_rx_mgr () { return multiplayer_rx_mgr; }
inline void set_multiplayer_rx_mgr (FGMultiplayRxMgr * mgr)
{
multiplayer_rx_mgr = mgr;
}
#endif
inline string_list *get_channel_options_list () {
return channel_options_list;
}
@ -300,6 +328,7 @@ public:
FGIO* get_io() const { return io; }
/**
* Save the current state as the initial state.
*/

View file

@ -109,6 +109,12 @@ SG_USING_STD(endl);
#ifdef FG_NETWORK_OLK
#include <NetworkOLK/network.h>
#endif
#ifdef FG_MPLAYER_AS
#include <MultiPlayer/multiplaytxmgr.hxx>
#include <MultiPlayer/multiplayrxmgr.hxx>
#endif
#include <Objects/matlib.hxx>
#include <Scenery/scenery.hxx>
#include <Scenery/tilemgr.hxx>
@ -433,7 +439,7 @@ void trRenderFrame( void ) {
// Update all Visuals (redraws anything graphics related)
void fgRenderFrame() {
GLfloat black[4] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat white[4] = { 1.0, 1.0, 1.0, 1.0 };
@ -484,7 +490,7 @@ void fgRenderFrame() {
// GLfloat terrain_color[4] = { 0.54, 0.44, 0.29, 1.0 };
// GLfloat mat_shininess[] = { 10.0 };
GLbitfield clear_mask;
if ( idle_state != 1000 ) {
// still initializing, draw the splash screen
if ( fgGetBool("/sim/startup/splash-screen") ) {
@ -533,12 +539,12 @@ void fgRenderFrame() {
if ( fgGetBool("/sim/rendering/skyblend") ) {
if ( fgGetBool("/sim/rendering/textures") ) {
// glClearColor(black[0], black[1], black[2], black[3]);
glClearColor(l->adj_fog_color[0], l->adj_fog_color[1],
glClearColor(l->adj_fog_color[0], l->adj_fog_color[1],
l->adj_fog_color[2], l->adj_fog_color[3]);
clear_mask |= GL_COLOR_BUFFER_BIT;
}
} else {
glClearColor(l->sky_color[0], l->sky_color[1],
glClearColor(l->sky_color[0], l->sky_color[1],
l->sky_color[2], l->sky_color[3]);
clear_mask |= GL_COLOR_BUFFER_BIT;
}
@ -591,7 +597,7 @@ void fgRenderFrame() {
globals->get_ephem()->getPlanets(),
globals->get_ephem()->getNumStars(),
globals->get_ephem()->getStars() );
/* cout << "thesky->reposition( view_pos = " << view_pos[0] << " "
<< view_pos[1] << " " << view_pos[2] << endl;
cout << " zero_elev = " << zero_elev[0] << " "
@ -601,7 +607,7 @@ void fgRenderFrame() {
cout << " sun_rot = " << cur_light_params.sun_rotation
<< " gst = " << SGTime::cur_time_params->getGst() << endl;
cout << " sun ra = " << globals->get_ephem()->getSunRightAscension()
<< " sun dec = " << globals->get_ephem()->getSunDeclination()
<< " sun dec = " << globals->get_ephem()->getSunDeclination()
<< " moon ra = " << globals->get_ephem()->getMoonRightAscension()
<< " moon dec = " << globals->get_ephem()->getMoonDeclination() << endl; */
@ -693,6 +699,11 @@ void fgRenderFrame() {
}
# endif
#ifdef FG_MPLAYER_AS
// Update any multiplayer models
globals->get_multiplayer_rx_mgr()->Update();
#endif
if ( fgGetBool("/sim/rendering/skyblend") ) {
// draw the sky backdrop

View file

@ -82,7 +82,7 @@ static double
atof( const string& str )
{
#ifdef __MWERKS__
#ifdef __MWERKS__
// -dw- if ::atof is called, then we get an infinite loop
return std::atof( str.c_str() );
#else
@ -93,7 +93,7 @@ atof( const string& str )
static int
atoi( const string& str )
{
#ifdef __MWERKS__
#ifdef __MWERKS__
// -dw- if ::atoi is called, then we get an infinite loop
return std::atoi( str.c_str() );
#else
@ -106,7 +106,7 @@ atoi( const string& str )
* Set a few fail-safe default property values.
*
* These should all be set in $FG_ROOT/preferences.xml, but just
* in case, we provide some initial sane values here. This method
* in case, we provide some initial sane values here. This method
* should be invoked *before* reading any init files.
*/
void
@ -224,6 +224,15 @@ fgSetDefaults ()
fgSetBool("/sim/freeze/position", false);
fgSetBool("/sim/freeze/clock", false);
fgSetBool("/sim/freeze/fuel", false);
#ifdef FG_MPLAYER_AS
fgSetString("/sim/multiplay/callsign", "callsign");
fgSetString("/sim/multiplay/rxhost", "0");
fgSetString("/sim/multiplay/txhost", "0");
fgSetInt("/sim/multiplay/rxport", 0);
fgSetInt("/sim/multiplay/txport", 0);
#endif
}
@ -506,20 +515,22 @@ parse_fov( const string& arg ) {
// baud = {300, 1200, 2400, ..., 230400}
//
// Socket exacmple "--native=socket,dir,hz,machine,port,style" where
//
//
// machine = machine name or ip address if client (leave empty if server)
// port = port, leave empty to let system choose
// style = tcp or udp
//
// File example "--garmin=file,dir,hz,filename" where
//
//
// filename = file system file name
static bool
static bool
add_channel( const string& type, const string& channel_str ) {
// cout << "Channel string = " << channel_str << endl;
cout << "Channel string = " << channel_str << endl;
globals->get_channel_options_list()->push_back( type + "," + channel_str );
cout << "here" << endl;
return true;
}
@ -1156,8 +1167,8 @@ struct OptionDesc {
#endif
// Parse a single option
static int
parse_option (const string& arg)
static int
parse_option (const string& arg)
{
#ifdef NEW_OPTION_PARSING
if ( fgOptionMap.size() == 0 ) {
@ -1287,7 +1298,7 @@ parse_option (const string& arg)
} else if ( arg == "--enable-game-mode" ) {
fgSetBool("/sim/startup/game-mode", true);
} else if ( arg == "--disable-splash-screen" ) {
fgSetBool("/sim/startup/splash-screen", false);
fgSetBool("/sim/startup/splash-screen", false);
} else if ( arg == "--enable-splash-screen" ) {
fgSetBool("/sim/startup/splash-screen", true);
} else if ( arg == "--disable-intro-music" ) {
@ -1559,6 +1570,10 @@ parse_option (const string& arg)
add_channel( "atc610x", "dummy" );
} else if ( arg.find( "--atlas=" ) == 0 ) {
add_channel( "atlas", arg.substr(8) );
} else if ( arg.find( "--multiplay=" ) == 0 ) {
add_channel( "multiplay", arg.substr(12) );
} else if ( arg.find( "--httpd=" ) == 0 ) {
add_channel( "httpd", arg.substr(8) );
#ifdef FG_JPEG_SERVER
@ -1604,6 +1619,12 @@ parse_option (const string& arg)
} else if ( arg.find( "--net-id=") == 0 ) {
fgSetString("sim/networking/call-sign", arg.substr(9).c_str());
#endif
#ifdef FG_MPLAYER_AS
} else if ( arg.find( "--callsign=") == 0 ) {
fgSetString("sim/multiplay/callsign", arg.substr(11).c_str());
#endif
} else if ( arg.find( "--prop:" ) == 0 ) {
string assign = arg.substr(7);
string::size_type pos = assign.find('=');
@ -1640,7 +1661,7 @@ parse_option (const string& arg)
} else {
default_view_offset = atof( woffset.c_str() ) * SGD_DEGREES_TO_RADIANS;
}
/* apparently not used (CLO, 11 Jun 2002)
/* apparently not used (CLO, 11 Jun 2002)
FGViewer *pilot_view =
(FGViewer *)globals->get_viewmgr()->get_view( 0 ); */
// this will work without calls to the viewer...

View file

@ -4,18 +4,24 @@ else
WEATHER_DIR = Environment
endif
if ENABLE_NETWORK_OLK
NETWORK_DIRS = Network NetworkOLK
else
NETWORK_DIRS = Network
endif
if HAVE_PLIB_PSL
SCRIPTING_DIRS = Scripting
else
SCRIPTING_DIRS =
endif
if ENABLE_MPLAYER_AS
MPLAYER_DIRS = MultiPlayer
else
MPLAYER_DIRS =
endif
if ENABLE_NETWORK_OLK
NETWORK_DIRS = NetworkOLK
else
NETWORK_DIRS =
endif
SUBDIRS = \
Include \
Aircraft \
@ -30,6 +36,8 @@ SUBDIRS = \
Instrumentation \
Model \
Navaids \
Network \
$(MPLAYER_DIRS) \
$(NETWORK_DIRS) \
Objects \
Scenery \

View file

@ -649,9 +649,8 @@ FGModelPlacement::update ()
_location->setPosition( _lon_deg, _lat_deg, _elev_ft );
_location->setOrientation( _roll_deg, _pitch_deg, _heading_deg );
sgMat4 POS;
sgCopyMat4(POS, _location->getTransformMatrix());
sgVec3 trans;
sgCopyVec3(trans, _location->get_view_pos());

View file

@ -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

View file

@ -0,0 +1,3 @@
.deps
Makefile.in
Makefile

View 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

View 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

View 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';
}

View 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

View 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;
}
}
}
}

View 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

View 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;
}
}
}

View 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

View file

@ -21,6 +21,7 @@ libNetwork_a_SOURCES = \
net_ctrls.hxx net_fdm.hxx net_fdm_mini.hxx \
nmea.cxx nmea.hxx \
opengc.cxx opengc.hxx opengc_data.hxx \
multiplay.cxx multiplay.hxx \
props.cxx props.hxx \
pve.cxx pve.hxx \
ray.cxx ray.hxx \

135
src/Network/multiplay.cxx Normal file
View 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
View 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