first commit

This commit is contained in:
Your Name
2022-10-20 20:29:11 +08:00
commit 4d531f8044
3238 changed files with 1387862 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
include(FlightGearComponent)
set(SOURCES
multiplaymgr.cxx
tiny_xdr.cxx
MPServerResolver.cxx
mpirc.cxx
cpdlc.cxx
)
set(HEADERS
multiplaymgr.hxx
tiny_xdr.hxx
MPServerResolver.hxx
mpirc.hxx
cpdlc.hxx
mpmessages.hxx
)
flightgear_component(MultiPlayer "${SOURCES}" "${HEADERS}")

View File

@@ -0,0 +1,203 @@
/*
MPServerResolver.cxx - mpserver names lookup via DNS
Written and copyright by Torsten Dreyer - November 2016
This file is part of FlightGear.
FlightGear is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
FlightGear is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FlightGear. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <Network/DNSClient.hxx>
#include <Main/fg_props.hxx>
#include <cJSON.h>
#include <cstdlib>
#include "MPServerResolver.hxx"
using namespace simgear;
/**
* Build a name=value map from base64 encoded JSON string
*/
class MPServerProperties : public std::map<string, string> {
public:
MPServerProperties (string b64)
{
std::vector<unsigned char> b64dec;
simgear::strutils::decodeBase64 (b64, b64dec);
auto jsonString = string ((char*) b64dec.data (), b64dec.size ());
cJSON * json = ::cJSON_Parse (jsonString.c_str ());
if (json) {
for (int i = 0; i < ::cJSON_GetArraySize (json); i++) {
cJSON * cj = ::cJSON_GetArrayItem (json, i);
if (cj->string && cj->valuestring)
emplace (cj->string, cj->valuestring);
}
::cJSON_Delete (json);
} else {
SG_LOG(SG_NETWORK,SG_WARN, "MPServerResolver: Can't parse JSON string '" << jsonString << "'" );
}
}
};
class MPServerResolver::MPServerResolver_priv {
public:
enum {
INIT, LOADING_SRV_RECORDS, LOAD_NEXT_TXT_RECORD, LOADING_TXT_RECORDS, DONE,
} _state = INIT;
FGDNSClient * _dnsClient = globals->get_subsystem<FGDNSClient> ();
DNS::Request_ptr _dnsRequest;
PropertyList _serverNodes;
PropertyList::const_iterator _serverNodes_it;
};
MPServerResolver::~MPServerResolver ()
{
if (_priv->_dnsRequest) {
_priv->_dnsRequest->cancel();
}
delete _priv;
}
MPServerResolver::MPServerResolver () :
_priv (new MPServerResolver_priv ())
{
}
void
MPServerResolver::run ()
{
//SG_LOG(SG_NETWORK, SG_DEBUG, "MPServerResolver::run() with state=" << _priv->_state );
switch (_priv->_state) {
// First call - fire DNS lookup for SRV records
case MPServerResolver_priv::INIT:
if (!_priv->_dnsClient) {
SG_LOG(SG_NETWORK, SG_WARN, "MPServerResolver: DNS subsystem not available.");
onFailure ();
return;
}
_priv->_dnsRequest = new DNS::SRVRequest (_dnsName, _service, _protocol);
SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: sending DNS request for " << _priv->_dnsRequest->getDn());
_priv->_dnsClient->makeRequest (_priv->_dnsRequest);
_priv->_state = MPServerResolver_priv::LOADING_SRV_RECORDS;
break;
// Check if response from SRV Query
case MPServerResolver_priv::LOADING_SRV_RECORDS:
if (_priv->_dnsRequest->isTimeout ()) {
SG_LOG(SG_NETWORK, SG_WARN, "Timeout waiting for DNS response. Query was: " << _priv->_dnsRequest->getDn());
onFailure ();
return;
}
if (_priv->_dnsRequest->isComplete ()) {
// Create a child node under _targetNode for each SRV entry of the response
SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: got DNS response for " << _priv->_dnsRequest->getDn());
int idx = 0;
for (DNS::SRVRequest::SRV_ptr entry : dynamic_cast<DNS::SRVRequest*> (_priv->_dnsRequest.get ())->entries) {
SG_LOG(SG_NETWORK, SG_DEBUG,
"MPServerResolver: SRV " << entry->priority << " " << entry->weight << " " << entry->port << " " << entry->target);
if( 0 == entry->port ) {
SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: Skipping offline host " << entry->target );
continue;
}
SGPropertyNode * serverNode = _targetNode->getNode ("server", idx++, true);
serverNode->getNode ("hostname", true)->setStringValue (entry->target);
serverNode->getNode ("priority", true)->setIntValue (entry->priority);
serverNode->getNode ("weight", true)->setIntValue (entry->weight);
serverNode->getNode ("port", true)->setIntValue (entry->port);
}
// prepare an iterator over the server-nodes to be used later when loading the TXT records
_priv->_serverNodes = _targetNode->getChildren ("server");
_priv->_serverNodes_it = _priv->_serverNodes.begin ();
if (_priv->_serverNodes_it == _priv->_serverNodes.end ()) {
// No SRV records found - flag failure
SG_LOG(SG_NETWORK, SG_WARN, "MPServerResolver: no multiplayer servers defined via DNS");
onFailure ();
return;
}
_priv->_state = MPServerResolver_priv::LOAD_NEXT_TXT_RECORD;
break;
}
break;
// get the next TXT record
case MPServerResolver_priv::LOAD_NEXT_TXT_RECORD:
if (_priv->_serverNodes_it == _priv->_serverNodes.end ()) {
// we are done with all servers
_priv->_state = MPServerResolver_priv::DONE;
break;
}
// send the DNS query for the hostnames TXT record
_priv->_dnsRequest = new DNS::TXTRequest ((*_priv->_serverNodes_it)->getStringValue ("hostname"));
SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: sending DNS request for " << _priv->_dnsRequest->getDn());
_priv->_dnsClient->makeRequest (_priv->_dnsRequest);
_priv->_state = MPServerResolver_priv::LOADING_TXT_RECORDS;
break;
// check if response for TXT query
case MPServerResolver_priv::LOADING_TXT_RECORDS:
if (_priv->_dnsRequest->isTimeout ()) {
// on timeout, try proceeding with next server
SG_LOG(SG_NETWORK, SG_WARN, "Timeout waiting for DNS response. Query was: " << _priv->_dnsRequest->getDn());
_priv->_state = MPServerResolver_priv::LOAD_NEXT_TXT_RECORD;
++_priv->_serverNodes_it;
break;
}
if (_priv->_dnsRequest->isComplete ()) {
SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: got DNS response for " << _priv->_dnsRequest->getDn());
// DNS::TXTRequest automatically extracts name=value entries for us, lets retrieve them
auto attributes = dynamic_cast<DNS::TXTRequest*> (_priv->_dnsRequest.get ())->attributes;
auto mpserverAttribute = attributes["flightgear-mpserver"];
if (!mpserverAttribute.empty ()) {
// we are only interested in the 'flightgear-mpserver=something' entry, this is a base64 encoded
// JSON string, convert this into a map<string,string>
MPServerProperties mpserverProperties (mpserverAttribute);
for (auto prop : mpserverProperties) {
// and store each as a node under our servers node.
SG_LOG(SG_NETWORK, SG_DEBUG, "MPServerResolver: TXT record attribute " << prop.first << "=" << prop.second);
// sanitize property name, don't allow dots or forward slash
auto propertyName = prop.first;
std::replace( propertyName.begin(), propertyName.end(), '.', '_');
std::replace( propertyName.begin(), propertyName.end(), '/', '_');
(*_priv->_serverNodes_it)->setStringValue (propertyName, prop.second);
}
} else {
SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: TXT record attributes empty");
}
// procede with the net node
++_priv->_serverNodes_it;
_priv->_state = MPServerResolver_priv::LOAD_NEXT_TXT_RECORD;
break;
}
break;
case MPServerResolver_priv::DONE:
_priv->_dnsRequest.clear();
onSuccess();
return;
}
// Relinguish control, call me back on the next frame
globals->get_event_mgr ()->addEvent ("MPServerResolver_update", [this](){ this->run(); }, .0);
}

