From b47fe67263e2888e04800e76c1bb9bf65246d19f Mon Sep 17 00:00:00 2001
From: TwinFan
Date: Sat, 14 Nov 2020 00:52:38 +0100
Subject: [PATCH 01/10] Data type conversion cleanup / Release AI to XPMP2
Remote Client
---
Include/Constants.h | 10 +-
Include/DataRefs.h | 2 +-
Include/LTADSBEx.h | 4 +-
Include/LTRealTraffic.h | 2 +
Include/LiveTraffic.h | 6 +-
Include/Network.h | 106 ++---
Lib/ImGui/misc/cpp/imgui_stdlib.cpp | 2 +-
Lib/ImgWindow/ImgFontAtlas.cpp | 2 +-
Lib/ImgWindow/ImgWindow.cpp | 4 +-
.../Versions/1.0/Headers/XPMPAircraft.h | 39 +-
.../Versions/1.0/Headers/XPMPMultiplayer.h | 8 +-
.../Versions/1.0/Headers/XPMPRemote.h | 426 ++++++++++++++++++
.../Versions/1.0/Resources/Info.plist | 8 +-
LiveTraffic.xcodeproj/project.pbxproj | 30 +-
.../xcschemes/LiveTraffic.xcscheme | 2 +-
Src/ACTable.cpp | 6 +-
Src/DataRefs.cpp | 6 +-
Src/LTApt.cpp | 4 +-
Src/LTChannel.cpp | 1 -
Src/LTFlightData.cpp | 4 +-
Src/LTMain.cpp | 8 +-
Src/LTOpenGlider.cpp | 8 +-
Src/LiveTraffic.cpp | 14 +-
Src/Network.cpp | 12 +-
Src/SettingsUI.cpp | 2 +-
25 files changed, 607 insertions(+), 109 deletions(-)
create mode 100644 Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h
diff --git a/Include/Constants.h b/Include/Constants.h
index e54e367e..10d2219c 100644
--- a/Include/Constants.h
+++ b/Include/Constants.h
@@ -29,8 +29,8 @@
//
// MARK: Version Information (CHANGE VERSION HERE)
//
-constexpr float VERSION_NR = 2.20f;
-constexpr bool VERSION_BETA = false;
+constexpr float VERSION_NR = 2.21f;
+constexpr bool VERSION_BETA = true;
extern float verXPlaneOrg; // version on X-Plane.org
extern int verDateXPlaneOrg; // and its date
@@ -151,6 +151,7 @@ constexpr int LT_NEW_VER_CHECK_TIME = 48; // [h] between two checks of a new
#define LT_FM_VERSION "2.2" // expected version of flight model file format
#define PLUGIN_SIGNATURE "TwinFan.plugin.LiveTraffic"
#define PLUGIN_DESCRIPTION "Create Multiplayer Aircraft based on live traffic."
+constexpr const char* REMOTE_SIGNATURE = "TwinFan.plugin.XPMP2.Remote";
#define LT_DOWNLOAD_URL "https://forums.x-plane.org/index.php?/files/file/49749-livetraffic/"
#define LT_DOWNLOAD_CH "X-Plane.org"
#define MSG_DISABLED "Disabled"
@@ -175,6 +176,7 @@ constexpr int LT_NEW_VER_CHECK_TIME = 48; // [h] between two checks of a new
#define INFO_AC_REMOVED "Removed aircraft %s"
#define INFO_AC_ALL_REMOVED "Removed all aircraft"
#define INFO_REQU_AI_RELEASE "%s requested us to release TCAS / AI control. Switch off '" MENU_HAVE_TCAS "' if you want so."
+#define INFO_REQU_AI_REMOTE "XPMP2 Remote Client requested us to release TCAS / AI control, so we do."
#define INFO_GOT_AI_CONTROL LIVE_TRAFFIC " has TCAS / AI control now"
#define INFO_RETRY_GET_AI "Another plugin released AI control, will try again to get control..."
#define INFO_AC_HIDDEN "A/c %s hidden"
@@ -354,6 +356,10 @@ constexpr int SERR_LEN = 100; // size of buffer for IO error t
#define ERR_FM_UNKNOWN_PARENT "Parent section missing in '%s', line %d: %s"
#define ERR_FM_REGEX "%s in '%s', line %d: %s"
#define ERR_FM_NOT_FOUND "Found no flight model for ICAO %s/match-string %s: will use default"
+#define ERR_TCP_LISTENACCEPT "%s: Error opening the TCP port on %s:%s: %s"
+#define ERR_SOCK_SEND_FAILED "%s: Could not send position: send operation failed"
+#define ERR_UDP_SOCKET_CREAT "%s: Error creating UDP socket for %s:%d: %s"
+#define ERR_UDP_RCVR_RCVR "%s: Error receiving UDP: %s"
constexpr int ERR_CFG_FILE_MAXWARN = 10; // maximum number of warnings while reading config file, then: dead
//MARK: Debug Texts
diff --git a/Include/DataRefs.h b/Include/DataRefs.h
index 42fb4f19..b32604bf 100644
--- a/Include/DataRefs.h
+++ b/Include/DataRefs.h
@@ -507,7 +507,7 @@ class DataRefs
// this is a bit ugly but avoids a wrapper union with an int
inline unsigned GetUInt() const { return *reinterpret_cast(this); }
- inline void SetUInt(int i) { *reinterpret_cast(this) = i; }
+ inline void SetUInt(unsigned i) { *reinterpret_cast(this) = i; }
inline bool operator != (const LabelCfgTy& o) const
{ return GetUInt() != o.GetUInt(); }
};
diff --git a/Include/LTADSBEx.h b/Include/LTADSBEx.h
index 27b886d2..1f68aa01 100644
--- a/Include/LTADSBEx.h
+++ b/Include/LTADSBEx.h
@@ -159,8 +159,8 @@ class ADSBExchangeConnection : public LTOnlineChannel, LTFlightDataChannel
#define ADSBEX_HIST_NAME "ADS-B Exchange Historic"
constexpr int ADSBEX_HIST_MIN_CHARS = 20; // minimum nr chars per line to be a 'reasonable' line
constexpr int ADSBEX_HIST_MAX_ERR_CNT = 5; // after that many errorneous line we stop reading
-#define ADSBEX_HIST_PATH "Custom Data/ADSB" // TODO: Move to options: relative to XP main
-#define ADSBEX_HIST_PATH_2 "Custom Data/ADSB2" // TODO: Move to options: fallback, if first one doesn't work
+#define ADSBEX_HIST_PATH "Custom Data/ADSB" // TODO : Move to options: relative to XP main
+#define ADSBEX_HIST_PATH_2 "Custom Data/ADSB2" // TODO : Move to options: fallback, if first one doesn't work
#define ADSBEX_HIST_DATE_PATH "%c%04d-%02d-%02d"
#define ADSBEX_HIST_FILE_NAME "%c%04d-%02d-%02d-%02d%02dZ.json"
#define ADSBEX_HIST_PATH_EMPTY "Historic Data Path doesn't exist or folder empty at %s"
diff --git a/Include/LTRealTraffic.h b/Include/LTRealTraffic.h
index cb207ebc..03f5ae67 100644
--- a/Include/LTRealTraffic.h
+++ b/Include/LTRealTraffic.h
@@ -51,6 +51,8 @@ constexpr double RT_VSI_AIRBORNE = 80.0; ///< if VSI is more than this then w
#define ERR_RT_CANTLISTEN "RealTraffic: Cannot listen to network, can't tell RealTraffic our position"
#define ERR_RT_WEATHER_QNH "RealTraffic reports unreasonable QNH %ld - ignored"
#define ERR_RT_DISCARDED_MSG "RealTraffic: Discarded invalid message: %s"
+#define ERR_SOCK_NOTCONNECTED "%s: Cannot send position: not connected"
+#define ERR_SOCK_INV_POS "%s: Cannot send position: position not fully valid"
// Traffic data format and fields
#define RT_TRAFFIC_AITFC "AITFC"
diff --git a/Include/LiveTraffic.h b/Include/LiveTraffic.h
index 87751ee7..7bb7550e 100644
--- a/Include/LiveTraffic.h
+++ b/Include/LiveTraffic.h
@@ -372,15 +372,11 @@ inline struct tm *localtime_s(struct tm * result, const time_t * time)
#define STRCPY_S(dest,src) strncpy_s(dest,sizeof(dest),src,sizeof(dest)-1)
#define STRCPY_ATMOST(dest,src) strncpy_s(dest,sizeof(dest),strAtMost(src,sizeof(dest)-1).c_str(),sizeof(dest)-1)
-#if APL == 1
+#if APL == 1 or LIN == 1
// XCode/Linux don't provide the _s functions, not even with __STDC_WANT_LIB_EXT1__ 1
inline int strerror_s( char *buf, size_t bufsz, int errnum )
{ return strerror_r(errnum, buf, bufsz); }
#endif
-#if LIN == 1
-inline int strerror_s( char *buf, size_t bufsz, int errnum )
-{ strncpy_s(buf,bufsz,strerror(errnum),bufsz-1); return 0; }
-#endif
// MARK: Thread names
#ifdef DEBUG
diff --git a/Include/Network.h b/Include/Network.h
index 0bcca051..e03c337f 100644
--- a/Include/Network.h
+++ b/Include/Network.h
@@ -32,36 +32,29 @@
#else
#include
#include
-typedef int SOCKET; // Windows defines SOCKET, so we define it for non-Windows manually
-constexpr SOCKET INVALID_SOCKET = -1;
#endif
#include
-// error messages used in derived classes
-#define ERR_TCP_LISTENACCEPT "%s: Error opening the TCP port on %s:%s: %s"
-#define ERR_SOCK_NOTCONNECTED "%s: Cannot send position: not connected"
-#define ERR_SOCK_INV_POS "%s: Cannot send position: position not fully valid"
-#define ERR_SOCK_SEND_FAILED "%s: Could not send position: send operation failed"
-#define ERR_NETW_TECH_ERROR "%s: Technical network error: %s"
-
-#define ERR_UDP_SOCKET_CREAT "%s: Error creating UDP socket for %s:%d: %s"
-#define ERR_UDP_RCVR_RCVR "%s: Error receiving UDP: %s"
-
-
-// The UDPRuntimeError exception is raised when the address
-// and port combinaison cannot be resolved or if the socket cannot be
-// opened.
+#if not IBM
+typedef int SOCKET; ///< Windows defines SOCKET, so we define it for non-Windows manually
+constexpr SOCKET INVALID_SOCKET = -1;
+#endif
+/// @brief Exception raised by XPMP2::SocketNetworking objects
+/// @details This exception is raised when the address
+/// and port combinaison cannot be resolved or if the socket cannot be
+/// opened.
class NetRuntimeError : public std::runtime_error
{
public:
- std::string errTxt;
- std::string fullWhat;
- NetRuntimeError(const char *w);
+ std::string errTxt; ///< OS text for what `errno` says, output of strerror_s()
+ std::string fullWhat; ///< combines `w` and `errTxt`
+ NetRuntimeError(const char *w); ///< Constructor sets the above texts
+ /// Return the full message, ie. `fullWhat`
const char* what() const noexcept override { return fullWhat.c_str(); }
};
-// Base class for socket-based networking
+/// Base class for any socket-based networking
class SocketNetworking
{
protected:
@@ -74,94 +67,101 @@ class SocketNetworking
size_t bufSize = 512;
public:
- // The address is a string and it can represent an IPv4 or IPv6 address.
+ /// Default constructor is not doing anything
SocketNetworking() {}
- SocketNetworking(const std::string& _addr, int _port, size_t _bufSize,
+ /// Constructor creates a socket and binds it to the given address
+ SocketNetworking(const std::string& _addr, int _port, size_t _bufSize = 512,
unsigned _timeOut_ms = 0, bool _bBroadcast = false);
+ /// Destructor makes sure the socket is closed
virtual ~SocketNetworking();
- // Opens the socket for listening
- virtual void Open(const std::string& _addr, int _port, size_t _bufSize,
+ /// Creates a socket and binds it to the given local address
+ virtual void Open(const std::string& _addr, int _port, size_t _bufSize = 512,
unsigned _timeOut_ms = 0, bool _bBroadcast = false);
- // Connect to a server
+ /// Creates a socket and connects it to the given remote server
virtual void Connect(const std::string& _addr, int _port, size_t _bufSize,
unsigned _timeOut_ms = 0);
- // Close the connection(s)
+ /// Thread-safely close the connection(s) and frees the buffer
virtual void Close();
-
+ /// Is a socket open?
inline bool isOpen() const { return (f_socket != INVALID_SOCKET); }
-
+ /// (Re)Sets the buffer size (or clears it if `_bufSize==0`)
void SetBufSize (size_t _bufSize);
- std::string GetLastErr();
+ /// Returns a human-readable text for the last error
+ static std::string GetLastErr();
// attribute access
- inline SOCKET getSocket() const { return f_socket; }
- inline int getPort() const { return f_port; }
- inline std::string getAddr() const { return f_addr; }
+ SOCKET getSocket() const { return f_socket; } ///< the socket
+ int getPort() const { return f_port; } ///< the port
+ std::string getAddr() const { return f_addr; } ///< the interface address
- // return the buffer
+ /// returna the buffer
const char* getBuf () const { return buf ? buf : ""; }
- // receive messages
+ /// Waits to receive a message, ensures zero-termination in the buffer
long recv();
+ /// Waits to receive a message with timeout, ensures zero-termination in the buffer
long timedRecv(int max_wait_ms);
- // send broadcast message
+ /// Sends a broadcast message
bool broadcast (const char* msg);
- // convert addresses to string
+ /// Convert addresses to string
static std::string GetAddrString (const struct sockaddr* addr);
protected:
- // subclass tell which addresses to look for
+ /// Subclass to tell which addresses to look for
virtual void GetAddrHints (struct addrinfo& hints) = 0;
};
-// Receives UDP messages
+/// Receives UDP messages
class UDPReceiver : public SocketNetworking
{
public:
- // The address is a string and it can represent an IPv4 or IPv6 address.
+ /// Default constructor is not doing anything
UDPReceiver() : SocketNetworking() {}
- UDPReceiver(const std::string& _addr, int _port, size_t _bufSize,
+ /// Constructor creates a socket and binds it to the given address
+ UDPReceiver(const std::string& _addr, int _port, size_t _bufSize = 512,
unsigned _timeOut_ms = 0) :
SocketNetworking(_addr,_port,_bufSize,_timeOut_ms) {}
protected:
+ /// Sets flags to AI_PASSIVE, AF_INET, SOCK_DGRAM, IPPROTO_UDP
virtual void GetAddrHints (struct addrinfo& hints);
};
-// Receives TCP connections
+/// Listens to TCP connections and opens a session socket upon connect
class TCPConnection : public SocketNetworking
{
protected:
- SOCKET f_session_socket = INVALID_SOCKET;
- struct sockaddr_in f_session_addr;
+ SOCKET f_session_socket = INVALID_SOCKET; ///< session socket, ie. the socket opened when a counterparty connects
+ struct sockaddr_in f_session_addr; ///< address of the connecting counterparty
#if APL == 1 || LIN == 1
- // the self-pipe to shut down the TCP listener gracefully
+ /// the self-pipe to shut down the TCP listener gracefully
SOCKET selfPipe[2] = { INVALID_SOCKET, INVALID_SOCKET };
#endif
public:
- // The address is a string and it can represent an IPv4 or IPv6 address.
+ /// Default constructor is not doing anything
TCPConnection() : SocketNetworking() {}
- TCPConnection(const std::string& _addr, int _port, size_t _bufSize,
+ /// Constructor creates a socket and binds it to the given address
+ TCPConnection(const std::string& _addr, int _port, size_t _bufSize = 512,
unsigned _timeOut_ms = 0) :
SocketNetworking(_addr,_port,_bufSize,_timeOut_ms) {}
- virtual void Close(); // also close session connection
- void CloseListenerOnly(); // only closes the listening socket, but not a session connection
+ virtual void Close(); ///< also close session connection
+ void CloseListenerOnly(); ///< only closes the listening socket, but not a connected session
- // Listen for and accept connections
- void listen (int numConnections = 1);
- bool accept (bool bUnlisten = false);
- bool listenAccept (int numConnections = 1);
+ void listen (int numConnections = 1); ///< listen for incoming connections
+ bool accept (bool bUnlisten = false); ///< accept an incoming connections, optinally stop listening
+ bool listenAccept (int numConnections = 1); ///< combines listening and accepting
+ /// Connected to a counterparty?
bool IsConnected () const { return f_session_socket != INVALID_SOCKET; };
- // send messages on session connection
+ /// send messages on session connection
bool send(const char* msg);
protected:
diff --git a/Lib/ImGui/misc/cpp/imgui_stdlib.cpp b/Lib/ImGui/misc/cpp/imgui_stdlib.cpp
index cb1fe174..ee694aa0 100644
--- a/Lib/ImGui/misc/cpp/imgui_stdlib.cpp
+++ b/Lib/ImGui/misc/cpp/imgui_stdlib.cpp
@@ -27,7 +27,7 @@ static int InputTextCallback(ImGuiInputTextCallbackData* data)
// If for some reason we refuse the new length (BufTextLen) and/or capacity (BufSize) we need to set them back to what we want.
std::string* str = user_data->Str;
IM_ASSERT(data->Buf == str->c_str());
- str->resize(data->BufTextLen);
+ str->resize(std::string::size_type(data->BufTextLen));
data->Buf = (char*)str->c_str();
}
else if (user_data->ChainCallback)
diff --git a/Lib/ImgWindow/ImgFontAtlas.cpp b/Lib/ImgWindow/ImgFontAtlas.cpp
index d8ee0bfa..2b0aeba3 100644
--- a/Lib/ImgWindow/ImgFontAtlas.cpp
+++ b/Lib/ImgWindow/ImgFontAtlas.cpp
@@ -46,7 +46,7 @@ ImgFontAtlas::ImgFontAtlas():
ImgFontAtlas::~ImgFontAtlas()
{
if (mTextureBound) {
- GLuint glTexNum = mGLTextureNum;
+ GLuint glTexNum = (GLuint)mGLTextureNum;
glDeleteTextures(1, &glTexNum);
mTextureBound = false;
}
diff --git a/Lib/ImgWindow/ImgWindow.cpp b/Lib/ImgWindow/ImgWindow.cpp
index 1923db08..4c64e1c0 100644
--- a/Lib/ImgWindow/ImgWindow.cpp
+++ b/Lib/ImgWindow/ImgWindow.cpp
@@ -131,7 +131,7 @@ ImgWindow::ImgWindow(
mFontTexture = (GLuint)texNum;
// upload texture.
- XPLMBindTexture2d(mFontTexture, 0);
+ XPLMBindTexture2d((int)mFontTexture, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
@@ -295,7 +295,7 @@ ImgWindow::RenderImGui(ImDrawData *draw_data)
if (pcmd->UserCallback) {
pcmd->UserCallback(cmd_list, pcmd);
} else {
- XPLMBindTexture2d((GLuint)(intptr_t)pcmd->TextureId, 0);
+ XPLMBindTexture2d((int)(intptr_t)pcmd->TextureId, 0);
// Scissors work in viewport space - must translate the coordinates from ImGui -> Boxels, then Boxels -> Native.
//FIXME: it must be possible to apply the scale+transform manually to the projection matrix so we don't need to doublestep.
diff --git a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPAircraft.h b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPAircraft.h
index 306d2740..2b72e638 100644
--- a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPAircraft.h
+++ b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPAircraft.h
@@ -27,6 +27,7 @@
#include "XPLMCamera.h"
#include "XPLMMap.h"
+#include
#include
#include
#include
@@ -48,7 +49,7 @@ constexpr double M_per_FT = 0.3048; // meter per 1 foot
constexpr int M_per_NM = 1852; // meter per one nautical mile
/// The dataRefs provided by XPMP2 to the CSL models
-enum DR_VALS {
+enum DR_VALS : std::uint8_t {
V_CONTROLS_GEAR_RATIO = 0, ///< `libxplanemp/controls/gear_ratio` and \n`sim/cockpit2/tcas/targets/position/gear_deploy`
V_CONTROLS_NWS_RATIO, ///< `libxplanemp/controls/nws_ratio`, the nose wheel angle, actually in degrees
V_CONTROLS_FLAP_RATIO, ///< `libxplanemp/controls/flap_ratio` and \n`sim/cockpit2/tcas/targets/position/flap_ratio` and `...flap_ratio2`
@@ -244,20 +245,42 @@ class Aircraft {
bool bDestroyInst = false; ///< Instance to be destroyed in next flight loop callback?
public:
- /// Constructor creates a new aircraft object, which will be managed and displayed
+ /// @brief Constructor creates a new aircraft object, which will be managed and displayed
/// @exception XPMP2::XPMP2Error Mode S id invalid or duplicate, no model found during model matching
/// @param _icaoType ICAO aircraft type designator, like 'A320', 'B738', 'C172'
/// @param _icaoAirline ICAO airline code, like 'BAW', 'DLH', can be an empty string
/// @param _livery Special livery designator, can be an empty string
/// @param _modeS_id (optional) **Unique** identification of the plane [0x01..0xFFFFFF], e.g. the 24bit mode S transponder code. XPMP2 assigns an arbitrary unique number of not given
- /// @param _modelId (optional) specific model id to be used (no folder/package name, just the id as defined in the `OBJ8_AIRCRAFT` line)
+ /// @param _cslId (optional) specific unique model id to be used (package name/short id, as defined in the `OBJ8_AIRCRAFT` line)
Aircraft (const std::string& _icaoType,
const std::string& _icaoAirline,
const std::string& _livery,
XPMPPlaneID _modeS_id = 0,
- const std::string& _modelId = "");
+ const std::string& _cslId = "");
+ /// Default constructor creates an empty, invalid(!) and invisible shell; call XPMP2::Aircraft::Create() to actually create a plane
+ Aircraft ();
/// Destructor cleans up all resources acquired
virtual ~Aircraft();
+
+ /// Aircraft must not be copied as they reference non-copyable resources like XP instances
+ Aircraft (const Aircraft&) = delete;
+ /// Aircraft must not be copied as they reference non-copyable resources like XP instances
+ Aircraft& operator=(const Aircraft&) = delete;
+
+ /// @brief Creates a plane, only a valid operation if object was created using the default constructor
+ /// @exception Tried on already defined object; XPMP2::XPMP2Error Mode S id invalid or duplicate, no model found during model matching
+ /// @param _icaoType ICAO aircraft type designator, like 'A320', 'B738', 'C172'
+ /// @param _icaoAirline ICAO airline code, like 'BAW', 'DLH', can be an empty string
+ /// @param _livery Special livery designator, can be an empty string
+ /// @param _modeS_id (optional) **Unique** identification of the plane [0x01..0xFFFFFF], e.g. the 24bit mode S transponder code. XPMP2 assigns an arbitrary unique number of not given
+ /// @param _cslId (optional) specific unique model id to be used (package name/short id, as defined in the `OBJ8_AIRCRAFT` line)
+ /// @param _pCSLModel (optional) The actual model to use (no matching or search by `_cslId` if model is given this way)
+ void Create (const std::string& _icaoType,
+ const std::string& _icaoAirline,
+ const std::string& _livery,
+ XPMPPlaneID _modeS_id = 0,
+ const std::string& _cslId = "",
+ CSLModel* _pCSLModel = nullptr);
/// return the XPMP2 plane id
XPMPPlaneID GetModeS_ID () const { return modeS_id; }
@@ -295,8 +318,12 @@ class Aircraft {
/// @return match quality, the lower the better
int ReMatchModel () { return ChangeModel(acIcaoType,acIcaoAirline,acLivery); }
- /// Assigns the given model per name, returns if successful
- bool AssignModel (const std::string& _modelName);
+ /// @brief Assigns the given model
+ /// @param _cslId Search for this id (package/short)
+ /// @param _pCSLModel (optional) If given use this model and don't search
+ /// @return Successfuly found and assigned a model?
+ bool AssignModel (const std::string& _cslId,
+ CSLModel* _pCSLModel = nullptr);
/// return a pointer to the CSL model in use (Note: The CSLModel structure is not public.)
XPMP2::CSLModel* GetModel () const { return pCSLMdl; }
/// return the name of the CSL model in use
diff --git a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPMultiplayer.h b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPMultiplayer.h
index ff7817f7..5e5f74a4 100644
--- a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPMultiplayer.h
+++ b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPMultiplayer.h
@@ -262,6 +262,7 @@ constexpr XPMPPlaneID MAX_MODE_S_ID = 0x00FFFFFF;
#define XPMP_CFG_ITM_REPLTEXTURE "replace_texture" ///< Config key: Replace textures in OBJ8 files upon load if needed (specified on the OBJ8 line in xsb_aircraft.txt), creating new OBJ8 files
#define XPMP_CFG_ITM_CLAMPALL "clamp_all_to_ground" ///< Config key: Ensure no plane sinks below ground, no matter of XPMP2::Aircraft::bClampToGround
#define XPMP_CFG_ITM_HANDLE_DUP_ID "handle_dup_id" ///< Config key: Boolean: If XPMP2::Aircraft::modeS_id already exists then assign a new unique one, overwrites XPMP2::Aircraft::modeS_id
+#define XPMP_CFG_ITM_SUPPORT_REMOTE "support_remote" ///< Config key: Support remote connections? `<0` force off, `0` default: on if in a networked or multiplayer setup, `>0` force on
#define XPMP_CFG_ITM_LOGLEVEL "log_level" ///< Config key: General level of logging into `Log.txt` (0 = Debug, 1 = Info, 2 = Warning, 3 = Error, 4 = Fatal)
#define XPMP_CFG_ITM_MODELMATCHING "model_matching" ///< Config key: Write information on model matching into `Log.txt`
@@ -275,6 +276,7 @@ constexpr XPMPPlaneID MAX_MODE_S_ID = 0x00FFFFFF;
/// `models | replace_texture | int | 1 | Replace textures in OBJ8 files upon load if needed (specified on the OBJ8 line in xsb_aircraft.txt), creating new OBJ8 files`\n
/// `planes | clamp_all_to_ground | int | 1 | Ensure no plane sinks below ground, no matter of XPMP2::Aircraft::bClampToGround`\n
/// `planes | handle_dup_id | int | 0 | Boolean: If XPMP2::Aircraft::modeS_id already exists then assign a new unique one, overwrites XPMP2::Aircraft::modeS_id`\n
+/// `planes | support_remote | int | 0 | 3-state integer: Support remote connections? <0 force off, 0 default: on if in a networked or multiplayer setup, >0 force on`\n
/// `debug | log_level | int | 2 | General level of logging into Log.txt (0 = Debug, 1 = Info, 2 = Warning, 3 = Error, 4 = Fatal)`\n
/// `debug | model_matching | int | 0 | Write information on model matching into Log.txt`\n
/// @note There is no immediate requirement to check the value of `_section` in your implementation.
@@ -456,12 +458,16 @@ bool XPMPIsICAOValid(const char * inICAO);
/// in your XPMP2::Aircraft::UpdatePosition() implementation.
/// @return The functions returns the array index you need to use in the
/// XPMP2::Aircraft::v array to provide the actual value.
-/// Or `0` when the function was called while planes are active.
+/// Or `0` when an error occured.
/// @note There is a duplication check: Adding an already existing dataRef,
/// no matter if XPMP2-predefined or by the plugin will return the
/// already existing index.
/// @note Can only be called while no plane exists as it influence the size
/// of important data arrays. Returns `0` if called while planes exist.
+/// @note Can only be called from XP's main thread as XPLM SDK functions are called.
+/// Returns `0` if called from a worker thread.
+/// @note User-defined dataRefs are _not_ transfered to the XPMP2 Remote Client
+/// for display on network-connected X-Plane instances.
size_t XPMPAddModelDataRef (const std::string& dataRef);
/************************************************************************************
diff --git a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h
new file mode 100644
index 00000000..c7d5ae9a
--- /dev/null
+++ b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h
@@ -0,0 +1,426 @@
+/// @file XPMPRemote.h
+/// @brief Semi-public remote network functionality for master/client operations
+/// @details Technically, this is public functionality from a library
+/// point of view. But it is intended for the "XPMP Remote Client" only,
+/// not for a standard plugin.\n
+/// Network messages are packed for space efficiency, but also to avoid
+/// layout differences between compilers/platforms.
+/// However, manual layout tries to do reasonable alignment of numeric values
+/// and 8-byte-alignment of each structure, so that arrays of structures
+/// also align well.
+/// @author Birger Hoppe
+/// @copyright (c) 2020 Birger Hoppe
+/// @copyright Permission is hereby granted, free of charge, to any person obtaining a
+/// copy of this software and associated documentation files (the "Software"),
+/// to deal in the Software without restriction, including without limitation
+/// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+/// and/or sell copies of the Software, and to permit persons to whom the
+/// Software is furnished to do so, subject to the following conditions:\n
+/// The above copyright notice and this permission notice shall be included in
+/// all copies or substantial portions of the Software.\n
+/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+/// THE SOFTWARE.
+
+#ifndef _XPMPRemote_h_
+#define _XPMPRemote_h_
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace XPMP2 {
+
+//
+// MARK: Global Helpers
+//
+
+/// @brief Produces a reproducible(!) hash value for strings
+/// @details Result is the same if the same string is provided, across platform
+/// and across executions, unlike what std::hash requires.\n
+/// It is implemented as a 16-bit version of the PJW hash:
+/// @see https://en.wikipedia.org/wiki/PJW_hash_function
+std::uint16_t PJWHash16 (const char *s);
+
+/// @brief Find a model by package name hash and short id
+/// @details This approach is used by the remote client to safe network bandwith.
+/// If an exact match with `pkgHash` _and_ `shortID` is not found,
+/// then a model matching the short id alone is returned if available.
+CSLModel* CSLModelByPkgShortId (std::uint16_t _pkgHash,
+ const std::string& _shortId);
+
+//
+// MARK: Network Data Definitions
+//
+
+// To ensure best network capacity usage as well as identical alignment across platform we enforce tightly packed structures.
+// The approach is very different between Visual C++ and clang / gcc, though:
+#ifdef _MSC_VER // Visual C++
+#pragma pack(push,1) // set packing once (ie. not per struct)
+#define PACKED
+#elif defined(__clang__) || defined(__GNUC__) // Clang (Mac XCode etc) or GNU (Linux)
+#define PACKED __attribute__ ((__packed__))
+#else
+#error Unhandled Compiler!
+#endif
+
+/// Message type
+enum RemoteMsgTy : std::uint8_t {
+ RMT_MSG_INTEREST_BEACON = 0, ///< beacon sent by a remote client to signal interest in data
+ RMT_MSG_SEND, ///< internal indicator telling to send out all pending messages
+ RMT_MSG_SETTINGS, ///< a sender's id and its settings
+ RMT_MSG_AC_DETAILED, ///< aircraft full details, needed to create new a/c objects and to re-synch all remote data
+ RMT_MSG_AC_POS_UPDATE, ///< aircraft differences only
+ RMT_MSG_AC_ANIM, ///< aircraft animation values (dataRef values) only
+ RMT_MSG_AC_REMOVE, ///< aircraft is removed
+};
+
+/// Definition for how to map dataRef values to (u)int8, ie. to an integer range of 8 bits
+struct RemoteDataRefPackTy {
+ float minV = 0.0f; ///< minimum transferred value
+ float range = 0.0f; ///< range of transferred value = maxV - minV
+
+ /// Constructor sets minimum value and range
+ RemoteDataRefPackTy (float _min, float _max) : minV(_min), range(_max - _min) { assert(range != 0.0f); }
+
+ /// pack afloat value to integer
+ std::uint8_t pack (float f) const { return std::uint8_t(std::clamp(f-minV,0.0f,range) * UINT8_MAX / range); }
+ /// unpack an integer value to float
+ float unpack (std::uint8_t i) const { return minV + range*float(i)/255.0f; }
+};
+
+/// An array holding all dataRef packing definitions
+extern const std::array REMOTE_DR_DEF;
+
+//
+// MARK: Message Header (Base)
+//
+
+/// Message header, identical for all message types
+struct RemoteMsgBaseTy {
+ RemoteMsgTy msgTy : 4; ///< message type
+ std::uint8_t msgVer : 4; ///< message version
+ std::uint8_t filler1 = 0; ///< yet unsed
+ std::uint16_t pluginId = 0; ///< lower 16 bit of the sending plugin's id
+ std::uint32_t filler2 = 0; ///< yet unused, fills up to size 8
+ /// Constructor just sets the values
+ RemoteMsgBaseTy (RemoteMsgTy _ty, std::uint8_t _ver);
+} PACKED;
+
+//
+// MARK: Beacon of Interest
+//
+
+/// Interest Beacon message version number
+constexpr std::uint8_t RMT_VER_BEACON = 0;
+/// "Beacon of Interest", ie. some message on the multicast just to wake up sender
+struct RemoteMsgBeaconTy : public RemoteMsgBaseTy {
+ // don't need additional data fields
+ /// Constructor sets appropriate message type
+ RemoteMsgBeaconTy();
+} PACKED;
+
+//
+// MARK: Settings
+//
+
+/// How often to send settings? [s]
+constexpr int REMOTE_SEND_SETTINGS_INTVL = 20;
+
+/// Setttings message version number
+constexpr std::uint8_t RMT_VER_SETTINGS = 0;
+/// Settings message, identifying a sending plugin, regularly providing its settings
+struct RemoteMsgSettingsTy : public RemoteMsgBaseTy {
+ char name[16]; ///< plugin's name, not necessarily zero-terminated if using full 12 chars
+ float maxLabelDist; ///< Maximum distance for drawing labels? [m]
+ char defaultIcao[4]; ///< Default ICAO aircraft type designator if no match can be found
+ char carIcaoType[4]; ///< Ground vehicle type identifier
+ std::uint8_t logLvl :3; ///< logging level
+ bool bLogMdlMatch :1; ///< Debug model matching?
+ bool bObjReplDataRefs :1; ///< Replace dataRefs in `.obj` files on load?
+ bool bObjReplTextures :1; ///< Replace textures in `.obj` files on load if needed?
+ bool bLabelCutOffAtVisibility :1; ///< Cut off labels at XP's reported visibility mit?
+ bool bMapEnabled :1; ///< Do we feed X-Plane's maps with our aircraft positions?
+ bool bMapLabels :1; ///< Do we show labels with the aircraft icons?
+ bool bHaveTCASControl :1; ///< Do we have AI/TCAS control?
+ std::uint16_t filler; ///< yet unused, fills size up for a multiple of 8
+
+ /// Constructor sets most values to zero
+ RemoteMsgSettingsTy ();
+
+} PACKED;
+
+//
+// MARK: A/C Details
+//
+
+/// A/C detail message version number
+constexpr std::uint8_t RMT_VER_AC_DETAIL = 0;
+/// A/C details, packed into an array message
+struct RemoteAcDetailTy {
+ std::uint32_t modeS_id; ///< plane's unique id at the sender side (might differ remotely in case of duplicates)
+ char icaoType[4]; ///< icao a/c type
+ char icaoOp[4]; ///< icao airline code
+ char sShortId[20]; ///< CSL model's short id
+ std::uint16_t pkgHash; ///< hash value of package name
+ char label[23]; ///< label
+ std::uint8_t labelCol[3]; ///< label color (RGB)
+ float alt_ft; ///< [ft] current altitude
+ // ^ the above has 64 bytes, so that these doubles start on an 8-byte bounday:
+ double lat; ///< latitude
+ double lon; ///< longitude
+ std::int16_t pitch; ///< [0.01°] pitch/100
+ std::uint16_t heading; ///< [0.01°] heading/100
+ std::int16_t roll; ///< [0.01°] roll/100
+
+ std::int16_t aiPrio; ///< priority for display in limited TCAS target slots, `-1` indicates "no TCAS display"
+ std::uint16_t dTime; ///< [0.0001s] time difference to previous position in 1/10000s
+ bool bValid : 1; ///< is this object valid? (Will be reset in case of exceptions)
+ bool bVisible : 1; ///< Shall this plane be drawn at the moment?
+
+ std::uint8_t filler[3]; ///< yet unused
+
+ ///< Array of _packed_ dataRef values for CSL model animation
+ std::uint8_t v[XPMP2::V_COUNT]; // 42
+
+ /// Default Constructor sets all to zero
+ RemoteAcDetailTy ();
+ /// A/c copy constructor fills from passed-in XPMP2::Aircraft object
+ RemoteAcDetailTy (const Aircraft& _ac, double _lat, double _lon, float _alt_ft, std::uint16_t _dTime);
+ /// Copies values from passed-in XPMP2::Aircraft object
+ void CopyFrom (const Aircraft& _ac, double _lat, double _lon, float _alt_ft, std::uint16_t _dTime);
+
+ void SetLabelCol (const float _col[4]); ///< set the label color from a float array (4th number, alpha, is always considered 1.0)
+ void GetLabelCol (float _col[4]) const; ///< writes color out into a float array
+
+ void SetPitch (float _p) { pitch = std::int16_t(_p*100.0f); } ///< sets pitch from float
+ float GetPitch () const { return float(pitch) / 100.0f; } ///< returns float pitch
+
+ /// @brief Sets heading from float
+ /// @note Only works well for `0 <= _h < 360`
+ void SetHeading (float _h);
+ float GetHeading () const { return float(heading) / 100.0f; } ///< returns float heading
+
+ void SetRoll (float _r) { roll = std::int16_t(std::lround(_r*100.0f)); } ///< sets pitch from float
+ float GetRoll () const { return float(roll) / 100.0f; } ///< returns float pitch
+
+ static constexpr size_t msgSize () { return sizeof(RemoteAcDetailTy); } ///< message size
+} PACKED;
+
+/// A/C detail message, has an inherited header plus an array of XPMP2::RemoteAcDetailTy elements
+struct RemoteMsgAcDetailTy : public RemoteMsgBaseTy {
+ RemoteAcDetailTy arr[1]; ///< basis for the array of actual details
+
+ /// Constructor sets expected message type and version
+ RemoteMsgAcDetailTy () : RemoteMsgBaseTy(RMT_MSG_AC_DETAILED, RMT_VER_AC_DETAIL) {}
+ /// Convert msg len to number of arr elements
+ static constexpr size_t NumElem (size_t _msgLen) { return (_msgLen - sizeof(RemoteMsgBaseTy)) / sizeof(RemoteAcDetailTy); }
+} PACKED;
+
+//
+// MARK: A/C Position Update
+//
+
+/// A/C Position update message version number
+constexpr std::uint8_t RMT_VER_AC_POS_UPDATE = 0;
+
+/// What is the maximum difference a RemoteAcPosUpdateTy can hold?
+constexpr double REMOTE_DEGREE_RES = 0.00000001; ///< resolution of degree updates
+constexpr double REMOTE_MAX_DIFF_DEGREE = REMOTE_DEGREE_RES * INT16_MAX;///< maximum degree difference that can be represented in a pos update msg
+constexpr double REMOTE_ALT_FT_RES = 0.01; ///< resolution of altitude[ft] updates
+constexpr double REMOTE_MAX_DIFF_ALT_FT = REMOTE_ALT_FT_RES * INT16_MAX;///< maximum altitude[ft] difference that can be represented in a pos update msg
+constexpr float REMOTE_TIME_RES = 0.0001f; ///< resolution of time difference
+constexpr float REMOTE_MAX_DIFF_TIME = REMOTE_TIME_RES * UINT16_MAX; ///< maximum time difference thatn can be represented in a pos update msg
+
+/// @brief A/C Position updates based on global coordinates
+/// @details for space efficiency only deltas to last msg are given in
+/// 0.0000001 degree lat/lon (roughly 1 centimeter resolution) and
+/// 0.01 ft altitude
+struct RemoteAcPosUpdateTy {
+ std::uint32_t modeS_id; ///< plane's unique id at the sender side (might differ remotely in case of duplicates)
+ std::int16_t dLat; ///< [0.0000001 degrees] latitude position difference
+ std::int16_t dLon; ///< [0.0000001 degrees] longitude position difference
+ std::int16_t dAlt_ft; ///< [0.01 ft] altitude difference
+ std::uint16_t dTime; ///< [0.0001s] time difference to previous position in 1/10000s
+ std::int16_t pitch; ///< [0.01 degree] pitch/100
+ std::uint16_t heading; ///< [0.01 degree] heading/100
+ std::int16_t roll; ///< [0.01 degree] roll/100
+ std::uint16_t filler1; ///< not yet used (for 4-byte alignment)
+
+ /// Default Constructor sets all 0
+ RemoteAcPosUpdateTy () { std::memset(this,0,sizeof(*this)); }
+ /// Constructor sets all values
+ RemoteAcPosUpdateTy (XPMPPlaneID _modeS_id,
+ std::int16_t _dLat, std::int16_t _dLon,
+ std::int16_t _dAlt_ft, std::uint16_t _dTime,
+ float _pitch, float _heading, float _roll);
+
+ void SetPitch (float _p) { pitch = std::int16_t(_p*100.0f); } ///< sets pitch from float
+ float GetPitch () const { return float(pitch) / 100.0f; } ///< returns float pitch
+
+ /// @brief Sets heading from float
+ /// @note Only works well for `0 <= _h < 360`
+ void SetHeading (float _h);
+ float GetHeading () const { return float(heading) / 100.0f; } ///< returns float heading
+
+ void SetRoll (float _r) { roll = std::int16_t(std::lround(_r*100.0f)); }///< sets pitch from float
+ float GetRoll () const { return float(roll) / 100.0f; } ///< returns float pitch
+
+ static constexpr size_t msgSize () { return sizeof(RemoteAcPosUpdateTy); } ///< message size
+} PACKED;
+
+/// A/C detail message, has an inherited header plus an array of XPMP2::RemoteAcDetailTy elements
+struct RemoteMsgAcPosUpdateTy : public RemoteMsgBaseTy {
+ RemoteAcPosUpdateTy arr[1]; ///< basis for the array of actual position updates
+
+ /// Constructor sets expected message type and version
+ RemoteMsgAcPosUpdateTy () : RemoteMsgBaseTy(RMT_MSG_AC_POS_UPDATE, RMT_VER_AC_POS_UPDATE) {}
+ /// Convert msg len to number of arr elements
+ static constexpr size_t NumElem (size_t _msgLen) { return (_msgLen - sizeof(RemoteMsgBaseTy)) / sizeof(RemoteAcPosUpdateTy); }
+} PACKED;
+
+//
+// MARK: A/C animation dataRefs
+//
+
+/// A/C Position update message version number
+constexpr std::uint8_t RMT_VER_AC_ANIM = 0;
+
+/// @brief A/C animation dataRef changes
+/// @details This structure has variable length depending on the number of
+/// actual dataRef values to carry. And several of these variable
+/// length structures are added into one variable length network message.
+/// @note Structure must stay aligned with XPMP2::RmtDataAcAnimTy
+struct RemoteAcAnimTy {
+ std::uint32_t modeS_id = 0; ///< plane's unique id at the sender side (might differ remotely in case of duplicates)
+ std::uint8_t numVals = 0; ///< number of dataRef values in the following array
+ std::uint8_t filler = 0; ///< not yet used
+
+ /// dataRef animation types and value
+ struct DataRefValTy {
+ DR_VALS idx; ///< index into XPMP2::Aircraft::v
+ std::uint8_t v; ///< dataRef animation value
+ } v[1]; ///< array of dataRef animation types and value
+
+ /// Constructor
+ RemoteAcAnimTy (XPMPPlaneID _id) : modeS_id(_id) { v[0].idx = DR_VALS(0); v[0].v = 0; }
+
+ /// message size assuming `num` array elements
+ static constexpr size_t msgSize (std::uint8_t num)
+ { return sizeof(RemoteAcAnimTy) + (num-1) * sizeof(DataRefValTy); }
+ /// current message size
+ size_t msgSize() const { return msgSize(numVals); }
+} PACKED;
+
+/// A/C animation dataRef message, has an inherited header plus an array of _variable sized_ XPMP2::RemoteAcAnimTy elements
+struct RemoteMsgAcAnimTy : public RemoteMsgBaseTy {
+ RemoteAcAnimTy animData; ///< message data starts here but extend beyond this point!
+
+ /// Constructor sets expected message type and version
+ RemoteMsgAcAnimTy () : RemoteMsgBaseTy(RMT_MSG_AC_ANIM, RMT_VER_AC_ANIM), animData(0) {}
+
+ /// Returns a pointer to the first/next animation data element in the message
+ const RemoteAcAnimTy* next (size_t _msgLen, const RemoteAcAnimTy* pCurr = nullptr) const;
+} PACKED;
+
+
+//
+// MARK: A/C Removal
+//
+
+
+/// A/C removal message version number
+constexpr std::uint8_t RMT_VER_AC_REMOVE = 0;
+
+/// A/C Removal only includes the plane id, structure required for msgSize() function
+struct RemoteAcRemoveTy {
+ std::uint32_t modeS_id; ///< plane's unique id at the sender side (might differ remotely in case of duplicates)
+
+ /// Constructor sets plane id
+ RemoteAcRemoveTy (XPMPPlaneID _id = 0) : modeS_id(_id) {}
+
+ static constexpr size_t msgSize () { return sizeof(RemoteAcRemoveTy); } ///< message size
+} PACKED;
+
+/// A/C removal message, an array of plane ids
+struct RemoteMsgAcRemoveTy : public RemoteMsgBaseTy {
+ RemoteAcRemoveTy arr[1]; ///< plane's unique id at the sender side (might differ remotely in case of duplicates)
+
+ /// Constructor sets expected message type and version
+ RemoteMsgAcRemoveTy () : RemoteMsgBaseTy(RMT_MSG_AC_REMOVE, RMT_VER_AC_REMOVE) {}
+ /// Convert msg len to number of arr elements
+ static constexpr size_t NumElem (size_t _msgLen) { return (_msgLen - sizeof(RemoteMsgBaseTy)) / sizeof(arr[0]); }
+} PACKED;
+
+#ifdef _MSC_VER // Visual C++
+#pragma pack(pop) // reseting packing
+#endif
+
+// A few static validations just to make sure that no compiler fiddles with my network message layout.
+static_assert(sizeof(RemoteMsgBaseTy) == 8, "RemoteMsgBaseTy doesn't have expected size");
+static_assert(sizeof(RemoteMsgSettingsTy) == 40, "RemoteMsgSettingsTy doesn't have expected size");
+static_assert(sizeof(RemoteAcDetailTy) == 94+42, "RemoteAcDetailTy doesn't have expected size");
+static_assert(sizeof(RemoteMsgAcDetailTy) == 102+42, "RemoteMsgAcDetailTy doesn't have expected size");
+static_assert(sizeof(RemoteAcPosUpdateTy) == 20, "RemoteAcPosUpdateTy doesn't have expected size");
+static_assert(sizeof(RemoteMsgAcPosUpdateTy)== 28, "RemoteMsgAcPosUpdateTy doesn't have expected size");
+static_assert(sizeof(RemoteAcAnimTy) == 8, "RemoteAcAnimTy doesn't have expected size");
+static_assert(RemoteAcAnimTy::msgSize(V_COUNT) == 90, "RemoteAcAnimTy for V_COUNT dataRefs doesn't have expected size");
+static_assert(sizeof(RemoteMsgAcRemoveTy) == 12, "RemoteMsgAcRemoveTy doesn't have expected size");
+
+//
+// MARK: Miscellaneous
+//
+
+/// Function prototypes for callback functions to handle the received messages
+struct RemoteCBFctTy {
+ /// Called in flight loop before processing first aircraft
+ void (*pfBeforeFirstAc)() = nullptr;
+ /// Called in flight loop after processing last aircraft
+ void (*pfAfterLastAc)() = nullptr;
+ /// Callback for processing Settings messages
+ void (*pfMsgSettings) (std::uint32_t from[4],
+ const std::string& sFrom,
+ const RemoteMsgSettingsTy&) = nullptr;
+ /// Callback for processing A/C Details messages
+ void (*pfMsgACDetails) (std::uint32_t from[4], size_t msgLen,
+ const RemoteMsgAcDetailTy&) = nullptr;
+ /// Callback for processing A/C Details messages
+ void (*pfMsgACPosUpdate) (std::uint32_t from[4], size_t msgLen,
+ const RemoteMsgAcPosUpdateTy&) = nullptr;
+ /// Callback for processing A/C Animation dataRef messages
+ void (*pfMsgACAnim) (std::uint32_t from[4], size_t msgLen,
+ const RemoteMsgAcAnimTy&) = nullptr;
+ /// Callback for processing A/C Removal messages
+ void (*pfMsgACRemove) (std::uint32_t from[4], size_t msgLen,
+ const RemoteMsgAcRemoveTy&) = nullptr;
+};
+
+/// State of remote communcations
+enum RemoteStatusTy : unsigned {
+ REMOTE_OFF = 0, ///< no remote connectivtiy, not listening, not sending
+ REMOTE_SEND_WAITING, ///< listening for a request to send data, but not actively sending data
+ REMOTE_SENDING, ///< actively sending aircraft data out to the network
+ REMOTE_RECV_WAITING, ///< waiting to receive data, periodically sending a token of interest
+ REMOTE_RECEIVING, ///< actively receiving data
+};
+
+/// Returns the current Remote status
+RemoteStatusTy RemoteGetStatus();
+
+/// Starts the listener, will call provided callback functions with received messages
+void RemoteRecvStart (const RemoteCBFctTy& _rmtCBFcts);
+
+/// Stops the receiver
+void RemoteRecvStop ();
+
+}
+
+
+#endif
diff --git a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Resources/Info.plist b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Resources/Info.plist
index b69ce493..69c1146e 100644
--- a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Resources/Info.plist
+++ b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Resources/Info.plist
@@ -3,7 +3,7 @@
BuildMachineOSBuild
- 19H2
+ 19H15CFBundleDevelopmentRegionenCFBundleExecutable
@@ -27,7 +27,7 @@
DTCompilercom.apple.compilers.llvm.clang.1_0DTPlatformBuild
- 12A7300
+ 12A7403DTPlatformNamemacosxDTPlatformVersion
@@ -37,9 +37,9 @@
DTSDKNamemacosx10.15DTXcode
- 1201
+ 1210DTXcodeBuild
- 12A7300
+ 12A7403LSMinimumSystemVersion10.12NSHumanReadableCopyright
diff --git a/LiveTraffic.xcodeproj/project.pbxproj b/LiveTraffic.xcodeproj/project.pbxproj
index 3d608574..683cc364 100755
--- a/LiveTraffic.xcodeproj/project.pbxproj
+++ b/LiveTraffic.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 46;
+ objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@@ -41,7 +41,7 @@
25C59465207ABDC700E52073 /* LTMain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 25C59464207ABDC700E52073 /* LTMain.cpp */; };
25E9C2AF207D5B8100D3C642 /* LTFlightData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 25E9C2AE207D5B8100D3C642 /* LTFlightData.cpp */; };
25EFC76E24959662005DB0A6 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25EFC76D24959662005DB0A6 /* ApplicationServices.framework */; };
- 25F3471C24D2C7DE004574E7 /* parson.c in Sources */ = {isa = PBXBuildFile; fileRef = 25F3471B24D2C7DE004574E7 /* parson.c */; };
+ 25F3471C24D2C7DE004574E7 /* parson.c in Sources */ = {isa = PBXBuildFile; fileRef = 25F3471B24D2C7DE004574E7 /* parson.c */; settings = {COMPILER_FLAGS = "-Wno-sign-conversion"; }; };
25F3471F24D2CB20004574E7 /* ACTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 25F3471E24D2CB20004574E7 /* ACTable.cpp */; };
25FEB7B9224D7B10002A051F /* LTForeFlight.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 25FEB7B8224D7B10002A051F /* LTForeFlight.cpp */; };
D67297EB0F9E0FCC00CFD1FA /* LiveTraffic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D67297EA0F9E0FCC00CFD1FA /* LiveTraffic.cpp */; };
@@ -519,6 +519,7 @@
buildPhases = (
D607B19609A556E400699BC3 /* Sources */,
D607B19709A556E400699BC3 /* Frameworks */,
+ 255C337E2544DA0C002C166F /* ShellScript */,
);
buildRules = (
);
@@ -538,7 +539,7 @@
KnownAssetTags = (
New,
);
- LastUpgradeCheck = 1200;
+ LastUpgradeCheck = 1210;
};
buildConfigurationList = D607B16209A5563100699BC3 /* Build configuration list for PBXProject "LiveTraffic" */;
compatibilityVersion = "Xcode 3.2";
@@ -558,6 +559,27 @@
};
/* End PBXProject section */
+/* Begin PBXShellScriptBuildPhase section */
+ 255C337E2544DA0C002C166F /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "TAGS=\"TODO:|FIXME:\"\necho \"searching ${SRCROOT} for ${TAGS}\"\nfind {\"${SRCROOT}/Include\",\"${SRCROOT}/Src\"} \\( -name \"*.cpp\" -or -name \"*.h\" \\) -print0 | xargs -0 egrep --with-filename --line-number --only-matching \"($TAGS).*\\$\" | perl -p -e \"s/($TAGS)/ warning: \\$1/\"\n";
+ };
+/* End PBXShellScriptBuildPhase section */
+
/* Begin PBXSourcesBuildPhase section */
D607B19609A556E400699BC3 /* Sources */ = {
isa = PBXSourcesBuildPhase;
@@ -614,6 +636,7 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
@@ -707,6 +730,7 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
diff --git a/LiveTraffic.xcodeproj/xcuserdata/birger.xcuserdatad/xcschemes/LiveTraffic.xcscheme b/LiveTraffic.xcodeproj/xcuserdata/birger.xcuserdatad/xcschemes/LiveTraffic.xcscheme
index 4e00739b..01159e44 100644
--- a/LiveTraffic.xcodeproj/xcuserdata/birger.xcuserdatad/xcschemes/LiveTraffic.xcscheme
+++ b/LiveTraffic.xcodeproj/xcuserdata/birger.xcuserdatad/xcschemes/LiveTraffic.xcscheme
@@ -1,6 +1,6 @@
second.GetAircraft();
if (dr == DR_AC_BULK_QUICK)
- ac.CopyBulkData ((LTAPIAircraft::LTAPIBulkData*)pOut, size);
+ ac.CopyBulkData ((LTAPIAircraft::LTAPIBulkData*)pOut, (size_t)size);
else
- ac.CopyBulkData((LTAPIAircraft::LTAPIBulkInfoTexts*)pOut, size);
+ ac.CopyBulkData((LTAPIAircraft::LTAPIBulkInfoTexts*)pOut, (size_t)size);
}
// how many bytes copied?
@@ -1830,7 +1830,7 @@ bool DataRefs::LoadConfigFile()
}
// the second part is again an array of icao types, separated by space or comma or so
- aFlarmToIcaoAcTy[std::stoi(ln[0])] = str_tokenize(ln[1], " ,;/");
+ aFlarmToIcaoAcTy[std::stoul(ln[0])] = str_tokenize(ln[1], " ,;/");
}
}
diff --git a/Src/LTApt.cpp b/Src/LTApt.cpp
index 854f2252..e90cfc94 100644
--- a/Src/LTApt.cpp
+++ b/Src/LTApt.cpp
@@ -419,7 +419,7 @@ class Apt {
double bestDist2 = HUGE_VAL;
const vecTaxiNodesTy::const_iterator dontIter =
dontCombineWith == ULONG_MAX ? vecTaxiNodes.cend() :
- std::next(vecTaxiNodes.cbegin(),dontCombineWith);
+ std::next(vecTaxiNodes.cbegin(),(long)dontCombineWith);
vecTaxiNodesTy::const_iterator bestIter = vecTaxiNodes.cend();
for (auto iter = vecTaxiNodes.cbegin(); iter != vecTaxiNodes.cend(); ++iter)
{
@@ -439,7 +439,7 @@ class Apt {
// Found something?
return (bestIter != vecTaxiNodes.cend() ?
- std::distance(vecTaxiNodes.cbegin(), bestIter) :
+ (size_t)std::distance(vecTaxiNodes.cbegin(), bestIter) :
ULONG_MAX);
}
diff --git a/Src/LTChannel.cpp b/Src/LTChannel.cpp
index 7ce43e3e..e32d2a24 100644
--- a/Src/LTChannel.cpp
+++ b/Src/LTChannel.cpp
@@ -583,7 +583,6 @@ bool LTFlightDataEnable()
if ( dataRefs.GetUseHistData() ) {
// load historic data readers
listFDC.emplace_back(new ADSBExchangeHistorical);
- // TODO: master data readers for historic data, like reading CSV file
} else {
// load live feed readers (in order of priority)
listFDC.emplace_back(new RealTrafficConnection(mapFd));
diff --git a/Src/LTFlightData.cpp b/Src/LTFlightData.cpp
index 1e041b09..2357ef11 100644
--- a/Src/LTFlightData.cpp
+++ b/Src/LTFlightData.cpp
@@ -786,7 +786,7 @@ void LTFlightData::DataSmoothing (bool& bChanged)
}
// we went one too far...so how far did we go into the deque?
--itLast;
- const size_t numPos = std::distance(posDeque.begin(), itLast);
+ const long numPos = std::distance(posDeque.begin(), itLast);
// not far enough for any smoothing?
if (numPos < 2)
@@ -1687,7 +1687,7 @@ void LTFlightData::AppendNewPos()
// and for that we need 2 positions before insert
const positionTy* pBefore = nullptr;
double heading = NAN;
- size_t idx = std::distance(posDeque.begin(), i);
+ size_t idx = (size_t)std::distance(posDeque.begin(), i);
if (idx >= 2) {
pBefore = &(posDeque[idx-1]);
heading = posDeque[idx-2].angle(*pBefore);
diff --git a/Src/LTMain.cpp b/Src/LTMain.cpp
index 61ac24f2..110c7290 100644
--- a/Src/LTMain.cpp
+++ b/Src/LTMain.cpp
@@ -201,7 +201,7 @@ bool FileRecLookup (std::ifstream& f, size_t& n,
// Determine number of records if not (yet) known
if (f && n == 0) {
f.seekg(0, std::ios_base::end);
- n = f.tellg() / recLen;
+ n = size_t(f.tellg()) / recLen;
if (!f) return false;
}
@@ -218,15 +218,15 @@ bool FileRecLookup (std::ifstream& f, size_t& n,
m = L + (size_t)std::floor(float(key-Al)/float(Ar-Al) * (R-L));
// test if record at m is less than the key
- f.seekg(m * recLen);
- f.read((char*)outRec, recLen);
+ f.seekg((long long)(m * recLen));
+ f.read((char*)outRec, (std::streamsize)recLen);
if (!f) return false;
if (*pAm == key) {
return true;
}
else if (*pAm < key) {
L = m+1; // move to _next_ record as we are too small
- f.read((char*)outRec, recLen);
+ f.read((char*)outRec, (std::streamsize)recLen);
Al = *(unsigned long*)outRec;
if (Al == key) // that next record is our value?
return true;
diff --git a/Src/LTOpenGlider.cpp b/Src/LTOpenGlider.cpp
index a8411cf9..0c485a0a 100644
--- a/Src/LTOpenGlider.cpp
+++ b/Src/LTOpenGlider.cpp
@@ -152,7 +152,7 @@ std::string OpenGliderConnection::GetURL (const positionTy& pos)
return std::string(url);
} else {
// otherwise (and by default) we are to use the direct APRS connection
- APRSStartUpdate(pos, dataRefs.GetFdStdDistance_km());
+ APRSStartUpdate(pos, (unsigned)dataRefs.GetFdStdDistance_km());
return std::string();
}
}
@@ -196,7 +196,7 @@ bool OpenGliderConnection::ProcessFetchedData (mapLTFlightDataTy& fdMap)
}
// then this is the marker definition to work on
- const std::string sMarker (sPos, sEnd-sPos);
+ const std::string sMarker (sPos, std::string::size_type(sEnd-sPos));
std::vector tok = str_tokenize(sMarker, ",", false);
if (tok.size() != GNF_COUNT) {
LOG_MSG(logERR, ERR_OGN_WRONG_NUM_FIELDS, sMarker.c_str());
@@ -796,7 +796,7 @@ static void OGNAcListOneLine (OGNCbHandoverTy& ho, std::string::size_type posEnd
if (tok[0][0] == '#') {
tok[0].erase(0,1); // remove the #
// walk the tokens and remember the column indexes per field
- for (int i = 0; i < (int)tok.size(); i++) {
+ for (unsigned i = 0; i < (unsigned)tok.size(); i++) {
if (tok[i] == "DEVICE_ID") ho.devIdIdx = i;
else if (tok[i] == "DEVICE_TYPE") ho.devTypeIdx = i;
else if (tok[i] == "AIRCRAFT_MODEL") ho.mdlIdx = i;
@@ -991,7 +991,7 @@ const std::string& OGNGetIcaoAcType (FlarmAircraftTy _acTy)
if (icaoTypes.empty()) return dataRefs.GetDefaultAcIcaoType();
if (icaoTypes.size() == 1) return icaoTypes.front();
// more than one type defined, take a random pick
- const size_t i = randoml(0, long(icaoTypes.size())-1);
+ const size_t i = (size_t)randoml(0, long(icaoTypes.size())-1);
assert(0 <= i && i < icaoTypes.size());
return icaoTypes[i];
}
diff --git a/Src/LiveTraffic.cpp b/Src/LiveTraffic.cpp
index bc916627..7cdb7d41 100755
--- a/Src/LiveTraffic.cpp
+++ b/Src/LiveTraffic.cpp
@@ -578,8 +578,18 @@ PLUGIN_API void XPluginReceiveMessage(XPLMPluginID inFrom, int inMsg, void * /*i
if (inMsg == XPLM_MSG_RELEASE_PLANES)
{
char who[256] = "?";
- XPLMGetPluginInfo(inFrom, who, NULL, NULL, NULL);
- SHOW_MSG(logINFO, INFO_REQU_AI_RELEASE, who);
+ char signature[256] = "";
+ XPLMGetPluginInfo(inFrom, who, NULL, signature, NULL);
+
+ // We do cease control to the XPMP2 Remote Control plugin
+ // because that one will display our traffic, too:
+ if (strncmp(signature, REMOTE_SIGNATURE, sizeof(signature)) == 0) {
+ LOG_MSG(logINFO, INFO_REQU_AI_REMOTE);
+ LTMainReleaseAIAircraft();
+ MenuUpdateAllItemStatus();
+ } else {
+ SHOW_MSG(logINFO, INFO_REQU_AI_RELEASE, who);
+ }
return;
}
diff --git a/Src/Network.cpp b/Src/Network.cpp
index 6ba8fa1f..b43788e7 100644
--- a/Src/Network.cpp
+++ b/Src/Network.cpp
@@ -25,6 +25,7 @@
// All includes are collected in one header
#include "LiveTraffic.h"
+
#if IBM
#include
#include
@@ -32,6 +33,7 @@
#define errno WSAGetLastError() // https://docs.microsoft.com/en-us/windows/desktop/WinSock/error-codes-errno-h-errno-and-wsagetlasterror-2
#define close closesocket
typedef USHORT in_port_t;
+typedef int socklen_t; // in Winsock, an int is passed as length argument
#else
#include
#include
@@ -135,7 +137,7 @@ void SocketNetworking::Open(const std::string& _addr, int _port,
}
// bind the socket to the address:port
- r = bind(f_socket, addrinfo->ai_addr, (int)addrinfo->ai_addrlen);
+ r = bind(f_socket, addrinfo->ai_addr, (socklen_t)addrinfo->ai_addrlen);
if(r != 0)
throw NetRuntimeError(("could not bind UDP socket with: \"" + f_addr + ":" + decimal_port + "\"").c_str());
@@ -199,7 +201,7 @@ void SocketNetworking::Connect(const std::string& _addr, int _port,
#endif
// actually connect
- r = connect(f_socket, addrinfo->ai_addr, (int)addrinfo->ai_addrlen);
+ r = connect(f_socket, addrinfo->ai_addr, (socklen_t)addrinfo->ai_addrlen);
if(r != 0)
throw NetRuntimeError(("could not connect to: \"" + f_addr + ":" + decimal_port + "\"").c_str());
@@ -303,7 +305,7 @@ long SocketNetworking::recv()
return -1;
}
- long ret = ::recv(f_socket, buf, (int)bufSize-1, 0);
+ long ret = ::recv(f_socket, buf, (socklen_t)bufSize-1, 0);
if (ret >= 0) { // we did receive something
buf[ret] = 0; // zero-termination
} else {
@@ -383,7 +385,7 @@ bool SocketNetworking::broadcast (const char* msg)
s.sin_addr.s_addr = htonl(INADDR_BROADCAST);
while (index
Date: Sat, 14 Nov 2020 01:14:34 +0100
Subject: [PATCH 02/10] Windows build updates
---
Include/LiveTraffic.h | 2 +-
Include/Network.h | 5 +----
LiveTraffic.vcxproj | 12 ++++++++----
Src/LTFlightData.cpp | 4 +---
4 files changed, 11 insertions(+), 12 deletions(-)
diff --git a/Include/LiveTraffic.h b/Include/LiveTraffic.h
index 7bb7550e..0b8eee94 100644
--- a/Include/LiveTraffic.h
+++ b/Include/LiveTraffic.h
@@ -372,7 +372,7 @@ inline struct tm *localtime_s(struct tm * result, const time_t * time)
#define STRCPY_S(dest,src) strncpy_s(dest,sizeof(dest),src,sizeof(dest)-1)
#define STRCPY_ATMOST(dest,src) strncpy_s(dest,sizeof(dest),strAtMost(src,sizeof(dest)-1).c_str(),sizeof(dest)-1)
-#if APL == 1 or LIN == 1
+#if APL == 1 || LIN == 1
// XCode/Linux don't provide the _s functions, not even with __STDC_WANT_LIB_EXT1__ 1
inline int strerror_s( char *buf, size_t bufsz, int errnum )
{ return strerror_r(errnum, buf, bufsz); }
diff --git a/Include/Network.h b/Include/Network.h
index e03c337f..1572ac9e 100644
--- a/Include/Network.h
+++ b/Include/Network.h
@@ -32,13 +32,10 @@
#else
#include
#include
-#endif
-#include
-
-#if not IBM
typedef int SOCKET; ///< Windows defines SOCKET, so we define it for non-Windows manually
constexpr SOCKET INVALID_SOCKET = -1;
#endif
+#include
/// @brief Exception raised by XPMP2::SocketNetworking objects
/// @details This exception is raised when the address
diff --git a/LiveTraffic.vcxproj b/LiveTraffic.vcxproj
index d6c0d658..de86e533 100644
--- a/LiveTraffic.vcxproj
+++ b/LiveTraffic.vcxproj
@@ -159,10 +159,12 @@
$(IntDir)$(TargetName).lib
- copy /Y "$(TargetDir)$(TargetName)$(TargetExt)" "C:\X-Plane\11\Resources\plugins\LiveTraffic\win_x64"
+
+
- Copying win.xpl into my X-Plane plugin paths
+
+
@@ -205,8 +207,10 @@
$(IntDir)$(TargetName).lib
- copy /Y "$(TargetDir)$(TargetName)$(TargetExt)" "C:\X-Plane\11\Resources\plugins\LiveTraffic\win_x64"
- Copying win.xpl into my X-Plane plugin paths
+
+
+
+
diff --git a/Src/LTFlightData.cpp b/Src/LTFlightData.cpp
index 2357ef11..87e4e602 100644
--- a/Src/LTFlightData.cpp
+++ b/Src/LTFlightData.cpp
@@ -786,10 +786,8 @@ void LTFlightData::DataSmoothing (bool& bChanged)
}
// we went one too far...so how far did we go into the deque?
--itLast;
- const long numPos = std::distance(posDeque.begin(), itLast);
-
// not far enough for any smoothing?
- if (numPos < 2)
+ if (std::distance(posDeque.begin(), itLast) < 2)
return;
// what is the total distance travelled between first and last?
From ac2fa373b4ce0ec507f9deef8bcc261d6011569b Mon Sep 17 00:00:00 2001
From: TwinFan
Date: Wed, 18 Nov 2020 01:13:28 +0100
Subject: [PATCH 03/10] Windows build updates for XPMP2 Remote,
TwinFan/XPMP2#16
---
CMakeLists.txt | 2 +-
.../Versions/1.0/Headers/XPMPAircraft.h | 8 +++-
.../Versions/1.0/Headers/XPMPRemote.h | 38 ++++++++++---------
LiveTraffic.vcxproj | 4 +-
4 files changed, 30 insertions(+), 22 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cdb9c09f..59616c09 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -239,7 +239,7 @@ endif ()
if (WIN32)
# Link platform-specific libraries especially for networking
- target_link_libraries(LiveTraffic ws2_32.lib wldap32.lib advapi32.lib crypt32.lib)
+ target_link_libraries(LiveTraffic ws2_32.lib iphlpapi wldap32.lib advapi32.lib crypt32.lib)
elseif (APPLE)
# X-Plane supports OS X 10.10+, so this should ensure FlyWithLua can run on
# all supported versions.
diff --git a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPAircraft.h b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPAircraft.h
index 2b72e638..f1d2deaa 100644
--- a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPAircraft.h
+++ b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPAircraft.h
@@ -209,7 +209,8 @@ class Aircraft {
protected:
bool bValid = true; ///< is this object valid? (Will be reset in case of exceptions)
- bool bVisible = true; ///< Shall this plane be drawn at the moment?
+ bool bVisible = true; ///< Shall this plane be drawn at the moment and be visible to TCAS/interfaces?
+ bool bRender = true; ///< Shall the CSL model be drawn in 3D world? (if !bRender && bVivile then still visible on TCAS/interfaces, Remote Client uses this for local senders' planes to take over TCAS but not drawing)
XPMP2::CSLModel* pCSLMdl = nullptr; ///< the CSL model in use
int matchQuality = -1; ///< quality of the match with the CSL model
@@ -345,6 +346,11 @@ class Aircraft {
/// Is the plane visible?
bool IsVisible () const { return bVisible && bValid; }
+ /// Switch rendering of the CSL model on or off
+ virtual void SetRender (bool _bRender);
+ /// Is this plane to be rendered?
+ bool IsRendered () const { return bRender && IsVisible(); }
+
/// Distance to camera [m]
float GetCameraDist () const { return camDist; }
/// Bearing from camera [°]
diff --git a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h
index c7d5ae9a..72f4807b 100644
--- a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h
+++ b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h
@@ -60,17 +60,6 @@ CSLModel* CSLModelByPkgShortId (std::uint16_t _pkgHash,
// MARK: Network Data Definitions
//
-// To ensure best network capacity usage as well as identical alignment across platform we enforce tightly packed structures.
-// The approach is very different between Visual C++ and clang / gcc, though:
-#ifdef _MSC_VER // Visual C++
-#pragma pack(push,1) // set packing once (ie. not per struct)
-#define PACKED
-#elif defined(__clang__) || defined(__GNUC__) // Clang (Mac XCode etc) or GNU (Linux)
-#define PACKED __attribute__ ((__packed__))
-#else
-#error Unhandled Compiler!
-#endif
-
/// Message type
enum RemoteMsgTy : std::uint8_t {
RMT_MSG_INTEREST_BEACON = 0, ///< beacon sent by a remote client to signal interest in data
@@ -103,11 +92,23 @@ extern const std::array REMOTE_DR_DEF;
// MARK: Message Header (Base)
//
+// To ensure best network capacity usage as well as identical alignment across platform we enforce tightly packed structures.
+// The approach is very different between Visual C++ and clang / gcc, though:
+#ifdef _MSC_VER // Visual C++
+#pragma pack(push,1) // set packing once (ie. not per struct)
+#define PACKED
+#elif defined(__clang__) || defined(__GNUC__) // Clang (Mac XCode etc) or GNU (Linux)
+#define PACKED __attribute__ ((__packed__))
+#else
+#error Unhandled Compiler!
+#endif
+
/// Message header, identical for all message types
struct RemoteMsgBaseTy {
RemoteMsgTy msgTy : 4; ///< message type
std::uint8_t msgVer : 4; ///< message version
- std::uint8_t filler1 = 0; ///< yet unsed
+ bool bLocalSender : 1; ///< is the sender "local", ie. on same machine?
+ std::uint8_t filler1 : 7; ///< yet unsed
std::uint16_t pluginId = 0; ///< lower 16 bit of the sending plugin's id
std::uint32_t filler2 = 0; ///< yet unused, fills up to size 8
/// Constructor just sets the values
@@ -184,7 +185,8 @@ struct RemoteAcDetailTy {
std::uint16_t dTime; ///< [0.0001s] time difference to previous position in 1/10000s
bool bValid : 1; ///< is this object valid? (Will be reset in case of exceptions)
bool bVisible : 1; ///< Shall this plane be drawn at the moment?
-
+ bool bRender : 1; ///< Shall the CSL model be drawn in 3D world?
+
std::uint8_t filler[3]; ///< yet unused
///< Array of _packed_ dataRef values for CSL model animation
@@ -385,20 +387,20 @@ struct RemoteCBFctTy {
/// Called in flight loop after processing last aircraft
void (*pfAfterLastAc)() = nullptr;
/// Callback for processing Settings messages
- void (*pfMsgSettings) (std::uint32_t from[4],
+ void (*pfMsgSettings) (const std::uint32_t from[4],
const std::string& sFrom,
const RemoteMsgSettingsTy&) = nullptr;
/// Callback for processing A/C Details messages
- void (*pfMsgACDetails) (std::uint32_t from[4], size_t msgLen,
+ void (*pfMsgACDetails) (const std::uint32_t from[4], size_t msgLen,
const RemoteMsgAcDetailTy&) = nullptr;
/// Callback for processing A/C Details messages
- void (*pfMsgACPosUpdate) (std::uint32_t from[4], size_t msgLen,
+ void (*pfMsgACPosUpdate) (const std::uint32_t from[4], size_t msgLen,
const RemoteMsgAcPosUpdateTy&) = nullptr;
/// Callback for processing A/C Animation dataRef messages
- void (*pfMsgACAnim) (std::uint32_t from[4], size_t msgLen,
+ void (*pfMsgACAnim) (const std::uint32_t from[4], size_t msgLen,
const RemoteMsgAcAnimTy&) = nullptr;
/// Callback for processing A/C Removal messages
- void (*pfMsgACRemove) (std::uint32_t from[4], size_t msgLen,
+ void (*pfMsgACRemove) (const std::uint32_t from[4], size_t msgLen,
const RemoteMsgAcRemoveTy&) = nullptr;
};
diff --git a/LiveTraffic.vcxproj b/LiveTraffic.vcxproj
index de86e533..58e7ccd0 100644
--- a/LiveTraffic.vcxproj
+++ b/LiveTraffic.vcxproj
@@ -143,7 +143,7 @@
Lib/XPMP2;Lib/SDK/Libraries/Win;Lib/CURL;%(AdditionalLibraryDirectories)
- Opengl32.lib;XPLM_64.lib;XPMP2.lib;libcurl.lib;ws2_32.lib;wldap32.lib;advapi32.lib;crypt32.lib;%(AdditionalDependencies)
+ Opengl32.lib;XPLM_64.lib;XPMP2.lib;libcurl.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;advapi32.lib;crypt32.lib;%(AdditionalDependencies)Console
@@ -193,7 +193,7 @@
Lib/XPMP2;Lib/SDK/Libraries/Win;Lib/CURL;%(AdditionalLibraryDirectories)
- Opengl32.lib;XPLM_64.lib;XPMP2.lib;libcurl.lib;ws2_32.lib;wldap32.lib;advapi32.lib;crypt32.lib;%(AdditionalDependencies)
+ Opengl32.lib;XPLM_64.lib;XPMP2.lib;libcurl.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;advapi32.lib;crypt32.lib;%(AdditionalDependencies)ConsoleUseLinkTimeCodeGeneration
From e23cc60f2aada3f1d3b042a042d91df814b1152d Mon Sep 17 00:00:00 2001
From: TwinFan
Date: Wed, 18 Nov 2020 22:48:39 +0100
Subject: [PATCH 04/10] XPMP2 header update for TwinFan/XPMP2#16
---
Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h
index 72f4807b..528d5e5e 100644
--- a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h
+++ b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h
@@ -38,6 +38,9 @@
namespace XPMP2 {
+/// The signature of the XPMP2 Remote Client
+constexpr const char* REMOTE_SIGNATURE = "TwinFan.plugin.XPMP2.Remote";
+
//
// MARK: Global Helpers
//
@@ -374,6 +377,7 @@ static_assert(sizeof(RemoteAcPosUpdateTy) == 20, "RemoteAcPosUpdateTy doe
static_assert(sizeof(RemoteMsgAcPosUpdateTy)== 28, "RemoteMsgAcPosUpdateTy doesn't have expected size");
static_assert(sizeof(RemoteAcAnimTy) == 8, "RemoteAcAnimTy doesn't have expected size");
static_assert(RemoteAcAnimTy::msgSize(V_COUNT) == 90, "RemoteAcAnimTy for V_COUNT dataRefs doesn't have expected size");
+static_assert(sizeof(RemoteMsgAcAnimTy) == 16, "RemoteMsgAcAnimTy doesn't have expected size");
static_assert(sizeof(RemoteMsgAcRemoveTy) == 12, "RemoteMsgAcRemoveTy doesn't have expected size");
//
From aed05f55fed553655861591473ddca9c7dc5bbe6 Mon Sep 17 00:00:00 2001
From: TwinFan
Date: Sun, 22 Nov 2020 18:05:53 +0100
Subject: [PATCH 05/10] Allow FD objects to remain when Aircraft is removed,
fixes #201
---
Include/Constants.h | 2 +-
Include/LTFlightData.h | 4 +-
.../Versions/1.0/Resources/Info.plist | 12 +-
LiveTraffic.xcodeproj/project.pbxproj | 4 +
.../xcdebugger/Expressions.xcexplist | 47 +++--
Src/LTFlightData.cpp | 181 +++++++++---------
Src/LTOpenGlider.cpp | 5 +-
Src/LTOpenSky.cpp | 3 +-
Src/LTRealTraffic.cpp | 4 -
docs/readme.html | 68 +++----
10 files changed, 170 insertions(+), 160 deletions(-)
diff --git a/Include/Constants.h b/Include/Constants.h
index 10d2219c..b3345e45 100644
--- a/Include/Constants.h
+++ b/Include/Constants.h
@@ -29,7 +29,7 @@
//
// MARK: Version Information (CHANGE VERSION HERE)
//
-constexpr float VERSION_NR = 2.21f;
+constexpr float VERSION_NR = 2.30f;
constexpr bool VERSION_BETA = true;
extern float verXPlaneOrg; // version on X-Plane.org
extern int verDateXPlaneOrg; // and its date
diff --git a/Include/LTFlightData.h b/Include/LTFlightData.h
index c120697c..a893e198 100644
--- a/Include/LTFlightData.h
+++ b/Include/LTFlightData.h
@@ -283,8 +283,6 @@ class LTFlightData
bool validForAcCreate( double simTime = NAN ) const;
// a/c available for this FD?
inline bool hasAc() const { return pAc != nullptr; }
- // is the data outdated (considered too old to be useful)?
- bool outdated ( double simTime = NAN ) const;
// produce a/c label
void UpdateStaticLabel();
@@ -370,7 +368,7 @@ class LTFlightData
// access/create/destroy aircraft
bool AircraftMaintenance ( double simTime ); // returns: delete me?
bool DetermineAcModel (); ///< try interpreting model text or check for ground vehicle, last resort: default a/c type
- bool AcSlotAvailable (double simTime); ///< checks if there is a slot available to create this a/c, tries to remove the farest a/c if too many a/c rendered
+ bool AcSlotAvailable (); ///< checks if there is a slot available to create this a/c, tries to remove the farest a/c if too many a/c rendered
bool CreateAircraft ( double simTime );
void DestroyAircraft ();
LTAircraft* GetAircraft () const { return pAc; }
diff --git a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Resources/Info.plist b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Resources/Info.plist
index 69c1146e..8732bee0 100644
--- a/Lib/XPMP2/XPMP2.framework/Versions/1.0/Resources/Info.plist
+++ b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Resources/Info.plist
@@ -27,19 +27,19 @@
DTCompilercom.apple.compilers.llvm.clang.1_0DTPlatformBuild
- 12A7403
+ 12B45bDTPlatformNamemacosxDTPlatformVersion
- 10.15.6
+ 11.0DTSDKBuild
- 19G68
+ 20A2408DTSDKName
- macosx10.15
+ macosx11.0DTXcode
- 1210
+ 1220DTXcodeBuild
- 12A7403
+ 12B45bLSMinimumSystemVersion10.12NSHumanReadableCopyright
diff --git a/LiveTraffic.xcodeproj/project.pbxproj b/LiveTraffic.xcodeproj/project.pbxproj
index 683cc364..66518327 100755
--- a/LiveTraffic.xcodeproj/project.pbxproj
+++ b/LiveTraffic.xcodeproj/project.pbxproj
@@ -656,6 +656,7 @@
DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
+ EXCLUDED_ARCHS = arm64;
EXECUTABLE_EXTENSION = xpl;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -748,6 +749,7 @@
DYLIB_COMPATIBILITY_VERSION = "";
DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)";
ENABLE_STRICT_OBJC_MSGSEND = YES;
+ EXCLUDED_ARCHS = arm64;
EXECUTABLE_EXTENSION = xpl;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -811,6 +813,7 @@
CLANG_ENABLE_OBJC_WEAK = YES;
CODE_SIGN_IDENTITY = "-";
DEPLOYMENT_LOCATION = YES;
+ EXCLUDED_ARCHS = "";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Lib/XPMP2",
@@ -843,6 +846,7 @@
CLANG_ENABLE_OBJC_WEAK = YES;
CODE_SIGN_IDENTITY = "-";
DEPLOYMENT_LOCATION = YES;
+ EXCLUDED_ARCHS = "";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Lib/XPMP2",
diff --git a/LiveTraffic.xcodeproj/project.xcworkspace/xcuserdata/birger.xcuserdatad/xcdebugger/Expressions.xcexplist b/LiveTraffic.xcodeproj/project.xcworkspace/xcuserdata/birger.xcuserdatad/xcdebugger/Expressions.xcexplist
index 9cddb523..c403e320 100644
--- a/LiveTraffic.xcodeproj/project.xcworkspace/xcuserdata/birger.xcuserdatad/xcdebugger/Expressions.xcexplist
+++ b/LiveTraffic.xcodeproj/project.xcworkspace/xcuserdata/birger.xcuserdatad/xcdebugger/Expressions.xcexplist
@@ -59,13 +59,13 @@
contextName = "Apt::FindEdgesForHeading(double, double, std::__1::vector<unsigned long, std::__1::allocator<unsigned long> >&, TaxiEdge::edgeTy) const:LTApt.cpp">
+ value = "vecTaxiEdges[222]">
+ value = "lst[1385]">
+ value = "vecTaxiEdgesIdxHead[3321]">
@@ -106,10 +106,10 @@
contextName = "LTFlightData::AddDynData(LTFlightData::FDDynamicData const&, int, int, positionTy*):LTFlightData.cpp">
+ value = "posDeque">
+ value = "dynDataDeque">
@@ -237,10 +237,10 @@
value = "cull">
+ value = "iter->second">
+ value = "tcas">
@@ -405,14 +405,11 @@
contextName = "GLOBAL">
+ value = "dataRefs">
-
-
+ value = "from">
@@ -434,7 +431,7 @@
value = "ppos">
+ value = "turn">
@@ -532,10 +529,10 @@
value = "mapFd">
+ value = "sizeof(bool)">
+ value = "dataRefs">
@@ -547,14 +544,6 @@
-
-
-
-
-
-
@@ -1072,10 +1061,10 @@
value = "_startTime">
+ value = "_targetTime">
+ value = "_deltaDist">
@@ -1088,6 +1077,14 @@
+
+
+
+
+
+
diff --git a/Src/LTFlightData.cpp b/Src/LTFlightData.cpp
index 87e4e602..fb4ea95c 100644
--- a/Src/LTFlightData.cpp
+++ b/Src/LTFlightData.cpp
@@ -385,44 +385,22 @@ bool LTFlightData::validForAcCreate(double simTime) const
if (std::isnan(simTime))
simTime = dataRefs.GetSimTime();
- // so we have two positions...one in the past, one in the future?
- return
- posDeque.front().ts() <= simTime && simTime < posDeque[1].ts();
-}
-
-// is the data outdated (considered too old to be useful)?
-bool LTFlightData::outdated (double simTime) const
-{
- // general re-init necessary?
- if (dataRefs.IsReInitAll())
- return true;
-
- // a/c turned invalid (due to exceptions)?
- if (pAc && !pAc->IsValid())
- return true;
-
- // flown out of sight? -> outdated, remove
- if (pAc &&
- pAc->GetVecView().dist > dataRefs.GetFdStdDistance_m())
- return true;
+ // so we have two positions...
+ // if it is _not_ one in the past, one in the future, then bail
+ if (!(posDeque.front().ts() <= simTime && simTime < posDeque[1].ts()))
+ return false;
- // cover the special case of finishing landing and roll-out without live positions
- // i.e. during approach and landing we don't destroy the aircraft
- // if it is approaching some runway
- // until it finally stopped on the runway
- if (pAc &&
- (pAc->GetFlightPhase() >= FPH_LANDING ||
- (pAc->GetFlightPhase() >= FPH_APPROACH && posRwy.isNormal())) &&
- pAc->GetFlightPhase() < FPH_STOPPED_ON_RWY)
- {
+ // So first pos is in the past, second in the future, great...
+ // both are within limits in terms of distance?
+ if (CoordDistance(dataRefs.GetViewPos(), posDeque.front()) > dataRefs.GetFdStdDistance_m() ||
+ CoordDistance(dataRefs.GetViewPos(), posDeque[1] ) > dataRefs.GetFdStdDistance_m())
return false;
- }
- // youngestTS longer ago than allowed? -> remove
- return
- youngestTS + dataRefs.GetAcOutdatedIntvl() < (std::isnan(simTime) ? dataRefs.GetSimTime() : simTime);
+ // All checks passed
+ return true;
}
+
#define ADD_LABEL(b,txt) if (b && !txt.empty()) { labelStat += txt; labelStat += ' '; }
// update static data parts of the a/c label for reuse for performance reasons
void LTFlightData::UpdateStaticLabel()
@@ -2286,32 +2264,62 @@ bool LTFlightData::AircraftMaintenance ( double simTime )
if ( !lock ) // we didn't get the lock, just return w/o deletion
return false;
- // if the a/c became invalid then remove the aircraft object,
- // but retain the remaining flight data
- if (pAc && !pAc->IsValid())
- DestroyAircraft();
-
- // if outdated just return 'delete me'
- if ( outdated(simTime) )
- return true;
-
// do we need to recalc the static part of the a/c label due to config change?
if (dataRefs.GetLabelCfg() != labelCfg)
UpdateStaticLabel();
- // doesn't yet have an associated aircraft but two positions?
- if ( !hasAc() && posDeque.size() >= 2 ) {
- // is already valid for a/c creation?
- if ( validForAcCreate(simTime) )
- // then do create the aircraft
- CreateAircraft(simTime);
- else // not yet valid
- // but the oldest position is at or before current simTime?
- // then chances are good that we can calculate positions
- if ( posDeque.front().ts() <= simTime)
- // start thread for position calculation...next time we might be valid for creation
- TriggerCalcNewPos(NAN);
+ // general re-init necessary?
+ if (dataRefs.IsReInitAll())
+ return true;
+
+ // Tests on an existing aircraft object
+ if (hasAc())
+ {
+ // if the a/c became invalid or has flown out of sight
+ // then remove the aircraft object,
+ // but retain the remaining flight data
+ if (!pAc->IsValid() ||
+ pAc->GetVecView().dist > dataRefs.GetFdStdDistance_m())
+ DestroyAircraft();
+ else {
+ // cover the special case of finishing landing and roll-out without live positions
+ // i.e. during approach and landing we don't destroy the aircraft
+ // if it is approaching some runway
+ // until it finally stopped on the runway
+ if ((pAc->GetFlightPhase() >= FPH_LANDING ||
+ (pAc->GetFlightPhase() >= FPH_APPROACH && posRwy.isNormal())) &&
+ pAc->GetFlightPhase() < FPH_STOPPED_ON_RWY)
+ {
+ return false;
+ }
+ }
}
+ // Tests when not (yet) having an aircraft object
+ else {
+ // Remove position from the beginning for as long as there is past data,
+ // i.e.: Only the .front pos may be in the past
+ while (posDeque.size() >= 2 && posDeque[1].ts() < simTime)
+ posDeque.pop_front();
+
+ // Have at least two positions?
+ if (posDeque.size() >= 2 ) {
+ // is already valid for a/c creation?
+ if ( validForAcCreate(simTime) )
+ // then do create the aircraft
+ CreateAircraft(simTime);
+ else // not yet valid
+ // but the oldest position is at or before current simTime?
+ // then chances are good that we can calculate positions
+ if ( posDeque.front().ts() <= simTime)
+ // start thread for position calculation...next time we might be valid for creation
+ TriggerCalcNewPos(NAN);
+ }
+ }
+
+ // youngestTS longer ago than allowed? -> remove the entire FD object
+ if (youngestTS + dataRefs.GetAcOutdatedIntvl() <
+ (std::isnan(simTime) ? dataRefs.GetSimTime() : simTime))
+ return true;
// don't delete me
return false;
@@ -2390,49 +2398,50 @@ bool LTFlightData::DetermineAcModel()
// checks if there is a slot available to create this a/c, tries to remove the farest a/c if too many a/c rendered
/// @warning Caller must own `mapFdMutex`!
-bool LTFlightData::AcSlotAvailable (double simTime)
+bool LTFlightData::AcSlotAvailable ()
{
// time we had shown the "Too many a/c" warning last:
- static double tTooManyAcMsgShown = 0.0;
+ static float tTooManyAcMsgShown = 0.0;
- // Haven't reach the limit in terms of number of a/c yet?
- if (dataRefs.GetNumAc() < dataRefs.GetMaxNumAc())
- return true;
-
// Have no positions? (Need one to determine distance to camera)
if (posDeque.empty())
return false;
- // Now we need to see if we are closer to the camera than other a/c.
- // If so remove the farest a/c to make room for us.
- LTFlightData* pFarestAc = nullptr;
-
- // NOTE: We can loop mapFd without lock only because we assume that
- // calling function owns mapFdMutex already!
- // find the farest a/c...if it is further away than us:
- double farestDist = CoordDistance(dataRefs.GetViewPos(), posDeque.front());
- for (mapLTFlightDataTy::value_type& p: mapFd)
+ // If we have too many aircraft show message (at most every 5 minutes)
+ if (dataRefs.GetNumAc() >= dataRefs.GetMaxNumAc()) {
+ if (CheckEverySoOften(tTooManyAcMsgShown, 300.0f))
+ SHOW_MSG(logWARN,MSG_TOO_MANY_AC,dataRefs.GetMaxNumAc());
+ }
+
+ // As long as there are too many a/c remove the ones farest away
+ while (dataRefs.GetNumAc() >= dataRefs.GetMaxNumAc())
{
- LTFlightData& fd = p.second;
- if (fd.hasAc() && fd.pAc->GetVecView().dist > farestDist) {
- farestDist = fd.pAc->GetVecView().dist;
- pFarestAc = &fd;
+ // Now we need to see if we are closer to the camera than other a/c.
+ // If so remove the farest a/c to make room for us.
+ LTFlightData* pFarestAc = nullptr;
+
+ // NOTE: We can loop mapFd without lock only because we assume that
+ // calling function owns mapFdMutex already!
+ // find the farest a/c...if it is further away than us:
+ double farestDist = CoordDistance(dataRefs.GetViewPos(), posDeque.front());
+ for (mapLTFlightDataTy::value_type& p: mapFd)
+ {
+ LTFlightData& fd = p.second;
+ if (fd.hasAc() && fd.pAc->GetVecView().dist > farestDist) {
+ farestDist = fd.pAc->GetVecView().dist;
+ pFarestAc = &fd;
+ }
}
- }
- // So we definitely have too many aircraft!
- if (tTooManyAcMsgShown + 180.0 < simTime) { // Show message at most every 3 minutes
- SHOW_MSG(logWARN,MSG_TOO_MANY_AC,dataRefs.GetMaxNumAc());
- tTooManyAcMsgShown = simTime;
- }
-
- // If we didn't find an active a/c farther away than us then bail with message
- if (!pFarestAc) {
- return false;
+ // If we didn't find an active a/c farther away than us then bail
+ if (!pFarestAc)
+ return false;
+
+ // We found the a/c farest away...remove it to make room for us!
+ pFarestAc->DestroyAircraft();
}
- // We found the a/c farest away...remove it!
- pFarestAc->DestroyAircraft();
+ // There is a slot now - either there was already or we made room
return true;
}
@@ -2445,7 +2454,7 @@ bool LTFlightData::CreateAircraft ( double simTime )
if ( hasAc() ) return true;
// exit if too many a/c shown and this one wouldn't be one of the nearest ones
- if (!AcSlotAvailable(simTime))
+ if (!AcSlotAvailable())
return false;
try {
diff --git a/Src/LTOpenGlider.cpp b/Src/LTOpenGlider.cpp
index 0c485a0a..fec36d7e 100644
--- a/Src/LTOpenGlider.cpp
+++ b/Src/LTOpenGlider.cpp
@@ -141,7 +141,8 @@ std::string OpenGliderConnection::GetURL (const positionTy& pos)
// We only return a URL if we are to use the request/reply procedure
if (bFailoverToHttp || DataRefs::GetCfgInt(DR_CFG_OGN_USE_REQUREPL)) {
APRSClose(); // make sure the ARPS connection is off, will return quickly if not even running
- boundingBoxTy box (pos, dataRefs.GetFdStdDistance_m());
+ // Bounding box the size of the configured distance...plus 10% so we have data in hand once the plane comes close enough
+ boundingBoxTy box (pos, double(dataRefs.GetFdStdDistance_m()) * 1.10 );
char url[128] = "";
snprintf(url, sizeof(url),
OPGLIDER_URL,
@@ -608,7 +609,7 @@ bool OpenGliderConnection::APRSProcessLine (const std::string& ln)
dyn.gnd = false; // there is no GND indicator in OGN data
dyn.heading = std::stod(m.str(M_HEAD));
dyn.spd = std::stod(m.str(M_SPEED));
- if (m.size() > M_VSI)
+ if (m.size() > M_VSI && !m.str(M_VSI).empty())
dyn.vsi = std::stod(m.str(M_VSI));
dyn.pChannel = this;
diff --git a/Src/LTOpenSky.cpp b/Src/LTOpenSky.cpp
index 206b242c..8b478a0f 100644
--- a/Src/LTOpenSky.cpp
+++ b/Src/LTOpenSky.cpp
@@ -44,7 +44,8 @@ LTFlightDataChannel()
// put together the URL to fetch based on current view position
std::string OpenSkyConnection::GetURL (const positionTy& pos)
{
- boundingBoxTy box (pos, dataRefs.GetFdStdDistance_m());
+ // we add 10% to the bounding box to have some data ready once the plane is close enough for display
+ boundingBoxTy box (pos, double(dataRefs.GetFdStdDistance_m()) * 1.10);
char url[128] = "";
snprintf(url, sizeof(url),
OPSKY_URL_ALL,
diff --git a/Src/LTRealTraffic.cpp b/Src/LTRealTraffic.cpp
index c8305c77..467384e4 100644
--- a/Src/LTRealTraffic.cpp
+++ b/Src/LTRealTraffic.cpp
@@ -701,10 +701,6 @@ bool RealTrafficConnection::ProcessRecvedTrafficData (const char* traffic)
return false;
}
- // is position close enough to current pos?
- if (posCamera.dist(pos) > dataRefs.GetFdStdDistance_m())
- return true; // ignore silently, no error
-
try {
// from here on access to fdMap guarded by a mutex
// until FD object is inserted and updated
diff --git a/docs/readme.html b/docs/readme.html
index 6d179944..9374e942 100755
--- a/docs/readme.html
+++ b/docs/readme.html
@@ -25,41 +25,13 @@
Documentation
and, certainly,
- do the setup.
+ do the setup,
+ which includes not only copying the plugin itself,
+ but also the installation of CSL models.
-
Upgrade from v1.5 to v2.0
-
-
Please follow
- upgrade instructions
- for upgrading an existing LiveTraffic installation to version 2.0.
-
-
-
Essential Installation
-
-
LiveTraffic plugin
-
-
- The LiveTraffic plugin folder is to be copied into X-Plane's
- Resources/plugins folder, the same as with other plugins.
-
-
-
CSL Packages
-
- You must install CSL packages, otherwise there will be no
- aircraft models to display and LiveTraffic will fail to
- start up.
-
-
- Recommendation is the "Bluebell" OBJ8 CSL packages by Oktalist found at the
- X-Plane.org forums.
- Follow their instructions. Also download and install the
- BB_IVAO_vert_offsets_* files in the downloads as recommended
- for the X-IvAp users. LiveTraffic can handle them, too.
-
-
Resulting Directory Structure
...under X-Plane's Resources/plugins folder:
@@ -82,7 +54,39 @@
Resulting Directory Structure
Release Notes
-
The number-link refers to the issue in GitHub with (often technical) details.
+
The issue number links refer to issue on GitHub with (often technical) details.
+
+
v2.3
+
+
v2.30
+
+
For updating coming from v2.20
+
+
copy lin|mac|win_x64/LiveTraffic.xpl.
+
+
+
+
Change log:
+
+
+
ADDED support for XPMP2 Remote Client and TCAS Concentrator (XPMP2 issues
+ #22 and
+ #16):
+ After installing XPMP2 Remote Client, LiveTraffic's planes are
+ synchronized across X-Plane instances in the network, e.g. supporting
+ External Visuals, Networked Multiplayer.
+ Locally installed, the Remote Client combines planes from several
+ XPMP2-based clients for one combined view on TCAS.
+ See documentation
+ for more.
+
FIXED issue #201,
+ avoiding creation and immediate destruction of planes when max number
+ of configured planes is reached. Also, available tracking data is
+ kept in memory if plane is removed for whatever reason, so it is
+ faster available again when coming back in reach.
+
FIXED a potential crash related to unexpectedly formatted
+ Open Glider Network data.
+
v2.2
From 9f773854affa0e47fd33ac5b0f163db7b6643cf5 Mon Sep 17 00:00:00 2001
From: TwinFan
Date: Mon, 23 Nov 2020 00:34:07 +0100
Subject: [PATCH 06/10] RealTraffic: Support historic data feed, closes #199
---
Data/RealTraffic/SendTraffic.py | 6 +-
Include/LTRealTraffic.h | 13 +-
.../xcdebugger/Expressions.xcexplist | 11 +-
Src/LTRealTraffic.cpp | 129 +++++++++++++++---
docs/readme.html | 4 +
5 files changed, 131 insertions(+), 32 deletions(-)
diff --git a/Data/RealTraffic/SendTraffic.py b/Data/RealTraffic/SendTraffic.py
index 99d7beea..e4abeef4 100755
--- a/Data/RealTraffic/SendTraffic.py
+++ b/Data/RealTraffic/SendTraffic.py
@@ -62,6 +62,9 @@ def compWaitTS(ts_s: str) -> str:
print (f"Waiting for {ts-now} seconds...", end='\r')
time.sleep (ts-now)
+ # Adjust returned timestamp value for historic timestamp
+ ts -= args.historic
+
return str(ts)
""" === Handle traffic data ==="""
@@ -97,11 +100,12 @@ def sendWeatherData(ln: str) -> int:
""" === MAIN === """
# --- Handling command line argumens ---
-parser = argparse.ArgumentParser(description='SendTraffic 0.1.0: Sends air traffic tracking data from a file out on a UDP port for LiveTraffic to receive it on the RealTraffic channel',fromfile_prefix_chars='@')
+parser = argparse.ArgumentParser(description='SendTraffic 0.2.0: Sends air traffic tracking data from a file out on a UDP port for LiveTraffic to receive it on the RealTraffic channel',fromfile_prefix_chars='@')
parser.add_argument('inFile', help='Tracking data file: records in CSV format holding air traffic data in RealTraffic\'s AITraffic format and weather data.\n by default', nargs='?', type=argparse.FileType('r'), default=sys.stdin)
parser.add_argument('-a', '--aircraft', metavar='HEX_LIST', help='List of aircraft to read, others skipped. Add one or several transponder hex id codes, separate by comma.')
parser.add_argument('-d', '--aircraftDecimal', metavar='NUM_LIST', help='Same as -a, but specify decimal values (as used in the CSV file).')
parser.add_argument('-b', '--bufPeriod', metavar='NUM', help='Buffering period: Number of seconds the first record is pushed into the past so that LiveTraffic\'s buffer fills more quickly. Recommended to be slightly less than LiveTraffic\'s buffering period.', type=int, default=0)
+parser.add_argument('--historic', metavar='NUM', help='Send historic data, ie. reduce included timestamp by this many seconds', type=int, default=0)
parser.add_argument('-l', '--loop', help='Endless loop: restart from the beginning when reaching end of file. Will only work if data contains loop with last position(s) being roughly equal to first position(s).', action='store_true')
parser.add_argument('--host', metavar='NAME_OR_IP', help='UDP target host or ip to send the data to, defaults to \'localhost\'', default='localhost')
parser.add_argument('--port', metavar='NUM', help='UDP port to send traffic data to, defaults to 49003', type=int, default=49003)
diff --git a/Include/LTRealTraffic.h b/Include/LTRealTraffic.h
index 03f5ae67..e1e35183 100644
--- a/Include/LTRealTraffic.h
+++ b/Include/LTRealTraffic.h
@@ -47,7 +47,10 @@ constexpr double RT_VSI_AIRBORNE = 80.0; ///< if VSI is more than this then w
#define MSG_RT_STATUS "RealTraffic network status changed to: %s"
#define MSG_RT_LAST_RCVD " | last: %lds ago"
+#define MSG_RT_ADJUST " | historic traffic from %s ago"
+#define INFO_RT_REAL_TIME "RealTraffic: Tracking data is real-time again."
+#define INFO_RT_ADJUST_TS "RealTraffic: Detected tracking data from %s in the past, will adjust them to display now."
#define ERR_RT_CANTLISTEN "RealTraffic: Cannot listen to network, can't tell RealTraffic our position"
#define ERR_RT_WEATHER_QNH "RealTraffic reports unreasonable QNH %ld - ignored"
#define ERR_RT_DISCARDED_MSG "RealTraffic: Discarded invalid message: %s"
@@ -144,6 +147,10 @@ class RealTrafficConnection : public LTOnlineChannel, LTFlightDataChannel
std::map mapDatagrams;
// weather, esp. current barometric pressure to correct altitude values
std::string lastWeather; // for duplicate detection
+ /// rolling list of timestamp (diff to now) for detecting historic sending
+ std::deque dequeTS;
+ /// current timestamp adjustment
+ double tsAdjust = 0.0;
public:
RealTrafficConnection (mapLTFlightDataTy& _fdMap);
@@ -202,10 +209,14 @@ class RealTrafficConnection : public LTOnlineChannel, LTFlightDataChannel
bool ProcessRecvedTrafficData (const char* traffic);
bool ProcessRecvedWeatherData (const char* weather);
+ /// Determine timestamp adjustment necessairy in case of historic data
+ void AdjustTimestamp (double& ts);
+ /// Return a string describing the current timestamp adjustment
+ std::string GetAdjustTSText () const;
+
// UDP datagram duplicate check
// Is it a duplicate? (if not datagram is copied into a map)
bool IsDatagramDuplicate (unsigned long numId,
- double posTime,
const char* datagram);
// remove outdated entries from mapDatagrams
void CleanupMapDatagrams();
diff --git a/LiveTraffic.xcodeproj/project.xcworkspace/xcuserdata/birger.xcuserdatad/xcdebugger/Expressions.xcexplist b/LiveTraffic.xcodeproj/project.xcworkspace/xcuserdata/birger.xcuserdatad/xcdebugger/Expressions.xcexplist
index c403e320..9d244fa6 100644
--- a/LiveTraffic.xcodeproj/project.xcworkspace/xcuserdata/birger.xcuserdatad/xcdebugger/Expressions.xcexplist
+++ b/LiveTraffic.xcodeproj/project.xcworkspace/xcuserdata/birger.xcuserdatad/xcdebugger/Expressions.xcexplist
@@ -278,14 +278,6 @@
-
-
-
-
-
-
@@ -907,6 +899,9 @@
+
+
diff --git a/Src/LTRealTraffic.cpp b/Src/LTRealTraffic.cpp
index 467384e4..c69e8499 100644
--- a/Src/LTRealTraffic.cpp
+++ b/Src/LTRealTraffic.cpp
@@ -243,9 +243,16 @@ std::string RealTrafficConnection::GetStatusWithTimeStr() const
std::string s (GetStatusStr());
if (IsConnected() && lastReceivedTime > 0.0) {
char sIntvl[50];
+ // add when the last msg was received
long intvl = std::lround(dataRefs.GetSimTime() - lastReceivedTime);
snprintf(sIntvl,sizeof(sIntvl),MSG_RT_LAST_RCVD,intvl);
s += sIntvl;
+ // if receiving historic traffic say so
+ if (tsAdjust > 1.0) {
+ snprintf(sIntvl, sizeof(sIntvl), MSG_RT_ADJUST,
+ GetAdjustTSText().c_str());
+ s += sIntvl;
+ }
}
return s;
}
@@ -303,6 +310,9 @@ bool RealTrafficConnection::StopConnections()
// stop both TCP and UDP
StopTcpConnection();
StopUdpConnection();
+
+ // Clear the list of historic time stamp differences
+ dequeTS.clear();
// stopped
SetStatus(RT_STATUS_NONE);
@@ -649,32 +659,37 @@ bool RealTrafficConnection::ProcessRecvedTrafficData (const char* traffic)
if (numId == 0)
return true; // ignore silently
+ // RealTraffic sends bursts of data every 10s, but that doesn't necessarily
+ // mean that anything really moved. Data could be stale.
+ // So here we just completely ignore data which looks exactly like the previous datagram
+ if (IsDatagramDuplicate(numId, traffic))
+ return true; // ignore silently
+
// *** position time ***
- using namespace std::chrono;
// There are 2 possibilities:
// 1. As of v7.0.55 RealTraffic can send a timestamp (when configured
// to use the "LiveTraffic" as Simulator in use, I assume)
// 2. Before that or with other settings there is no timestamp
- // so we assume 'now', corrected by network time offset
+ // so we assume 'now'
- const double posTime =
+ double posTime;
// Timestamp included?
- (tfc[RT_TFC_MSG_TYPE] == RT_TRAFFIC_AITFC &&
- tfc.size() >= RT_TFC_TIMESTAMP+1) ?
- // use that delivered timestamp
- std::stod(tfc[RT_TFC_TIMESTAMP]) :
- // system time in microseconds
- double(duration_cast(system_clock::now().time_since_epoch()).count())
- // divided by 1000000 to create seconds with fractionals
- / 1000000.0;
-
- // check for duplicate data
- // RealTraffic sends bursts of data every 10s, but that doesn't necessarily
- // mean that anything really moved. Data could be stale.
- // But as data doesn't come with a timestamp we have no means of identifying it.
- // So here we just completely ignore data which looks exactly like the previous datagram
- if (IsDatagramDuplicate(numId, posTime, traffic))
- return true; // ignore silently
+ if (tfc[RT_TFC_MSG_TYPE] == RT_TRAFFIC_AITFC && tfc.size() >= RT_TFC_TIMESTAMP+1)
+ {
+ // use that delivered timestamp and (potentially) adjust it if it is in the past
+ posTime = std::stod(tfc[RT_TFC_TIMESTAMP]);
+ AdjustTimestamp(posTime);
+ }
+ else
+ {
+ // No Timestamp provided: assume 'now'
+ using namespace std::chrono;
+ posTime =
+ // system time in microseconds
+ double(duration_cast(system_clock::now().time_since_epoch()).count())
+ // divided by 1000000 to create seconds with fractionals
+ / 1000000.0;
+ }
// *** Process received data ***
@@ -803,9 +818,77 @@ bool RealTrafficConnection::ProcessRecvedTrafficData (const char* traffic)
}
+// Determine timestamp adjustment necessairy in case of historic data
+void RealTrafficConnection::AdjustTimestamp (double& ts)
+{
+ // the assumed 'now' is simTime + buffering period
+ const double now = dataRefs.GetSimTime() + dataRefs.GetFdBufPeriod();
+
+ // *** Keep the rolling list of timestamps diffs, max length: 11 ***
+ dequeTS.push_back(now - ts);
+ while (dequeTS.size() > 11)
+ dequeTS.pop_front();
+
+ // *** Determine Median of timestamp differences ***
+ double medianTs;
+ if (dequeTS.size() >= 3)
+ {
+ // To find the (lower) Median while at the same time preserve the deque in its order,
+ // we do a partial sort into another array
+ std::vector v((dequeTS.size()+1)/2);
+ std::partial_sort_copy(dequeTS.cbegin(), dequeTS.cend(),
+ v.begin(), v.end());
+ medianTs = v.back();
+ }
+ // with less than 3 sample we just pick the last
+ else {
+ medianTs = dequeTS.back();
+ }
+
+ // *** Need to change the timestamp adjustment?
+ // Priority has to change back to zero if we are half the buffering period away from "now"
+ const int halfBufPeriod = dataRefs.GetFdBufPeriod()/2;
+ if (medianTs < 0.0 ||
+ std::abs(medianTs) <= halfBufPeriod) {
+ if (tsAdjust > 0.0) {
+ tsAdjust = 0.0;
+ SHOW_MSG(logINFO, INFO_RT_REAL_TIME);
+ }
+ }
+ // ...if that median is more than half the buffering period away from current adjustment
+ else if (std::abs(medianTs - tsAdjust) > halfBufPeriod)
+ {
+ // new adjustment is that median, rounded to 10 seconds
+ tsAdjust = std::round(medianTs / 10.0) * 10.0;
+ SHOW_MSG(logINFO, INFO_RT_ADJUST_TS, GetAdjustTSText().c_str());
+ }
+
+ // Adjust the passed-in timestamp by the determined adjustment
+ ts += tsAdjust;
+}
+
+
+// Return a string describing the current timestamp adjustment
+std::string RealTrafficConnection::GetAdjustTSText () const
+{
+ char timeTxt[25];
+ if (tsAdjust < 300.0) // less than 5 minutes: tell seconds
+ snprintf(timeTxt, sizeof(timeTxt), "%.0fs", tsAdjust);
+ else if (tsAdjust < 86400.0) // less than 1 day
+ snprintf(timeTxt, sizeof(timeTxt), "%ld:%02ldh",
+ long(tsAdjust/3600.0), // hours
+ long(tsAdjust/60.0) % 60); // minutes
+ else
+ snprintf(timeTxt, sizeof(timeTxt), "%ldd %ld:%02ldh",
+ long(tsAdjust/86400), // days
+ long(tsAdjust/3600.0) % 24, // hours
+ long(tsAdjust/60.0) % 60); // minutes
+ return std::string(timeTxt);
+}
+
+
// Is it a duplicate? (if not datagram is copied into a map)
bool RealTrafficConnection::IsDatagramDuplicate (unsigned long numId,
- double posTime,
const char* datagram)
{
// access is guarded by a lock
@@ -815,7 +898,9 @@ bool RealTrafficConnection::IsDatagramDuplicate (unsigned long numId,
auto it = mapDatagrams.find(numId);
if (it == mapDatagrams.end()) {
// add the datagram the first time for this plane
- mapDatagrams.emplace(numId, RTUDPDatagramTy(posTime,datagram));
+ mapDatagrams.emplace(std::piecewise_construct,
+ std::forward_as_tuple(numId),
+ std::forward_as_tuple(dataRefs.GetSimTime(),datagram));
// no duplicate
return false;
}
@@ -826,7 +911,7 @@ bool RealTrafficConnection::IsDatagramDuplicate (unsigned long numId,
return true;
// plane known, but data different, replace data in map
- d.posTime = posTime;
+ d.posTime = dataRefs.GetSimTime();
d.datagram = datagram;
// no duplicate
diff --git a/docs/readme.html b/docs/readme.html
index 9374e942..2d083426 100755
--- a/docs/readme.html
+++ b/docs/readme.html
@@ -79,6 +79,10 @@
v2.30
XPMP2-based clients for one combined view on TCAS. See documentation
for more.
+
ADDED issue #199
+ support for RealTraffic's historic data feed: LiveTraffic recognized
+ when fed with data from the past and adjusts the data so that it displays now.
+ Use the time slider in the RealTraffic app to go back in time.
FIXED issue #201,
avoiding creation and immediate destruction of planes when max number
of configured planes is reached. Also, available tracking data is
From fbdb2be10ae20f29dd8239200053e897ad3007cf Mon Sep 17 00:00:00 2001
From: TwinFan
Date: Mon, 23 Nov 2020 00:51:44 +0100
Subject: [PATCH 07/10] Readme correction, #199
---
docs/readme.html | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/docs/readme.html b/docs/readme.html
index 2d083426..0415037d 100755
--- a/docs/readme.html
+++ b/docs/readme.html
@@ -80,7 +80,8 @@
ADDED issue #199
- support for RealTraffic's historic data feed: LiveTraffic recognized
+ support for RealTraffic's historic data feed:
+ LiveTraffic recognizes
when fed with data from the past and adjusts the data so that it displays now.
Use the time slider in the RealTraffic app to go back in time.