1 //////////////////////////////////////////////////////////////////////
5 // Written by Duncan McCreanor, started February 2003.
6 // duncan.mccreanor@airservicesaustralia.com
8 // Copyright (C) 2003 Airservices Australia
9 // Copyright (C) 2005 Oliver Schroeder
10 // Copyright (C) 2006 Mathias Froehlich
12 // This program is free software; you can redistribute it and/or
13 // modify it under the terms of the GNU General Public License as
14 // published by the Free Software Foundation; either version 2 of the
15 // License, or (at your option) any later version.
17 // This program is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 // General Public License for more details.
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 //////////////////////////////////////////////////////////////////////
34 #include <plib/netSocket.h>
36 #include <simgear/timing/timestamp.hxx>
37 #include <simgear/debug/logstream.hxx>
39 #include <AIModel/AIManager.hxx>
40 #include <Main/fg_props.hxx>
42 #include "multiplaymgr.hxx"
43 #include "mpmessages.hxx"
45 #define MAX_PACKET_SIZE 1200
47 // These constants are provided so that the ident
48 // command can list file versions
49 const char sMULTIPLAYMGR_BID[] = "$Id$";
50 const char sMULTIPLAYMGR_HID[] = MULTIPLAYTXMGR_HID;
52 // A static map of protocol property id values to property paths,
53 // This should be extendable dynamically for every specific aircraft ...
54 // For now only that static list
55 FGMultiplayMgr::IdPropertyList
56 FGMultiplayMgr::sIdPropertyList[] = {
57 {100, "surface-positions/left-aileron-pos-norm"},
58 {101, "surface-positions/right-aileron-pos-norm"},
59 {102, "surface-positions/elevator-pos-norm"},
60 {103, "surface-positions/rudder-pos-norm"},
61 {104, "surface-positions/flap-pos-norm"},
62 {105, "surface-positions/speedbrake-pos-norm"},
63 {106, "gear/tailhook/position-norm"},
65 {200, "gear/gear[0]/compression-norm"},
66 {201, "gear/gear[0]/position-norm"},
67 {210, "gear/gear[1]/compression-norm"},
68 {211, "gear/gear[1]/position-norm"},
69 {220, "gear/gear[2]/compression-norm"},
70 {221, "gear/gear[2]/position-norm"},
71 {230, "gear/gear[3]/compression-norm"},
72 {231, "gear/gear[3]/position-norm"},
73 {240, "gear/gear[4]/compression-norm"},
74 {241, "gear/gear[4]/position-norm"},
76 {300, "engines/engine[0]/n1"},
77 {301, "engines/engine[0]/n2"},
78 {302, "engines/engine[0]/rpm"},
79 {310, "engines/engine[1]/n1"},
80 {311, "engines/engine[1]/n2"},
81 {312, "engines/engine[1]/rpm"},
82 {320, "engines/engine[2]/n1"},
83 {321, "engines/engine[2]/n2"},
84 {322, "engines/engine[2]/rpm"},
85 {330, "engines/engine[3]/n1"},
86 {331, "engines/engine[3]/n2"},
87 {332, "engines/engine[3]/rpm"},
88 {340, "engines/engine[4]/n1"},
89 {341, "engines/engine[4]/n2"},
90 {342, "engines/engine[4]/rpm"},
91 {350, "engines/engine[5]/n1"},
92 {351, "engines/engine[5]/n2"},
93 {352, "engines/engine[5]/rpm"},
94 {360, "engines/engine[6]/n1"},
95 {361, "engines/engine[6]/n2"},
96 {362, "engines/engine[6]/rpm"},
97 {370, "engines/engine[7]/n1"},
98 {371, "engines/engine[7]/n2"},
99 {372, "engines/engine[7]/rpm"},
100 {380, "engines/engine[8]/n1"},
101 {381, "engines/engine[8]/n2"},
102 {382, "engines/engine[8]/rpm"},
103 {390, "engines/engine[9]/n1"},
104 {391, "engines/engine[9]/n2"},
105 {392, "engines/engine[9]/rpm"},
107 {1001, "controls/flight/slats"},
108 {1002, "controls/flight/speedbrake"},
109 {1003, "controls/flight/spoilers"},
110 {1004, "controls/gear/gear-down"},
111 {1005, "controls/lighting/nav-lights"},
117 //////////////////////////////////////////////////////////////////////
119 // MultiplayMgr constructor
121 //////////////////////////////////////////////////////////////////////
122 FGMultiplayMgr::FGMultiplayMgr()
124 mInitialised = false;
126 } // FGMultiplayMgr::FGMultiplayMgr()
127 //////////////////////////////////////////////////////////////////////
129 //////////////////////////////////////////////////////////////////////
131 // MultiplayMgr destructor
133 //////////////////////////////////////////////////////////////////////
134 FGMultiplayMgr::~FGMultiplayMgr()
137 } // FGMultiplayMgr::~FGMultiplayMgr()
138 //////////////////////////////////////////////////////////////////////
140 //////////////////////////////////////////////////////////////////////
144 //////////////////////////////////////////////////////////////////////
146 FGMultiplayMgr::init (void)
148 //////////////////////////////////////////////////
149 // Initialise object if not already done
150 //////////////////////////////////////////////////
152 SG_LOG(SG_NETWORK, SG_WARN, "FGMultiplayMgr::init - already initialised");
155 //////////////////////////////////////////////////
156 // Set members from property values
157 //////////////////////////////////////////////////
158 short rxPort = fgGetInt("/sim/multiplay/rxport");
161 mCallsign = fgGetString("/sim/multiplay/callsign");
162 if (mCallsign.empty())
163 // FIXME: use getpwuid
164 mCallsign = "JohnDoe";
165 string rxAddress = fgGetString("/sim/multiplay/rxhost");
166 if (rxAddress.empty())
167 rxAddress = "127.0.0.1";
168 short txPort = fgGetInt("/sim/multiplay/txport");
169 string txAddress = fgGetString("/sim/multiplay/txhost");
170 if (txPort > 0 && !txAddress.empty()) {
172 mServer.set(txAddress.c_str(), txPort);
174 SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txaddress= "<<txAddress);
175 SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txport= "<<txPort );
176 SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxaddress="<<rxAddress );
177 SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxport= "<<rxPort);
178 SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-callsign= "<<mCallsign);
179 mSocket = new netSocket();
180 if (!mSocket->open(false)) {
181 SG_LOG( SG_NETWORK, SG_ALERT,
182 "FGMultiplayMgr::init - Failed to create data socket" );
185 mSocket->setBlocking(false);
186 mSocket->setBroadcast(true);
187 if (mSocket->bind(rxAddress.c_str(), rxPort) != 0) {
189 SG_LOG( SG_NETWORK, SG_ALERT,
190 "FGMultiplayMgr::Open - Failed to bind receive socket" );
195 } // FGMultiplayMgr::init()
196 //////////////////////////////////////////////////////////////////////
198 //////////////////////////////////////////////////////////////////////
200 // Closes and deletes the local player object. Closes
201 // and deletes the tx socket. Resets the object state to unitialised.
203 //////////////////////////////////////////////////////////////////////
205 FGMultiplayMgr::Close (void)
207 mMultiPlayerMap.clear();
214 mInitialised = false;
215 } // FGMultiplayMgr::Close(void)
216 //////////////////////////////////////////////////////////////////////
218 //////////////////////////////////////////////////////////////////////
220 // Description: Sends the position data for the local position.
222 //////////////////////////////////////////////////////////////////////
224 FGMultiplayMgr::SendMyPosition(const FGExternalMotionData& motionInfo)
226 if ((! mInitialised) || (! mHaveServer)) {
228 SG_LOG( SG_NETWORK, SG_ALERT,
229 "FGMultiplayMgr::SendMyPosition - not initialised" );
231 SG_LOG( SG_NETWORK, SG_ALERT,
232 "FGMultiplayMgr::SendMyPosition - no server" );
236 T_PositionMsg PosMsg;
237 strncpy(PosMsg.Model, fgGetString("/sim/model/path"), MAX_MODEL_NAME_LEN);
238 PosMsg.Model[MAX_MODEL_NAME_LEN - 1] = '\0';
240 PosMsg.time = XDR_encode_double (motionInfo.time);
241 PosMsg.lag = XDR_encode_double (motionInfo.lag);
242 for (unsigned i = 0 ; i < 3; ++i)
243 PosMsg.position[i] = XDR_encode_double (motionInfo.position(i));
245 motionInfo.orientation.getAngleAxis(angleAxis);
246 for (unsigned i = 0 ; i < 3; ++i)
247 PosMsg.orientation[i] = XDR_encode_float (angleAxis(i));
248 for (unsigned i = 0 ; i < 3; ++i)
249 PosMsg.linearVel[i] = XDR_encode_float (motionInfo.linearVel(i));
250 for (unsigned i = 0 ; i < 3; ++i)
251 PosMsg.angularVel[i] = XDR_encode_float (motionInfo.angularVel(i));
252 for (unsigned i = 0 ; i < 3; ++i)
253 PosMsg.linearAccel[i] = XDR_encode_float (motionInfo.linearAccel(i));
254 for (unsigned i = 0 ; i < 3; ++i)
255 PosMsg.angularAccel[i] = XDR_encode_float (motionInfo.angularAccel(i));
257 char Msg[MAX_PACKET_SIZE];
258 memcpy(Msg + sizeof(T_MsgHdr), &PosMsg, sizeof(T_PositionMsg));
260 char* ptr = Msg + sizeof(T_MsgHdr) + sizeof(T_PositionMsg);
261 std::vector<FGFloatPropertyData>::const_iterator it;
262 it = motionInfo.properties.begin();
263 while (it != motionInfo.properties.end()
264 && ptr < (Msg + MAX_PACKET_SIZE - sizeof(T_PropertyMsg))) {
266 pMsg.id = XDR_encode_uint32(it->id);
267 pMsg.value = XDR_encode_float(it->value);
268 memcpy(ptr, &pMsg, sizeof(T_PropertyMsg));
269 ptr += sizeof(T_PropertyMsg);
274 FillMsgHdr(&MsgHdr, POS_DATA_ID, ptr - Msg);
275 memcpy(Msg, &MsgHdr, sizeof(T_MsgHdr));
277 mSocket->sendto(Msg, ptr - Msg, 0, &mServer);
278 SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition");
279 } // FGMultiplayMgr::SendMyPosition()
280 //////////////////////////////////////////////////////////////////////
282 //////////////////////////////////////////////////////////////////////
284 // Name: SendTextMessage
285 // Description: Sends a message to the player. The message must
286 // contain a valid and correctly filled out header and optional
289 //////////////////////////////////////////////////////////////////////
291 FGMultiplayMgr::SendTextMessage(const string &MsgText)
293 if (!mInitialised || !mHaveServer)
297 FillMsgHdr(&MsgHdr, CHAT_MSG_ID);
298 //////////////////////////////////////////////////
299 // Divide the text string into blocks that fit
300 // in the message and send the blocks.
301 //////////////////////////////////////////////////
302 unsigned iNextBlockPosition = 0;
304 char Msg[sizeof(T_MsgHdr) + sizeof(T_ChatMsg)];
305 while (iNextBlockPosition < MsgText.length()) {
306 strncpy (ChatMsg.Text,
307 MsgText.substr(iNextBlockPosition, MAX_CHAT_MSG_LEN - 1).c_str(),
309 ChatMsg.Text[MAX_CHAT_MSG_LEN - 1] = '\0';
310 memcpy (Msg, &MsgHdr, sizeof(T_MsgHdr));
311 memcpy (Msg + sizeof(T_MsgHdr), &ChatMsg, sizeof(T_ChatMsg));
312 mSocket->sendto (Msg, sizeof(T_MsgHdr) + sizeof(T_ChatMsg), 0, &mServer);
313 iNextBlockPosition += MAX_CHAT_MSG_LEN - 1;
315 } // FGMultiplayMgr::SendTextMessage ()
316 //////////////////////////////////////////////////////////////////////
318 //////////////////////////////////////////////////////////////////////
321 // Description: Processes data waiting at the receive socket. The
322 // processing ends when there is no more data at the socket.
324 //////////////////////////////////////////////////////////////////////
326 FGMultiplayMgr::Update(void)
332 SGTimeStamp timestamper;
334 long stamp = timestamper.get_seconds();
336 //////////////////////////////////////////////////
337 // Read the receive socket and process any data
338 //////////////////////////////////////////////////
341 char Msg[MAX_PACKET_SIZE];
342 //////////////////////////////////////////////////
343 // Although the recv call asks for
344 // MAX_PACKET_SIZE of data, the number of bytes
345 // returned will only be that of the next
346 // packet waiting to be processed.
347 //////////////////////////////////////////////////
348 netAddress SenderAddress;
349 bytes = mSocket->recvfrom(Msg, sizeof(Msg), 0, &SenderAddress);
350 //////////////////////////////////////////////////
352 //////////////////////////////////////////////////
354 if (errno != EAGAIN && errno != 0) // MSVC output "NoError" otherwise
355 perror("FGMultiplayMgr::MP_ProcessData");
358 if (bytes <= sizeof(T_MsgHdr)) {
359 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
360 << "received message with insufficient data" );
363 //////////////////////////////////////////////////
365 //////////////////////////////////////////////////
366 T_MsgHdr* MsgHdr = (T_MsgHdr *)Msg;
367 MsgHdr->Magic = XDR_decode_uint32 (MsgHdr->Magic);
368 MsgHdr->Version = XDR_decode_uint32 (MsgHdr->Version);
369 MsgHdr->MsgId = XDR_decode_uint32 (MsgHdr->MsgId);
370 MsgHdr->MsgLen = XDR_decode_uint32 (MsgHdr->MsgLen);
371 MsgHdr->ReplyPort = XDR_decode_uint32 (MsgHdr->ReplyPort);
372 if (MsgHdr->Magic != MSG_MAGIC) {
373 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
374 << "message has invalid magic number!" );
376 if (MsgHdr->Version != PROTO_VER) {
377 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
378 << "message has invalid protocoll number!" );
380 if (MsgHdr->MsgLen != bytes) {
381 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
382 << "message has invalid length!" );
384 //////////////////////////////////////////////////
386 //////////////////////////////////////////////////
387 switch (MsgHdr->MsgId) {
389 ProcessChatMsg(Msg, SenderAddress);
392 ProcessPosMsg(Msg, SenderAddress, bytes, stamp);
394 case UNUSABLE_POS_DATA_ID:
395 case OLD_OLD_POS_DATA_ID:
396 case OLD_PROP_MSG_ID:
397 case OLD_POS_DATA_ID:
400 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
401 << "Unknown message Id received: " << MsgHdr->MsgId );
407 MultiPlayerMap::iterator it = mMultiPlayerMap.begin();
408 while (it != mMultiPlayerMap.end()) {
409 if (it->second->getLastTimestamp() + 10 < stamp) {
410 std::string name = it->first;
411 it->second->setDie(true);
412 mMultiPlayerMap.erase(it);
413 it = mMultiPlayerMap.upper_bound(name);
417 } // FGMultiplayMgr::ProcessData(void)
418 //////////////////////////////////////////////////////////////////////
420 //////////////////////////////////////////////////////////////////////
422 // handle a position message
424 //////////////////////////////////////////////////////////////////////
426 FGMultiplayMgr::ProcessPosMsg(const char *Msg, netAddress & SenderAddress,
427 unsigned len, long stamp)
429 T_MsgHdr* MsgHdr = (T_MsgHdr *)Msg;
430 if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) {
431 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
432 << "Position message received with insufficient data" );
435 T_PositionMsg* PosMsg = (T_PositionMsg *)(Msg + sizeof(T_MsgHdr));
436 FGExternalMotionData motionInfo;
437 motionInfo.time = XDR_decode_double(PosMsg->time);
438 motionInfo.lag = XDR_decode_double(PosMsg->lag);
439 for (unsigned i = 0; i < 3; ++i)
440 motionInfo.position(i) = XDR_decode_double(PosMsg->position[i]);
442 for (unsigned i = 0; i < 3; ++i)
443 angleAxis(i) = XDR_decode_float(PosMsg->orientation[i]);
444 motionInfo.orientation = SGQuatf::fromAngleAxis(angleAxis);
445 for (unsigned i = 0; i < 3; ++i)
446 motionInfo.linearVel(i) = XDR_decode_float(PosMsg->linearVel[i]);
447 for (unsigned i = 0; i < 3; ++i)
448 motionInfo.angularVel(i) = XDR_decode_float(PosMsg->angularVel[i]);
449 for (unsigned i = 0; i < 3; ++i)
450 motionInfo.linearAccel(i) = XDR_decode_float(PosMsg->linearAccel[i]);
451 for (unsigned i = 0; i < 3; ++i)
452 motionInfo.angularAccel(i) = XDR_decode_float(PosMsg->angularAccel[i]);
454 T_PropertyMsg* PropMsg
455 = (T_PropertyMsg*)(Msg + sizeof(T_MsgHdr) + sizeof(T_PositionMsg));
456 while ((char*)PropMsg < Msg + len) {
457 FGFloatPropertyData pData;
458 pData.id = XDR_decode_uint32(PropMsg->id);
459 pData.value = XDR_decode_float(PropMsg->value);
460 motionInfo.properties.push_back(pData);
464 FGAIMultiplayer* mp = getMultiplayer(MsgHdr->Callsign);
466 mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model);
467 mp->addMotionInfo(motionInfo, stamp);
468 } // FGMultiplayMgr::ProcessPosMsg()
469 //////////////////////////////////////////////////////////////////////
471 //////////////////////////////////////////////////////////////////////
473 // handle a chat message
474 // FIXME: display chat message withi flightgear
476 //////////////////////////////////////////////////////////////////////
478 FGMultiplayMgr::ProcessChatMsg(const char *Msg, netAddress& SenderAddress)
480 T_MsgHdr* MsgHdr = (T_MsgHdr *)Msg;
481 if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + 1) {
482 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
483 << "Chat message received with insufficient data" );
487 char *MsgBuf = new char[MsgHdr->MsgLen - sizeof(T_MsgHdr)];
488 strncpy(MsgBuf, ((T_ChatMsg *)(Msg + sizeof(T_MsgHdr)))->Text,
489 MsgHdr->MsgLen - sizeof(T_MsgHdr));
490 MsgBuf[MsgHdr->MsgLen - sizeof(T_MsgHdr) - 1] = '\0';
492 T_ChatMsg* ChatMsg = (T_ChatMsg *)(Msg + sizeof(T_MsgHdr));
493 SG_LOG ( SG_NETWORK, SG_ALERT, "Chat [" << MsgHdr->Callsign << "]"
494 << " " << MsgBuf << endl);
496 } // FGMultiplayMgr::ProcessChatMsg ()
497 //////////////////////////////////////////////////////////////////////
500 FGMultiplayMgr::FillMsgHdr(T_MsgHdr *MsgHdr, int MsgId, unsigned _len)
505 len = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
511 len = sizeof(T_MsgHdr);
514 MsgHdr->Magic = XDR_encode_uint32(MSG_MAGIC);
515 MsgHdr->Version = XDR_encode_uint32(PROTO_VER);
516 MsgHdr->MsgId = XDR_encode_uint32(MsgId);
517 MsgHdr->MsgLen = XDR_encode_uint32(len);
518 MsgHdr->ReplyAddress = 0; // Are obsolete, keep them for the server for
519 MsgHdr->ReplyPort = 0; // now
520 strncpy(MsgHdr->Callsign, mCallsign.c_str(), MAX_CALLSIGN_LEN);
521 MsgHdr->Callsign[MAX_CALLSIGN_LEN - 1] = '\0';
525 FGMultiplayMgr::addMultiplayer(const std::string& callsign,
526 const std::string& modelName)
528 if (0 < mMultiPlayerMap.count(callsign))
529 return mMultiPlayerMap[callsign];
531 FGAIMultiplayer* mp = new FGAIMultiplayer;
532 mp->setPath(modelName.c_str());
533 mp->setCallSign(callsign);
534 mMultiPlayerMap[callsign] = mp;
536 FGAIManager *aiMgr = (FGAIManager*)globals->get_subsystem("ai_model");
540 /// FIXME: that must follow the attach ATM ...
542 while (sIdPropertyList[i].name) {
543 mp->addPropertyId(sIdPropertyList[i].id, sIdPropertyList[i].name);
552 FGMultiplayMgr::getMultiplayer(const std::string& callsign)
554 if (0 < mMultiPlayerMap.count(callsign))
555 return mMultiPlayerMap[callsign];