View File

@@ -0,0 +1,83 @@
/*
MPServerResolver.hxx - mpserver names lookup via DNS
Written and copyright by Torsten Dreyer - November 2016
This file is part of FlightGear.
FlightGear is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
FlightGear is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FlightGear. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __FG_MPSERVERRESOLVER_HXX
#define __FG_MPSERVERRESOLVER_HXX
#include <string>
#include <simgear/props/props.hxx>
class MPServerResolver {
public:
MPServerResolver();
virtual ~MPServerResolver();
void run();
/**
* Set the target property where the server-list gets stored
*
* \param value the property node to use as a target
*/
void setTarget( SGPropertyNode_ptr value ) { _targetNode = value; }
/**
* Set the dns domain name to query. This could be either a full qualified name including the
* service and the protocol like _fgms._udp.flightgear.org or just the domain name like
* flightgear.org. Use setService() and setProtocol() in the latter case.
*
* \param value the dnsname to use for the query.
*/
void setDnsName( const std::string & value ) { _dnsName = value; }
/** Set the service name to use for the query. Don't add the underscore, this gets added
* automatically. This builds the fully qualified DNS name to query, together with
* setProtocol() and setDnsName().
*
* \param value the service name to use for the query sans the leading underscore
*/
void setService( const std::string & value ) { _service = value; }
/** Set the protocol name to use for the query. Don't add the underscore, this gets added
* automatically. This builds the fully qualified DNS name to query, together with
* setService() and setDnsName().
*
* \param value the protocol name to use for the query sans the leading underscore
*/
void setProtocol( const std::string & value ) { _protocol = value; }
/** Handler to be called if the resolver process finishes with success. Does nothing by
* default and should be overridden be the user.
*/
virtual void onSuccess() {};
/** Handler to be called if the resolver process terminates with an error. Does nothing by
* default and should be overridden be the user.
*/
virtual void onFailure() {};
private:
class MPServerResolver_priv;
std::string _dnsName;
std::string _service;
std::string _protocol;
SGPropertyNode_ptr _targetNode;
MPServerResolver_priv * _priv;
};
#endif // __FG_MPSERVERRESOLVER_HXX

190
src/MultiPlayer/cpdlc.cxx Normal file
View File

