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/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/Constants.h b/Include/Constants.h index e54e367e..81e8e2db 100644 --- a/Include/Constants.h +++ b/Include/Constants.h @@ -29,7 +29,7 @@ // // MARK: Version Information (CHANGE VERSION HERE) // -constexpr float VERSION_NR = 2.20f; +constexpr float VERSION_NR = 2.30f; constexpr bool VERSION_BETA = false; 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..60b1d79d 100644 --- a/Include/DataRefs.h +++ b/Include/DataRefs.h @@ -359,6 +359,7 @@ enum dataRefsLT { DR_CFG_HIDE_NEARBY_GND, DR_CFG_HIDE_NEARBY_AIR, DR_CFG_COPY_OBJ_FILES, + DR_CFG_REMOTE_SUPPORT, DR_CFG_LAST_CHECK_NEW_VER, // debug options @@ -507,7 +508,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(); } }; @@ -612,13 +613,14 @@ class DataRefs int fdRefreshIntvl = DEF_FD_REFRESH_INTVL; ///< how often to fetch new flight data int fdBufPeriod = DEF_FD_BUF_PERIOD; ///< seconds to buffer before simulating aircraft int acOutdatedIntvl = DEF_AC_OUTDATED_INTVL; ///< a/c considered outdated if latest flight data more older than this compare to 'now' - int netwTimeout = 90; // [s] of network request timeout + int netwTimeout = DEF_NETW_TIMEOUT; ///< [s] of network request timeout int bLndLightsTaxi = false; // keep landing lights on while taxiing? (to be able to see the a/c as there is no taxi light functionality) int hideBelowAGL = 0; // if positive: a/c visible only above this height AGL int hideTaxiing = 0; // hide a/c while taxiing? int hideNearbyGnd = 0; // [m] hide a/c if closer than this to user's aircraft on the ground int hideNearbyAir = 0; // [m] hide a/c if closer than this to user's aircraft in the air int cpyObjFiles = 1; ///< copy `.obj` files for replacing dataRefs and textures + int remoteSupport = 0; ///< support XPMP2 Remote Client? (3-way: -1 off, 0 auto, 1 on) // channel config options int ognUseRequRepl = 0; ///< OGN: Use Request/Reply instead of TCP receiver @@ -807,6 +809,7 @@ class DataRefs { return hideBelowAGL > 0 || hideTaxiing != 0 || hideNearbyGnd > 0 || hideNearbyAir > 0; } bool ShallCpyObjFiles () const { return cpyObjFiles != 0; } + int GetRemoteSupport () const { return remoteSupport; } bool NeedNewVerCheck () const; void SetLastCheckedNewVerNow (); 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/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/Include/LTRealTraffic.h b/Include/LTRealTraffic.h index cb207ebc..e1e35183 100644 --- a/Include/LTRealTraffic.h +++ b/Include/LTRealTraffic.h @@ -47,10 +47,15 @@ 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" +#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" @@ -142,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); @@ -200,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/Include/LiveTraffic.h b/Include/LiveTraffic.h index 87751ee7..3dd203f3 100644 --- a/Include/LiveTraffic.h +++ b/Include/LiveTraffic.h @@ -317,15 +317,7 @@ bool begins_with(const TContainer& input, const TContainer& match) && std::equal(match.cbegin(), match.cend(), input.cbegin()); } -/// Clamps `v` between `lo` and `hi`: `lo` if `v` < `lo`, `hi` if `hi` < `v`, otherwise `v` -/// @see C++17, https://en.cppreference.com/w/cpp/algorithm/clamp -template -constexpr const T& clamp( const T& v, const T& lo, const T& hi ) -{ - assert( !(hi < lo) ); - return (v < lo) ? lo : (hi < v) ? hi : v; -} - +/// Is value `lo <= v <= hi`? template constexpr bool between( const T& v, const T& lo, const T& hi ) { @@ -372,15 +364,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 || 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..1572ac9e 100644 --- a/Include/Network.h +++ b/Include/Network.h @@ -32,36 +32,26 @@ #else #include #include -typedef int SOCKET; // Windows defines SOCKET, so we define it for non-Windows manually +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. - +/// @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 +64,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..f1d2deaa 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` @@ -208,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 @@ -244,20 +246,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 +319,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 @@ -318,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/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..528d5e5e --- /dev/null +++ b/Lib/XPMP2/XPMP2.framework/Versions/1.0/Headers/XPMPRemote.h @@ -0,0 +1,432 @@ +/// @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 { + +/// The signature of the XPMP2 Remote Client +constexpr const char* REMOTE_SIGNATURE = "TwinFan.plugin.XPMP2.Remote"; + +// +// 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 +// + +/// 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) +// + +// 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 + 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 + 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? + 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 + 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(RemoteMsgAcAnimTy) == 16, "RemoteMsgAcAnimTy 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) (const std::uint32_t from[4], + const std::string& sFrom, + const RemoteMsgSettingsTy&) = nullptr; + /// Callback for processing A/C Details messages + void (*pfMsgACDetails) (const std::uint32_t from[4], size_t msgLen, + const RemoteMsgAcDetailTy&) = nullptr; + /// Callback for processing A/C Details messages + void (*pfMsgACPosUpdate) (const std::uint32_t from[4], size_t msgLen, + const RemoteMsgAcPosUpdateTy&) = nullptr; + /// Callback for processing A/C Animation dataRef messages + void (*pfMsgACAnim) (const std::uint32_t from[4], size_t msgLen, + const RemoteMsgAcAnimTy&) = nullptr; + /// Callback for processing A/C Removal messages + void (*pfMsgACRemove) (const 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..8732bee0 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 + 19H15 CFBundleDevelopmentRegion en CFBundleExecutable @@ -27,19 +27,19 @@ DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild - 12A7300 + 12B45b DTPlatformName macosx DTPlatformVersion - 10.15.6 + 11.0 DTSDKBuild - 19G68 + 20A2408 DTSDKName - macosx10.15 + macosx11.0 DTXcode - 1201 + 1220 DTXcodeBuild - 12A7300 + 12B45b LSMinimumSystemVersion 10.12 NSHumanReadableCopyright diff --git a/Lib/XPMP2/XPMP2.framework/Versions/1.0/XPMP2 b/Lib/XPMP2/XPMP2.framework/Versions/1.0/XPMP2 index 7904b851..7600b704 100644 Binary files a/Lib/XPMP2/XPMP2.framework/Versions/1.0/XPMP2 and b/Lib/XPMP2/XPMP2.framework/Versions/1.0/XPMP2 differ diff --git a/Lib/XPMP2/XPMP2.lib b/Lib/XPMP2/XPMP2.lib index a3a972bd..bd3a8202 100644 Binary files a/Lib/XPMP2/XPMP2.lib and b/Lib/XPMP2/XPMP2.lib differ diff --git a/Lib/XPMP2/libXPMP2.a b/Lib/XPMP2/libXPMP2.a index 96e2609a..7f44cd78 100644 Binary files a/Lib/XPMP2/libXPMP2.a and b/Lib/XPMP2/libXPMP2.a differ diff --git a/LiveTraffic.rc b/LiveTraffic.rc index 7e9e2b07..ebbad27e 100644 Binary files a/LiveTraffic.rc and b/LiveTraffic.rc differ diff --git a/LiveTraffic.vcxproj b/LiveTraffic.vcxproj index d6c0d658..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 @@ -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 + + @@ -191,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) Console UseLinkTimeCodeGeneration @@ -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/LiveTraffic.xcodeproj/project.pbxproj b/LiveTraffic.xcodeproj/project.pbxproj index 3d608574..66518327 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; @@ -633,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)", @@ -707,6 +731,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; @@ -724,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)", @@ -787,6 +813,7 @@ CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_IDENTITY = "-"; DEPLOYMENT_LOCATION = YES; + EXCLUDED_ARCHS = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Lib/XPMP2", @@ -819,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..9d244fa6 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"> @@ -278,14 +278,6 @@ - - - - - - @@ -405,14 +397,11 @@ contextName = "GLOBAL"> + value = "dataRefs"> - - + value = "from"> @@ -434,7 +423,7 @@ value = "ppos"> + value = "turn"> @@ -532,10 +521,10 @@ value = "mapFd"> + value = "sizeof(bool)"> + value = "dataRefs"> @@ -547,14 +536,6 @@ - - - - - - @@ -918,6 +899,9 @@ + + @@ -1072,10 +1056,10 @@ value = "_startTime"> + value = "_targetTime"> + value = "_deltaDist"> @@ -1088,6 +1072,14 @@ + + + + + + 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 +1832,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/LTAircraft.cpp b/Src/LTAircraft.cpp index f646ef33..66ff1c95 100644 --- a/Src/LTAircraft.cpp +++ b/Src/LTAircraft.cpp @@ -1564,8 +1564,8 @@ bool LTAircraft::CalcPPos() // nearly no drag, so try calculating the flight path angle and use // it also as pitch if ( vsi > mdl.VSI_STABLE ) { - toPitch = clamp(vsi2deg(vec.speed, vec.vsi), - mdl.PITCH_MIN, mdl.PITCH_MAX); + toPitch = std::clamp(vsi2deg(vec.speed, vec.vsi), + mdl.PITCH_MIN, mdl.PITCH_MAX); } // add some degrees in case flaps will be set 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..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() @@ -786,10 +764,8 @@ 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); - // 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? @@ -1687,7 +1663,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); @@ -2288,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; @@ -2392,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; } @@ -2447,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/LTImgWindow.cpp b/Src/LTImgWindow.cpp index 67a9b21e..2df253a1 100644 --- a/Src/LTImgWindow.cpp +++ b/Src/LTImgWindow.cpp @@ -271,7 +271,7 @@ IMGUI_API bool SliderDr(const char* label, dataRefsLT idx, if (v_step > 1) iV = (iV+(v_step/2))/v_step * v_step; // When entering manually [Ctrl+Click], values aren't clamped, so we take care of it - cfgSet(idx, clamp(iV, v_min, v_max)); // set dataRef value + cfgSet(idx, std::clamp(iV, v_min, v_max)); // set dataRef value return true; } else diff --git a/Src/LTMain.cpp b/Src/LTMain.cpp index 61ac24f2..434a54b1 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; @@ -631,6 +631,9 @@ int MPIntPrefsFunc (const char*, const char* key, int iDefault) if (!strcmp(key, XPMP_CFG_ITM_REPLDATAREFS) || !strcmp(key, XPMP_CFG_ITM_REPLTEXTURE)) return dataRefs.ShallCpyObjFiles(); + // Support XPMP2 Remote Clinet? + if (!strcmp(key, XPMP_CFG_ITM_SUPPORT_REMOTE)) + return dataRefs.GetRemoteSupport(); // dont' know/care about the option, return the default value return iDefault; diff --git a/Src/LTOpenGlider.cpp b/Src/LTOpenGlider.cpp index a8411cf9..65a8b299 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, @@ -152,7 +153,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 +197,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()); @@ -244,8 +245,8 @@ bool OpenGliderConnection::ProcessFetchedData (mapLTFlightDataTy& fdMap) fd.SetKey(fdKey); // Aircraft type converted from Flarm AcftType - const FlarmAircraftTy acTy = (FlarmAircraftTy)clamp(std::stoi(tok[GNF_FLARM_ACFT_TYPE]), - FAT_UNKNOWN, FAT_STATIC_OBJ); + const FlarmAircraftTy acTy = (FlarmAircraftTy)std::clamp(std::stoi(tok[GNF_FLARM_ACFT_TYPE]), + FAT_UNKNOWN, FAT_STATIC_OBJ); stat.catDescr = OGNGetAcTypeName(acTy); // If we still have no accurate ICAO type then we need to fall back to some configured defaults @@ -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; @@ -796,7 +797,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 +992,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/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..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 *** @@ -701,10 +716,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 @@ -807,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 @@ -819,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; } @@ -830,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/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(dataRefs.GetRemoteSupport(),-1,1); // this turns the order around: 0 - on, 1 - Auto, 2 - Off + if (ImGui::Combo("##RemoteSupport", &n, "Always On\0Auto Detect (default)\0Off\0", 5)) + DATA_REFS_LT[DR_CFG_REMOTE_SUPPORT].setData(1-n); + ImGui::TableNextCell(); + } if (!*sFilter) ImGui::TreePop(); } @@ -698,7 +707,7 @@ void LTSettingsUI::buildInterface() DataRefs::vecCSLPaths& vec = dataRefs.GetCSLPaths(); for (int i = 0; (size_t)i < vec.size(); ++i) { - DataRefs::CSLPathCfgTy& pathCfg = vec[i]; + DataRefs::CSLPathCfgTy& pathCfg = vec[size_t(i)]; ImGui::PushID(pathCfg.getPath().c_str()); // Enable Checkbox / Load Button diff --git a/docs/readme.html b/docs/readme.html index 6d179944..0415037d 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,44 @@

    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.
    • +
    • ADDED issue #199 + 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.
    • +
    • 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