]> git.mxchange.org Git - flightgear.git/blob - src/Network/multiplay.cxx
Merge branch 'next' of git://gitorious.org/fg/flightgear into next
[flightgear.git] / src / Network / multiplay.cxx
1 // multiplay.cxx -- protocol object for multiplay in Flightgear
2 //
3 // Written by Diarmuid Tyson, started February 2003.
4 // diarmuid.tyson@airservicesaustralia.com
5 //
6 // With addtions by Vivian Meazza, January 2006
7 //
8 // Copyright (C) 2003  Airservices Australia
9 //
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License as
12 // published by the Free Software Foundation; either version 2 of the
13 // License, or (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 // General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
23 //
24
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>
27 #endif
28
29 #include <simgear/compiler.h>
30
31 #include <string>
32
33 #include <iostream>
34 #include <map>
35 #include <string>
36
37 #include <simgear/debug/logstream.hxx>
38 #include <simgear/math/SGMath.hxx>
39
40 #include <FDM/flightProperties.hxx>
41 #include <MultiPlayer/mpmessages.hxx>
42
43 #include "multiplay.hxx"
44
45 using std::string;
46
47
48 // These constants are provided so that the ident command can list file versions.
49 const char sFG_MULTIPLAY_BID[] = "$Id$";
50 const char sFG_MULTIPLAY_HID[] = FG_MULTIPLAY_HID;
51
52 typedef std::set<std::string> string_set;
53
54 class MPPropertyListener : public SGPropertyChangeListener
55 {
56 public:
57   MPPropertyListener(FGMultiplay* mp) :
58     _multiplay(mp)
59   {
60   }
61
62   virtual void childAdded(SGPropertyNode*, SGPropertyNode*)
63   {
64     _multiplay->setPropertiesChanged();
65   }
66
67 private:
68   FGMultiplay* _multiplay;
69 };
70
71 /******************************************************************
72 * Name: FGMultiplay
73 * Description: Constructor.  Initialises the protocol and stores
74 * host and port information.
75 ******************************************************************/
76 FGMultiplay::FGMultiplay (const string &dir, const int rate, const string &host, const int port) {
77
78   set_hz(rate);
79
80   set_direction(dir);
81
82   if (get_direction() == SG_IO_IN) {
83
84     fgSetInt("/sim/multiplay/rxport", port);
85     fgSetString("/sim/multiplay/rxhost", host.c_str());
86
87   } else if (get_direction() == SG_IO_OUT) {
88
89     fgSetInt("/sim/multiplay/txport", port);
90     fgSetString("/sim/multiplay/txhost", host.c_str());
91
92   }
93
94   mPropertiesChanged = true;
95 }
96
97
98 /******************************************************************
99 * Name: ~FGMultiplay
100 * Description: Destructor.
101 ******************************************************************/
102 FGMultiplay::~FGMultiplay () {
103 }
104
105
106 /******************************************************************
107 * Name: open
108 * Description: Enables the protocol.
109 ******************************************************************/
110 bool FGMultiplay::open() {
111
112     if ( is_enabled() ) {
113         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
114                 << "is already in use, ignoring" );
115         return false;
116     }
117
118     set_enabled(true);
119     
120     mPropertiesChanged = true;
121     
122     MPPropertyListener* pl = new MPPropertyListener(this);
123     globals->get_props()->addChangeListener(pl, false);
124     return is_enabled();
125 }
126
127 void FGMultiplay::findProperties()
128 {
129   if (!mPropertiesChanged) {
130     return;
131   }
132   
133   mPropertiesChanged = false;
134   
135   for (unsigned i = 0; i < FGMultiplayMgr::numProperties; ++i) {
136       const char* name = FGMultiplayMgr::sIdPropertyList[i].name;
137       SGPropertyNode* pNode = globals->get_props()->getNode(name);
138       if (!pNode) {
139         continue;
140       }
141       
142       int id = FGMultiplayMgr::sIdPropertyList[i].id;
143       if (mPropertyMap.find(id) != mPropertyMap.end()) {
144         continue; // already activated
145       }
146       
147       mPropertyMap[id] = pNode;
148       SG_LOG(SG_NETWORK, SG_INFO, "activating MP property:" << pNode->getPath());
149     }
150
151 }
152
153
154 /******************************************************************
155 * Name: process
156 * Description: Prompts the multiplayer mgr to either send
157 * or receive data over the network
158 ******************************************************************/
159 bool FGMultiplay::process() {
160   using namespace simgear;
161   if (get_direction() == SG_IO_OUT) {
162     findProperties();
163     
164     // check if we have left initialization phase. That will not provide
165     // interresting data, also the freeze in simulation time hurts the
166     // multiplayer clients
167     double sim_time = globals->get_sim_time_sec();
168 //     if (sim_time < 20)
169 //       return true;
170
171     FlightProperties ifce;
172
173     // put together a motion info struct, you will get that later
174     // from FGInterface directly ...
175     FGExternalMotionData motionInfo;
176
177     // The current simulation time we need to update for,
178     // note that the simulation time is updated before calling all the
179     // update methods. Thus it contains the time intervals *end* time.
180     // The FDM is already run, so the states belong to that time.
181     motionInfo.time = sim_time;
182
183     // The typical lag will be the reciprocal of the output frequency
184     double hz = get_hz();
185     if (hz != 0) // I guess we can test a double for exact zero in this case
186       motionInfo.lag = 1/get_hz();
187     else
188       motionInfo.lag = 0.1; //??
189
190     // These are for now converted from lat/lon/alt and euler angles.
191     // But this should change in FGInterface ...
192     double lon = ifce.get_Longitude();
193     double lat = ifce.get_Latitude();
194     // first the aprioriate structure for the geodetic one
195     SGGeod geod = SGGeod::fromRadFt(lon, lat, ifce.get_Altitude());
196     // Convert to cartesion coordinate
197     motionInfo.position = SGVec3d::fromGeod(geod);
198     
199     // The quaternion rotating from the earth centered frame to the
200     // horizontal local frame
201     SGQuatf qEc2Hl = SGQuatf::fromLonLatRad((float)lon, (float)lat);
202     // The orientation wrt the horizontal local frame
203     float heading = ifce.get_Psi();
204     float pitch = ifce.get_Theta();
205     float roll = ifce.get_Phi();
206     SGQuatf hlOr = SGQuatf::fromYawPitchRoll(heading, pitch, roll);
207     // The orientation of the vehicle wrt the earth centered frame
208     motionInfo.orientation = qEc2Hl*hlOr;
209
210     if (!globals->get_subsystem("flight")->is_suspended()) {
211       // velocities
212       motionInfo.linearVel = SG_FEET_TO_METER*SGVec3f(ifce.get_uBody(),
213                                                       ifce.get_vBody(),
214                                                       ifce.get_wBody());
215       motionInfo.angularVel = SGVec3f(ifce.get_P_body(),
216                                       ifce.get_Q_body(),
217                                       ifce.get_R_body());
218       
219       // accels, set that to zero for now.
220       // Angular accelerations are missing from the interface anyway,
221       // linear accelerations are screwed up at least for JSBSim.
222 //  motionInfo.linearAccel = SG_FEET_TO_METER*SGVec3f(ifce.get_U_dot_body(),
223 //                                                    ifce.get_V_dot_body(),
224 //                                                    ifce.get_W_dot_body());
225       motionInfo.linearAccel = SGVec3f::zeros();
226       motionInfo.angularAccel = SGVec3f::zeros();
227     } else {
228       // if the interface is suspendend, prevent the client from
229       // wild extrapolations
230       motionInfo.linearVel = SGVec3f::zeros();
231       motionInfo.angularVel = SGVec3f::zeros();
232       motionInfo.linearAccel = SGVec3f::zeros();
233       motionInfo.angularAccel = SGVec3f::zeros();
234     }
235
236     // now send the properties
237     PropertyMap::iterator it;
238     for (it = mPropertyMap.begin(); it != mPropertyMap.end(); ++it) {
239       FGPropertyData* pData = new FGPropertyData;
240       pData->id = it->first;
241       pData->type = it->second->getType();
242       
243       switch (pData->type) {
244         case props::INT:
245         case props::LONG:
246         case props::BOOL:
247           pData->int_value = it->second->getIntValue();
248           break;
249         case props::FLOAT:
250         case props::DOUBLE:
251           pData->float_value = it->second->getFloatValue();
252           break;
253         case props::STRING:
254         case props::UNSPECIFIED:
255           {
256             // FIXME: We assume unspecified are strings for the moment.
257
258             const char* cstr = it->second->getStringValue();
259             int len = strlen(cstr);
260             
261             if (len > 0)
262             {            
263               pData->string_value = new char[len + 1];
264               strcpy(pData->string_value, cstr);
265             }
266             else
267             {
268               // Size 0 - ignore
269               pData->string_value = 0;            
270             }
271
272             //cout << " Sending property " << pData->id << " " << pData->type << " " <<  pData->string_value << "\n";
273             break;        
274           }
275         default:
276           // FIXME Currently default to a float. 
277           //cout << "Unknown type when iterating through props: " << pData->type << "\n";
278           pData->float_value = it->second->getFloatValue();
279           break;
280       }
281       
282       motionInfo.properties.push_back(pData);
283     }
284
285     FGMultiplayMgr* mpmgr = (FGMultiplayMgr*) globals->get_subsystem("mp");
286     mpmgr->SendMyPosition(motionInfo);
287     
288     // Now remove the data
289     std::vector<FGPropertyData*>::const_iterator propIt;
290     std::vector<FGPropertyData*>::const_iterator propItEnd;
291     propIt = motionInfo.properties.begin();
292     propItEnd = motionInfo.properties.end();
293
294     //cout << "Deleting data\n";
295
296     while (propIt != propItEnd)
297     {
298       delete *propIt;
299       propIt++;
300     }    
301   }
302
303   return true;
304 }
305
306
307 /******************************************************************
308 * Name: close
309 * Description:  Closes the multiplayer mgrs to stop any further
310 * network processing
311 ******************************************************************/
312 bool FGMultiplay::close()
313 {
314   mPropertyMap.clear();
315   
316   FGMultiplayMgr* mgr = (FGMultiplayMgr*) globals->get_subsystem("mp");
317
318   if (mgr == 0) {
319     return false;
320   }
321
322   if (get_direction() == SG_IO_IN) {
323
324     mgr->Close();
325
326   } else if (get_direction() == SG_IO_OUT) {
327
328     mgr->Close();
329
330   }
331
332   return true;
333 }
334