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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 {800, "rotors/main/rpm"},
108 {801, "rotors/tail/rpm"},
109 {810, "rotors/main/blade1_pos"},
110 {811, "rotors/main/blade2_pos"},
111 {812, "rotors/main/blade3_pos"},
112 {813, "rotors/main/blade4_pos"},
113 {820, "rotors/main/blade1_flap"},
114 {821, "rotors/main/blade2_flap"},
115 {822, "rotors/main/blade3_flap"},
116 {823, "rotors/main/blade4_flap"},
117 {830, "rotors/tail/blade1_pos"},
118 {831, "rotors/tail/blade2_pos"},
120 {1001, "controls/flight/slats"},
121 {1002, "controls/flight/speedbrake"},
122 {1003, "controls/flight/spoilers"},
123 {1004, "controls/gear/gear-down"},
124 {1005, "controls/lighting/nav-lights"},
130 //////////////////////////////////////////////////////////////////////
132 // MultiplayMgr constructor
134 //////////////////////////////////////////////////////////////////////
135 FGMultiplayMgr::FGMultiplayMgr()
137 mInitialised = false;
139 } // FGMultiplayMgr::FGMultiplayMgr()
140 //////////////////////////////////////////////////////////////////////
142 //////////////////////////////////////////////////////////////////////
144 // MultiplayMgr destructor
146 //////////////////////////////////////////////////////////////////////
147 FGMultiplayMgr::~FGMultiplayMgr()
150 } // FGMultiplayMgr::~FGMultiplayMgr()
151 //////////////////////////////////////////////////////////////////////
153 //////////////////////////////////////////////////////////////////////
157 //////////////////////////////////////////////////////////////////////
159 FGMultiplayMgr::init (void)
161 //////////////////////////////////////////////////
162 // Initialise object if not already done
163 //////////////////////////////////////////////////
165 SG_LOG(SG_NETWORK, SG_WARN, "FGMultiplayMgr::init - already initialised");
168 //////////////////////////////////////////////////
169 // Set members from property values
170 //////////////////////////////////////////////////
171 short rxPort = fgGetInt("/sim/multiplay/rxport");
174 mCallsign = fgGetString("/sim/multiplay/callsign");
175 if (mCallsign.empty())
176 // FIXME: use getpwuid
177 mCallsign = "JohnDoe";
178 string rxAddress = fgGetString("/sim/multiplay/rxhost");
179 if (rxAddress.empty())
180 rxAddress = "127.0.0.1";
181 short txPort = fgGetInt("/sim/multiplay/txport");
182 string txAddress = fgGetString("/sim/multiplay/txhost");
183 if (txPort > 0 && !txAddress.empty()) {
185 mServer.set(txAddress.c_str(), txPort);
187 SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txaddress= "<<txAddress);
188 SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txport= "<<txPort );
189 SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxaddress="<<rxAddress );
190 SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxport= "<<rxPort);
191 SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-callsign= "<<mCallsign);
192 mSocket = new netSocket();
193 if (!mSocket->open(false)) {
194 SG_LOG( SG_NETWORK, SG_ALERT,
195 "FGMultiplayMgr::init - Failed to create data socket" );
198 mSocket->setBlocking(false);
199 mSocket->setBroadcast(true);
200 if (mSocket->bind(rxAddress.c_str(), rxPort) != 0) {
202 SG_LOG( SG_NETWORK, SG_ALERT,
203 "FGMultiplayMgr::Open - Failed to bind receive socket" );
208 } // FGMultiplayMgr::init()
209 //////////////////////////////////////////////////////////////////////
211 //////////////////////////////////////////////////////////////////////
213 // Closes and deletes the local player object. Closes
214 // and deletes the tx socket. Resets the object state to unitialised.
216 //////////////////////////////////////////////////////////////////////
218 FGMultiplayMgr::Close (void)
220 mMultiPlayerMap.clear();
227 mInitialised = false;
228 } // FGMultiplayMgr::Close(void)
229 //////////////////////////////////////////////////////////////////////
231 //////////////////////////////////////////////////////////////////////
233 // Description: Sends the position data for the local position.
235 //////////////////////////////////////////////////////////////////////
237 FGMultiplayMgr::SendMyPosition(const FGExternalMotionData& motionInfo)
239 if ((! mInitialised) || (! mHaveServer)) {
241 SG_LOG( SG_NETWORK, SG_ALERT,
242 "FGMultiplayMgr::SendMyPosition - not initialised" );
244 SG_LOG( SG_NETWORK, SG_ALERT,
245 "FGMultiplayMgr::SendMyPosition - no server" );
249 T_PositionMsg PosMsg;
250 strncpy(PosMsg.Model, fgGetString("/sim/model/path"), MAX_MODEL_NAME_LEN);
251 PosMsg.Model[MAX_MODEL_NAME_LEN - 1] = '\0';
253 PosMsg.time = XDR_encode_double (motionInfo.time);
254 PosMsg.lag = XDR_encode_double (motionInfo.lag);
255 for (unsigned i = 0 ; i < 3; ++i)
256 PosMsg.position[i] = XDR_encode_double (motionInfo.position(i));
258 motionInfo.orientation.getAngleAxis(angleAxis);
259 for (unsigned i = 0 ; i < 3; ++i)
260 PosMsg.orientation[i] = XDR_encode_float (angleAxis(i));
261 for (unsigned i = 0 ; i < 3; ++i)
262 PosMsg.linearVel[i] = XDR_encode_float (motionInfo.linearVel(i));
263 for (unsigned i = 0 ; i < 3; ++i)
264 PosMsg.angularVel[i] = XDR_encode_float (motionInfo.angularVel(i));
265 for (unsigned i = 0 ; i < 3; ++i)
266 PosMsg.linearAccel[i] = XDR_encode_float (motionInfo.linearAccel(i));
267 for (unsigned i = 0 ; i < 3; ++i)
268 PosMsg.angularAccel[i] = XDR_encode_float (motionInfo.angularAccel(i));
270 char Msg[MAX_PACKET_SIZE];
271 memcpy(Msg + sizeof(T_MsgHdr), &PosMsg, sizeof(T_PositionMsg));
273 char* ptr = Msg + sizeof(T_MsgHdr) + sizeof(T_PositionMsg);
274 std::vector<FGFloatPropertyData>::const_iterator it;
275 it = motionInfo.properties.begin();
276 while (it != motionInfo.properties.end()
277 && ptr < (Msg + MAX_PACKET_SIZE - sizeof(T_PropertyMsg))) {
279 pMsg.id = XDR_encode_uint32(it->id);
280 pMsg.value = XDR_encode_float(it->value);
281 memcpy(ptr, &pMsg, sizeof(T_PropertyMsg));
282 ptr += sizeof(T_PropertyMsg);
287 FillMsgHdr(&MsgHdr, POS_DATA_ID, ptr - Msg);
288 memcpy(Msg, &MsgHdr, sizeof(T_MsgHdr));
290 mSocket->sendto(Msg, ptr - Msg, 0, &mServer);
291 SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition");
292 } // FGMultiplayMgr::SendMyPosition()
293 //////////////////////////////////////////////////////////////////////
295 //////////////////////////////////////////////////////////////////////
297 // Name: SendTextMessage
298 // Description: Sends a message to the player. The message must
299 // contain a valid and correctly filled out header and optional
302 //////////////////////////////////////////////////////////////////////
304 FGMultiplayMgr::SendTextMessage(const string &MsgText)
306 if (!mInitialised || !mHaveServer)
310 FillMsgHdr(&MsgHdr, CHAT_MSG_ID);
311 //////////////////////////////////////////////////
312 // Divide the text string into blocks that fit
313 // in the message and send the blocks.
314 //////////////////////////////////////////////////
315 unsigned iNextBlockPosition = 0;
317 char Msg[sizeof(T_MsgHdr) + sizeof(T_ChatMsg)];
318 while (iNextBlockPosition < MsgText.length()) {
319 strncpy (ChatMsg.Text,
320 MsgText.substr(iNextBlockPosition, MAX_CHAT_MSG_LEN - 1).c_str(),
322 ChatMsg.Text[MAX_CHAT_MSG_LEN - 1] = '\0';
323 memcpy (Msg, &MsgHdr, sizeof(T_MsgHdr));
324 memcpy (Msg + sizeof(T_MsgHdr), &ChatMsg, sizeof(T_ChatMsg));
325 mSocket->sendto (Msg, sizeof(T_MsgHdr) + sizeof(T_ChatMsg), 0, &mServer);
326 iNextBlockPosition += MAX_CHAT_MSG_LEN - 1;
328 } // FGMultiplayMgr::SendTextMessage ()
329 //////////////////////////////////////////////////////////////////////
331 //////////////////////////////////////////////////////////////////////
334 // Description: Processes data waiting at the receive socket. The
335 // processing ends when there is no more data at the socket.
337 //////////////////////////////////////////////////////////////////////
339 FGMultiplayMgr::Update(void)
345 SGTimeStamp timestamper;
347 long stamp = timestamper.get_seconds();
349 //////////////////////////////////////////////////
350 // Read the receive socket and process any data
351 //////////////////////////////////////////////////
354 char Msg[MAX_PACKET_SIZE];
355 //////////////////////////////////////////////////
356 // Although the recv call asks for
357 // MAX_PACKET_SIZE of data, the number of bytes
358 // returned will only be that of the next
359 // packet waiting to be processed.
360 //////////////////////////////////////////////////
361 netAddress SenderAddress;
362 bytes = mSocket->recvfrom(Msg, sizeof(Msg), 0, &SenderAddress);
363 //////////////////////////////////////////////////
365 //////////////////////////////////////////////////
367 if (errno != EAGAIN && errno != 0) // MSVC output "NoError" otherwise
368 perror("FGMultiplayMgr::MP_ProcessData");
371 if (bytes <= sizeof(T_MsgHdr)) {
372 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
373 << "received message with insufficient data" );
376 //////////////////////////////////////////////////
378 //////////////////////////////////////////////////
379 T_MsgHdr* MsgHdr = (T_MsgHdr *)Msg;
380 MsgHdr->Magic = XDR_decode_uint32 (MsgHdr->Magic);
381 MsgHdr->Version = XDR_decode_uint32 (MsgHdr->Version);
382 MsgHdr->MsgId = XDR_decode_uint32 (MsgHdr->MsgId);
383 MsgHdr->MsgLen = XDR_decode_uint32 (MsgHdr->MsgLen);
384 MsgHdr->ReplyPort = XDR_decode_uint32 (MsgHdr->ReplyPort);
385 if (MsgHdr->Magic != MSG_MAGIC) {
386 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
387 << "message has invalid magic number!" );
389 if (MsgHdr->Version != PROTO_VER) {
390 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
391 << "message has invalid protocoll number!" );
393 if (MsgHdr->MsgLen != bytes) {
394 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
395 << "message has invalid length!" );
397 //////////////////////////////////////////////////
399 //////////////////////////////////////////////////
400 switch (MsgHdr->MsgId) {
402 ProcessChatMsg(Msg, SenderAddress);
405 ProcessPosMsg(Msg, SenderAddress, bytes, stamp);
407 case UNUSABLE_POS_DATA_ID:
408 case OLD_OLD_POS_DATA_ID:
409 case OLD_PROP_MSG_ID:
410 case OLD_POS_DATA_ID:
413 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
414 << "Unknown message Id received: " << MsgHdr->MsgId );
420 MultiPlayerMap::iterator it = mMultiPlayerMap.begin();
421 while (it != mMultiPlayerMap.end()) {
422 if (it->second->getLastTimestamp() + 10 < stamp) {
423 std::string name = it->first;
424 it->second->setDie(true);
425 mMultiPlayerMap.erase(it);
426 it = mMultiPlayerMap.upper_bound(name);
430 } // FGMultiplayMgr::ProcessData(void)
431 //////////////////////////////////////////////////////////////////////
433 //////////////////////////////////////////////////////////////////////
435 // handle a position message
437 //////////////////////////////////////////////////////////////////////
439 FGMultiplayMgr::ProcessPosMsg(const char *Msg, netAddress & SenderAddress,
440 unsigned len, long stamp)
442 T_MsgHdr* MsgHdr = (T_MsgHdr *)Msg;
443 if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) {
444 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
445 << "Position message received with insufficient data" );
448 T_PositionMsg* PosMsg = (T_PositionMsg *)(Msg + sizeof(T_MsgHdr));
449 FGExternalMotionData motionInfo;
450 motionInfo.time = XDR_decode_double(PosMsg->time);
451 motionInfo.lag = XDR_decode_double(PosMsg->lag);
452 for (unsigned i = 0; i < 3; ++i)
453 motionInfo.position(i) = XDR_decode_double(PosMsg->position[i]);
455 for (unsigned i = 0; i < 3; ++i)
456 angleAxis(i) = XDR_decode_float(PosMsg->orientation[i]);
457 motionInfo.orientation = SGQuatf::fromAngleAxis(angleAxis);
458 for (unsigned i = 0; i < 3; ++i)
459 motionInfo.linearVel(i) = XDR_decode_float(PosMsg->linearVel[i]);
460 for (unsigned i = 0; i < 3; ++i)
461 motionInfo.angularVel(i) = XDR_decode_float(PosMsg->angularVel[i]);
462 for (unsigned i = 0; i < 3; ++i)
463 motionInfo.linearAccel(i) = XDR_decode_float(PosMsg->linearAccel[i]);
464 for (unsigned i = 0; i < 3; ++i)
465 motionInfo.angularAccel(i) = XDR_decode_float(PosMsg->angularAccel[i]);
467 T_PropertyMsg* PropMsg
468 = (T_PropertyMsg*)(Msg + sizeof(T_MsgHdr) + sizeof(T_PositionMsg));
469 while ((char*)PropMsg < Msg + len) {
470 FGFloatPropertyData pData;
471 pData.id = XDR_decode_uint32(PropMsg->id);
472 pData.value = XDR_decode_float(PropMsg->value);
473 motionInfo.properties.push_back(pData);
477 FGAIMultiplayer* mp = getMultiplayer(MsgHdr->Callsign);
479 mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model);
480 mp->addMotionInfo(motionInfo, stamp);
481 } // FGMultiplayMgr::ProcessPosMsg()
482 //////////////////////////////////////////////////////////////////////
484 //////////////////////////////////////////////////////////////////////
486 // handle a chat message
487 // FIXME: display chat message withi flightgear
489 //////////////////////////////////////////////////////////////////////
491 FGMultiplayMgr::ProcessChatMsg(const char *Msg, netAddress& SenderAddress)
493 T_MsgHdr* MsgHdr = (T_MsgHdr *)Msg;
494 if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + 1) {
495 SG_LOG( SG_NETWORK, SG_ALERT, "FGMultiplayMgr::MP_ProcessData - "
496 << "Chat message received with insufficient data" );
500 char *MsgBuf = new char[MsgHdr->MsgLen - sizeof(T_MsgHdr)];
501 strncpy(MsgBuf, ((T_ChatMsg *)(Msg + sizeof(T_MsgHdr)))->Text,
502 MsgHdr->MsgLen - sizeof(T_MsgHdr));
503 MsgBuf[MsgHdr->MsgLen - sizeof(T_MsgHdr) - 1] = '\0';
505 T_ChatMsg* ChatMsg = (T_ChatMsg *)(Msg + sizeof(T_MsgHdr));
506 SG_LOG ( SG_NETWORK, SG_ALERT, "Chat [" << MsgHdr->Callsign << "]"
507 << " " << MsgBuf << endl);
509 } // FGMultiplayMgr::ProcessChatMsg ()
510 //////////////////////////////////////////////////////////////////////
513 FGMultiplayMgr::FillMsgHdr(T_MsgHdr *MsgHdr, int MsgId, unsigned _len)
518 len = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
524 len = sizeof(T_MsgHdr);
527 MsgHdr->Magic = XDR_encode_uint32(MSG_MAGIC);
528 MsgHdr->Version = XDR_encode_uint32(PROTO_VER);
529 MsgHdr->MsgId = XDR_encode_uint32(MsgId);
530 MsgHdr->MsgLen = XDR_encode_uint32(len);
531 MsgHdr->ReplyAddress = 0; // Are obsolete, keep them for the server for
532 MsgHdr->ReplyPort = 0; // now
533 strncpy(MsgHdr->Callsign, mCallsign.c_str(), MAX_CALLSIGN_LEN);
534 MsgHdr->Callsign[MAX_CALLSIGN_LEN - 1] = '\0';
538 FGMultiplayMgr::addMultiplayer(const std::string& callsign,
539 const std::string& modelName)
541 if (0 < mMultiPlayerMap.count(callsign))
542 return mMultiPlayerMap[callsign];
544 FGAIMultiplayer* mp = new FGAIMultiplayer;
545 mp->setPath(modelName.c_str());
546 mp->setCallSign(callsign);
547 mMultiPlayerMap[callsign] = mp;
549 FGAIManager *aiMgr = (FGAIManager*)globals->get_subsystem("ai_model");
553 /// FIXME: that must follow the attach ATM ...
555 while (sIdPropertyList[i].name) {
556 mp->addPropertyId(sIdPropertyList[i].id, sIdPropertyList[i].name);
565 FGMultiplayMgr::getMultiplayer(const std::string& callsign)
567 if (0 < mMultiPlayerMap.count(callsign))
568 return mMultiPlayerMap[callsign];