1
0
Fork 0

Mathias Frhlich:

The new multiplayer patch with an extension to transmit some properties with
the base package. The properties are transmitted in a way that will not
immediately brake the packet format if we need new ones.
Even if the maxmimum number needs to be limited somehow, that format might
work well until we have an improoved packet format which is even more compact
and that does not require to retransmit redundant information with each
packet.

That part is relatively fresh and based on that what Oliver provides on his
multiplayer server web page.

The properties are transferred to the client and I have modified the seahawks
rudder animation property to use a relative property path to verify that it
works appart from the fact that you can see it changing in the property
browser.

The movement is still a bit jerky, but that can be fixed/tuned later without
again braking the packet format.
This commit is contained in:
ehofman 2006-02-17 09:43:33 +00:00
parent 31621f50af
commit da6568ad50
23 changed files with 1000 additions and 1279 deletions

View file

@ -90,18 +90,6 @@ if test "x$with_logging" = "xno" ; then
AC_DEFINE([FG_NDEBUG], 1, [Define for no logging output]) AC_DEFINE([FG_NDEBUG], 1, [Define for no logging output])
fi fi
# Specify if we want to build with Multiplayer support
# default to with_network=yes
AC_ARG_WITH(multiplayer, [ --with-multiplayer Include multiplayer support [default=yes]])
if test "x$with_multiplayer" = "xno"; then
echo "Building without any kind of multiplayer support"
else
echo "Building with multiplayer support"
AC_DEFINE([FG_MPLAYER_AS], 1, [Define to build with multiplayer support])
fi
AM_CONDITIONAL(ENABLE_MPLAYER_AS, test "x$with_multiplayer" != "xno")
AC_ARG_ENABLE(sp_fdms, [ --enable-sp-fdms Include special purpose Flight Models], [enable_sp_fdms="$enableval"] ) AC_ARG_ENABLE(sp_fdms, [ --enable-sp-fdms Include special purpose Flight Models], [enable_sp_fdms="$enableval"] )
AC_DEFINE([ENABLE_SP_FMDS], test "x$enable_sp_fdms" = "xyes", [Define to include special purpose FDMs]) AC_DEFINE([ENABLE_SP_FMDS], test "x$enable_sp_fdms" = "xyes", [Define to include special purpose FDMs])
AM_CONDITIONAL(ENABLE_SP_FDM, test "x$enable_sp_fdms" != "xno") AM_CONDITIONAL(ENABLE_SP_FDM, test "x$enable_sp_fdms" != "xno")

View file