@@ -0,0 +1,190 @@
//////////////////////////////////////////////////////////////////////
//
// cpdlc.cxx
//
// started November 2020
// Authors: Michael Filhol, Henning Stahlke
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
//
//////////////////////////////////////////////////////////////////////
#include "cpdlc.hxx"
#include <Main/fg_props.hxx>
const std::string CPDLC_IRC_SERVER {"mpirc.flightgear.org"};
const std::string CPDLC_MSGPREFIX_CONNECT {"___CPDLC_CONNECT___"};
const std::string CPDLC_MSGPREFIX_MSG {"___CPDLC_MSG___"};
const std::string CPDLC_MSGPREFIX_DISCONNECT {"___CPDLC_DISCONNECT___"};
///////////////////////////////////////////////////////////////////////////////
// CPDLCManager
///////////////////////////////////////////////////////////////////////////////
CPDLCManager::CPDLCManager(IRCConnection* irc) :
_irc(irc)
{
// CPDLC link status props
_pStatus = fgGetNode("/network/cpdlc/link/status", true);
_pStatus->setIntValue(_status);
_pDataAuthority = fgGetNode("/network/cpdlc/link/data-authority", true);
_pDataAuthority->setStringValue(_data_authority);
// CPDLC message output
_pMessage = fgGetNode("/network/cpdlc/rx/message", true);
_pMessage->setStringValue("");
_pNewMessage = fgGetNode("/network/cpdlc/rx/new-message", true);
_pNewMessage->setBoolValue(0);
}
CPDLCManager::~CPDLCManager()
{
}
/*
connect will be called by
1) user via a fgcommand defined in multiplaymgr
2) CPDLC::update() while waiting for IRC to become ready
authority parameter is accepted only when coming from disconnected state,
disconnect must be called first if user wants to change authority (however, ATC
handover is not blocked by this)
IRC connection will be initiated if necessary, it takes a while to connect to IRC
which is why connect is called from update() when in state CPDLC_WAIT_IRC_READY
*/
bool CPDLCManager::connect(const std::string authority = "")
{
// ensure we get an authority on first call but do not accept a change before
// resetting _data_authority in disconnect()
if (_data_authority.empty()) {
if (authority.empty()) {
SG_LOG(SG_NETWORK, SG_WARN, "cpdlcConnect not possible: empty argument!");
return false;
} else {
_data_authority = authority;
}
}
if (!authority.empty() && authority != _data_authority) {
SG_LOG(SG_NETWORK, SG_WARN, "cpdlcConnect: cannot change authority now, use disconnect first!");
return false;
}
// launch IRC connection as needed
if (!_irc->isConnected()) {
SG_LOG(SG_NETWORK, SG_INFO, "Connecting to IRC server...");
if (!_irc->login()) {
SG_LOG(SG_NETWORK, SG_WARN, "IRC login failed.");
return false;
}
_status = CPDLC_WAIT_IRC_READY;
return true;
} else if (_irc->isReady() && _status != CPDLC_ONLINE) {
SG_LOG(SG_NETWORK, SG_INFO, "CPDLC sending 'connect'");
_status = CPDLC_CONNECTING;
_pStatus->setIntValue(_status);
return _irc->sendPrivmsg(_data_authority, CPDLC_MSGPREFIX_CONNECT);
}
return false;
}
void CPDLCManager::disconnect()
{
if (_irc && _irc->isConnected() && !_data_authority.empty()) {
_irc->sendPrivmsg(_data_authority, CPDLC_MSGPREFIX_DISCONNECT);
}
_data_authority = "";
_pDataAuthority->setStringValue(_data_authority);
_status = CPDLC_OFFLINE;
_pStatus->setIntValue(_status);
}
bool CPDLCManager::send(const std::string message)
{
std::string textline(CPDLC_MSGPREFIX_MSG); // TODO surely a way to format std string in line
textline += ' ';
textline += message;
return _irc->sendPrivmsg(_data_authority, textline);
}
// move next message from input queue to property tree
void CPDLCManager::getMessage()
{
if (!_incoming_messages.empty()) {
struct IRCMessage entry = _incoming_messages.front();
_incoming_messages.pop_front();
_pMessage->setStringValue(entry.textline.substr(CPDLC_MSGPREFIX_MSG.length() + 1));
if (_incoming_messages.empty()) {
_pNewMessage->setBoolValue(0);
}
}
}
/*
update() call this regularly to complete connect and check for new messages
update frequency requirement should be low (e.g. once per second) but frame rate
will not hurt as the code is slim
*/
void CPDLCManager::update()
{
if (_irc) {
if (_irc->isConnected()) {
if (_status == CPDLC_WAIT_IRC_READY && _irc->isReady()) {
SG_LOG(SG_NETWORK, SG_INFO, "CPDLC IRC ready, connecting...");
connect();
}
if (_irc->hasMessage()) {
processMessage(_irc->getMessage());
}
}
// IRC disconnected (unexpectedly)
else if (_status != CPDLC_OFFLINE) {
_status = CPDLC_OFFLINE;
_pStatus->setIntValue(_status);
}
}
}
// process incoming message
void CPDLCManager::processMessage(struct IRCMessage message)
{
// connection accepted by ATC, or new data authority (been transferred)
if (message.textline.find(CPDLC_MSGPREFIX_CONNECT) == 0) {
SG_LOG(SG_NETWORK, SG_INFO, "CPDLC got connected.");
_data_authority = message.sender;
_pDataAuthority->setStringValue(_data_authority); // make this known to ACFT
_status = CPDLC_ONLINE;
_pStatus->setIntValue(_status);
}
// do not process message if sender does not match our current data authority
if (message.sender != _data_authority) {
SG_LOG(SG_NETWORK, SG_WARN, "Received CPDLC message from foreign authority.");
return;
}
// connection rejected, or terminated by ATC
if (message.textline.find(CPDLC_MSGPREFIX_DISCONNECT) == 0) {
if (message.sender == _data_authority) {
SG_LOG(SG_NETWORK, SG_INFO, "CPDLC got disconnect.");
disconnect();
}
}
// store valid message in queue for later retrieval by aircraft
else if (message.textline.find(CPDLC_MSGPREFIX_MSG) == 0) {
SG_LOG(SG_NETWORK, SG_INFO, "CPDLC message");
_incoming_messages.push_back(message);
_pNewMessage->setBoolValue(1);
}
}

76
src/MultiPlayer/cpdlc.hxx Normal file
View File

@@ -0,0 +1,76 @@
//////////////////////////////////////////////////////////////////////
//
// cpdlc.hxx
//
// started November 2020
// Authors: Henning Stahlke, Michael Filhol
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
//
//////////////////////////////////////////////////////////////////////
#ifndef CPDLC_H
#define CPDLC_H
#include <deque>
#include <string>
#include <simgear/compiler.h>
#include <simgear/props/props.hxx>
#include <simgear/io/raw_socket.hxx>
#include <simgear/io/sg_socket.hxx>
#include "mpirc.hxx"
enum CPDLCStatus {
CPDLC_OFFLINE,
CPDLC_CONNECTING,
CPDLC_ONLINE,
// private (not exposed to property tree) below this line:
CPDLC_WAIT_IRC_READY,
};
//
// CPDLCManager implements a ControllerPilotDataLinkConnection via an IRC connection
//
class CPDLCManager
{
public:
CPDLCManager(IRCConnection* irc);
~CPDLCManager();
bool connect(const std::string authority);
void disconnect();
bool send(const std::string message);
void getMessage();
void update();
private:
IRCConnection *_irc;
std::string _data_authority {""};
std::deque<struct IRCMessage> _incoming_messages;
CPDLCStatus _status {CPDLC_OFFLINE};
SGPropertyNode *_pStatus {nullptr};
SGPropertyNode *_pDataAuthority {nullptr};
SGPropertyNode *_pMessage {nullptr};
SGPropertyNode *_pNewMessage {nullptr};
void processMessage(struct IRCMessage entry);
};
#endif

