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