@ -64,7 +64,6 @@ FGAIBase::FGAIBase(object_type ot)
invisible = true; invisible = true;
no_roll = true; no_roll = true;
life = 900; life = 900;
index = 0;
delete_me = false; delete_me = false;
} }
@ -74,10 +73,13 @@ FGAIBase::~FGAIBase() {
globals->get_scenery()->unregister_placement_transform(aip.getTransform()); globals->get_scenery()->unregister_placement_transform(aip.getTransform());
globals->get_scenery()->get_scene_graph()->removeKid(aip.getSceneGraph()); globals->get_scenery()->get_scene_graph()->removeKid(aip.getSceneGraph());
} }
SGPropertyNode *root = globals->get_props()->getNode("ai/models", true); if (props) {
root->removeChild(getTypeString(), index); SGPropertyNode* parent = props->getParent();
if (parent)
parent->removeChild(props->getName(), props->getIndex());
}
delete fp; delete fp;
fp = NULL; fp = 0;
} }
@ -121,7 +123,8 @@ bool FGAIBase::init() {
SGPropertyNode *root = globals->get_props()->getNode("ai/models", true); SGPropertyNode *root = globals->get_props()->getNode("ai/models", true);
index = manager->getNum(_otype) - 1; unsigned index = root->getChildren(getTypeString()).size();
props = root->getNode(getTypeString(), index, true); props = root->getNode(getTypeString(), index, true);
if (!model_path.empty()) { if (!model_path.empty()) {

View file

@ -131,8 +131,6 @@ protected:
void CalculateMach(); void CalculateMach();
double UpdateRadar(FGAIManager* manager); double UpdateRadar(FGAIManager* manager);
int index;
static int _newAIModelID(); static int _newAIModelID();
private: private:

View file

@ -34,10 +34,8 @@
#include "AIMultiplayer.hxx" #include "AIMultiplayer.hxx"
FGAIManager::FGAIManager() { FGAIManager::FGAIManager() {
for (int i=0; i < FGAIBase::MAX_OBJECTS; i++)
numObjects[i] = 0;
_dt = 0.0; _dt = 0.0;
scenario_filename = ""; mNumAiModels = 0;
} }
FGAIManager::~FGAIManager() { FGAIManager::~FGAIManager() {
@ -88,7 +86,8 @@ void FGAIManager::reinit() {
void FGAIManager::bind() { void FGAIManager::bind() {
root = globals->get_props()->getNode("ai/models", true); root = globals->get_props()->getNode("ai/models", true);
root->tie("count", SGRawValuePointer<int>(&numObjects[0])); root->tie("count", SGRawValueMethods<FGAIManager, int>(*this,
&FGAIManager::getNumAiObjects));
} }
@ -99,42 +98,34 @@ void FGAIManager::unbind() {
void FGAIManager::update(double dt) { void FGAIManager::update(double dt) {
// initialize these for finding nearest thermals // initialize these for finding nearest thermals
range_nearest = 10000.0; range_nearest = 10000.0;
strength = 0.0; strength = 0.0;
FGTrafficManager *tmgr = (FGTrafficManager*) globals->get_subsystem("Traffic Manager"); if (!enabled)
return;
if (!enabled) FGTrafficManager *tmgr = (FGTrafficManager*) globals->get_subsystem("Traffic Manager");
return; _dt = dt;
_dt = dt; ai_list_iterator ai_list_itr = ai_list.begin();
while(ai_list_itr != ai_list.end()) {
ai_list_iterator ai_list_itr = ai_list.begin(); if ((*ai_list_itr)->getDie()) {
while(ai_list_itr != ai_list.end()) { tmgr->release((*ai_list_itr)->getID());
if ((*ai_list_itr)->getDie()) { --mNumAiModels;
tmgr->release((*ai_list_itr)->getID()); (*ai_list_itr)->unbind();
--numObjects[(*ai_list_itr)->getType()]; ai_list_itr = ai_list.erase(ai_list_itr);
--numObjects[0]; } else {
(*ai_list_itr)->unbind(); fetchUserState();
if ( ai_list_itr == ai_list.begin() ) { if ((*ai_list_itr)->isa(FGAIBase::otThermal)) {
ai_list.erase(ai_list_itr); FGAIBase *base = *ai_list_itr;
ai_list_itr = ai_list.begin(); processThermal((FGAIThermal*)base);
continue; } else {
} else { (*ai_list_itr)->update(_dt);
ai_list.erase(ai_list_itr--); }
} ++ai_list_itr;
} else { }
fetchUserState(); }
if ((*ai_list_itr)->isa(FGAIBase::otThermal)) { wind_from_down_node->setDoubleValue( strength ); // for thermals
FGAIBase *base = *ai_list_itr;
processThermal((FGAIThermal*)base);
} else {
(*ai_list_itr)->update(_dt);
}
}
++ai_list_itr;
}
wind_from_down_node->setDoubleValue( strength ); // for thermals
} }
void void
@ -142,27 +133,29 @@ FGAIManager::attach(SGSharedPtr<FGAIBase> model)
{ {
model->setManager(this); model->setManager(this);
ai_list.push_back(model); ai_list.push_back(model);
++numObjects[0]; ++mNumAiModels;
++numObjects[model->getType()];
model->init(); model->init();
model->bind(); model->bind();
} }
void FGAIManager::destroyObject( int ID ) { void FGAIManager::destroyObject( int ID ) {
ai_list_iterator ai_list_itr = ai_list.begin(); ai_list_iterator ai_list_itr = ai_list.begin();
while(ai_list_itr != ai_list.end()) { while(ai_list_itr != ai_list.end()) {
if ((*ai_list_itr)->getID() == ID) { if ((*ai_list_itr)->getID() == ID) {
--numObjects[0]; --mNumAiModels;
--numObjects[(*ai_list_itr)->getType()]; (*ai_list_itr)->unbind();
(*ai_list_itr)->unbind(); ai_list_itr = ai_list.erase(ai_list_itr);
ai_list.erase(ai_list_itr); } else
break; ++ai_list_itr;
} }
++ai_list_itr;
}
} }
int
FGAIManager::getNumAiObjects(void) const
{
return mNumAiModels;
}
void FGAIManager::fetchUserState( void ) { void FGAIManager::fetchUserState( void ) {
user_latitude = user_latitude_node->getDoubleValue(); user_latitude = user_latitude_node->getDoubleValue();
@ -174,8 +167,6 @@ void FGAIManager::fetchUserState( void ) {
user_speed = user_speed_node->getDoubleValue() * 0.592484; user_speed = user_speed_node->getDoubleValue() * 0.592484;
wind_from_east = wind_from_east_node->getDoubleValue(); wind_from_east = wind_from_east_node->getDoubleValue();
wind_from_north = wind_from_north_node->getDoubleValue(); wind_from_north = wind_from_north_node->getDoubleValue();
} }

View file

@ -95,9 +95,7 @@ public:
inline double get_wind_from_east() const {return wind_from_east; } inline double get_wind_from_east() const {return wind_from_east; }
inline double get_wind_from_north() const {return wind_from_north; } inline double get_wind_from_north() const {return wind_from_north; }
inline int getNum( FGAIBase::object_type ot ) const { int getNumAiObjects(void) const;
return (0 < ot && ot < FGAIBase::MAX_OBJECTS) ? numObjects[ot] : numObjects[0];
}
void processScenario( const string &filename ); void processScenario( const string &filename );
@ -112,7 +110,8 @@ public:
private: private:
bool enabled; bool enabled;
int numObjects[FGAIBase::MAX_OBJECTS]; int mNumAiModels;
SGPropertyNode* root; SGPropertyNode* root;
SGPropertyNode* wind_from_down_node; SGPropertyNode* wind_from_down_node;
SGPropertyNode* user_latitude_node; SGPropertyNode* user_latitude_node;

View file

@ -3,7 +3,6 @@
// Based on FGAIAircraft // Based on FGAIAircraft
// Written by David Culp, started October 2003. // Written by David Culp, started October 2003.
// Also by Gregor Richards, started December 2005. // Also by Gregor Richards, started December 2005.
// With additions by Vivian Meazza, January 2006
// //
// Copyright (C) 2003 David P. Culp - davidculp2@comcast.net // Copyright (C) 2003 David P. Culp - davidculp2@comcast.net
// Copyright (C) 2005 Gregor Richards // Copyright (C) 2005 Gregor Richards
@ -26,51 +25,18 @@
# include <config.h> # include <config.h>
#endif #endif
#include <simgear/math/point3d.hxx>
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <Main/viewer.hxx>
#include <Scenery/scenery.hxx>
#include <Scenery/tilemgr.hxx>
#include <simgear/route/waypoint.hxx>
#include <string> #include <string>
#include <math.h>
#include <time.h>
#ifdef _MSC_VER
# include <float.h>
# define finite _finite
#elif defined(__sun) || defined(sgi)
# include <ieeefp.h>
#endif
SG_USING_STD(string);
#include "AIMultiplayer.hxx" #include "AIMultiplayer.hxx"
static string tempReg;
// #define SG_DEBUG SG_ALERT
FGAIMultiplayer::FGAIMultiplayer() : FGAIBase(otMultiplayer) { FGAIMultiplayer::FGAIMultiplayer() : FGAIBase(otMultiplayer) {
_time_node = fgGetNode("/sim/time/elapsed-sec", true); no_roll = false;
//initialise values mTimeOffsetSet = false;
speedN = speedE = rateH = rateR = rateP = 0.0; mAllowExtrapolation = true;
raw_hdg = hdg; mLagAdjustSystemSpeed = 10;
raw_roll = roll;
raw_pitch = pitch;
raw_speed_east_deg_sec = speedE / ft_per_deg_lon;
raw_speed_north_deg_sec = speedN / ft_per_deg_lat;
raw_lon = damp_lon = pos.lon();
raw_lat = damp_lat = pos.lat();
raw_alt = damp_alt = pos.elev() / SG_FEET_TO_METER;
//Exponentially weighted moving average time constants
speed_north_deg_sec_constant = speed_east_deg_sec_constant = 0.1;
alt_constant = 0.1;
lat_constant = 0.05;
lon_constant = 0.05;
hdg_constant = 0.1;
roll_constant = 0.1;
pitch_constant = 0.1;
} }
@ -83,192 +49,273 @@ bool FGAIMultiplayer::init() {
void FGAIMultiplayer::bind() { void FGAIMultiplayer::bind() {
FGAIBase::bind(); FGAIBase::bind();
props->setStringValue("callsign", company.c_str());
props->tie("controls/constants/roll", #define AIMPROProp(type, name) \
SGRawValuePointer<double>(&roll_constant)); SGRawValueMethods<FGAIMultiplayer, type>(*this, &FGAIMultiplayer::get##name)
props->tie("controls/constants/pitch",
SGRawValuePointer<double>(&pitch_constant));
props->tie("controls/constants/hdg",
SGRawValuePointer<double>(&hdg_constant));
props->tie("controls/constants/altitude",
SGRawValuePointer<double>(&alt_constant));
/*props->tie("controls/constants/speedE",
SGRawValuePointer<double>(&speed_east_deg_sec_constant));
props->tie("controls/constants/speedN",
SGRawValuePointer<double>(&speed_north_deg_sec_constant));*/
props->tie("controls/constants/lat",
SGRawValuePointer<double>(&lat_constant));
props->tie("controls/constants/lon",
SGRawValuePointer<double>(&lon_constant));
props->tie("surface-positions/rudder-pos-norm",
SGRawValuePointer<double>(&rudder));
props->tie("surface-positions/elevator-pos-norm",
SGRawValuePointer<double>(&elevator));
props->tie("velocities/speedE-fps",
SGRawValuePointer<double>(&speedE));
#define AIMPRWProp(type, name) \
SGRawValueMethods<FGAIMultiplayer, type>(*this, \
&FGAIMultiplayer::get##name, &FGAIMultiplayer::set##name)
props->setDoubleValue("sim/current-view/view-number", 1); props->tie("callsign", AIMPROProp(const char *, CallSign));
} props->tie("controls/allow-extrapolation",
AIMPRWProp(bool, AllowExtrapolation));
void FGAIMultiplayer::setCompany(string comp) { props->tie("controls/lag-adjust-system-speed",
company = comp; AIMPRWProp(double, LagAdjustSystemSpeed));
if (props)
props->setStringValue("callsign", company.c_str());
#undef AIMPROProp
#undef AIMPRWProp
} }
void FGAIMultiplayer::unbind() { void FGAIMultiplayer::unbind() {
FGAIBase::unbind(); FGAIBase::unbind();
props->untie("controls/constants/roll"); props->untie("callsign");
props->untie("controls/constants/pitch"); props->untie("controls/allow-extrapolation");
props->untie("controls/constants/hdg"); props->untie("controls/lag-adjust-system-speed");
props->untie("controls/constants/altitude");
/*props->untie("controls/constants/speedE");
props->untie("controls/constants/speedN");*/
props->untie("controls/constants/lat");
props->untie("controls/constants/lon");
props->untie("surface-positions/rudder-pos-norm");
props->untie("surface-positions/elevator-pos-norm");
props->untie("velocities/speedE-fps");
} }
void FGAIMultiplayer::update(double dt)
void FGAIMultiplayer::update(double dt) {
FGAIBase::update(dt);
Run(dt);
Transform();
}
void FGAIMultiplayer::Run(double dt) {
// strangely, this is called with a dt of 0 quite often
SG_LOG( SG_GENERAL, SG_DEBUG, "AIMultiplayer::main loop dt " << dt ) ;
//if (dt == 0) return;
//FGAIMultiplayer::dt = dt;
//double rhr, rha; // "real" heading radius/angle
// get the current sim elapsed time
double time =_time_node->getDoubleValue(); //secs
dt = 0;
//calulate the time difference, dt. Then use this value to extrapolate position and orientation
dt = time - time_stamp;
SG_LOG(SG_GENERAL, SG_DEBUG, "time: "
<< time << " timestamp: " << time_stamp << " dt: " << dt << " freq Hz: " << 1/dt);
// change heading/roll/pitch
raw_hdg = hdg + rateH * dt;
raw_roll = roll + rateR * dt;
raw_pitch = pitch + rateP * dt;
//apply lowpass filters
hdg = (raw_hdg * hdg_constant) + (hdg * (1 - hdg_constant));
roll = (raw_roll * roll_constant) + (roll * (1 - roll_constant));
pitch = (raw_pitch * pitch_constant) + (pitch * (1 - pitch_constant));
/*cout << "raw roll " << raw_roll <<" damp hdg " << roll << endl;
cout << "raw hdg" << raw_hdg <<" damp hdg " << hdg << endl;
cout << "raw pitch " << raw_pitch <<" damp pitch " << pitch << endl;*/
// sanitize HRP
while (hdg < 0) hdg += 360;
while (hdg >= 360) hdg -= 360;
while (roll <= -180) roll += 360;
while (roll > 180) roll -= 360;
while (pitch <= -180) pitch += 360;
while (pitch > 180) pitch -= 360;
// calculate the new accelerations by change in the rate of heading
/*rhr = sqrt(pow(accN,2) + pow(accE,2));
rha = atan2(accN, accE);
rha += rateH * dt;
accN = sin(rha);
accE = cos(rha);*/
// calculate new speed by acceleration
speedN += accN * dt;
speedE += accE * dt;
speedD += accD * dt;
// convert speed to degrees per second
// 1.686
speed_north_deg_sec = speedN / ft_per_deg_lat;
speed_east_deg_sec = speedE / ft_per_deg_lon;
// calculate new position by speed
raw_lat = pos.lat() + speed_north_deg_sec * dt;
raw_lon = pos.lon() + speed_east_deg_sec * dt ;
raw_alt = (pos.elev() / SG_FEET_TO_METER) + (speedD * dt);
//apply lowpass filters if the difference is small
if ( fabs ( pos.lat() - raw_lat) < 0.001 ) {
SG_LOG(SG_GENERAL, SG_DEBUG,"lat lowpass filter");
damp_lat = (raw_lat * lat_constant) + (damp_lat * (1 - lat_constant));
}else {
// skip the filter
SG_LOG(SG_GENERAL, SG_DEBUG,"lat high pass filter");
damp_lat = raw_lat;
}
if ( fabs ( pos.lon() - raw_lon) < 0.001 ) {
SG_LOG(SG_GENERAL, SG_DEBUG,"lon lowpass filter");
damp_lon = (raw_lon * lon_constant) + (damp_lon * (1 - lon_constant));
}else {
// skip the filter
SG_LOG(SG_GENERAL, SG_DEBUG,"lon high pass filter");
damp_lon = raw_lon;
}
if ( fabs ( (pos.elev()/SG_FEET_TO_METER) - raw_alt) < 10 ) {
SG_LOG(SG_GENERAL, SG_DEBUG,"alt lowpass filter");
damp_alt = (raw_alt * alt_constant) + (damp_alt * (1 - alt_constant));
}else {
// skip the filter
SG_LOG(SG_GENERAL, SG_DEBUG,"alt high pass filter");
damp_alt = raw_alt;
}
// cout << "raw lat" << raw_lat <<" damp lat " << damp_lat << endl;
//cout << "raw lon" << raw_lon <<" damp lon " << damp_lon << endl;
//cout << "raw alt" << raw_alt <<" damp alt " << damp_alt << endl;
// set new position
pos.setlat( damp_lat );
pos.setlon( damp_lon );
pos.setelev( damp_alt * SG_FEET_TO_METER );
//save the values
time_stamp = time;
//###########################//
// do calculations for radar //
//###########################//
//double range_ft2 = UpdateRadar(manager);
}
void FGAIMultiplayer::setTimeStamp()
{ {
// this function sets the timestamp as the sim elapsed time if (dt <= 0)
time_stamp = _time_node->getDoubleValue(); //secs return;
//calculate the elapsed time since the latst update for display purposes only FGAIBase::update(dt);
double elapsed_time = time_stamp - last_time_stamp;
SG_LOG( SG_GENERAL, SG_DEBUG, " net input time s" << time_stamp << " freq Hz: " << 1/elapsed_time ) ; // Check if we already got data
if (mMotionInfo.empty())
return;
//save the values // The current simulation time we need to update for,
last_time_stamp = time_stamp; // note that the simulation time is updated before calling all the
// update methods. Thus it contains the time intervals *end* time
double curtime = globals->get_sim_time_sec();
// Get the last available time
MotionInfo::reverse_iterator it = mMotionInfo.rbegin();
double curentPkgTime = it->second.time;
// Dynamically optimize the time offset between the feeder and the client
// Well, 'dynamically' means that the dynamic of that update must be very
// slow. You would otherwise notice huge jumps in the multiplayer models.
// The reason is that we want to avoid huge extrapolation times since
// extrapolation is highly error prone. For that we need something
// approaching the average latency of the packets. This first order lag
// component will provide this. We just take the error of the currently
// requested time to the most recent available packet. This is the
// target we want to reach in average.
double lag = it->second.lag;
if (!mTimeOffsetSet) {
mTimeOffsetSet = true;
mTimeOffset = curentPkgTime - curtime - lag;
} else {
double offset = curentPkgTime - curtime - lag;
if (!mAllowExtrapolation && offset + lag < mTimeOffset) {
mTimeOffset = offset;
SG_LOG(SG_GENERAL, SG_DEBUG, "Resetting time offset adjust system to "
"avoid extrapolation: time offset = " << mTimeOffset);
} else {
// the error of the offset, respectively the negative error to avoid
// a minus later ...
double err = offset - mTimeOffset;
// limit errors leading to shorter lag values somehow, that is late
// arriving packets will pessimize the overall lag much more than
// early packets will shorten the overall lag
double sysSpeed;
if (err < 0) {
// Ok, we have some very late packets and nothing newer increase the
// lag by the given speedadjust
sysSpeed = mLagAdjustSystemSpeed*err;
} else {
// We have a too pessimistic display delay shorten that a small bit
sysSpeed = SGMiscd::min(0.1*err*err, 0.5);
}
// simple euler integration for that first order system including some
// overshooting guard to prevent to aggressive system speeds
// (stiff systems) to explode the systems state
double systemIncrement = dt*sysSpeed;
if (fabs(err) < fabs(systemIncrement))
systemIncrement = err;
mTimeOffset += systemIncrement;
SG_LOG(SG_GENERAL, SG_DEBUG, "Offset adjust system: time offset = "
<< mTimeOffset << ", expected longitudinal position error due to "
" current adjustment of the offset: "
<< fabs(norm(it->second.linearVel)*systemIncrement));
}
}
// Compute the time in the feeders time scale which fits the current time
// we need to
double tInterp = curtime + mTimeOffset;
SGVec3d ecPos;
SGQuatf ecOrient;
if (tInterp <= curentPkgTime) {
// Ok, we need a time prevous to the last available packet,
// that is good ...
// Find the first packet before the target time
MotionInfo::iterator nextIt = mMotionInfo.upper_bound(tInterp);
if (nextIt == mMotionInfo.begin()) {
SG_LOG(SG_GENERAL, SG_DEBUG, "Taking oldest packet!");
// We have no packet before the target time, just use the first one
MotionInfo::iterator firstIt = mMotionInfo.begin();
ecPos = firstIt->second.position;
ecOrient = firstIt->second.orientation;
std::vector<FGFloatPropertyData>::const_iterator firstPropIt;
std::vector<FGFloatPropertyData>::const_iterator firstPropItEnd;
firstPropIt = firstIt->second.properties.begin();
firstPropItEnd = firstIt->second.properties.end();
while (firstPropIt != firstPropItEnd) {
float val = firstPropIt->value;
PropertyMap::iterator pIt = mPropertyMap.find(firstPropIt->id);
if (pIt != mPropertyMap.end())
pIt->second->setFloatValue(val);
++firstPropIt;
}
} else {
// Ok, we have really found something where our target time is in between
// do interpolation here
MotionInfo::iterator prevIt = nextIt;
--prevIt;
// Interpolation coefficient is between 0 and 1
double intervalStart = prevIt->second.time;
double intervalEnd = nextIt->second.time;
double intervalLen = intervalEnd - intervalStart;
double tau = (tInterp - intervalStart)/intervalLen;
SG_LOG(SG_GENERAL, SG_DEBUG, "Multiplayer vehicle interpolation: ["
<< intervalStart << ", " << intervalEnd << "], intervalLen = "
<< intervalLen << ", interpolation parameter = " << tau);
// Here we do just linear interpolation on the position
ecPos = ((1-tau)*prevIt->second.position + tau*nextIt->second.position);
ecOrient = interpolate((float)tau, prevIt->second.orientation,
nextIt->second.orientation);
if (prevIt->second.properties.size()
== nextIt->second.properties.size()) {
std::vector<FGFloatPropertyData>::const_iterator prevPropIt;
std::vector<FGFloatPropertyData>::const_iterator prevPropItEnd;
std::vector<FGFloatPropertyData>::const_iterator nextPropIt;
std::vector<FGFloatPropertyData>::const_iterator nextPropItEnd;
prevPropIt = prevIt->second.properties.begin();
prevPropItEnd = prevIt->second.properties.end();
nextPropIt = nextIt->second.properties.begin();
nextPropItEnd = nextIt->second.properties.end();
while (prevPropIt != prevPropItEnd) {
float val = (1-tau)*prevPropIt->value + tau*nextPropIt->value;
PropertyMap::iterator pIt = mPropertyMap.find(prevPropIt->id);
if (pIt != mPropertyMap.end())
pIt->second->setFloatValue(val);
++prevPropIt;
++nextPropIt;
}
}
// Now throw away too old data
if (prevIt != mMotionInfo.begin()) {
--prevIt;
mMotionInfo.erase(mMotionInfo.begin(), prevIt);
}
}
} else {
// Ok, we need to predict the future, so, take the best data we can have
// and do some eom computation to guess that for now.
FGExternalMotionData motionInfo = it->second;
// The time to predict, limit to 5 seconds
double t = tInterp - motionInfo.time;
t = SGMisc<double>::min(t, 5);
SG_LOG(SG_GENERAL, SG_DEBUG, "Multiplayer vehicle extrapolation: "
"extrapolation time = " << t);
// Do a few explicit euler steps with the constant acceleration's
// This must be sufficient ...
ecPos = motionInfo.position;
ecOrient = motionInfo.orientation;
SGVec3f linearVel = motionInfo.linearVel;
SGVec3f angularVel = motionInfo.angularVel;
while (0 < t) {
double h = 1e-1;
if (t < h)
h = t;
SGVec3d ecVel = toVec3d(ecOrient.backTransform(linearVel));
ecPos += h*ecVel;
ecOrient += h*ecOrient.derivative(angularVel);
linearVel += h*(cross(linearVel, angularVel) + motionInfo.linearAccel);
angularVel += h*motionInfo.angularAccel;
t -= h;
}
std::vector<FGFloatPropertyData>::const_iterator firstPropIt;
std::vector<FGFloatPropertyData>::const_iterator firstPropItEnd;
firstPropIt = it->second.properties.begin();
firstPropItEnd = it->second.properties.end();
while (firstPropIt != firstPropItEnd) {
float val = firstPropIt->value;
PropertyMap::iterator pIt = mPropertyMap.find(firstPropIt->id);
if (pIt != mPropertyMap.end())
pIt->second->setFloatValue(val);
++firstPropIt;
}
}
// extract the position
SGGeod geod = ecPos;
pos.setlat(geod.getLatitudeDeg());
pos.setlon(geod.getLongitudeDeg());
pos.setelev(geod.getElevationM());
// The quaternion rotating from the earth centered frame to the
// horizontal local frame
SGQuatf qEc2Hl = SGQuatf::fromLonLat((float)geod.getLongitudeRad(),
(float)geod.getLatitudeRad());
// The orientation wrt the horizontal local frame
SGQuatf hlOr = conj(qEc2Hl)*ecOrient;
float hDeg, pDeg, rDeg;
hlOr.getEulerDeg(hDeg, pDeg, rDeg);
hdg = hDeg;
roll = rDeg;
pitch = pDeg;
SG_LOG(SG_GENERAL, SG_DEBUG, "Multiplayer position and orientation: "
<< geod << ", " << hlOr);
//###########################//
// do calculations for radar //
//###########################//
UpdateRadar(manager);
Transform();
}
void
FGAIMultiplayer::addMotionInfo(const FGExternalMotionData& motionInfo,
long stamp)
{
mLastTimestamp = stamp;
// Drop packets arriving out of order
if (!mMotionInfo.empty() && motionInfo.time < mMotionInfo.rbegin()->first)
return;
mMotionInfo[motionInfo.time] = motionInfo;
}
void
FGAIMultiplayer::setDoubleProperty(const std::string& prop, double val)
{
SGPropertyNode* pNode = props->getChild(prop.c_str(), true);
pNode->setDoubleValue(val);
} }

View file

@ -1,7 +1,6 @@
// FGAIMultiplayer - AIBase derived class creates an AI multiplayer aircraft // FGAIMultiplayer - AIBase derived class creates an AI multiplayer aircraft
// //
// Written by David Culp, started October 2003. // Written by David Culp, started October 2003.
// With additions by Vivian Meazza
// //
// Copyright (C) 2003 David P. Culp - davidculp2@comcast.net // Copyright (C) 2003 David P. Culp - davidculp2@comcast.net
// //
@ -22,82 +21,72 @@
#ifndef _FG_AIMultiplayer_HXX #ifndef _FG_AIMultiplayer_HXX
#define _FG_AIMultiplayer_HXX #define _FG_AIMultiplayer_HXX
#include "AIManager.hxx" #include <map>
#include <string>
#include <MultiPlayer/mpmessages.hxx>
#include "AIBase.hxx" #include "AIBase.hxx"
//#include <Traffic/SchedFlight.hxx>
//#include <Traffic/Schedule.hxx>
#include <string>
SG_USING_STD(string);
class FGAIMultiplayer : public FGAIBase { class FGAIMultiplayer : public FGAIBase {
public:
FGAIMultiplayer();
virtual ~FGAIMultiplayer();
public: virtual bool init();
FGAIMultiplayer(); virtual void bind();
~FGAIMultiplayer(); virtual void unbind();
virtual void update(double dt);
bool init(); void addMotionInfo(const FGExternalMotionData& motionInfo, long stamp);
virtual void bind(); void setDoubleProperty(const std::string& prop, double val);
virtual void unbind();
void update(double dt);
void setCallSign(const string& callSign)
{ mCallSign = callSign; }
const char* getCallSign(void) const
{ return mCallSign.c_str(); }
void setSpeedN(double sn); long getLastTimestamp(void) const
void setSpeedE(double se); { return mLastTimestamp; }
void setSpeedD(double sd);
void setAccN(double an);
void setAccE(double ae);
void setAccD(double ad);
void setRateH(double rh);
void setRateR(double rr);
void setRateP(double rp);
void setRudder( double r ) { rudder = r;}
void setElevator( double e ) { elevator = e; }
void setLeftAileron( double la ) { left_aileron = la; }
void setRightAileron( double ra ) { right_aileron = ra; }
void setTimeStamp();
inline SGPropertyNode *FGAIMultiplayer::getProps() { return props; } void setAllowExtrapolation(bool allowExtrapolation)
{ mAllowExtrapolation = allowExtrapolation; }
bool getAllowExtrapolation(void) const
{ return mAllowExtrapolation; }
void setLagAdjustSystemSpeed(double lagAdjustSystemSpeed)
{
if (lagAdjustSystemSpeed < 0)
lagAdjustSystemSpeed = 0;
mLagAdjustSystemSpeed = lagAdjustSystemSpeed;
}
double getLagAdjustSystemSpeed(void) const
{ return mLagAdjustSystemSpeed; }
void setAcType(string ac) { acType = ac; }; void addPropertyId(unsigned id, const char* name)
void setCompany(string comp); { mPropertyMap[id] = props->getNode(name, true); }
virtual const char* getTypeString(void) const { return "multiplayer"; } virtual const char* getTypeString(void) const { return "multiplayer"; }
double dt; private:
double speedN, speedE, speedD;
double rateH, rateR, rateP;
double raw_hdg , raw_roll , raw_pitch ;
double raw_speed_north_deg_sec, raw_speed_east_deg_sec;
double raw_lat, damp_lat, lat_constant;
double raw_lon, damp_lon, lon_constant;
double raw_alt, damp_alt, alt_constant;
double hdg_constant, roll_constant, pitch_constant;
double speed_north_deg_sec_constant, speed_east_deg_sec_constant;
double speed_north_deg_sec, speed_east_deg_sec;
double accN, accE, accD;
double rudder, elevator, left_aileron, right_aileron;
double time_stamp, last_time_stamp;
SGPropertyNode_ptr _time_node; // Automatic sorting of motion data according to its timestamp
typedef std::map<double,FGExternalMotionData> MotionInfo;
MotionInfo mMotionInfo;
void Run(double dt); // Map between the property id's from the multiplayers network packets
inline double sign(double x) { return (x < 0.0) ? -1.0 : 1.0; } // and the property nodes
typedef std::map<unsigned, SGSharedPtr<SGPropertyNode> > PropertyMap;
PropertyMap mPropertyMap;
string acType; std::string mCallSign;
string company;
double mTimeOffset;
bool mTimeOffsetSet;
/// Properties which are for now exposed for testing
bool mAllowExtrapolation;
double mLagAdjustSystemSpeed;
long mLastTimestamp;
}; };
inline void FGAIMultiplayer::setSpeedN(double sn) { speedN = sn; }
inline void FGAIMultiplayer::setSpeedE(double se) { speedE = se; }
inline void FGAIMultiplayer::setSpeedD(double sd) { speedD = sd; }
inline void FGAIMultiplayer::setAccN(double an) { accN = an; }
inline void FGAIMultiplayer::setAccE(double ae) { accE = ae; }
inline void FGAIMultiplayer::setAccD(double ad) { accD = ad; }
inline void FGAIMultiplayer::setRateH(double rh) { rateH = rh; }
inline void FGAIMultiplayer::setRateR(double rr) { rateR = rr; }
inline void FGAIMultiplayer::setRateP(double rp) { rateP = rp; }
#endif // _FG_AIMultiplayer_HXX #endif // _FG_AIMultiplayer_HXX

View file

@ -1,11 +1,7 @@
EXTRA_DIST = 3dfx.sh runfgfs.in runfgfs.bat.in \ EXTRA_DIST = 3dfx.sh runfgfs.in runfgfs.bat.in \
fg_os_sdl.cxx fg_os.cxx fg_os.hxx fg_os_sdl.cxx fg_os.cxx fg_os.hxx
if ENABLE_MPLAYER_AS
MPLAYER_LIBS = $(top_builddir)/src/MultiPlayer/libMultiPlayer.a MPLAYER_LIBS = $(top_builddir)/src/MultiPlayer/libMultiPlayer.a
else
MPLAYER_LIBS =
endif
if ENABLE_SP_FDM if ENABLE_SP_FDM
SP_FDM_LIBS = $(top_builddir)/src/FDM/SP/libSPFDM.a SP_FDM_LIBS = $(top_builddir)/src/FDM/SP/libSPFDM.a

View file

@ -111,10 +111,7 @@
#include <Time/sunsolver.hxx> #include <Time/sunsolver.hxx>
#include <Time/tmp.hxx> #include <Time/tmp.hxx>
#include <Traffic/TrafficMgr.hxx> #include <Traffic/TrafficMgr.hxx>
#ifdef FG_MPLAYER_AS
#include <MultiPlayer/multiplaymgr.hxx> #include <MultiPlayer/multiplaymgr.hxx>
#endif
#include <Environment/environment_mgr.hxx> #include <Environment/environment_mgr.hxx>
@ -1805,14 +1802,12 @@ bool fgInitSubsystems() {
globals->get_subsystem_mgr()->bind(); globals->get_subsystem_mgr()->bind();
globals->get_subsystem_mgr()->init(); globals->get_subsystem_mgr()->init();
#ifdef FG_MPLAYER_AS
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Initialize multiplayer subsystem // Initialize multiplayer subsystem
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
globals->set_multiplayer_mgr(new FGMultiplayMgr); globals->set_multiplayer_mgr(new FGMultiplayMgr);
globals->get_multiplayer_mgr()->init(); globals->get_multiplayer_mgr()->init();
#endif
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// Initialize the Nasal interpreter. // Initialize the Nasal interpreter.

View file

@ -58,10 +58,7 @@
#include <Network/ray.hxx> #include <Network/ray.hxx>
#include <Network/rul.hxx> #include <Network/rul.hxx>
#include <Network/generic.hxx> #include <Network/generic.hxx>
#ifdef FG_MPLAYER_AS
#include <Network/multiplay.hxx> #include <Network/multiplay.hxx>
#endif
#include "globals.hxx" #include "globals.hxx"
#include "fg_io.hxx" #include "fg_io.hxx"
@ -182,8 +179,6 @@ FGIO::parse_port_config( const string& config )
else if (tokens[1] == "file") n--; else if (tokens[1] == "file") n--;
FGGeneric *generic = new FGGeneric( tokens[n] ); FGGeneric *generic = new FGGeneric( tokens[n] );
io = generic; io = generic;
#ifdef FG_MPLAYER_AS
} else if ( protocol == "multiplay" ) {\ } else if ( protocol == "multiplay" ) {\
//Determine dir, rate, host & port //Determine dir, rate, host & port
string dir = tokens[1]; string dir = tokens[1];
@ -191,8 +186,6 @@ FGIO::parse_port_config( const string& config )
string host = tokens[3]; string host = tokens[3];
string port = tokens[4]; string port = tokens[4];
return new FGMultiplay(dir, atoi(rate.c_str()), host, atoi(port.c_str())); return new FGMultiplay(dir, atoi(rate.c_str()), host, atoi(port.c_str()));
#endif
} else { } else {
return NULL; return NULL;
} }

View file

@ -96,12 +96,12 @@ FGGlobals::FGGlobals() :
FGGlobals::~FGGlobals() FGGlobals::~FGGlobals()
{ {
delete soundmgr; delete soundmgr;
delete io;
delete subsystem_mgr; delete subsystem_mgr;
delete event_mgr; delete event_mgr;
delete initial_state; delete initial_state;
delete props; delete props;
delete commands; delete commands;
delete io;
delete renderer; delete renderer;
delete initial_waypoints; delete initial_waypoints;
} }

View file

@ -79,9 +79,7 @@ class FGLight;
class FGModelMgr; class FGModelMgr;
class FGRouteMgr; class FGRouteMgr;
class FGScenery; class FGScenery;
#ifdef FG_MPLAYER_AS
class FGMultiplayMgr; class FGMultiplayMgr;
#endif
class FGPanel; class FGPanel;
class FGTileMgr; class FGTileMgr;
class FGViewMgr; class FGViewMgr;
@ -210,11 +208,8 @@ private:
FGTACANList *channellist; FGTACANList *channellist;
FGFixList *fixlist; FGFixList *fixlist;
#ifdef FG_MPLAYER_AS
//Mulitplayer managers //Mulitplayer managers
FGMultiplayMgr *multiplayer_mgr; FGMultiplayMgr *multiplayer_mgr;
#endif
public: public:
@ -326,7 +321,6 @@ public:
model_mgr = mgr; model_mgr = mgr;
} }
#ifdef FG_MPLAYER_AS
inline FGMultiplayMgr *get_multiplayer_mgr () { return multiplayer_mgr; } inline FGMultiplayMgr *get_multiplayer_mgr () { return multiplayer_mgr; }
inline void set_multiplayer_mgr (FGMultiplayMgr * mgr) inline void set_multiplayer_mgr (FGMultiplayMgr * mgr)
@ -334,8 +328,6 @@ public:
multiplayer_mgr = mgr; multiplayer_mgr = mgr;
} }
#endif
inline string_list *get_channel_options_list () { inline string_list *get_channel_options_list () {
return channel_options_list; return channel_options_list;
} }

View file

@ -74,12 +74,7 @@
#include <Time/fg_timer.hxx> #include <Time/fg_timer.hxx>
#include <Environment/environment_mgr.hxx> #include <Environment/environment_mgr.hxx>
#include <GUI/new_gui.hxx> #include <GUI/new_gui.hxx>
#ifdef FG_MPLAYER_AS
#include <MultiPlayer/multiplaymgr.hxx> #include <MultiPlayer/multiplaymgr.hxx>
#endif
#include "fg_commands.hxx" #include "fg_commands.hxx"
#include "fg_io.hxx" #include "fg_io.hxx"
@ -454,19 +449,20 @@ static void fgMainLoop( void ) {
++frames; ++frames;
#endif #endif
// Update any multiplayer's network queues, the AIMultiplayer
// implementation is an AI model and depends on that
globals->get_multiplayer_mgr()->Update();
// Run ATC subsystem // Run ATC subsystem
if (fgGetBool("/sim/atc/enabled")) if (fgGetBool("/sim/atc/enabled"))
globals->get_ATC_mgr()->update(delta_time_sec); globals->get_ATC_mgr()->update(delta_time_sec);
// Run the AI subsystem // Run the AI subsystem
// FIXME: run that also if we have multiplying enabled since the
// multiplayer information is interpreted by an AI model
if (fgGetBool("/sim/ai-traffic/enabled")) if (fgGetBool("/sim/ai-traffic/enabled"))
globals->get_AI_mgr()->update(delta_time_sec); globals->get_AI_mgr()->update(delta_time_sec);
#ifdef FG_MPLAYER_AS
// Update any multiplayer models
globals->get_multiplayer_mgr()->Update();
#endif
// Run flight model // Run flight model
// Calculate model iterations needed for next frame // Calculate model iterations needed for next frame

View file

@ -230,14 +230,11 @@ fgSetDefaults ()
fgSetBool("/sim/freeze/clock", false); fgSetBool("/sim/freeze/clock", false);
fgSetBool("/sim/freeze/fuel", false); fgSetBool("/sim/freeze/fuel", false);
#ifdef FG_MPLAYER_AS
fgSetString("/sim/multiplay/callsign", "callsign"); fgSetString("/sim/multiplay/callsign", "callsign");
fgSetString("/sim/multiplay/rxhost", "0"); fgSetString("/sim/multiplay/rxhost", "0");
fgSetString("/sim/multiplay/txhost", "0"); fgSetString("/sim/multiplay/txhost", "0");
fgSetInt("/sim/multiplay/rxport", 0); fgSetInt("/sim/multiplay/rxport", 0);
fgSetInt("/sim/multiplay/txport", 0); fgSetInt("/sim/multiplay/txport", 0);
#endif
} }
static bool static bool
@ -1343,10 +1340,8 @@ struct OptionDesc {
{"joyclient", true, OPTION_CHANNEL, "", false, "", 0 }, {"joyclient", true, OPTION_CHANNEL, "", false, "", 0 },
{"jsclient", true, OPTION_CHANNEL, "", false, "", 0 }, {"jsclient", true, OPTION_CHANNEL, "", false, "", 0 },
{"proxy", true, OPTION_FUNC, "", false, "", fgSetupProxy }, {"proxy", true, OPTION_FUNC, "", false, "", fgSetupProxy },
#ifdef FG_MPLAYER_AS
{"callsign", true, OPTION_STRING, "sim/multiplay/callsign", false, "", 0 }, {"callsign", true, OPTION_STRING, "sim/multiplay/callsign", false, "", 0 },
{"multiplay", true, OPTION_CHANNEL, "", false, "", 0 }, {"multiplay", true, OPTION_CHANNEL, "", false, "", 0 },
#endif
{"trace-read", true, OPTION_FUNC, "", false, "", fgOptTraceRead }, {"trace-read", true, OPTION_FUNC, "", false, "", fgOptTraceRead },
{"trace-write", true, OPTION_FUNC, "", false, "", fgOptTraceWrite }, {"trace-write", true, OPTION_FUNC, "", false, "", fgOptTraceWrite },
{"log-level", true, OPTION_FUNC, "", false, "", fgOptLogLevel }, {"log-level", true, OPTION_FUNC, "", false, "", fgOptLogLevel },

View file

@ -83,7 +83,6 @@ FGAircraftModel::~FGAircraftModel ()
globals->get_scenery()->unregister_placement_transform(_aircraft->getTransform()); globals->get_scenery()->unregister_placement_transform(_aircraft->getTransform());
delete _aircraft; delete _aircraft;
delete _scene;
// SSG will delete it // SSG will delete it
globals->get_scenery()->get_aircraft_branch()->removeKid(_selector); globals->get_scenery()->get_aircraft_branch()->removeKid(_selector);
} }

View file

@ -1,6 +1,6 @@
noinst_LIBRARIES = libMultiPlayer.a noinst_LIBRARIES = libMultiPlayer.a
libMultiPlayer_a_SOURCES = multiplaymgr.cxx multiplaymgr.hxx mpplayer.cxx mpplayer.hxx mpmessages.hxx tiny_xdr.cxx tiny_xdr.hxx libMultiPlayer_a_SOURCES = multiplaymgr.cxx multiplaymgr.hxx mpmessages.hxx tiny_xdr.cxx tiny_xdr.hxx
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src

View file

@ -4,8 +4,6 @@
// Written by Duncan McCreanor, started February 2003. // Written by Duncan McCreanor, started February 2003.
// duncan.mccreanor@airservicesaustralia.com // duncan.mccreanor@airservicesaustralia.com
// //
// With minor additions be Vivian Meazza, January 2006
//
// Copyright (C) 2003 Airservices Australia // Copyright (C) 2003 Airservices Australia
// //
// This program is free software; you can redistribute it and/or // This program is free software; you can redistribute it and/or
@ -37,8 +35,11 @@
* *
******************************************************************/ ******************************************************************/
#include <vector>
#include <plib/sg.h> #include <plib/sg.h>
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include <simgear/math/SGMath.hxx>
#include "tiny_xdr.hxx" #include "tiny_xdr.hxx"
// magic value for messages // magic value for messages
@ -49,9 +50,11 @@ const uint32_t PROTO_VER = 0x00010001; // 1.1
// Message identifiers // Message identifiers
#define CHAT_MSG_ID 1 #define CHAT_MSG_ID 1
#define UNUSABLE_POS_DATA_ID 2 #define UNUSABLE_POS_DATA_ID 2
#define OLD_POS_DATA_ID 3 #define OLD_OLD_POS_DATA_ID 3
#define POS_DATA_ID 4 #define OLD_POS_DATA_ID 4
#define PROP_MSG_ID 5 #define OLD_PROP_MSG_ID 5
#define RESET_DATA_ID 6
#define POS_DATA_ID 7
// XDR demands 4 byte alignment, but some compilers use8 byte alignment // XDR demands 4 byte alignment, but some compilers use8 byte alignment
// so it's safe to let the overall size of a network message be a // so it's safe to let the overall size of a network message be a
@ -61,13 +64,8 @@ const uint32_t PROTO_VER = 0x00010001; // 1.1
#define MAX_MODEL_NAME_LEN 96 #define MAX_MODEL_NAME_LEN 96
#define MAX_PROPERTY_LEN 52 #define MAX_PROPERTY_LEN 52
/** Aircraft position message */
typedef xdr_data2_t xdrPosition[3];
typedef xdr_data_t xdrOrientation[4];
// Header for use with all messages sent // Header for use with all messages sent
class T_MsgHdr { struct T_MsgHdr {
public:
xdr_data_t Magic; // Magic Value xdr_data_t Magic; // Magic Value
xdr_data_t Version; // Protocoll version xdr_data_t Version; // Protocoll version
xdr_data_t MsgId; // Message identifier xdr_data_t MsgId; // Message identifier
@ -78,50 +76,80 @@ public:
}; };
// Chat message // Chat message
class T_ChatMsg { struct T_ChatMsg {
public:
char Text[MAX_CHAT_MSG_LEN]; // Text of chat message char Text[MAX_CHAT_MSG_LEN]; // Text of chat message
}; };
// Position message // Position message
class T_PositionMsg { struct T_PositionMsg {
public:
char Model[MAX_MODEL_NAME_LEN]; // Name of the aircraft model char Model[MAX_MODEL_NAME_LEN]; // Name of the aircraft model
xdr_data_t time; // Time when this packet was generated
xdr_data_t timeusec; // Microsecs when this packet was generated // Time when this packet was generated
xdr_data2_t lat; // Position, orientation, speed xdr_data2_t time;
xdr_data2_t lon; // ... xdr_data2_t lag;
xdr_data2_t alt; // ...
xdr_data2_t hdg; // ... // position wrt the earth centered frame
xdr_data2_t roll; // ... xdr_data2_t position[3];
xdr_data2_t pitch; // ... // orientation wrt the earth centered frame, stored in the angle axis
xdr_data2_t speedN; // ... // representation where the angle is coded into the axis length
xdr_data2_t speedE; // ... xdr_data_t orientation[3];
xdr_data2_t speedD; // ...
xdr_data_t accN; // acceleration N // linear velocity wrt the earth centered frame measured in
xdr_data_t accE; // acceleration E // the earth centered frame
xdr_data_t accD; // acceleration D xdr_data_t linearVel[3];
xdr_data_t left_aileron; // control positions // angular velocity wrt the earth centered frame measured in
xdr_data_t right_aileron; // control positions // the earth centered frame
xdr_data_t elevator; // ... xdr_data_t angularVel[3];
xdr_data_t rudder; // ...
// xdr_data_t rpms[6]; // RPMs of all of the motors // linear acceleration wrt the earth centered frame measured in
xdr_data_t rateH; // Rate of change of heading // the earth centered frame
xdr_data_t rateR; // roll xdr_data_t linearAccel[3];
xdr_data_t rateP; // and pitch // angular acceleration wrt the earth centered frame measured in
// xdr_data_t dummy; // pad message length // the earth centered frame
xdr_data_t angularAccel[3];
}; };
// Property message // Property message
class T_PropertyMsg { struct T_PropertyMsg {
public: xdr_data_t id;
char property[MAX_PROPERTY_LEN]; // the property name xdr_data_t value;
xdr_data_t type; // the type };
xdr_data2_t val; // and value
// xdr_data2_t dummy; // pad message length
struct FGFloatPropertyData {
unsigned id;
float value;
};
// Position message
struct FGExternalMotionData {
// simulation time when this packet was generated
double time;
// the artificial lag the client should stay behind the average
// simulation time to arrival time diference
// FIXME: should be some 'per model' instead of 'per packet' property
double lag;
// position wrt the earth centered frame
SGVec3d position;
// orientation wrt the earth centered frame
SGQuatf orientation;
// linear velocity wrt the earth centered frame measured in
// the earth centered frame
SGVec3f linearVel;
// angular velocity wrt the earth centered frame measured in
// the earth centered frame
SGVec3f angularVel;
// linear acceleration wrt the earth centered frame measured in
// the earth centered frame
SGVec3f linearAccel;
// angular acceleration wrt the earth centered frame measured in
// the earth centered frame
SGVec3f angularAccel;
// The set of properties recieved for this timeslot
std::vector<FGFloatPropertyData> properties;
}; };
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -31,7 +31,6 @@
#define MULTIPLAYTXMGR_HID "$Id$" #define MULTIPLAYTXMGR_HID "$Id$"
#include "mpplayer.hxx"
#include "mpmessages.hxx" #include "mpmessages.hxx"
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -48,52 +47,48 @@ SG_USING_STD(vector);
#include <plib/netSocket.h> #include <plib/netSocket.h>
#include <Main/globals.hxx> #include <Main/globals.hxx>
// Maximum number of players that can exist at any time #include <AIModel/AIMultiplayer.hxx>
// FIXME: use a list<mplayer> instead
#define MAX_PLAYERS 10 struct FGExternalMotionInfo;
class FGMultiplayMgr class FGMultiplayMgr
{ {
public: public:
FGMultiplayMgr(); struct IdPropertyList {
~FGMultiplayMgr(); unsigned id;
bool init(void); const char* name;
void Close(void); };
// transmitter static IdPropertyList sIdPropertyList[];
void SendMyPosition (const double lat, const double lon, const double alt,
const double heading, const double roll, const double pitch,
const double speedN, const double speedE, const double speedD,
const double left_aileron, const double right_aileron, const double elevator, const double rudder,
//const double rpms[6],
const double rateH, const double rateR, const double rateP,
const double accN, const double accE, const double accD
);
void SendPropMessage (const string &property, SGPropertyNode::Type type, double value);
void SendTextMessage (const string &sMsgText) const;
// receiver
void ProcessData(void);
void ProcessPosMsg ( const char *Msg, netAddress & SenderAddress );
void ProcessChatMsg ( const char *Msg, netAddress & SenderAddress );
void ProcessPropMsg ( const char *Msg, netAddress & SenderAddress );
void Update(void);
/* getters/setters */ FGMultiplayMgr();
bool getSendAllProps(); ~FGMultiplayMgr();
void setSendAllProps(bool s); bool init(void);
bool send_all_props; void Close(void);
// transmitter
void SendMyPosition(const FGExternalMotionData& motionInfo);
void SendTextMessage(const string &sMsgText);
void FillMsgHdr(T_MsgHdr *MsgHdr, int iMsgId, unsigned _len = 0u);
// receiver
void ProcessPosMsg(const char *Msg, netAddress & SenderAddress,
unsigned len, long stamp);
void ProcessChatMsg(const char *Msg, netAddress & SenderAddress);
void Update(void);
private: private:
typedef vector<MPPlayer*> t_MPClientList; FGAIMultiplayer* addMultiplayer(const std::string& callsign,
typedef t_MPClientList::iterator t_MPClientListIterator; const std::string& modelName);
MPPlayer* m_LocalPlayer; FGAIMultiplayer* getMultiplayer(const std::string& callsign);
netSocket* m_DataSocket;
netAddress m_Server; /// maps from the callsign string to the FGAIMultiplayer
bool m_HaveServer; typedef std::map<std::string, SGSharedPtr<FGAIMultiplayer> > MultiPlayerMap;
bool m_Initialised; MultiPlayerMap mMultiPlayerMap;
t_MPClientList m_MPClientList;
string m_RxAddress; netSocket* mSocket;
int m_RxPort; netAddress mServer;
string m_Callsign; bool mHaveServer;
bool mInitialised;
string mCallsign;
}; };
#endif #endif

View file

@ -11,7 +11,10 @@
// //
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
#include <string>
#include <plib/ul.h> #include <plib/ul.h>
#include <plib/netSocket.h>
#include "tiny_xdr.hxx" #include "tiny_xdr.hxx"
@ -168,4 +171,3 @@ XDR_decode_double ( const xdr_data2_t & d_Val )
return tmp.d; return tmp.d;
} }

View file

@ -9,11 +9,7 @@ else
JPEG_SERVER = JPEG_SERVER =
endif endif
if ENABLE_MPLAYER_AS
MPLAYER_AS = multiplay.cxx multiplay.hxx MPLAYER_AS = multiplay.cxx multiplay.hxx
else
MPLAYER_AS =
endif
libNetwork_a_SOURCES = \ libNetwork_a_SOURCES = \
protocol.cxx protocol.hxx \ protocol.cxx protocol.hxx \

View file

@ -35,10 +35,10 @@
#include <string> #include <string>
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
#include <simgear/scene/model/placement.hxx> #include <simgear/math/SGMath.hxx>
#include <simgear/scene/model/placementtrans.hxx>
#include <Scenery/scenery.hxx> #include <FDM/flight.hxx>
#include <MultiPlayer/mpmessages.hxx>
#include "multiplay.hxx" #include "multiplay.hxx"
@ -57,123 +57,22 @@ const char sFG_MULTIPLAY_HID[] = FG_MULTIPLAY_HID;
******************************************************************/ ******************************************************************/
FGMultiplay::FGMultiplay (const string &dir, const int rate, const string &host, const int port) { FGMultiplay::FGMultiplay (const string &dir, const int rate, const string &host, const int port) {
last_time = 0; set_hz(rate);
last_speedN = last_speedE = last_speedD = 0;
calcaccN = calcaccE = calcaccD = 0;
set_hz(rate);
set_direction(dir); set_direction(dir);
if (get_direction() == SG_IO_IN) { if (get_direction() == SG_IO_IN) {
fgSetInt("/sim/multiplay/rxport", port); fgSetInt("/sim/multiplay/rxport", port);
fgSetString("/sim/multiplay/rxhost", host.c_str()); fgSetString("/sim/multiplay/rxhost", host.c_str());
} else if (get_direction() == SG_IO_OUT) { } else if (get_direction() == SG_IO_OUT) {
fgSetInt("/sim/multiplay/txport", port); fgSetInt("/sim/multiplay/txport", port);
fgSetString("/sim/multiplay/txhost", host.c_str()); fgSetString("/sim/multiplay/txhost", host.c_str());
} }
lat_n = fgGetNode("/position/latitude-deg", true);
lon_n = fgGetNode("/position/longitude-deg", true);
alt_n = fgGetNode("/position/altitude-ft", true);
heading_n = fgGetNode("/orientation/heading-deg", true);
roll_n = fgGetNode("/orientation/roll-deg", true);
pitch_n = fgGetNode("/orientation/pitch-deg", true);
speedN_n = fgGetNode("/velocities/speed-north-fps", true);
speedE_n = fgGetNode("/velocities/speed-east-fps", true);
speedD_n = fgGetNode("/velocities/speed-down-fps", true);
left_aileron_n = fgGetNode("/surface-positions/left-aileron-pos-norm", true);
right_aileron_n = fgGetNode("/surface-positions/right-aileron-pos-norm", true);
elevator_n = fgGetNode("/surface-positions/elevator-pos-norm", true);
rudder_n = fgGetNode("/surface-positions/rudder-pos-norm", true);
/*rpms_n[0] = fgGetNode("/engines/engine/rpm", true);
rpms_n[1] = fgGetNode("/engines/engine[1]/rpm", true);
rpms_n[2] = fgGetNode("/engines/engine[2]/rpm", true);
rpms_n[3] = fgGetNode("/engines/engine[3]/rpm", true);
rpms_n[4] = fgGetNode("/engines/engine[4]/rpm", true);
rpms_n[5] = fgGetNode("/engines/engine[5]/rpm", true);*/
rateH_n = fgGetNode("/orientation/yaw-rate-degps", true);
rateR_n = fgGetNode("/orientation/roll-rate-degps", true);
rateP_n = fgGetNode("/orientation/pitch-rate-degps", true);
SGPropertyNode_ptr n = fgGetNode("/controls/flight/slats",true);
_node_cache *c = new _node_cache( n->getDoubleValue(), n );
props["controls/flight/slats"] = c;
n = fgGetNode("/controls/flight/speedbrake", true);
c = new _node_cache( n->getDoubleValue(), n );
props["controls/flight/speedbrake"] = c;
n = fgGetNode("/controls/flight/spoilers", true);
c = new _node_cache( n->getDoubleValue(), n );
props["controls/flight/spoilers"] = c;
n = fgGetNode("/controls/gear/gear-down", true);
c = new _node_cache( n->getDoubleValue(), n );
props["controls/gear/gear-down"] = c;
n = fgGetNode("/controls/lighting/nav-lights", true);
c = new _node_cache( n->getDoubleValue(), n );
props["controls/lighting/nav-lights"] = c;
n = fgGetNode("/surface-positions/flap-pos-norm", true);
c = new _node_cache( n->getDoubleValue(), n );
props["surface-positions/flap-pos-norm"] = c;
n = fgGetNode("/surface-positions/speedbrake-pos-norm", true);
c = new _node_cache( n->getDoubleValue(), n );
props["surface-positions/speedbrake-pos-norm"] = c;
for (int i = 0; i < 6; i++)
{
char *s = new char[32];
snprintf(s, 32, "engines/engine[%i]/n1", i);
n = fgGetNode(s, true);
c = new _node_cache( n->getDoubleValue(), n );
props["s"] = c;
snprintf(s, 32, "engines/engine[%i]/n2", i);
n = fgGetNode(s, true);
c = new _node_cache( n->getDoubleValue(), n );
props[s] = c;
snprintf(s, 32, "engines/engine[%i]/rpm", i);
n = fgGetNode(s, true);
c = new _node_cache( n->getDoubleValue(), n );
props[s] = c;
delete [] s;
}
for (int j = 0; j < 5; j++)
{
char *s = new char[32];
snprintf(s, 32, "gear/gear[%i]/compression-norm", j);
n = fgGetNode(s, true);
c = new _node_cache( n->getDoubleValue(), n );
props["s"] = c;
snprintf(s, 32, "gear/gear[%i]/position-norm", j);
n = fgGetNode(s, true);
c = new _node_cache( n->getDoubleValue(), n );
props[s] = c;
#if 0
snprintf(s, 32, "gear/gear[%i]/rollspeed-ms", j);
n = fgGetNode(s, true);
c = new _node_cache( n->getDoubleValue(), n );
props[s] = c;
#endif
delete [] s;
}
n = fgGetNode("gear/tailhook/position-norm", true);
c = new _node_cache( n->getDoubleValue(), n );
props["gear/tailhook/position-norm"] = c;
} }
@ -182,7 +81,6 @@ FGMultiplay::FGMultiplay (const string &dir, const int rate, const string &host,
* Description: Destructor. * Description: Destructor.
******************************************************************/ ******************************************************************/
FGMultiplay::~FGMultiplay () { FGMultiplay::~FGMultiplay () {
props.clear();
} }
@ -193,13 +91,25 @@ FGMultiplay::~FGMultiplay () {
bool FGMultiplay::open() { bool FGMultiplay::open() {
if ( is_enabled() ) { if ( is_enabled() ) {
SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
<< "is already in use, ignoring" ); << "is already in use, ignoring" );
return false; return false;
} }
set_enabled(true); set_enabled(true);
SGPropertyNode* root = globals->get_props();
/// Build up the id to property map
unsigned i = 0;
while (FGMultiplayMgr::sIdPropertyList[i].name) {
const char* name = FGMultiplayMgr::sIdPropertyList[i].name;
SGPropertyNode* pNode = root->getNode(name);
if (pNode)
mPropertyMap[FGMultiplayMgr::sIdPropertyList[i].id] = pNode;
++i;
}
return is_enabled(); return is_enabled();
} }
@ -211,83 +121,94 @@ bool FGMultiplay::open() {
******************************************************************/ ******************************************************************/
bool FGMultiplay::process() { bool FGMultiplay::process() {
if (get_direction() == SG_IO_IN) { if (get_direction() == SG_IO_OUT) {
globals->get_multiplayer_mgr()->ProcessData(); // check if we have left initialization phase. That will not provide
// interresting data, also the freeze in simulation time hurts the
// multiplayer clients
double sim_time = globals->get_sim_time_sec();
// if (sim_time < 20)
// return true;
} else if (get_direction() == SG_IO_OUT) { FGInterface *ifce = cur_fdm_state;
double accN, accE, accD; // put together a motion info struct, you will get that later
string fdm = fgGetString("/sim/flight-model"); // from FGInterface directly ...
FGExternalMotionData motionInfo;
if(fdm == "jsb"){ // The current simulation time we need to update for,
calcAcc(speedN_n->getDoubleValue(), // note that the simulation time is updated before calling all the
speedE_n->getDoubleValue(), // update methods. Thus it contains the time intervals *end* time.
speedD_n->getDoubleValue()); // The FDM is already run, so the states belong to that time.
accN = calcaccN; motionInfo.time = sim_time;
accE = calcaccE;
accD = calcaccD; // The typical lag will be the reciprocal of the output frequency
}else{ double hz = get_hz();
SG_LOG(SG_GENERAL, SG_DEBUG," not doing acc calc" << fdm); if (hz != 0) // I guess we can test a double for exact zero in this case
accN = fgGetDouble("/accelerations/ned/north-accel-fps_sec"); motionInfo.lag = 1/get_hz();
accE = fgGetDouble("/accelerations/ned/east-accel-fps_sec"); else
accD = fgGetDouble("/accelerations/ned/down-accel-fps_sec"); motionInfo.lag = 0.1; //??
// These are for now converted from lat/lon/alt and euler angles.
// But this should change in FGInterface ...
double lon = ifce->get_Longitude();
double lat = ifce->get_Latitude();
// first the aprioriate structure for the geodetic one
SGGeod geod = SGGeod::fromRadFt(lon, lat, ifce->get_Altitude());
// Convert to cartesion coordinate
motionInfo.position = geod;
// The quaternion rotating from the earth centered frame to the
// horizontal local frame
SGQuatf qEc2Hl = SGQuatf::fromLonLat((float)lon, (float)lat);
// The orientation wrt the horizontal local frame
float heading = ifce->get_Psi();
float pitch = ifce->get_Theta();
float roll = ifce->get_Phi();
SGQuatf hlOr = SGQuatf::fromYawPitchRoll(heading, pitch, roll);
// The orientation of the vehicle wrt the earth centered frame
motionInfo.orientation = qEc2Hl*hlOr;
if (!ifce->is_suspended()) {
// velocities
motionInfo.linearVel = SG_FEET_TO_METER*SGVec3f(ifce->get_U_body(),
ifce->get_V_body(),
ifce->get_W_body());
motionInfo.angularVel = SGVec3f(ifce->get_P_body(),
ifce->get_Q_body(),
ifce->get_R_body());
// accels, set that to zero for now.
// Angular accelerations are missing from the interface anyway,
// linear accelerations are screwed up at least for JSBSim.
// motionInfo.linearAccel = SG_FEET_TO_METER*SGVec3f(ifce->get_U_dot_body(),
// ifce->get_V_dot_body(),
// ifce->get_W_dot_body());
motionInfo.linearAccel = SGVec3f::zeros();
motionInfo.angularAccel = SGVec3f::zeros();
} else {
// if the interface is suspendend, prevent the client from
// wild extrapolations
motionInfo.linearVel = SGVec3f::zeros();
motionInfo.angularVel = SGVec3f::zeros();
motionInfo.linearAccel = SGVec3f::zeros();
motionInfo.angularAccel = SGVec3f::zeros();
} }
globals->get_multiplayer_mgr()->SendMyPosition( // now send the properties
lat_n->getDoubleValue(), PropertyMap::iterator it;
lon_n->getDoubleValue(), for (it = mPropertyMap.begin(); it != mPropertyMap.end(); ++it) {
alt_n->getDoubleValue(), FGFloatPropertyData pData;
heading_n->getDoubleValue(), pData.id = it->first;
roll_n->getDoubleValue(), pData.value = it->second->getFloatValue();
pitch_n->getDoubleValue(), motionInfo.properties.push_back(pData);
speedN_n->getDoubleValue(),
speedE_n->getDoubleValue(),
speedD_n->getDoubleValue(),
left_aileron_n->getDoubleValue(),
right_aileron_n->getDoubleValue(),
elevator_n->getDoubleValue(),
rudder_n->getDoubleValue(),
rateH_n->getDoubleValue(),
rateR_n->getDoubleValue(),
rateP_n->getDoubleValue(),
accN, accE, accD);
// check for changes
for (propit = props.begin(); propit != props.end(); propit++)
{
double val = propit->second->val;
double curr_val = propit->second->node->getDoubleValue();
if (curr_val < val * 0.99 || curr_val > val * 1.01 )
{
SGPropertyNode::Type type = propit->second->node->getType();
propit->second->val = val = curr_val;
globals->get_multiplayer_mgr()->SendPropMessage(propit->first, type, val);
//cout << "Prop " << propit->first <<" type " << type << " val " << val << endl;
} else {
// cout << "no change" << endl;
}
} }
// send all properties when necessary FGMultiplayMgr* mpmgr = globals->get_multiplayer_mgr();
// FGMultiplayMgr::getSendAllProps(); mpmgr->SendMyPosition(motionInfo);
bool send_all = globals->get_multiplayer_mgr()->getSendAllProps();
//cout << "send_all in " << send;
if (send_all){
SG_LOG( SG_NETWORK, SG_ALERT,
"FGMultiplay::sending ALL property messages" );
for (propit = props.begin(); propit != props.end(); propit++) {
SGPropertyNode::Type type = propit->second->node->getType();
double val = propit->second->val;
globals->get_multiplayer_mgr()->SendPropMessage(propit->first, type, val);
}
send_all = false;
globals->get_multiplayer_mgr()->setSendAllProps(send_all);
}
//cout << " send_all out " << s << endl;
} }
return true; return true;
} }
@ -304,46 +225,10 @@ bool FGMultiplay::close() {
} else if (get_direction() == SG_IO_OUT) { } else if (get_direction() == SG_IO_OUT) {
// globals->get_multiplayer_mgr()->Close(); globals->get_multiplayer_mgr()->Close();
} }
return true; return true;
} }
/******************************************************************
* Name: CalcAcc
* Description: Calculate accelerations given speedN, speedE, speedD
******************************************************************/
void FGMultiplay::calcAcc(double speedN, double speedE, double speedD)
{
double time, dt; //secs
/*double accN, accE, accD; */ //fps2
dt = 0;
time = fgGetDouble("/sim/time/elapsed-sec");
dt = time-last_time;
SG_LOG(SG_GENERAL, SG_DEBUG," doing acc calc"
<<"time: "<< time << " last " << last_time << " dt " << dt );
//calculate the accelerations
calcaccN = (speedN - last_speedN)/dt;
calcaccE = (speedE - last_speedE)/dt;
calcaccD = (speedD - last_speedD)/dt;
//set the properties
/*fgSetDouble("/accelerations/ned/north-accel-fps_sec",accN);
fgSetDouble("/accelerations/ned/east-accel-fps_sec",accE);
fgSetDouble("/accelerations/ned/down-accel-fps_sec",accN);*/
//save the values
last_time = time;
last_speedN = speedN;
last_speedE = speedE;
last_speedD = speedD;
}// end calcAcc

View file

@ -31,6 +31,7 @@
#include STL_STRING #include STL_STRING
#include <simgear/props/props.hxx>
#include <simgear/scene/model/model.hxx> #include <simgear/scene/model/model.hxx>
#include <Main/globals.hxx> #include <Main/globals.hxx>
@ -58,8 +59,6 @@ SG_USING_STD(string);
* *
******************************************************************/ ******************************************************************/
#include <simgear/props/props.hxx>
class FGMultiplay : public FGProtocol { class FGMultiplay : public FGProtocol {
public: public:
@ -72,39 +71,20 @@ public:
/** Enables the FGMultiplay object. */ /** Enables the FGMultiplay object. */
bool open(); bool open();
/** Tells the multiplayer_mgr to send/receive data. */ /** Tells the multiplayer_mgr to send/receive data.
*/
bool process(); bool process();
/** Closes the multiplayer_mgr. */ /** Closes the multiplayer_mgr.
*/
bool close(); bool close();
private: private:
struct _node_cache { // Map between the property id's from the multiplayers network packets
double val; // and the property nodes
SGPropertyNode_ptr node; typedef std::map<unsigned, SGSharedPtr<SGPropertyNode> > PropertyMap;
_node_cache(double v, SGPropertyNode_ptr n) { PropertyMap mPropertyMap;
val = v; node = n;
};
};
/** calculate accelerations
*/
void calcAcc(double speedN, double speedE, double speedD);
double last_time ; //sec
double last_speedN, last_speedE, last_speedD; //fps
double calcaccN, calcaccE, calcaccD; //fps2
SGPropertyNode *lat_n, *lon_n, *alt_n;
SGPropertyNode *heading_n, *pitch_n, *roll_n;
SGPropertyNode *speedN_n, *speedE_n, *speedD_n;
SGPropertyNode *left_aileron_n, *right_aileron_n;
SGPropertyNode *elevator_n, *rudder_n;
// SGPropertyNode *rpms[5];
SGPropertyNode *rateH_n, *rateR_n, *rateP_n;
map<string,struct _node_cache*> props;
map<string,struct _node_cache*>::iterator propit;
}; };
#endif // _FG_MULTIPLAY_HXX #endif // _FG_MULTIPLAY_HXX