327
src/MultiPlayer/mpirc.cxx Normal file
View File

@@ -0,0 +1,327 @@
//////////////////////////////////////////////////////////////////////
//
// mpirc.cxx
//
// started November 2020
// Authors: Michael Filhol, Henning Stahlke
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
//
//////////////////////////////////////////////////////////////////////
#include "mpirc.hxx"
#include <Main/fg_props.hxx>
const std::string IRC_TEST_CHANNEL{"#mptest"}; // for development
const std::string IRC_MSG_TERMINATOR {"\r\n"};
// https://www.alien.net.au/irc/irc2numerics.html
const std::string IRC_RPL_WELCOME {"001"};
const std::string IRC_RPL_YOURID {"042"};
const std::string IRC_RPL_MOTD {"372"};
const std::string IRC_RPL_MOTDSTART {"375"};
const std::string IRC_RPL_ENDOFMOTD {"376"};
const std::string IRC_ERR_NOSUCHNICK {"401"};
IRCConnection::IRCConnection(const std::string &nickname, const std::string &servername, const std::string &port) : SGSocket(servername, port, "tcp"),
_nickname(nickname)
{
}
IRCConnection::~IRCConnection()
{
}
// setup properties to reflect the status of this IRC connection in the prop tree
void IRCConnection::setupProperties(std::string path)
{
if (path.back() != '/') path.push_back('/');
if (!_pReadyFlag) _pReadyFlag = fgGetNode(path + "irc-ready", true);
_pReadyFlag->setBoolValue(_logged_in);
if (!_pMessageCountIn) _pMessageCountIn = fgGetNode(path + "msg-count-in", true);
if (!_pMessageCountOut) _pMessageCountOut = fgGetNode(path + "msg-count-out", true);
if (!_pIRCReturnCode) _pIRCReturnCode = fgGetNode(path + "last-return-code", true);
}
bool IRCConnection::login(const std::string &nickname)
{
if (!_connected && !connect()) {
return false;
}
if (!nickname.empty()) {
_nickname = nickname;
} else {
SG_LOG(SG_NETWORK, SG_WARN, "IRC login requires nickname argument.");
return false;
}
std::string lines("NICK ");
lines += _nickname;
lines += IRC_MSG_TERMINATOR;
lines += "USER ";
lines += _nickname; //IRC <user>
lines += " 0 * :"; //IRC <mode> <unused>
lines += _nickname; //IRC <realname>
lines += IRC_MSG_TERMINATOR;
return writestring(lines.c_str());
}
// login with nickname given to constructor
bool IRCConnection::login()
{
return login(_nickname);
}
// the polite way to leave
void IRCConnection::quit()
{
if (!_connected) return;
writestring("QUIT goodbye\r\n");
disconnect();
}
bool IRCConnection::sendPrivmsg(const std::string &recipient, const std::string &textline)
{
if (!_logged_in) {
SG_LOG(SG_NETWORK, SG_WARN, "IRC 'privmsg' command unvailable. Login first!");
return false;
}
std::string line("PRIVMSG ");
line += recipient;
line += " :";
line += textline;
line += IRC_MSG_TERMINATOR;
if (writestring(line.c_str())) {
if (_pMessageCountOut) _pMessageCountOut->setIntValue(_pMessageCountOut->getIntValue() + 1);
if (_pIRCReturnCode) _pIRCReturnCode->setStringValue("");
return true;
} else {
SG_LOG(SG_NETWORK, SG_WARN, "IRC send privmsg failed.");
return false;
}
}
// join an IRC channel
bool IRCConnection::join(const std::string &channel)
{
if (!_logged_in) {
SG_LOG(SG_NETWORK, SG_WARN, "IRC 'join' command unvailable. Login first!");
return false;
}
std::string lines("JOIN ");
lines += channel;
lines += IRC_MSG_TERMINATOR;
return writestring(lines.c_str());
}
// leave an IRC channel
bool IRCConnection::part(const std::string &channel)
{
if (!_logged_in) {
SG_LOG(SG_NETWORK, SG_WARN, "IRC 'part' command unvailable. Login first!");
return false;
}
std::string lines("PART ");
lines += channel;
lines += IRC_MSG_TERMINATOR;
return writestring(lines.c_str());
}
/*
Call update() regularly to maintain connection (ping/pong) and process messages.
For information only:
The ping timeout appears to depend on the server settings and can be in the order
of minutes. However, for smooth message processing the update frequency should be
at least a few times per second and calling this at frame rate should not hurt.
*/
void IRCConnection::update()
{
if (_connected && readline(_read_buffer, sizeof(_read_buffer) - 1) > 0) {
std::string line(_read_buffer); // TODO: buffer size check required?
parseReceivedLine(line);
}
}
///////////////////////////////////////////////////////////////////////////////
// private methods
///////////////////////////////////////////////////////////////////////////////
// open a connection to IRC server
bool IRCConnection::connect()
{
if (_connected) {
return true;
}
_connected = open(SG_IO_OUT);
if (_connected) {
nonblock();
} else {
disconnect();
SG_LOG(SG_NETWORK, SG_WARN, "IRCConnection::connect error");
}
return _connected;
}
void IRCConnection::disconnect()
{
_logged_in = false;
if (_pReadyFlag) _pReadyFlag->setBoolValue(_logged_in);
if (_connected) {
_connected = false;
close();
SG_LOG(SG_NETWORK, SG_INFO, "IRCConnection::disconnect");
}
}
void IRCConnection::pong(const std::string &recipient)
{
if (!_connected) return;
std::string line("PONG ");
line += recipient;
line += IRC_MSG_TERMINATOR;
writestring(line.c_str());
}
bool IRCConnection::parseReceivedLine(std::string line)
{
/*
https://tools.ietf.org/html/rfc2812#section-3.7.2
2.3.1 Message format in Augmented BNF
The protocol messages must be extracted from the contiguous stream of
octets. The current solution is to designate two characters, CR and
LF, as message separators. Empty messages are silently ignored,
which permits use of the sequence CR-LF between messages without
extra problems.
The extracted message is parsed into the components <prefix>,
<command> and list of parameters (<params>).
The Augmented BNF representation for this is:
message = [ ":" prefix SPACE ] command [ params ] crlf
prefix = servername / ( nickname [ [ "!" user ] "@" host ] )
command = 1*letter / 3digit
params = *14( SPACE middle ) [ SPACE ":" trailing ]
=/ 14( SPACE middle ) [ SPACE [ ":" ] trailing ]
nospcrlfcl = %x01-09 / %x0B-0C / %x0E-1F / %x21-39 / %x3B-FF
; any octet except NUL, CR, LF, " " and ":"
middle = nospcrlfcl *( ":" / nospcrlfcl )
trailing = *( ":" / " " / nospcrlfcl )
SPACE = %x20 ; space character
crlf = %x0D %x0A ; "carriage return" "linefeed"
*/
// removes trailing '\r\n'
// TODO: for length(IRC_MSG_TERMINATOR) do line.pop_back();
line.pop_back();
line.pop_back();
std::string prefix;
std::string command;
std::string params;
std::size_t pos = line.find(" ", 1);
//prefix
if (line.at(0) == ':') {
prefix = line.substr(1, pos - 1); // remove leading ":"
std::size_t end = line.find(" ", pos + 1);
command = line.substr(pos + 1, end - pos - 1);
pos = end;
} else {
command = line.substr(0, pos);
}
params = line.substr(pos + 1);
// uncomment next line for debug output
//cout << "[prefix]" << prefix << "[cmd]" << command << "[params]" << params << "[end]" << endl;
// receiving a message
if (command == "PRIVMSG") {
if (_pMessageCountIn) _pMessageCountIn->setIntValue(_pMessageCountIn->getIntValue() + 1);
std::string recipient = params.substr(0, params.find(" :"));
// direct private message
if (recipient == _nickname) {
struct IRCMessage rcv;
rcv.sender = prefix.substr(0, prefix.find("!"));
rcv.textline = params.substr(params.find(":") + 1);
_incoming_private_messages.push_back(rcv);
} else {
// Most likely from an IRC channel if we joined any. In this case
// recipient equals channel name (e.g. "#mptest"). IRC channel
// support could be implemented here in future.
SG_LOG(SG_NETWORK, SG_DEV_WARN, "Ignoring PRIVMSG to '" + recipient + "' (should be '" + _nickname + "')");
}
} else if (command == "PING") {
// server pings us
std::string server = params.substr(0, params.find(" "));
pong(server);
} else if (command == "JOIN") {
// server acks our join request
std::string channel = params.substr(0, params.find(" "));
SG_LOG(SG_NETWORK, SG_DEV_WARN, "Joined IRC channel " + channel); //DEBUG
} else if (command == IRC_RPL_WELCOME) {
// after welcome we are logged in and allowed to send commands/messages to the IRC
_logged_in = true;
if (_pReadyFlag) _pReadyFlag->setBoolValue(1);
//joining channel might help while development, maybe removed later
//join(IRC_TEST_CHANNEL);
}
else if (command == IRC_RPL_MOTD) {
}
else if (command == IRC_RPL_MOTDSTART) {
}
else if (command == IRC_RPL_ENDOFMOTD) {
}
else if (command == IRC_ERR_NOSUCHNICK) {
// server return code if we send to invalid nickname
if (_pIRCReturnCode) _pIRCReturnCode->setStringValue(IRC_ERR_NOSUCHNICK);
}
else if (command == "ERROR") {
if (_pIRCReturnCode) _pIRCReturnCode->setStringValue(params);
disconnect();
}
// unexpected IRC message
else {
//SG_LOG(SG_NETWORK, SG_MANDATORY_INFO, "Unhandled IRC message "); //DEBUG
//cout << "[prefix]" << prefix << "[cmd]" << command << "[params]" << params << "[end]" << endl;
// TODO: anything sensitive here that we should handle?
// e.g. IRC user has disconnected and username == {current-cpdlc-authority}
return false;
}
return true;
}
IRCMessage IRCConnection::getMessage()
{
struct IRCMessage entry {
"", ""
};
if (!_incoming_private_messages.empty()) {
entry = _incoming_private_messages.front();
_incoming_private_messages.pop_front();
}
return entry;
}

96
src/MultiPlayer/mpirc.hxx Normal file
View File

@@ -0,0 +1,96 @@
//////////////////////////////////////////////////////////////////////
//
// mpirc.hxx
//
// started November 2020
// Authors: Henning Stahlke, Michael Filhol
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
//
//////////////////////////////////////////////////////////////////////
#ifndef MPIRC_H
#define MPIRC_H
#include <deque>
#include <string>
#include <simgear/compiler.h>
#include <simgear/props/props.hxx>
#include <simgear/io/raw_socket.hxx>
#include <simgear/io/sg_socket.hxx>
const std::string IRC_DEFAULT_PORT {"6667"};
const int IRC_BUFFER_SIZE = 1024;
struct IRCMessage {
std::string sender;
std::string textline;
};
/*
IRCConnection implements a basic IRC client for transmitting and receiving
private messages via an IRC server
In general it is possible to have multiple instances of this class but you
have to consider the following points:
- you cannot connect to a server with the same nickname more than once at a time
- if you want to expose status info to the property tree, you have to pass
an unique prefix to setupProperties() per instance
*/
class IRCConnection : SGSocket
{
public:
IRCConnection(const std::string &nickname, const std::string &servername, const std::string &port = IRC_DEFAULT_PORT);
~IRCConnection();
void setupProperties(std::string path);
void update();
bool login(const std::string &nickname);
bool login();
void quit();
bool sendPrivmsg(const std::string &recipient, const std::string &textline);
bool join(const std::string &channel);
bool part(const std::string &channel);
bool isConnected() const { return _connected; }
bool isReady() const { return _logged_in; }
bool hasMessage() const { return !_incoming_private_messages.empty(); }
IRCMessage getMessage();
private:
bool connect();
void disconnect();
void pong(const std::string &recipient);
bool parseReceivedLine(std::string irc_line);
bool _connected {false}; // TCP session ok
bool _logged_in {false}; // IRC login completed
std::string _nickname {""};
char _read_buffer[IRC_BUFFER_SIZE];
std::deque<IRCMessage> _incoming_private_messages;
SGPropertyNode *_pReadyFlag {nullptr};
SGPropertyNode *_pMessageCountIn {nullptr};
SGPropertyNode *_pMessageCountOut {nullptr};
SGPropertyNode *_pIRCReturnCode {nullptr};
};
#endif

View File

@@ -0,0 +1,186 @@
// mpmessages.hxx -- Message definitions for multiplayer communications
// within a multiplayer Flightgear
//
// Written by Duncan McCreanor, started February 2003.
// duncan.mccreanor@airservicesaustralia.com
//
// Copyright (C) 2003 Airservices Australia
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef MPMESSAGES_H
#define MPMESSAGES_H
#define MPMESSAGES_HID "$Id$"
/****************************************************************
* @version $Id$
*
* Description: Each message used for multiplayer communications
* consists of a header and optionally a block of data. The combined
* header and data is sent as one IP packet.
*
******************************************************************/
#include <vector>
#include <simgear/compiler.h>
#include <simgear/props/props.hxx>
#include <simgear/math/SGMath.hxx>
#include "tiny_xdr.hxx"
// magic value for messages
const uint32_t MSG_MAGIC = 0x46474653; // "FGFS"
// protocoll version
const uint32_t PROTO_VER = 0x00010001; // 1.1
// Message identifiers
#define CHAT_MSG_ID 1
#define UNUSABLE_POS_DATA_ID 2
#define OLD_OLD_POS_DATA_ID 3
#define OLD_POS_DATA_ID 4
#define OLD_PROP_MSG_ID 5
#define RESET_DATA_ID 6
#define POS_DATA_ID 7
#define MP_2017_DATA_ID 8
// 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
// multiple of 8!
#define MAX_CALLSIGN_LEN 8
#define MAX_CHAT_MSG_LEN 256
#define MAX_MODEL_NAME_LEN 96
#define MAX_PROPERTY_LEN 52
// Header for use with all messages sent
struct T_MsgHdr {
xdr_data_t Magic; // Magic Value
xdr_data_t Version; // Protocoll version
xdr_data_t MsgId; // Message identifier
xdr_data_t MsgLen; // absolute length of message
xdr_data_t RequestedRangeNm; // obsolete field (ReplyAddress) reused to request a range to fgms
xdr_data_t ReplyPort; // player's receiver port
char Callsign[MAX_CALLSIGN_LEN]; // Callsign used by the player
};
// Chat message
struct T_ChatMsg {
char Text[MAX_CHAT_MSG_LEN]; // Text of chat message
};
// Position message
struct T_PositionMsg {
char Model[MAX_MODEL_NAME_LEN]; // Name of the aircraft model
// Time when this packet was generated
xdr_data2_t time;
xdr_data2_t lag;
// position wrt the earth centered frame
xdr_data2_t position[3];
// orientation wrt the earth centered frame, stored in the angle axis
// representation where the angle is coded into the axis length
xdr_data_t orientation[3];
// linear velocity wrt the earth centered frame measured in
// the earth centered frame
xdr_data_t linearVel[3];
// angular velocity wrt the earth centered frame measured in
// the earth centered frame
xdr_data_t angularVel[3];
// linear acceleration wrt the earth centered frame measured in
// the earth centered frame
xdr_data_t linearAccel[3];
// angular acceleration wrt the earth centered frame measured in
// the earth centered frame
xdr_data_t angularAccel[3];
// Padding. The alignment is 8 bytes on x86_64 because there are
// 8-byte types in the message, so the size should be explicitly
// rounded out to a multiple of 8. Of course, it's a bad idea to
// put a C struct directly on the wire, but that's a fight for
// another day...
xdr_data_t pad;
};
struct FGPropertyData {
unsigned id;
// While the type isn't transmitted, it is needed for the destructor
simgear::props::Type type;
union {
int int_value;
float float_value;
char* string_value;
};
FGPropertyData() : string_value(nullptr) {}
~FGPropertyData() {
if ((type == simgear::props::STRING) || (type == simgear::props::UNSPECIFIED))
{
delete [] string_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 difference
// 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 received for this timeslot
std::vector<FGPropertyData*> properties;
~FGExternalMotionData()
{
std::vector<FGPropertyData*>::const_iterator propIt;
std::vector<FGPropertyData*>::const_iterator propItEnd;
propIt = properties.begin();
propItEnd = properties.end();
while (propIt != propItEnd)
{
delete *propIt;
propIt++;
}
}
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,163 @@
//////////////////////////////////////////////////////////////////////
//
// multiplaymgr.hxx
//
// Written by Duncan McCreanor, started February 2003.
// duncan.mccreanor@airservicesaustralia.com
//
// Copyright (C) 2003 Airservices Australia
// Copyright (C) 2005 Oliver Schroeder
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
//
//////////////////////////////////////////////////////////////////////
#ifndef MULTIPLAYMGR_H
#define MULTIPLAYMGR_H
#define MULTIPLAYTXMGR_HID "$Id$"
const int MIN_MP_PROTOCOL_VERSION = 1;
const int MAX_MP_PROTOCOL_VERSION = 2;
#include <deque>
#include <string>
#include <vector>
#include <memory>
#include <simgear/compiler.h>
#include <simgear/props/props.hxx>
#include <simgear/io/raw_socket.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
class IRCConnection;
class CPDLCManager;
const std::string MPIRC_SERVER_HOST_DEFAULT {"mpirc.flightgear.org"};
const std::string MPIRC_SERVER_HOST_PROPERTY {"/network/mpirc/server-host"};
const std::string MPIRC_SERVER_PORT_PROPERTY {"/network/mpirc/server-port"};
const std::string MPIRC_NICK_PREFIX {"MP_IRC_"};
struct FGExternalMotionData;
class MPPropertyListener;
struct T_MsgHdr;
class FGAIMultiplayer;
class FGMultiplayMgr : public SGSubsystem
{
public:
FGMultiplayMgr();
~FGMultiplayMgr();
// Subsystem API.
void init() override;
void reinit() override;
void shutdown() override;
void update(double dt) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "mp"; }
// transmitter
void SendTextMessage(const std::string &sMsgText);
// receiver
FGAIMultiplayer* getMultiplayer(const std::string& callsign);
std::shared_ptr<vector<char>> popMessageHistory();
void pushMessageHistory(std::shared_ptr<vector<char>> message);
// Remove motion information for all multiplayer aircraft, e.g. when
// scrubbing during replay.
void ClearMotion();
CPDLCManager *getCPDLC() { return _cpdlc.get(); };
private:
std::unique_ptr<IRCConnection> _mpirc;
std::unique_ptr<CPDLCManager> _cpdlc;
friend class MPPropertyListener;
void setPropertiesChanged()
{
mPropertiesChanged = true;
}
int getProtocolToUse()
{
int protocolVersion = pProtocolVersion->getIntValue();
if (protocolVersion >= MIN_MP_PROTOCOL_VERSION && protocolVersion <= MAX_MP_PROTOCOL_VERSION)
return protocolVersion;
else
return MIN_MP_PROTOCOL_VERSION;
}
void findProperties();
void Send(double currentMPTime);
void SendMyPosition(const FGExternalMotionData& motionInfo);
short get_scaled_short(double v, double scale);
union MsgBuf;
FGAIMultiplayer* addMultiplayer(const std::string& callsign,
const std::string& modelName,
const int fallback_model_index);
void FillMsgHdr(T_MsgHdr *MsgHdr, int iMsgId, unsigned _len = 0u);
void ProcessPosMsg(const MsgBuf& Msg, const simgear::IPAddress& SenderAddress,
long stamp);
void ProcessChatMsg(const MsgBuf& Msg, const simgear::IPAddress& SenderAddress);
bool isSane(const FGExternalMotionData& motionInfo);
int GetMsgNetwork(MsgBuf& msgBuf, simgear::IPAddress& SenderAddress);
int GetMsg(MsgBuf& msgBuf, simgear::IPAddress& SenderAddress);
/// maps from the callsign string to the FGAIMultiplayer
typedef std::map<std::string, SGSharedPtr<FGAIMultiplayer> > MultiPlayerMap;
MultiPlayerMap mMultiPlayerMap;
std::unique_ptr<simgear::Socket> mSocket;
simgear::IPAddress mServer;
bool mHaveServer;
bool mInitialised;
std::string mCallsign;
// Map between the property id's from the multiplayers network packets
// and the property nodes
typedef std::map<unsigned int, SGSharedPtr<SGPropertyNode> > PropertyMap;
PropertyMap mPropertyMap;
SGPropertyNode *pProtocolVersion;
SGPropertyNode *pXmitLen;
SGPropertyNode *pMultiPlayDebugLevel;
SGPropertyNode *pMultiPlayRange;
SGPropertyNode *pMultiPlayTransmitPropertyBase;
SGPropertyNode *pReplayState;
SGPropertyNode *pLogRawSpeedMultiplayer;
typedef std::map<unsigned int, const struct IdPropertyList*> PropertyDefinitionMap;
PropertyDefinitionMap mPropertyDefinition;
bool mPropertiesChanged;
MPPropertyListener* mListener;
double mDt; // reciprocal of /sim/multiplay/tx-rate-hz
double mNextTransmitTime = 0.0;
std::deque<std::shared_ptr<std::vector<char>>> mRecordMessageQueue;
std::deque<std::shared_ptr<std::vector<char>>> mReplayMessageQueue;
};
#endif

View File

@@ -0,0 +1,202 @@
//////////////////////////////////////////////////////////////////////
//
// Tiny XDR implementation for flightgear
// written by Oliver Schroeder
// released to the public domain
//
// This implementation is not complete, but implements
// everything we need.
//
// For further reading on XDR read RFC 1832.
//
//////////////////////////////////////////////////////////////////////
#include <string>
#include "tiny_xdr.hxx"
/* XDR 8bit integers */
xdr_data_t
XDR_encode_int8 ( const int8_t & n_Val )
{
return (SWAP32(static_cast<xdr_data_t> (n_Val)));
}
xdr_data_t
XDR_encode_uint8 ( const uint8_t & n_Val )
{
return (SWAP32(static_cast<xdr_data_t> (n_Val)));
}
int8_t
XDR_decode_int8 ( const xdr_data_t & n_Val )
{
return (static_cast<int8_t> (SWAP32(n_Val)));
}
uint8_t
XDR_decode_uint8 ( const xdr_data_t & n_Val )
{
return (static_cast<uint8_t> (SWAP32(n_Val)));
}
/* XDR 16bit integers */
xdr_data_t
XDR_encode_int16 ( const int16_t & n_Val )
{
return (SWAP32(static_cast<xdr_data_t> (n_Val)));
}
xdr_data_t
XDR_encode_uint16 ( const uint16_t & n_Val )
{
return (SWAP32(static_cast<xdr_data_t> (n_Val)));
}
int16_t
XDR_decode_int16 ( const xdr_data_t & n_Val )
{
return (static_cast<int16_t> (SWAP32(n_Val)));
}
uint16_t
XDR_decode_uint16 ( const xdr_data_t & n_Val )
{
return (static_cast<uint16_t> (SWAP32(n_Val)));
}
/* XDR 32bit integers */
xdr_data_t
XDR_encode_int32 ( const int32_t & n_Val )
{
return (SWAP32(static_cast<xdr_data_t> (n_Val)));
}
/*
* Safely convert from an int into a short. Anything outside the bounds of a short will
* simply be the max/min value of short (+/- 32767)
*/
static short XDR_convert_int_to_short(int v1)
{
if (v1 < -32767)
v1 = -32767;
if (v1 > 32767)
v1 = 32767;
return (short)v1;
}
/*
* Pack two 16bit shorts into a 32 bit int. By convention v1 is packed in the highword
*/
xdr_data_t XDR_encode_shortints32(const int v1, const int v2)
{
return XDR_encode_uint32(((XDR_convert_int_to_short(v1) << 16) & 0xffff0000) | ((XDR_convert_int_to_short(v2)) & 0xffff));
}
/* Decode packed shorts into two ints. V1 in the highword ($V1..V2..)*/
void XDR_decode_shortints32(const xdr_data_t & n_Val, int &v1, int &v2)
{
int _v1 = XDR_decode_int32(n_Val);
short s2 = (short)(_v1 & 0xffff);
short s1 = (short)(_v1 >> 16);
v1 = s1;
v2 = s2;
}
xdr_data_t
XDR_encode_uint32 ( const uint32_t & n_Val )
{
return (SWAP32(static_cast<xdr_data_t> (n_Val)));
}
int32_t
XDR_decode_int32 ( const xdr_data_t & n_Val )
{
return (static_cast<int32_t> (SWAP32(n_Val)));
}
uint32_t
XDR_decode_uint32 ( const xdr_data_t & n_Val )
{
return (static_cast<uint32_t> (SWAP32(n_Val)));
}
/* XDR 64bit integers */
xdr_data2_t
XDR_encode_int64 ( const int64_t & n_Val )
{
return (SWAP64(static_cast<xdr_data2_t> (n_Val)));
}
xdr_data2_t
XDR_encode_uint64 ( const uint64_t & n_Val )
{
return (SWAP64(static_cast<xdr_data2_t> (n_Val)));
}
int64_t
XDR_decode_int64 ( const xdr_data2_t & n_Val )
{
return (static_cast<int64_t> (SWAP64(n_Val)));
}
uint64_t
XDR_decode_uint64 ( const xdr_data2_t & n_Val )
{
return (static_cast<uint64_t> (SWAP64(n_Val)));
}
/* float */
xdr_data_t
XDR_encode_float ( const float & f_Val )
{
union {
xdr_data_t x;
float f;
} tmp;
tmp.f = f_Val;
return (XDR_encode_int32 (tmp.x));
}
float
XDR_decode_float ( const xdr_data_t & f_Val )
{
union {
xdr_data_t x;
float f;
} tmp;
tmp.x = XDR_decode_int32 (f_Val);
return tmp.f;
}
/* double */
xdr_data2_t
XDR_encode_double ( const double & d_Val )
{
union {
xdr_data2_t x;
double d;
} tmp;
tmp.d = d_Val;
return (XDR_encode_int64 (tmp.x));
}
double
XDR_decode_double ( const xdr_data2_t & d_Val )
{
union {
xdr_data2_t x;
double d;
} tmp;
tmp.x = XDR_decode_int64 (d_Val);
return tmp.d;
}

View File

@@ -0,0 +1,80 @@
//////////////////////////////////////////////////////////////////////
//
// Tiny XDR implementation for flightgear
// written by Oliver Schroeder
// released to the public domain
//
// This implementation is not complete, but implements
// everything we need.
//
// For further reading on XDR read RFC 1832.
//
// NEW
//
//////////////////////////////////////////////////////////////////////
#ifndef TINY_XDR_HEADER
#define TINY_XDR_HEADER
#if defined HAVE_CONFIG_H
# include <config.h>
#endif
#include <simgear/misc/stdint.hxx>
#define SWAP32(arg) sgIsLittleEndian() ? sg_bswap_32(arg) : arg
#define SWAP64(arg) sgIsLittleEndian() ? sg_bswap_64(arg) : arg
#define XDR_BYTES_PER_UNIT 4
typedef uint32_t xdr_data_t; /* 4 Bytes */
typedef uint64_t xdr_data2_t; /* 8 Bytes */
/* XDR 8bit integers */
xdr_data_t XDR_encode_int8 ( const int8_t & n_Val );
xdr_data_t XDR_encode_uint8 ( const uint8_t & n_Val );
int8_t XDR_decode_int8 ( const xdr_data_t & n_Val );
uint8_t XDR_decode_uint8 ( const xdr_data_t & n_Val );
/* XDR 16bit integers */
xdr_data_t XDR_encode_int16 ( const int16_t & n_Val );
xdr_data_t XDR_encode_uint16 ( const uint16_t & n_Val );
int16_t XDR_decode_int16 ( const xdr_data_t & n_Val );
uint16_t XDR_decode_uint16 ( const xdr_data_t & n_Val );
/* XDR 32bit integers */
xdr_data_t XDR_encode_int32 ( const int32_t & n_Val );
xdr_data_t XDR_encode_uint32 ( const uint32_t & n_Val );
int32_t XDR_decode_int32 ( const xdr_data_t & n_Val );
uint32_t XDR_decode_uint32 ( const xdr_data_t & n_Val );
/* XDR 64bit integers */
xdr_data2_t XDR_encode_int64 ( const int64_t & n_Val );
xdr_data2_t XDR_encode_uint64 ( const uint64_t & n_Val );
int64_t XDR_decode_int64 ( const xdr_data2_t & n_Val );
uint64_t XDR_decode_uint64 ( const xdr_data2_t & n_Val );
xdr_data_t XDR_encode_shortints32(const int v1, const int v2);
void XDR_decode_shortints32(const xdr_data_t & n_Val, int &v1, int &v2);
//////////////////////////////////////////////////
//
// FIXME: #1 these funtions must be fixed for
// none IEEE-encoding architecturs
// (eg. vax, big suns etc)
// FIXME: #2 some compilers return 'double'
// regardless of return-type 'float'
// this must be fixed, too
// FIXME: #3 some machines may need to use a
// different endianess for floats!
//
//////////////////////////////////////////////////
/* float */
xdr_data_t XDR_encode_float ( const float & f_Val );
float XDR_decode_float ( const xdr_data_t & f_Val );
/* double */
xdr_data2_t XDR_encode_double ( const double & d_Val );
double XDR_decode_double ( const xdr_data2_t & d_Val );
#endif