FlightGear next
multiplaymgr.cxx
Go to the documentation of this file.
1
2//
3// multiplaymgr.cxx
4//
5// Written by Duncan McCreanor, started February 2003.
6// duncan.mccreanor@airservicesaustralia.com
7//
8// Copyright (C) 2003 Airservices Australia
9// Copyright (C) 2005 Oliver Schroeder
10// Copyright (C) 2006 Mathias Froehlich
11//
12// This program is free software; you can redistribute it and/or
13// modify it under the terms of the GNU General Public License as
14// published by the Free Software Foundation; either version 2 of the
15// License, or (at your option) any later version.
16//
17// This program is distributed in the hope that it will be useful, but
18// WITHOUT ANY WARRANTY; without even the implied warranty of
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20// General Public License for more details.
21//
22// You should have received a copy of the GNU General Public License
23// along with this program; if not, write to the Free Software
24// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25//
26// $Id$
27//
29
30#include <config.h>
31
32#include <iostream>
33#include <algorithm>
34#include <cstring>
35#include <errno.h>
36#include <memory>
37
38#include <simgear/debug/logstream.hxx>
39#include <simgear/math/sg_random.hxx>
40#include <simgear/misc/sg_dir.hxx>
41#include <simgear/misc/stdint.hxx>
42#include <simgear/misc/strutils.hxx>
43#include <simgear/props/props.hxx>
44#include <simgear/props/props_io.hxx>
45#include <simgear/structure/commands.hxx>
46#include <simgear/structure/event_mgr.hxx>
47#include <simgear/timing/timestamp.hxx>
48
49#include <AIModel/AIManager.hxx>
51#include <Main/fg_props.hxx>
52#include "multiplaymgr.hxx"
53#include "mpmessages.hxx"
54#include "MPServerResolver.hxx"
55#include <FDM/fdm_shell.hxx>
57#include <Time/TimeManager.hxx>
59#include "mpirc.hxx"
60#include "cpdlc.hxx"
61
62#if defined(_MSC_VER) || defined(__MINGW32__)
63#include <WS2tcpip.h>
64#endif
65using namespace std;
66
67
68#define MAX_PACKET_SIZE 1200
69#define MAX_TEXT_SIZE 768 // Increased for 2017.3 to allow for long Emesary messages.
70/*
71 * With the MP2017(V2) protocol it is possible to transmit using a different type/encoding than the property has,
72 * for example a 32 bit int can be transmitted as a 16bit short int or a float transmitted in 16bits with appropriate precision.
73 * The TransmissionType defines how a property should be transmitted.
74 */
76 TT_ASIS = 0, // transmit as defined in the property. This is the default
77 TT_BOOL = simgear::props::BOOL,
78 TT_INT = simgear::props::INT,
79 TT_FLOAT = simgear::props::FLOAT,
80 TT_STRING = simgear::props::STRING,
81 TT_SHORTINT = 0x100,
82 TT_SHORT_FLOAT_NORM = 0x101, // -1 .. 1 encoded into a short int (16 bit)
83 TT_SHORT_FLOAT_1 = 0x102, //range -3276.7 .. 3276.7 float encoded into a short int (16 bit)
84 TT_SHORT_FLOAT_2 = 0x103, //range -327.67 .. 327.67 float encoded into a short int (16 bit)
85 TT_SHORT_FLOAT_3 = 0x104, //range -32.767 .. 32.767 float encoded into a short int (16 bit)
86 TT_SHORT_FLOAT_4 = 0x105, //range -3.2767 .. 3.2767 float encoded into a short int (16 bit)
89 TT_NOSEND, // Do not send this property - probably the receive element for a custom encoded property
90};
91/*
92 * Definitions for the version of the protocol to use to transmit the items defined in the IdPropertyList
93 *
94 * The MP2017(V2) protocol allows for much better packing of strings, new types that are transmitted in 4bytes by transmitting
95 * with short int (sometimes scaled) for the values (a lot of the properties that are transmitted will pack nicely into 16bits).
96 * The MP2017(V2) protocol also allows for properties to be transmitted automatically as a different type and the encode/decode will
97 * take this into consideration.
98 * The pad magic is used to force older clients to use verifyProperties and as the first property transmitted is short int encoded it
99 * will cause the rest of the packet to be discarded. This is the section of the packet that contains the properties defined in the list
100 * here - the basic motion properties remain compatible, so the older client will see just the model, not chat, not animations etc.
101 * The lower 16 bits of the prop_id (version) are the version and the upper 16bits are for meta data.
102 */
103const int V1_1_PROP_ID = 1;
104const int V1_1_2_PROP_ID = 2;
105const int V2_PROP_ID_PROTOCOL = 0x10001;
106
107const int V2_PAD_MAGIC = 0x1face002;
108
109/*
110 * boolean arrays are transmitted in blocks of 31 mapping to a single int.
111 * These parameters define where these are mapped and how they are sent.
112 * The blocks should be in the same property range (with no other properties inside the range)
113 * The blocksize is set to 40 to allow for ints being 32 bits, so block 0 will be 0..30 (31 bits)
114 */
115const int BOOLARRAY_BLOCKSIZE = 40;
116
117const int BOOLARRAY_BASE_1 = 11000;
120// define range of bool values for receive.
123// Transmission uses a buffer to build the value for each array block.
124const int MAX_BOOL_BUFFERS = 3;
125
126// starting with 2018.1 we will now append new properties for each version at the end of the list - as this provides much
127// better backwards compatibility.
128const int V2018_1_BASE = 11990;
129const int EMESARYBRIDGETYPE_BASE = 12200;
130const int EMESARYBRIDGE_BASE = 12000;
131const int V2018_3_BASE = 13000;
132const int FALLBACK_MODEL_ID = 13000;
133const int V2019_3_BASE = 13001;
134const int V2020_4_BASE = 13003;
135
136/*
137 * definition of properties that are to be transmitted.
138 * New for 2017.2:
139 * 1. TransmitAs - this causes the property to be transmitted on the wire using the
140 * specified format transparently.
141 * 2. version - the minimum version of the protocol that is required to transmit a property.
142 * Does not apply to incoming properties - as these will be decoded correctly when received
143 * 3. encode_for_transmit - method that will convert from and to the packet for the value
144 * Allows specific conversion rules to be applied; such as conversion of a string to an integer for transmission.
145 * 4. decode_received - decodes received data
146 * - when using the encode/decode methods there should be both specified, however if the result of the encode
147 * is to transmit in a different property index the encode/decode will be on different elements in the property id list.
148 * This is used to encode/decode the launchbar state - so that with 2017.2 instead of the string being transmitted in property 108
149 * a short int encoded version is sent in property 120 - which when received will be placed into property 108. This reduces transmission space
150 * and keeps compatibility.
151 */
153 unsigned id;
154 const char* name;
155 simgear::props::Type type;
158 xdr_data_t* (*encode_for_transmit)(const IdPropertyList *propDef, const xdr_data_t*, FGPropertyData*);
159 xdr_data_t* (*decode_received)(const IdPropertyList *propDef, const xdr_data_t*, FGPropertyData*);
160};
161
162static const IdPropertyList* findProperty(unsigned id);
163
164/*
165 * intermediate buffer used to build the ints that will be transmitted for the boolean arrays
166 */
171
173{
174 xdr_data_t *xdr = (xdr_data_t *)_xdr;
175
176 if (propDef->TransmitAs == TT_NOSEND)
177 return xdr;
178
179 int v = -1;
180 if (p && p->string_value)
181 {
182 if (!strcmp("Engaged", p->string_value))
183 v = 0;
184 else if (!strcmp("Launching", p->string_value))
185 v = 1;
186 else if (!strcmp("Completed", p->string_value))
187 v = 2;
188 else if (!strcmp("Disengaged", p->string_value))
189 v = 3;
190 else
191 return (xdr_data_t*)xdr;
192
193 *xdr++ = XDR_encode_shortints32(120, v);
194 }
195 return xdr;
196}
197
199{
200 xdr_data_t *xdr = (xdr_data_t *)_xdr;
201
202 int v1, v2;
203 XDR_decode_shortints32(*xdr, v1, v2);
204 xdr++;
205 const char *stringvalue = "";
206 switch (v2)
207 {
208 case 0: stringvalue = "Engaged"; break;
209 case 1: stringvalue = "Launching"; break;
210 case 2: stringvalue = "Completed"; break;
211 case 3: stringvalue = "Disengaged"; break;
212 }
213
214 p->id = 108; // this is for the string property for gear/launchbar/state
215 if (p->string_value && p->type == simgear::props::STRING)
216 delete[] p->string_value;
217 p->string_value = new char[strlen(stringvalue) + 1];
218 strcpy(p->string_value, stringvalue);
219 p->type = simgear::props::STRING;
220 return xdr;
221}
222
223// A static map of protocol property id values to property paths,
224// This should be extendable dynamically for every specific aircraft ...
225// For now only that static list
227 { 10, "sim/multiplay/protocol-version", simgear::props::INT, TT_SHORTINT, V2_PROP_ID_PROTOCOL, NULL, NULL },
228 { 100, "surface-positions/left-aileron-pos-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
229 { 101, "surface-positions/right-aileron-pos-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
230 { 102, "surface-positions/elevator-pos-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
231 { 103, "surface-positions/rudder-pos-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
232 { 104, "surface-positions/flap-pos-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
233 { 105, "surface-positions/speedbrake-pos-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
234 { 106, "gear/tailhook/position-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
235 { 107, "gear/launchbar/position-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
236 //
237 { 108, "gear/launchbar/state", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, encode_launchbar_state_for_transmission, NULL },
238 { 109, "gear/launchbar/holdback-position-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
239 { 110, "canopy/position-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
240 { 111, "surface-positions/wing-pos-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
241 { 112, "surface-positions/wing-fold-pos-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
242
243 // to enable decoding this is the transient ID record that is in the packet. This is not sent directly - instead it is the result
244 // of the conversion of property 108.
245 { 120, "gear/launchbar/state-value", simgear::props::INT, TT_NOSEND, V1_1_2_PROP_ID, NULL, decode_received_launchbar_state },
246
247 { 200, "gear/gear[0]/compression-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
248 { 201, "gear/gear[0]/position-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
249 { 210, "gear/gear[1]/compression-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
250 { 211, "gear/gear[1]/position-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
251 { 220, "gear/gear[2]/compression-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
252 { 221, "gear/gear[2]/position-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
253 { 230, "gear/gear[3]/compression-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
254 { 231, "gear/gear[3]/position-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
255 { 240, "gear/gear[4]/compression-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
256 { 241, "gear/gear[4]/position-norm", simgear::props::FLOAT, TT_SHORT_FLOAT_NORM, V1_1_PROP_ID, NULL, NULL },
257
258 { 300, "engines/engine[0]/n1", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
259 { 301, "engines/engine[0]/n2", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
260 { 302, "engines/engine[0]/rpm", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
261 { 310, "engines/engine[1]/n1", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
262 { 311, "engines/engine[1]/n2", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
263 { 312, "engines/engine[1]/rpm", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
264 { 320, "engines/engine[2]/n1", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
265 { 321, "engines/engine[2]/n2", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
266 { 322, "engines/engine[2]/rpm", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
267 { 330, "engines/engine[3]/n1", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
268 { 331, "engines/engine[3]/n2", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
269 { 332, "engines/engine[3]/rpm", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
270 { 340, "engines/engine[4]/n1", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
271 { 341, "engines/engine[4]/n2", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
272 { 342, "engines/engine[4]/rpm", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
273 { 350, "engines/engine[5]/n1", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
274 { 351, "engines/engine[5]/n2", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
275 { 352, "engines/engine[5]/rpm", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
276 { 360, "engines/engine[6]/n1", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
277 { 361, "engines/engine[6]/n2", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
278 { 362, "engines/engine[6]/rpm", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
279 { 370, "engines/engine[7]/n1", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
280 { 371, "engines/engine[7]/n2", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
281 { 372, "engines/engine[7]/rpm", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
282 { 380, "engines/engine[8]/n1", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
283 { 381, "engines/engine[8]/n2", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
284 { 382, "engines/engine[8]/rpm", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
285 { 390, "engines/engine[9]/n1", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
286 { 391, "engines/engine[9]/n2", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
287 { 392, "engines/engine[9]/rpm", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
288
289 { 800, "rotors/main/rpm", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
290 { 801, "rotors/tail/rpm", simgear::props::FLOAT, TT_SHORT_FLOAT_1, V1_1_PROP_ID, NULL, NULL },
291 { 810, "rotors/main/blade[0]/position-deg", simgear::props::FLOAT, TT_SHORT_FLOAT_3, V1_1_PROP_ID, NULL, NULL },
292 { 811, "rotors/main/blade[1]/position-deg", simgear::props::FLOAT, TT_SHORT_FLOAT_3, V1_1_PROP_ID, NULL, NULL },
293 { 812, "rotors/main/blade[2]/position-deg", simgear::props::FLOAT, TT_SHORT_FLOAT_3, V1_1_PROP_ID, NULL, NULL },
294 { 813, "rotors/main/blade[3]/position-deg", simgear::props::FLOAT, TT_SHORT_FLOAT_3, V1_1_PROP_ID, NULL, NULL },
295 { 820, "rotors/main/blade[0]/flap-deg", simgear::props::FLOAT, TT_SHORT_FLOAT_3, V1_1_PROP_ID, NULL, NULL },
296 { 821, "rotors/main/blade[1]/flap-deg", simgear::props::FLOAT, TT_SHORT_FLOAT_3, V1_1_PROP_ID, NULL, NULL },
297 { 822, "rotors/main/blade[2]/flap-deg", simgear::props::FLOAT, TT_SHORT_FLOAT_3, V1_1_PROP_ID, NULL, NULL },
298 { 823, "rotors/main/blade[3]/flap-deg", simgear::props::FLOAT, TT_SHORT_FLOAT_3, V1_1_PROP_ID, NULL, NULL },
299 { 830, "rotors/tail/blade[0]/position-deg", simgear::props::FLOAT, TT_SHORT_FLOAT_3, V1_1_PROP_ID, NULL, NULL },
300 { 831, "rotors/tail/blade[1]/position-deg", simgear::props::FLOAT, TT_SHORT_FLOAT_3, V1_1_PROP_ID, NULL, NULL },
301
302 { 900, "sim/hitches/aerotow/tow/length", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
303 { 901, "sim/hitches/aerotow/tow/elastic-constant", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
304 { 902, "sim/hitches/aerotow/tow/weight-per-m-kg-m", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
305 { 903, "sim/hitches/aerotow/tow/dist", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
306 { 904, "sim/hitches/aerotow/tow/connected-to-property-node", simgear::props::BOOL, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
307 { 905, "sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
308 { 906, "sim/hitches/aerotow/tow/break-force", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
309 { 907, "sim/hitches/aerotow/tow/end-force-x", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
310 { 908, "sim/hitches/aerotow/tow/end-force-y", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
311 { 909, "sim/hitches/aerotow/tow/end-force-z", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
312 { 930, "sim/hitches/aerotow/is-slave", simgear::props::BOOL, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
313 { 931, "sim/hitches/aerotow/speed-in-tow-direction", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
314 { 932, "sim/hitches/aerotow/open", simgear::props::BOOL, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
315 { 933, "sim/hitches/aerotow/local-pos-x", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
316 { 934, "sim/hitches/aerotow/local-pos-y", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
317 { 935, "sim/hitches/aerotow/local-pos-z", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
318
319 { 1001, "controls/flight/slats", simgear::props::FLOAT, TT_SHORT_FLOAT_4, V1_1_PROP_ID, NULL, NULL },
320 { 1002, "controls/flight/speedbrake", simgear::props::FLOAT, TT_SHORT_FLOAT_4, V1_1_PROP_ID, NULL, NULL },
321 { 1003, "controls/flight/spoilers", simgear::props::FLOAT, TT_SHORT_FLOAT_4, V1_1_PROP_ID, NULL, NULL },
322 { 1004, "controls/gear/gear-down", simgear::props::FLOAT, TT_SHORT_FLOAT_4, V1_1_PROP_ID, NULL, NULL },
323 { 1005, "controls/lighting/nav-lights", simgear::props::FLOAT, TT_SHORT_FLOAT_3, V1_1_PROP_ID, NULL, NULL },
324 { 1006, "controls/armament/station[0]/jettison-all", simgear::props::BOOL, TT_SHORTINT, V1_1_PROP_ID, NULL, NULL },
325
326 { 1100, "sim/model/variant", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
327 { 1101, "sim/model/livery/file", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
328
329 { 1200, "environment/wildfire/data", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
330 { 1201, "environment/contrail", simgear::props::INT, TT_SHORTINT, V1_1_PROP_ID, NULL, NULL },
331
332 { 1300, "tanker", simgear::props::INT, TT_SHORTINT, V1_1_PROP_ID, NULL, NULL },
333
334 { 1400, "scenery/events", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
335
336 { 1500, "instrumentation/transponder/transmitted-id", simgear::props::INT, TT_SHORTINT, V1_1_PROP_ID, NULL, NULL },
337 { 1501, "instrumentation/transponder/altitude", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
338 { 1502, "instrumentation/transponder/ident", simgear::props::BOOL, TT_SHORTINT, V1_1_PROP_ID, NULL, NULL },
339 { 1503, "instrumentation/transponder/inputs/mode", simgear::props::INT, TT_SHORTINT, V1_1_PROP_ID, NULL, NULL },
340 { 1504, "instrumentation/transponder/ground-bit", simgear::props::BOOL, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
341 { 1505, "instrumentation/transponder/airspeed-kt", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
342
343 { 10001, "sim/multiplay/transmission-freq-hz", simgear::props::STRING, TT_NOSEND, V1_1_2_PROP_ID, NULL, NULL },
344 { 10002, "sim/multiplay/chat", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
345
346 { 10100, "sim/multiplay/generic/string[0]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
347 { 10101, "sim/multiplay/generic/string[1]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
348 { 10102, "sim/multiplay/generic/string[2]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
349 { 10103, "sim/multiplay/generic/string[3]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
350 { 10104, "sim/multiplay/generic/string[4]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
351 { 10105, "sim/multiplay/generic/string[5]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
352 { 10106, "sim/multiplay/generic/string[6]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
353 { 10107, "sim/multiplay/generic/string[7]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
354 { 10108, "sim/multiplay/generic/string[8]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
355 { 10109, "sim/multiplay/generic/string[9]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
356 { 10110, "sim/multiplay/generic/string[10]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
357 { 10111, "sim/multiplay/generic/string[11]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
358 { 10112, "sim/multiplay/generic/string[12]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
359 { 10113, "sim/multiplay/generic/string[13]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
360 { 10114, "sim/multiplay/generic/string[14]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
361 { 10115, "sim/multiplay/generic/string[15]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
362 { 10116, "sim/multiplay/generic/string[16]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
363 { 10117, "sim/multiplay/generic/string[17]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
364 { 10118, "sim/multiplay/generic/string[18]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
365 { 10119, "sim/multiplay/generic/string[19]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
366
367 { 10200, "sim/multiplay/generic/float[0]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
368 { 10201, "sim/multiplay/generic/float[1]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
369 { 10202, "sim/multiplay/generic/float[2]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
370 { 10203, "sim/multiplay/generic/float[3]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
371 { 10204, "sim/multiplay/generic/float[4]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
372 { 10205, "sim/multiplay/generic/float[5]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
373 { 10206, "sim/multiplay/generic/float[6]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
374 { 10207, "sim/multiplay/generic/float[7]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
375 { 10208, "sim/multiplay/generic/float[8]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
376 { 10209, "sim/multiplay/generic/float[9]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
377 { 10210, "sim/multiplay/generic/float[10]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
378 { 10211, "sim/multiplay/generic/float[11]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
379 { 10212, "sim/multiplay/generic/float[12]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
380 { 10213, "sim/multiplay/generic/float[13]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
381 { 10214, "sim/multiplay/generic/float[14]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
382 { 10215, "sim/multiplay/generic/float[15]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
383 { 10216, "sim/multiplay/generic/float[16]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
384 { 10217, "sim/multiplay/generic/float[17]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
385 { 10218, "sim/multiplay/generic/float[18]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
386 { 10219, "sim/multiplay/generic/float[19]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
387
388 { 10220, "sim/multiplay/generic/float[20]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
389 { 10221, "sim/multiplay/generic/float[21]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
390 { 10222, "sim/multiplay/generic/float[22]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
391 { 10223, "sim/multiplay/generic/float[23]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
392 { 10224, "sim/multiplay/generic/float[24]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
393 { 10225, "sim/multiplay/generic/float[25]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
394 { 10226, "sim/multiplay/generic/float[26]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
395 { 10227, "sim/multiplay/generic/float[27]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
396 { 10228, "sim/multiplay/generic/float[28]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
397 { 10229, "sim/multiplay/generic/float[29]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
398 { 10230, "sim/multiplay/generic/float[30]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
399 { 10231, "sim/multiplay/generic/float[31]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
400 { 10232, "sim/multiplay/generic/float[32]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
401 { 10233, "sim/multiplay/generic/float[33]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
402 { 10234, "sim/multiplay/generic/float[34]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
403 { 10235, "sim/multiplay/generic/float[35]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
404 { 10236, "sim/multiplay/generic/float[36]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
405 { 10237, "sim/multiplay/generic/float[37]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
406 { 10238, "sim/multiplay/generic/float[38]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
407 { 10239, "sim/multiplay/generic/float[39]", simgear::props::FLOAT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
408
409 { 10300, "sim/multiplay/generic/int[0]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
410 { 10301, "sim/multiplay/generic/int[1]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
411 { 10302, "sim/multiplay/generic/int[2]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
412 { 10303, "sim/multiplay/generic/int[3]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
413 { 10304, "sim/multiplay/generic/int[4]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
414 { 10305, "sim/multiplay/generic/int[5]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
415 { 10306, "sim/multiplay/generic/int[6]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
416 { 10307, "sim/multiplay/generic/int[7]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
417 { 10308, "sim/multiplay/generic/int[8]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
418 { 10309, "sim/multiplay/generic/int[9]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
419 { 10310, "sim/multiplay/generic/int[10]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
420 { 10311, "sim/multiplay/generic/int[11]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
421 { 10312, "sim/multiplay/generic/int[12]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
422 { 10313, "sim/multiplay/generic/int[13]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
423 { 10314, "sim/multiplay/generic/int[14]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
424 { 10315, "sim/multiplay/generic/int[15]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
425 { 10316, "sim/multiplay/generic/int[16]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
426 { 10317, "sim/multiplay/generic/int[17]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
427 { 10318, "sim/multiplay/generic/int[18]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
428 { 10319, "sim/multiplay/generic/int[19]", simgear::props::INT, TT_ASIS, V1_1_PROP_ID, NULL, NULL },
429
430 { 10500, "sim/multiplay/generic/short[0]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
431 { 10501, "sim/multiplay/generic/short[1]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
432 { 10502, "sim/multiplay/generic/short[2]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
433 { 10503, "sim/multiplay/generic/short[3]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
434 { 10504, "sim/multiplay/generic/short[4]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
435 { 10505, "sim/multiplay/generic/short[5]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
436 { 10506, "sim/multiplay/generic/short[6]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
437 { 10507, "sim/multiplay/generic/short[7]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
438 { 10508, "sim/multiplay/generic/short[8]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
439 { 10509, "sim/multiplay/generic/short[9]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
440 { 10510, "sim/multiplay/generic/short[10]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
441 { 10511, "sim/multiplay/generic/short[11]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
442 { 10512, "sim/multiplay/generic/short[12]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
443 { 10513, "sim/multiplay/generic/short[13]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
444 { 10514, "sim/multiplay/generic/short[14]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
445 { 10515, "sim/multiplay/generic/short[15]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
446 { 10516, "sim/multiplay/generic/short[16]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
447 { 10517, "sim/multiplay/generic/short[17]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
448 { 10518, "sim/multiplay/generic/short[18]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
449 { 10519, "sim/multiplay/generic/short[19]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
450 { 10520, "sim/multiplay/generic/short[20]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
451 { 10521, "sim/multiplay/generic/short[21]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
452 { 10522, "sim/multiplay/generic/short[22]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
453 { 10523, "sim/multiplay/generic/short[23]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
454 { 10524, "sim/multiplay/generic/short[24]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
455 { 10525, "sim/multiplay/generic/short[25]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
456 { 10526, "sim/multiplay/generic/short[26]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
457 { 10527, "sim/multiplay/generic/short[27]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
458 { 10528, "sim/multiplay/generic/short[28]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
459 { 10529, "sim/multiplay/generic/short[29]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
460 { 10530, "sim/multiplay/generic/short[30]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
461 { 10531, "sim/multiplay/generic/short[31]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
462 { 10532, "sim/multiplay/generic/short[32]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
463 { 10533, "sim/multiplay/generic/short[33]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
464 { 10534, "sim/multiplay/generic/short[34]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
465 { 10535, "sim/multiplay/generic/short[35]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
466 { 10536, "sim/multiplay/generic/short[36]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
467 { 10537, "sim/multiplay/generic/short[37]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
468 { 10538, "sim/multiplay/generic/short[38]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
469 { 10539, "sim/multiplay/generic/short[39]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
470 { 10540, "sim/multiplay/generic/short[40]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
471 { 10541, "sim/multiplay/generic/short[41]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
472 { 10542, "sim/multiplay/generic/short[42]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
473 { 10543, "sim/multiplay/generic/short[43]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
474 { 10544, "sim/multiplay/generic/short[44]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
475 { 10545, "sim/multiplay/generic/short[45]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
476 { 10546, "sim/multiplay/generic/short[46]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
477 { 10547, "sim/multiplay/generic/short[47]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
478 { 10548, "sim/multiplay/generic/short[48]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
479 { 10549, "sim/multiplay/generic/short[49]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
480 { 10550, "sim/multiplay/generic/short[50]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
481 { 10551, "sim/multiplay/generic/short[51]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
482 { 10552, "sim/multiplay/generic/short[52]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
483 { 10553, "sim/multiplay/generic/short[53]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
484 { 10554, "sim/multiplay/generic/short[54]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
485 { 10555, "sim/multiplay/generic/short[55]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
486 { 10556, "sim/multiplay/generic/short[56]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
487 { 10557, "sim/multiplay/generic/short[57]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
488 { 10558, "sim/multiplay/generic/short[58]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
489 { 10559, "sim/multiplay/generic/short[59]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
490 { 10560, "sim/multiplay/generic/short[60]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
491 { 10561, "sim/multiplay/generic/short[61]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
492 { 10562, "sim/multiplay/generic/short[62]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
493 { 10563, "sim/multiplay/generic/short[63]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
494 { 10564, "sim/multiplay/generic/short[64]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
495 { 10565, "sim/multiplay/generic/short[65]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
496 { 10566, "sim/multiplay/generic/short[66]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
497 { 10567, "sim/multiplay/generic/short[67]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
498 { 10568, "sim/multiplay/generic/short[68]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
499 { 10569, "sim/multiplay/generic/short[69]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
500 { 10570, "sim/multiplay/generic/short[70]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
501 { 10571, "sim/multiplay/generic/short[71]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
502 { 10572, "sim/multiplay/generic/short[72]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
503 { 10573, "sim/multiplay/generic/short[73]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
504 { 10574, "sim/multiplay/generic/short[74]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
505 { 10575, "sim/multiplay/generic/short[75]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
506 { 10576, "sim/multiplay/generic/short[76]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
507 { 10577, "sim/multiplay/generic/short[77]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
508 { 10578, "sim/multiplay/generic/short[78]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
509 { 10579, "sim/multiplay/generic/short[79]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
510
511 { BOOLARRAY_BASE_1 + 0, "sim/multiplay/generic/bool[0]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
512 { BOOLARRAY_BASE_1 + 1, "sim/multiplay/generic/bool[1]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
513 { BOOLARRAY_BASE_1 + 2, "sim/multiplay/generic/bool[2]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
514 { BOOLARRAY_BASE_1 + 3, "sim/multiplay/generic/bool[3]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
515 { BOOLARRAY_BASE_1 + 4, "sim/multiplay/generic/bool[4]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
516 { BOOLARRAY_BASE_1 + 5, "sim/multiplay/generic/bool[5]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
517 { BOOLARRAY_BASE_1 + 6, "sim/multiplay/generic/bool[6]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
518 { BOOLARRAY_BASE_1 + 7, "sim/multiplay/generic/bool[7]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
519 { BOOLARRAY_BASE_1 + 8, "sim/multiplay/generic/bool[8]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
520 { BOOLARRAY_BASE_1 + 9, "sim/multiplay/generic/bool[9]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
521 { BOOLARRAY_BASE_1 + 10, "sim/multiplay/generic/bool[10]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
522 { BOOLARRAY_BASE_1 + 11, "sim/multiplay/generic/bool[11]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
523 { BOOLARRAY_BASE_1 + 12, "sim/multiplay/generic/bool[12]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
524 { BOOLARRAY_BASE_1 + 13, "sim/multiplay/generic/bool[13]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
525 { BOOLARRAY_BASE_1 + 14, "sim/multiplay/generic/bool[14]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
526 { BOOLARRAY_BASE_1 + 15, "sim/multiplay/generic/bool[15]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
527 { BOOLARRAY_BASE_1 + 16, "sim/multiplay/generic/bool[16]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
528 { BOOLARRAY_BASE_1 + 17, "sim/multiplay/generic/bool[17]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
529 { BOOLARRAY_BASE_1 + 18, "sim/multiplay/generic/bool[18]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
530 { BOOLARRAY_BASE_1 + 19, "sim/multiplay/generic/bool[19]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
531 { BOOLARRAY_BASE_1 + 20, "sim/multiplay/generic/bool[20]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
532 { BOOLARRAY_BASE_1 + 21, "sim/multiplay/generic/bool[21]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
533 { BOOLARRAY_BASE_1 + 22, "sim/multiplay/generic/bool[22]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
534 { BOOLARRAY_BASE_1 + 23, "sim/multiplay/generic/bool[23]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
535 { BOOLARRAY_BASE_1 + 24, "sim/multiplay/generic/bool[24]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
536 { BOOLARRAY_BASE_1 + 25, "sim/multiplay/generic/bool[25]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
537 { BOOLARRAY_BASE_1 + 26, "sim/multiplay/generic/bool[26]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
538 { BOOLARRAY_BASE_1 + 27, "sim/multiplay/generic/bool[27]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
539 { BOOLARRAY_BASE_1 + 28, "sim/multiplay/generic/bool[28]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
540 { BOOLARRAY_BASE_1 + 29, "sim/multiplay/generic/bool[29]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
541 { BOOLARRAY_BASE_1 + 30, "sim/multiplay/generic/bool[30]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
542
543 { BOOLARRAY_BASE_2 + 0, "sim/multiplay/generic/bool[31]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
544 { BOOLARRAY_BASE_2 + 1, "sim/multiplay/generic/bool[32]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
545 { BOOLARRAY_BASE_2 + 2, "sim/multiplay/generic/bool[33]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
546 { BOOLARRAY_BASE_2 + 3, "sim/multiplay/generic/bool[34]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
547 { BOOLARRAY_BASE_2 + 4, "sim/multiplay/generic/bool[35]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
548 { BOOLARRAY_BASE_2 + 5, "sim/multiplay/generic/bool[36]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
549 { BOOLARRAY_BASE_2 + 6, "sim/multiplay/generic/bool[37]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
550 { BOOLARRAY_BASE_2 + 7, "sim/multiplay/generic/bool[38]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
551 { BOOLARRAY_BASE_2 + 8, "sim/multiplay/generic/bool[39]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
552 { BOOLARRAY_BASE_2 + 9, "sim/multiplay/generic/bool[40]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
553 { BOOLARRAY_BASE_2 + 10, "sim/multiplay/generic/bool[41]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
554 // out of sequence between the block and the buffer becuase of a typo. repurpose the first as that way [72] will work
555 // correctly on older versions.
556 { BOOLARRAY_BASE_2 + 11, "sim/multiplay/generic/bool[91]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
557 { BOOLARRAY_BASE_2 + 12, "sim/multiplay/generic/bool[42]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
558 { BOOLARRAY_BASE_2 + 13, "sim/multiplay/generic/bool[43]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
559 { BOOLARRAY_BASE_2 + 14, "sim/multiplay/generic/bool[44]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
560 { BOOLARRAY_BASE_2 + 15, "sim/multiplay/generic/bool[45]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
561 { BOOLARRAY_BASE_2 + 16, "sim/multiplay/generic/bool[46]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
562 { BOOLARRAY_BASE_2 + 17, "sim/multiplay/generic/bool[47]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
563 { BOOLARRAY_BASE_2 + 18, "sim/multiplay/generic/bool[48]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
564 { BOOLARRAY_BASE_2 + 19, "sim/multiplay/generic/bool[49]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
565 { BOOLARRAY_BASE_2 + 20, "sim/multiplay/generic/bool[50]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
566 { BOOLARRAY_BASE_2 + 21, "sim/multiplay/generic/bool[51]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
567 { BOOLARRAY_BASE_2 + 22, "sim/multiplay/generic/bool[52]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
568 { BOOLARRAY_BASE_2 + 23, "sim/multiplay/generic/bool[53]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
569 { BOOLARRAY_BASE_2 + 24, "sim/multiplay/generic/bool[54]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
570 { BOOLARRAY_BASE_2 + 25, "sim/multiplay/generic/bool[55]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
571 { BOOLARRAY_BASE_2 + 26, "sim/multiplay/generic/bool[56]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
572 { BOOLARRAY_BASE_2 + 27, "sim/multiplay/generic/bool[57]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
573 { BOOLARRAY_BASE_2 + 28, "sim/multiplay/generic/bool[58]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
574 { BOOLARRAY_BASE_2 + 29, "sim/multiplay/generic/bool[59]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
575 { BOOLARRAY_BASE_2 + 30, "sim/multiplay/generic/bool[60]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
576
577 { BOOLARRAY_BASE_3 + 0, "sim/multiplay/generic/bool[61]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
578 { BOOLARRAY_BASE_3 + 1, "sim/multiplay/generic/bool[62]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
579 { BOOLARRAY_BASE_3 + 2, "sim/multiplay/generic/bool[63]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
580 { BOOLARRAY_BASE_3 + 3, "sim/multiplay/generic/bool[64]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
581 { BOOLARRAY_BASE_3 + 4, "sim/multiplay/generic/bool[65]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
582 { BOOLARRAY_BASE_3 + 5, "sim/multiplay/generic/bool[66]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
583 { BOOLARRAY_BASE_3 + 6, "sim/multiplay/generic/bool[67]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
584 { BOOLARRAY_BASE_3 + 7, "sim/multiplay/generic/bool[68]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
585 { BOOLARRAY_BASE_3 + 8, "sim/multiplay/generic/bool[69]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
586 { BOOLARRAY_BASE_3 + 9, "sim/multiplay/generic/bool[70]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
587 { BOOLARRAY_BASE_3 + 10, "sim/multiplay/generic/bool[71]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
588 // out of sequence between the block and the buffer becuase of a typo. repurpose the first as that way [72] will work
589 // correctly on older versions.
590 { BOOLARRAY_BASE_3 + 11, "sim/multiplay/generic/bool[92]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
591 { BOOLARRAY_BASE_3 + 12, "sim/multiplay/generic/bool[72]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
592 { BOOLARRAY_BASE_3 + 13, "sim/multiplay/generic/bool[73]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
593 { BOOLARRAY_BASE_3 + 14, "sim/multiplay/generic/bool[74]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
594 { BOOLARRAY_BASE_3 + 15, "sim/multiplay/generic/bool[75]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
595 { BOOLARRAY_BASE_3 + 16, "sim/multiplay/generic/bool[76]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
596 { BOOLARRAY_BASE_3 + 17, "sim/multiplay/generic/bool[77]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
597 { BOOLARRAY_BASE_3 + 18, "sim/multiplay/generic/bool[78]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
598 { BOOLARRAY_BASE_3 + 19, "sim/multiplay/generic/bool[79]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
599 { BOOLARRAY_BASE_3 + 20, "sim/multiplay/generic/bool[80]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
600 { BOOLARRAY_BASE_3 + 21, "sim/multiplay/generic/bool[81]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
601 { BOOLARRAY_BASE_3 + 22, "sim/multiplay/generic/bool[82]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
602 { BOOLARRAY_BASE_3 + 23, "sim/multiplay/generic/bool[83]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
603 { BOOLARRAY_BASE_3 + 24, "sim/multiplay/generic/bool[84]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
604 { BOOLARRAY_BASE_3 + 25, "sim/multiplay/generic/bool[85]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
605 { BOOLARRAY_BASE_3 + 26, "sim/multiplay/generic/bool[86]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
606 { BOOLARRAY_BASE_3 + 27, "sim/multiplay/generic/bool[87]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
607 { BOOLARRAY_BASE_3 + 28, "sim/multiplay/generic/bool[88]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
608 { BOOLARRAY_BASE_3 + 29, "sim/multiplay/generic/bool[89]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
609 { BOOLARRAY_BASE_3 + 30, "sim/multiplay/generic/bool[90]", simgear::props::BOOL, TT_BOOLARRAY, V1_1_2_PROP_ID, NULL, NULL },
610
611
612 { V2018_1_BASE + 0, "sim/multiplay/mp-clock-mode", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
613 // Direct support for emesary bridge properties. This is mainly to ensure that these properties do not overlap with the string
614 // properties; although the emesary bridge can use any string property.
615 { EMESARYBRIDGE_BASE + 0, "sim/multiplay/emesary/bridge[0]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
616 { EMESARYBRIDGE_BASE + 1, "sim/multiplay/emesary/bridge[1]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
617 { EMESARYBRIDGE_BASE + 2, "sim/multiplay/emesary/bridge[2]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
618 { EMESARYBRIDGE_BASE + 3, "sim/multiplay/emesary/bridge[3]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
619 { EMESARYBRIDGE_BASE + 4, "sim/multiplay/emesary/bridge[4]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
620 { EMESARYBRIDGE_BASE + 5, "sim/multiplay/emesary/bridge[5]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
621 { EMESARYBRIDGE_BASE + 6, "sim/multiplay/emesary/bridge[6]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
622 { EMESARYBRIDGE_BASE + 7, "sim/multiplay/emesary/bridge[7]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
623 { EMESARYBRIDGE_BASE + 8, "sim/multiplay/emesary/bridge[8]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
624 { EMESARYBRIDGE_BASE + 9, "sim/multiplay/emesary/bridge[9]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
625 { EMESARYBRIDGE_BASE + 10, "sim/multiplay/emesary/bridge[10]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
626 { EMESARYBRIDGE_BASE + 11, "sim/multiplay/emesary/bridge[11]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
627 { EMESARYBRIDGE_BASE + 12, "sim/multiplay/emesary/bridge[12]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
628 { EMESARYBRIDGE_BASE + 13, "sim/multiplay/emesary/bridge[13]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
629 { EMESARYBRIDGE_BASE + 14, "sim/multiplay/emesary/bridge[14]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
630 { EMESARYBRIDGE_BASE + 15, "sim/multiplay/emesary/bridge[15]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
631 { EMESARYBRIDGE_BASE + 16, "sim/multiplay/emesary/bridge[16]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
632 { EMESARYBRIDGE_BASE + 17, "sim/multiplay/emesary/bridge[17]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
633 { EMESARYBRIDGE_BASE + 18, "sim/multiplay/emesary/bridge[18]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
634 { EMESARYBRIDGE_BASE + 19, "sim/multiplay/emesary/bridge[19]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
635 { EMESARYBRIDGE_BASE + 20, "sim/multiplay/emesary/bridge[20]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
636 { EMESARYBRIDGE_BASE + 21, "sim/multiplay/emesary/bridge[21]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
637 { EMESARYBRIDGE_BASE + 22, "sim/multiplay/emesary/bridge[22]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
638 { EMESARYBRIDGE_BASE + 23, "sim/multiplay/emesary/bridge[23]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
639 { EMESARYBRIDGE_BASE + 24, "sim/multiplay/emesary/bridge[24]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
640 { EMESARYBRIDGE_BASE + 25, "sim/multiplay/emesary/bridge[25]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
641 { EMESARYBRIDGE_BASE + 26, "sim/multiplay/emesary/bridge[26]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
642 { EMESARYBRIDGE_BASE + 27, "sim/multiplay/emesary/bridge[27]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
643 { EMESARYBRIDGE_BASE + 28, "sim/multiplay/emesary/bridge[28]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
644 { EMESARYBRIDGE_BASE + 29, "sim/multiplay/emesary/bridge[29]", simgear::props::STRING, TT_ASIS, V1_1_2_PROP_ID, NULL, NULL },
645
646 // To allow the bridge to identify itself and allow quick filtering based on type/ID.
647 { EMESARYBRIDGETYPE_BASE + 0, "sim/multiplay/emesary/bridge-type[0]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
648 { EMESARYBRIDGETYPE_BASE + 1, "sim/multiplay/emesary/bridge-type[1]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
649 { EMESARYBRIDGETYPE_BASE + 2, "sim/multiplay/emesary/bridge-type[2]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
650 { EMESARYBRIDGETYPE_BASE + 3, "sim/multiplay/emesary/bridge-type[3]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
651 { EMESARYBRIDGETYPE_BASE + 4, "sim/multiplay/emesary/bridge-type[4]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
652 { EMESARYBRIDGETYPE_BASE + 5, "sim/multiplay/emesary/bridge-type[5]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
653 { EMESARYBRIDGETYPE_BASE + 6, "sim/multiplay/emesary/bridge-type[6]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
654 { EMESARYBRIDGETYPE_BASE + 7, "sim/multiplay/emesary/bridge-type[7]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
655 { EMESARYBRIDGETYPE_BASE + 8, "sim/multiplay/emesary/bridge-type[8]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
656 { EMESARYBRIDGETYPE_BASE + 9, "sim/multiplay/emesary/bridge-type[9]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
657 { EMESARYBRIDGETYPE_BASE + 10, "sim/multiplay/emesary/bridge-type[10]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
658 { EMESARYBRIDGETYPE_BASE + 11, "sim/multiplay/emesary/bridge-type[11]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
659 { EMESARYBRIDGETYPE_BASE + 12, "sim/multiplay/emesary/bridge-type[12]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
660 { EMESARYBRIDGETYPE_BASE + 13, "sim/multiplay/emesary/bridge-type[13]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
661 { EMESARYBRIDGETYPE_BASE + 14, "sim/multiplay/emesary/bridge-type[14]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
662 { EMESARYBRIDGETYPE_BASE + 15, "sim/multiplay/emesary/bridge-type[15]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
663 { EMESARYBRIDGETYPE_BASE + 16, "sim/multiplay/emesary/bridge-type[16]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
664 { EMESARYBRIDGETYPE_BASE + 17, "sim/multiplay/emesary/bridge-type[17]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
665 { EMESARYBRIDGETYPE_BASE + 18, "sim/multiplay/emesary/bridge-type[18]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
666 { EMESARYBRIDGETYPE_BASE + 19, "sim/multiplay/emesary/bridge-type[19]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
667 { EMESARYBRIDGETYPE_BASE + 20, "sim/multiplay/emesary/bridge-type[20]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
668 { EMESARYBRIDGETYPE_BASE + 21, "sim/multiplay/emesary/bridge-type[21]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
669 { EMESARYBRIDGETYPE_BASE + 22, "sim/multiplay/emesary/bridge-type[22]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
670 { EMESARYBRIDGETYPE_BASE + 23, "sim/multiplay/emesary/bridge-type[23]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
671 { EMESARYBRIDGETYPE_BASE + 24, "sim/multiplay/emesary/bridge-type[24]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
672 { EMESARYBRIDGETYPE_BASE + 25, "sim/multiplay/emesary/bridge-type[25]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
673 { EMESARYBRIDGETYPE_BASE + 26, "sim/multiplay/emesary/bridge-type[26]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
674 { EMESARYBRIDGETYPE_BASE + 27, "sim/multiplay/emesary/bridge-type[27]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
675 { EMESARYBRIDGETYPE_BASE + 28, "sim/multiplay/emesary/bridge-type[28]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
676 { EMESARYBRIDGETYPE_BASE + 29, "sim/multiplay/emesary/bridge-type[29]", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
677
678 { FALLBACK_MODEL_ID, "sim/model/fallback-model-index", simgear::props::INT, TT_SHORTINT, V1_1_2_PROP_ID, NULL, NULL },
679 { V2019_3_BASE, "sim/multiplay/comm-transmit-frequency-hz", simgear::props::INT, TT_INT, V1_1_2_PROP_ID, NULL, NULL },
680 { V2019_3_BASE+1, "sim/multiplay/comm-transmit-power-norm", simgear::props::INT, TT_SHORT_FLOAT_NORM , V1_1_2_PROP_ID, NULL, NULL },
681 // Add new MP properties here
682 { V2020_4_BASE, "instrumentation/transponder/mach-number", simgear::props::FLOAT, TT_SHORT_FLOAT_4, V1_1_2_PROP_ID, NULL, NULL },
683};
684/*
685 * For the 2017.x version 2 protocol the properties are sent in two partitions,
686 * the first of these is a V1 protocol packet (which should be fine with all clients), and a V2 partition
687 * which will contain the newly supported shortint and fixed string encoding schemes.
688 * This is to possibly allow for easier V1/V2 conversion - as the packet can simply be truncated at the
689 * first V2 property based on ID.
690 */
691const int MAX_PARTITIONS = 2;
692const unsigned int numProperties = (sizeof(sIdPropertyList) / sizeof(sIdPropertyList[0]));
693
694// Look up a property ID using binary search.
695namespace
696{
697 struct ComparePropertyId
698 {
699 bool operator()(const IdPropertyList& lhs,
700 const IdPropertyList& rhs)
701 {
702 return lhs.id < rhs.id;
703 }
704 bool operator()(const IdPropertyList& lhs,
705 unsigned id)
706 {
707 return lhs.id < id;
708 }
709 bool operator()(unsigned id,
710 const IdPropertyList& rhs)
711 {
712 return id < rhs.id;
713 }
714 };
715}
716
717const IdPropertyList* findProperty(unsigned id)
718{
719 std::pair<const IdPropertyList*, const IdPropertyList*> result
720 = std::equal_range(sIdPropertyList, sIdPropertyList + numProperties, id,
721 ComparePropertyId());
722 if (result.first == result.second) {
723 return 0;
724 } else {
725 return result.first;
726 }
727}
728
729namespace
730{
731 bool verifyProperties(const xdr_data_t* data, const xdr_data_t* end)
732 {
733 using namespace simgear;
734 const xdr_data_t* xdr = data;
735 while (xdr < end) {
736 unsigned id = XDR_decode_uint32(*xdr);
737 const IdPropertyList* plist = findProperty(id);
738
739 if (plist) {
740 xdr++;
741 // How we decode the remainder of the property depends on the type
742 switch (plist->type) {
743 case props::INT:
744 case props::BOOL:
745 case props::LONG:
746 xdr++;
747 break;
748 case props::FLOAT:
749 case props::DOUBLE:
750 {
751 float val = XDR_decode_float(*xdr);
752 if (SGMisc<float>::isNaN(val))
753 return false;
754 xdr++;
755 break;
756 }
757 case props::STRING:
758 case props::UNSPECIFIED:
759 {
760 // String is complicated. It consists of
761 // The length of the string
762 // The string itself
763 // Padding to the nearest 4-bytes.
764 // XXX Yes, each byte is padded out to a word! Too late
765 // to change...
766 uint32_t length = XDR_decode_uint32(*xdr);
767 xdr++;
768 // Old versions truncated the string but left the length
769 // unadjusted.
770 if (length > MAX_TEXT_SIZE)
771 length = MAX_TEXT_SIZE;
772 xdr += length;
773 // Now handle the padding
774 while ((length % 4) != 0)
775 {
776 xdr++;
777 length++;
778 //cout << "0";
779 }
780 }
781 break;
782 default:
783 // cerr << "Unknown Prop type " << id << " " << type << "\n";
784 xdr++;
785 break;
786 }
787 }
788 else {
789 // give up; this is a malformed property list.
790 return false;
791 }
792 }
793 return true;
794 }
795}
796
797class MPPropertyListener : public SGPropertyChangeListener
798{
799public:
801 _multiplay(mp)
802 {
803 }
804
805 virtual void childAdded(SGPropertyNode*, SGPropertyNode*)
806 {
807 _multiplay->setPropertiesChanged();
808 }
809private:
810 FGMultiplayMgr* _multiplay;
811};
812
813
814/*
815* 2018.1 introduces a new minimal generic packet concept.
816* This allows a model to choose to only transmit a few essential properties, which leaves the packet at around 380 bytes.
817* The rest of the packet can then be used for bridged Emesary notifications, which over allow much more control
818* at the model level, including different notifications being sent.
819* see $FGData/Nasal/Notifications.nas and $FGData/Nasal/emesary_mp_bridge.nas
820* The property /sim/multiplay/transmit-filter-property-base can be set to 1 to turn off all of the standard properties and only send generics.
821* or this property can be set to a number greater than 1 (e.g. 12000) to only transmit properties based on their index. It is a simple filtering
822* mechanism.
823* - in both cases the chat and transponder properties will be transmitted for compatibility.
824*/
825static inline bool IsIncludedInPacket(int filter_base, int property_id)
826{
827 if (filter_base == 1) // transmit-property-base of 1 is equivalent to only generics.
828 return property_id >= 10002
829 || (property_id == V2018_1_BASE) // MP time sync
830 || (property_id >= 1500 && property_id < 1600); // include chat and generic properties.
831 else
832 return property_id >= filter_base
833 || (property_id == V2018_1_BASE) // MP time sync
834 || (property_id >= 1500 && property_id < 1600); // include chat and generic properties.
835}
836
838//
839// handle command "multiplayer-connect"
840// args:
841// server: servername to connect (mandatory)
842// txport: outgoing port number (default: 5000)
843// rxport: incoming port number (default: 5000)
845static bool do_multiplayer_connect(const SGPropertyNode * arg, SGPropertyNode * root) {
846 const auto self = globals->get_subsystem<FGMultiplayMgr>();
847 if (!self) {
848 SG_LOG(SG_NETWORK, SG_WARN, "Multiplayer subsystem not available.");
849 return false;
850 }
851
852 string servername = arg->getStringValue("servername", "");
853 if (servername.empty()) {
854 SG_LOG(SG_NETWORK, SG_WARN,
855 "do_multiplayer.connect: no server name given, command ignored.");
856 return false;
857 }
858 int port = arg->getIntValue("rxport", -1);
859 if (port > 0 && port <= 0xffff) {
860 fgSetInt("/sim/multiplay/rxport", port);
861 }
862
863 port = arg->getIntValue("txport", -1);
864 if (port > 0 && port <= 0xffff) {
865 fgSetInt("/sim/multiplay/txport", port);
866 }
867
868 servername = servername.substr(0, servername.find_first_of(' '));
869 fgSetString("/sim/multiplay/txhost", servername);
870 self->reinit();
871 return true;
872}
873
875//
876// handle command "multiplayer-disconnect"
877// disconnect args:
878// none
880static bool do_multiplayer_disconnect(const SGPropertyNode * arg, SGPropertyNode * root) {
881 const auto self = globals->get_subsystem<FGMultiplayMgr>();
882 if (!self) {
883 SG_LOG(SG_NETWORK, SG_WARN, "Multiplayer subsystem not available.");
884 return false;
885 }
886
887 fgSetString("/sim/multiplay/txhost", "");
888 self->reinit();
889 return true;
890}
891
893//
894// handle command "multiplayer-refreshserverlist"
895// refreshserverlist args:
896// none
897//
899
900static bool
901do_multiplayer_refreshserverlist (const SGPropertyNode * arg, SGPropertyNode * root)
902{
903 using namespace simgear;
904
905 const auto self = globals->get_subsystem<FGMultiplayMgr>();
906 if (!self) {
907 SG_LOG(SG_NETWORK, SG_WARN, "Multiplayer subsystem not available.");
908 return false;
909 }
910
911 // MPServerResolver implementation to fill the mp server list
912 // deletes itself when done
913 class MyMPServerResolver : public MPServerResolver {
914 public:
915 MyMPServerResolver () :
917 {
918 setTarget (fgGetNode ("/sim/multiplay/server-list", true));
919 setDnsName (fgGetString ("/sim/multiplay/dns/query-dn", "flightgear.org"));
920 setService (fgGetString ("/sim/multiplay/dns/query-srv-service", "fgms"));
921 setProtocol (fgGetString ("/sim/multiplay/dns/query-srv-protocol", "udp"));
922 _completeNode->setBoolValue (false);
923 _failureNode->setBoolValue (false);
924 }
925
926 ~MyMPServerResolver ()
927 {
928 }
929
930 virtual void
931 onSuccess ()
932 {
933 SG_LOG(SG_NETWORK, SG_DEBUG, "MyMPServerResolver: trigger success");
934 _completeNode->setBoolValue (true);
935 delete this;
936 }
937 virtual void
938 onFailure ()
939 {
940 SG_LOG(SG_NETWORK, SG_DEBUG, "MyMPServerResolver: trigger failure");
941 _failureNode->setBoolValue (true);
942 delete this;
943 }
944
945 private:
946 SGPropertyNode *_completeNode = fgGetNode ("/sim/multiplay/got-servers", true);
947 SGPropertyNode *_failureNode = fgGetNode ("/sim/multiplay/get-servers-failure", true);
948 };
949
950 MyMPServerResolver * mpServerResolver = new MyMPServerResolver ();
951 mpServerResolver->run ();
952 return true;
953}
954
956//
957// CPDLC commands: cpdlc-connect, cpdlc-send-msg, cpdlc-disconnect
958//
960
961static bool do_cpdlc_connect(const SGPropertyNode* arg, SGPropertyNode* root)
962{
963 const auto self = globals->get_subsystem<FGMultiplayMgr>();
964 if (!self) {
965 SG_LOG(SG_NETWORK, SG_WARN, "Multiplayer subsystem not available.");
966 return false;
967 }
968
969 // check for atc argument
970 std::string authority = arg->getStringValue("atc");
971 // otherwise see if we got a property name to read out
972 if (authority.empty()) {
973 std::string name = arg->getStringValue("property");
974 if (!name.empty()) {
975 SGPropertyNode* pNode = globals->get_props()->getNode(name);
976 if (!pNode) { return false; }
977 authority = pNode->getStringValue();
978 }
979 }
980
981 if (self->getCPDLC()) {
982 return self->getCPDLC()->connect(authority);
983 }
984 return false;
985}
986
987static bool do_cpdlc_send_msg(const SGPropertyNode* arg, SGPropertyNode* root)
988{
989 const auto self = globals->get_subsystem<FGMultiplayMgr>();
990 if (!self) {
991 SG_LOG(SG_NETWORK, SG_WARN, "Multiplayer subsystem not available.");
992 return false;
993 }
994
995 // check for message argument
996 std::string message = arg->getStringValue("message");
997 // otherwise see if we got a property name to read out
998 if (message.empty()) {
999 std::string name = arg->getStringValue("property");
1000 if (!name.empty()) {
1001 SGPropertyNode* pNode = globals->get_props()->getNode(name);
1002 if (!pNode) { return false; }
1003 message = pNode->getStringValue();
1004 }
1005 }
1006 if (self->getCPDLC()) {
1007 return self->getCPDLC()->send(message);
1008 }
1009 return false;
1010}
1011
1012static bool do_cpdlc_next_msg(const SGPropertyNode* arg, SGPropertyNode* root)
1013{
1014 const auto self = globals->get_subsystem<FGMultiplayMgr>();
1015 if (!self) {
1016 SG_LOG(SG_NETWORK, SG_WARN, "Multiplayer subsystem not available.");
1017 return false;
1018 }
1019 if (self->getCPDLC()) self->getCPDLC()->getMessage();
1020 return true;
1021}
1022
1023static bool do_cpdlc_disconnect(const SGPropertyNode* arg, SGPropertyNode* root)
1024{
1025 const auto self = globals->get_subsystem<FGMultiplayMgr>();
1026 if (!self) {
1027 SG_LOG(SG_NETWORK, SG_WARN, "Multiplayer subsystem not available.");
1028 return false;
1029 }
1030 if (self->getCPDLC()) self->getCPDLC()->disconnect();
1031 return true;
1032}
1033
1034
1036//
1037// MultiplayMgr constructor
1038//
1041{
1042 mPropertiesChanged = false;
1043 mInitialised = false;
1044 mHaveServer = false;
1045 mListener = NULL;
1046 globals->get_commands()->addCommand("multiplayer-connect", do_multiplayer_connect);
1047 globals->get_commands()->addCommand("multiplayer-disconnect", do_multiplayer_disconnect);
1048 globals->get_commands()->addCommand("multiplayer-refreshserverlist", do_multiplayer_refreshserverlist);
1049
1050 globals->get_commands()->addCommand("cpdlc-connect", do_cpdlc_connect);
1051 globals->get_commands()->addCommand("cpdlc-send", do_cpdlc_send_msg);
1052 globals->get_commands()->addCommand("cpdlc-next-message", do_cpdlc_next_msg);
1053 globals->get_commands()->addCommand("cpdlc-disconnect", do_cpdlc_disconnect);
1054
1055
1056 pXmitLen = fgGetNode("/sim/multiplay/last-xmit-packet-len", true);
1057 pProtocolVersion = fgGetNode("/sim/multiplay/protocol-version", true);
1058 pMultiPlayDebugLevel = fgGetNode("/sim/multiplay/debug-level", true);
1059 pMultiPlayTransmitPropertyBase = fgGetNode("/sim/multiplay/transmit-filter-property-base", true);
1060 pMultiPlayRange = fgGetNode("/sim/multiplay/visibility-range-nm", true);
1061 pMultiPlayRange->setIntValue(100);
1062 pReplayState = fgGetNode("/sim/replay/replay-state", true);
1063 pLogRawSpeedMultiplayer = fgGetNode("/sim/replay/log-raw-speed-multiplayer", true);
1064
1065
1066} // FGMultiplayMgr::FGMultiplayMgr()
1067
1068
1070//
1071// MultiplayMgr destructor
1072//
1075{
1076 globals->get_commands()->removeCommand("multiplayer-connect");
1077 globals->get_commands()->removeCommand("multiplayer-disconnect");
1078 globals->get_commands()->removeCommand("multiplayer-refreshserverlist");
1079
1080 globals->get_commands()->removeCommand("cpdlc-connect");
1081 globals->get_commands()->removeCommand("cpdlc-send");
1082 globals->get_commands()->removeCommand("cpdlc-next-message");
1083 globals->get_commands()->removeCommand("cpdlc-disconnect");
1084} // FGMultiplayMgr::~FGMultiplayMgr()
1085
1086
1088//
1089// Initialise object
1090//
1092void
1094{
1096 // Initialise object if not already done
1098 if (mInitialised) {
1099 SG_LOG(SG_NETWORK, SG_WARN, "FGMultiplayMgr::init - already initialised");
1100 return;
1101 }
1102
1103 SGPropertyNode* propOnline = fgGetNode("/sim/multiplay/online", true);
1104 propOnline->setBoolValue(false);
1105 propOnline->setAttribute(SGPropertyNode::PRESERVE, true);
1106
1108 // Set members from property values
1110 short rxPort = fgGetInt("/sim/multiplay/rxport");
1111 string rxAddress = fgGetString("/sim/multiplay/rxhost");
1112 short txPort = fgGetInt("/sim/multiplay/txport", 5000);
1113 string txAddress = fgGetString("/sim/multiplay/txhost");
1114 bool broadcast = fgGetBool("/sim/multiplay/broadcast");
1115
1116 int txRateHz = fgGetInt("/sim/multiplay/tx-rate-hz", 10);
1117 if (txRateHz < 1) {
1118 txRateHz = 1;
1119 }
1120
1121 mDt = 1.0 / txRateHz;
1122 mNextTransmitTime = sg_random() * mDt;
1123 SG_LOG(SG_NETWORK, SG_DEBUG, "initial MP time: " << mNextTransmitTime);
1124
1125 mCallsign = fgGetString("/sim/multiplay/callsign");
1126 fgGetNode("/sim/multiplay/callsign", true)->setAttribute(SGPropertyNode::PRESERVE, true);
1127
1128 if ((!txAddress.empty()) && (txAddress!="0")) {
1129 mServer.set(txAddress.c_str(), txPort);
1130 if (strncmp (mServer.getHost(), "0.0.0.0", 8) == 0) {
1131 mHaveServer = false;
1132 SG_LOG(SG_NETWORK, SG_ALERT,
1133 "Cannot enable multiplayer mode: resolving MP server address '"
1134 << txAddress << "' failed.");
1135 return;
1136 } else {
1137 SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr - have server");
1138 mHaveServer = true;
1139 }
1140 if (rxPort <= 0)
1141 rxPort = txPort;
1142 } else {
1143 SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr - multiplayer mode disabled (no MP server specified).");
1144 return;
1145 }
1146
1147 if (rxPort <= 0) {
1148 SG_LOG(SG_NETWORK, SG_ALERT,
1149 "Cannot enable multiplayer mode: No receiver port specified.");
1150 return;
1151 }
1152 if (mCallsign.empty())
1153 mCallsign = "JohnDoe"; // FIXME: use getpwuid
1154 SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txaddress= "<<txAddress);
1155 SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-txport= "<<txPort );
1156 SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxaddress="<<rxAddress );
1157 SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-rxport= "<<rxPort);
1158 SG_LOG(SG_NETWORK,SG_INFO,"FGMultiplayMgr::init-callsign= "<<mCallsign);
1159
1160 mSocket.reset(new simgear::Socket());
1161 if (!mSocket->open(false)) {
1162 SG_LOG( SG_NETWORK, SG_ALERT,
1163 "Cannot enable multiplayer mode: creating data socket failed." );
1164 return;
1165 }
1166 mSocket->setBlocking(false);
1167 if (broadcast) {
1168 mSocket->setBroadcast(true);
1169 }
1170 if (mSocket->bind(rxAddress.c_str(), rxPort) != 0) {
1171 SG_LOG( SG_NETWORK, SG_ALERT,
1172 "Cannot enable multiplayer mode: binding receive socket failed. "
1173 << strerror(errno) << "(errno " << errno << ")");
1174 return;
1175 }
1176
1177 mPropertiesChanged = true;
1178 mListener = new MPPropertyListener(this);
1179 globals->get_props()->addChangeListener(mListener, false);
1180
1181 fgSetBool("/sim/multiplay/online", true);
1182 mInitialised = true;
1183
1184
1185 SG_LOG(SG_NETWORK, SG_MANDATORY_INFO, "Multiplayer mode active");
1186 flightgear::addSentryTag("mp", "active");
1187
1188 if (!fgGetBool("/sim/ai/enabled"))
1189 {
1190 // multiplayer depends on AI module
1191 fgSetBool("/sim/ai/enabled", true);
1192 }
1193
1194 // MP IRC CONNECTION SETUP
1197 SG_LOG(SG_NETWORK, SG_DEBUG, "Creating socket to MP IRC service " + host + " on port " + port);
1198
1199 _mpirc = std::make_unique<IRCConnection>(MPIRC_NICK_PREFIX + mCallsign, host, port);
1200 _mpirc->setupProperties("/network/mpirc/");
1201
1202 _cpdlc = std::make_unique<CPDLCManager>(_mpirc.get());
1203} // FGMultiplayMgr::init()
1204
1205
1207//
1208// Closes and deletes the local player object. Closes
1209// and deletes the tx socket. Resets the object state to unitialised.
1210//
1212void
1214{
1215 fgSetBool("/sim/multiplay/online", false);
1216
1217 if (mSocket.get()) {
1218 mSocket->close();
1219 mSocket.reset();
1220 }
1221
1222 MultiPlayerMap::iterator it = mMultiPlayerMap.begin(),
1223 end = mMultiPlayerMap.end();
1224 for (; it != end; ++it) {
1225 it->second->setDie(true);
1226 }
1227 mMultiPlayerMap.clear();
1228
1229 if (mListener) {
1230 globals->get_props()->removeChangeListener(mListener);
1231 delete mListener;
1232 mListener = NULL;
1233 }
1234
1235 mInitialised = false;
1236
1237 if (_cpdlc) _cpdlc->disconnect();
1238 if (_mpirc) _mpirc->quit();
1239
1240 _cpdlc.reset();
1241 _mpirc.reset();
1242} // FGMultiplayMgr::shutdown(void)
1243
1244
1245void
1247{
1248 shutdown();
1249 init();
1250}
1251
1253//
1254// Description: Sends the position data for the local position.
1255//
1257
1262{
1264 {
1265 memset(&Msg, 0, sizeof(Msg));
1266 }
1267
1269 {
1270 return &Header;
1271 }
1272
1273 const T_MsgHdr* msgHdr() const
1274 {
1275 return reinterpret_cast<const T_MsgHdr*>(&Header);
1276 }
1277
1279 {
1280 return reinterpret_cast<T_PositionMsg*>(Msg + sizeof(T_MsgHdr));
1281 }
1282
1283 const T_PositionMsg* posMsg() const
1284 {
1285 return reinterpret_cast<const T_PositionMsg*>(Msg + sizeof(T_MsgHdr));
1286 }
1287
1289 {
1290 return reinterpret_cast<xdr_data_t*>(Msg + sizeof(T_MsgHdr)
1291 + sizeof(T_PositionMsg));
1292 }
1293
1294 const xdr_data_t* properties() const
1295 {
1296 return reinterpret_cast<const xdr_data_t*>(Msg + sizeof(T_MsgHdr)
1297 + sizeof(T_PositionMsg));
1298 }
1299
1303 {
1304 return reinterpret_cast<xdr_data_t*>(Msg + MAX_PACKET_SIZE);
1305 };
1306
1307 const xdr_data_t* propsEnd() const
1308 {
1309 return reinterpret_cast<const xdr_data_t*>(Msg + MAX_PACKET_SIZE);
1310 };
1311
1316 {
1317 return reinterpret_cast<xdr_data_t*>(Msg + Header.MsgLen);
1318 }
1319
1321 {
1322 return reinterpret_cast<const xdr_data_t*>(Msg + Header.MsgLen);
1323 }
1324
1328};
1329
1330bool
1331FGMultiplayMgr::isSane(const FGExternalMotionData& motionInfo)
1332{
1333 // check for corrupted data (NaNs)
1334 bool isCorrupted = false;
1335 isCorrupted |= ((SGMisc<double>::isNaN(motionInfo.time )) ||
1336 (SGMisc<double>::isNaN(motionInfo.lag )) ||
1337 (osg::isNaN(motionInfo.orientation(3) )));
1338 for (unsigned i = 0; (i < 3)&&(!isCorrupted); ++i)
1339 {
1340 isCorrupted |= ((osg::isNaN(motionInfo.position(i) ))||
1341 (osg::isNaN(motionInfo.orientation(i) ))||
1342 (osg::isNaN(motionInfo.linearVel(i)) )||
1343 (osg::isNaN(motionInfo.angularVel(i)) )||
1344 (osg::isNaN(motionInfo.linearAccel(i)) )||
1345 (osg::isNaN(motionInfo.angularAccel(i)) ));
1346 }
1347 return !isCorrupted;
1348}
1349
1350void
1351FGMultiplayMgr::SendMyPosition(const FGExternalMotionData& motionInfo)
1352{
1353 int protocolToUse = getProtocolToUse();
1354 int transmitFilterPropertyBase = pMultiPlayTransmitPropertyBase->getIntValue();
1355 if ((! mInitialised) || (! mHaveServer))
1356 return;
1357
1358 if (! mHaveServer) {
1359 SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::SendMyPosition - no server");
1360 return;
1361 }
1362
1363 if (!isSane(motionInfo))
1364 {
1365 // Current local data is invalid (NaN), so stop MP transmission.
1366 // => Be nice to older FG versions (no NaN checks) and don't waste bandwidth.
1367 SG_LOG(SG_NETWORK, SG_ALERT, "FGMultiplayMgr::SendMyPosition - "
1368 << "Local data is invalid (NaN). Data not transmitted.");
1369 return;
1370 }
1371
1372 static MsgBuf msgBuf;
1373 static unsigned msgLen = 0;
1374 T_PositionMsg* PosMsg = msgBuf.posMsg();
1375
1376 /*
1377 * This is to provide a level of compatibility with the new V2 packets.
1378 * By setting padding it will force older clients to use verify properties which will
1379 * bail out if there are any unknown props
1380 * MP2017(V2) (for V1 clients) will always have an unknown property because V2 transmits
1381 * the protocol version as the very first property as a shortint.
1382 */
1383 if (protocolToUse > 1)
1385 else
1386 PosMsg->pad = 0;
1387
1388 strncpy(PosMsg->Model, fgGetString("/sim/model/path").c_str(), MAX_MODEL_NAME_LEN-1);
1389 PosMsg->Model[MAX_MODEL_NAME_LEN - 1] = '\0';
1390 if (fgGetBool("/sim/freeze/replay-state", true)&&
1391 fgGetBool("/sim/multiplay/freeze-on-replay",true))
1392 {
1393 // do not send position updates during replay
1394 for (unsigned i = 0 ; i < 3; ++i)
1395 {
1396 // no movement during replay
1397 PosMsg->linearVel[i] = XDR_encode_float (0.0);
1398 PosMsg->angularVel[i] = XDR_encode_float (0.0);
1399 PosMsg->linearAccel[i] = XDR_encode_float (0.0);
1400 PosMsg->angularAccel[i] = XDR_encode_float (0.0);
1401 }
1402 // all other data remains unchanged (resend last state)
1403 }
1404 else
1405 {
1406 PosMsg->time = XDR_encode_double (motionInfo.time);
1407 PosMsg->lag = XDR_encode_double (motionInfo.lag);
1408 for (unsigned i = 0 ; i < 3; ++i)
1409 PosMsg->position[i] = XDR_encode_double (motionInfo.position(i));
1410 SGVec3f angleAxis;
1411 motionInfo.orientation.getAngleAxis(angleAxis);
1412 for (unsigned i = 0 ; i < 3; ++i)
1413 PosMsg->orientation[i] = XDR_encode_float (angleAxis(i));
1414 if (fgGetBool("/sim/crashed",true))
1415 {
1416 for (unsigned i = 0 ; i < 3; ++i)
1417 {
1418 // no speed or acceleration sent when crashed, for better mp patch
1419 PosMsg->linearVel[i] = XDR_encode_float (0.0);
1420 PosMsg->angularVel[i] = XDR_encode_float (0.0);
1421 PosMsg->linearAccel[i] = XDR_encode_float (0.0);
1422 PosMsg->angularAccel[i] = XDR_encode_float (0.0);
1423 }
1424 }
1425 else
1426 {
1427 //including speed up time in velocity and acceleration
1428 double timeAccel = fgGetDouble("/sim/speed-up");
1429 for (unsigned i = 0 ; i < 3; ++i)
1430 PosMsg->linearVel[i] = XDR_encode_float (motionInfo.linearVel(i) * timeAccel);
1431 for (unsigned i = 0 ; i < 3; ++i)
1432 PosMsg->angularVel[i] = XDR_encode_float (motionInfo.angularVel(i) * timeAccel);
1433 for (unsigned i = 0 ; i < 3; ++i)
1434 PosMsg->linearAccel[i] = XDR_encode_float (motionInfo.linearAccel(i) * timeAccel * timeAccel);
1435 for (unsigned i = 0 ; i < 3; ++i)
1436 PosMsg->angularAccel[i] = XDR_encode_float (motionInfo.angularAccel(i) * timeAccel * timeAccel);
1437 }
1438 xdr_data_t* ptr = msgBuf.properties();
1439 xdr_data_t* data = ptr;
1440
1441 xdr_data_t* msgEnd = msgBuf.propsEnd();
1442
1443 //if (pMultiPlayDebugLevel->getIntValue())
1444 // msgBuf.zero();
1445 struct BoolArrayBuffer boolBuffer[MAX_BOOL_BUFFERS];
1446 memset(&boolBuffer, 0, sizeof(boolBuffer));
1447
1448 /* Read BOOLARRAY properties.
1449 *
1450 * All properties contained in a bool array must be read before adding it to the packet.
1451 * Earlier versions of this code read the properties in the main loop below,
1452 * and added the bool array at the very end of the packet.
1453 * This causes bool arrays to break whenever a new MP property is added:
1454 * older client will stop reading the packet when encountering the new property,
1455 * and never get to the bool array.
1456 *
1457 * Instead, read all properties early and send the array in the main loop.
1458 */
1459
1460 std::vector<FGPropertyData*>::const_iterator it = motionInfo.properties.begin();
1461 while (it != motionInfo.properties.end()) {
1462 switch (mPropertyDefinition[(*it)->id]->TransmitAs) {
1463 case TT_BOOLARRAY:
1464 {
1465 struct BoolArrayBuffer *boolBuf = nullptr;
1466 if ((*it)->id >= BOOLARRAY_START_ID && (*it)->id <= BOOLARRAY_END_ID + BOOLARRAY_BLOCKSIZE)
1467 {
1468 int buffer_block = ((*it)->id - BOOLARRAY_BASE_1) / BOOLARRAY_BLOCKSIZE;
1469 boolBuf = &boolBuffer[buffer_block];
1470 boolBuf->propertyId = BOOLARRAY_START_ID + buffer_block * BOOLARRAY_BLOCKSIZE;
1471 }
1472 if (boolBuf)
1473 {
1474 int bitidx = (*it)->id - boolBuf->propertyId;
1475 if ((*it)->int_value)
1476 boolBuf->boolValue |= 1 << bitidx;
1477 }
1478 break;
1479 }
1480 default:
1481 break;
1482 }
1483 ++it;
1484 }
1485
1486 for (int partition = 1; partition <= protocolToUse; partition++)
1487 {
1488 std::vector<FGPropertyData*>::const_iterator it = motionInfo.properties.begin();
1489 while (it != motionInfo.properties.end()) {
1490 const struct IdPropertyList* propDef = mPropertyDefinition[(*it)->id];
1491
1492 /*
1493 * Excludes the 2017.2 property for the protocol version from V1 packets.
1494 */
1495 if (protocolToUse == 1 && propDef->version == V2_PROP_ID_PROTOCOL)
1496 {
1497 ++it;
1498 continue;
1499 }
1500 /*
1501 * If requested only transmit the properties that are above the filter base index; and essential other properties
1502 * a value of 1 is equivalent to just transmitting generics (>10002)
1503 * a value of 12000 is for only emesary properties.
1504 */
1505 if (transmitFilterPropertyBase && !IsIncludedInPacket(transmitFilterPropertyBase, propDef->id))
1506 {
1507 ++it;
1508 continue;
1509 }
1510 /*
1511 * 2017.2 partitions the buffer sent into protocol versions. Originally this was intended to allow
1512 * compatability with older clients; however this will only work in the future or with support from fgms
1513 * - so if a future version adds more properties to the protocol these can be transmitted in a third partition
1514 * that will be ignored by older clients (such as 2017.2).
1515 */
1516 if ( (propDef->version & 0xffff) == partition || (propDef->version & 0xffff) > protocolToUse)
1517 {
1518 if (ptr + 2 >= msgEnd)
1519 {
1520 SG_LOG(SG_NETWORK, SG_ALERT, "Multiplayer packet truncated prop id: " << (*it)->id << ": " << propDef->name);
1521 break;
1522 }
1523
1524 // First element is the ID. Write it out when we know we have room for
1525 // the whole property.
1526 xdr_data_t id = XDR_encode_uint32((*it)->id);
1527
1528
1529 /*
1530 * 2017.2 protocol has the ability to transmit as a different type (to save space), so
1531 * process this when using this protocol (protocolVersion 2) or later
1532 */
1533 int transmit_type = (*it)->type;
1534
1535 if (propDef->TransmitAs != TT_ASIS && protocolToUse > 1)
1536 {
1537 transmit_type = propDef->TransmitAs;
1538 }
1539 else if (propDef->TransmitAs == TT_BOOLARRAY)
1540 transmit_type = propDef->TransmitAs;
1541
1542 if (pMultiPlayDebugLevel->getIntValue() & 2)
1543 SG_LOG(SG_NETWORK, SG_INFO,
1544 "[SEND] pt " << partition <<
1545 ": buf[" << (ptr - data) * sizeof(*ptr)
1546 << "] id=" << (*it)->id << " type " << transmit_type);
1547
1548 if (propDef->encode_for_transmit && protocolToUse > 1)
1549 {
1550 ptr = (*propDef->encode_for_transmit)(propDef, ptr, (*it));
1551 }
1552 else
1553 {
1554 // The actual data representation depends on the type
1555 switch (transmit_type) {
1556 case TT_NOSEND:
1557 break;
1558 case TT_SHORTINT:
1559 {
1560 *ptr++ = XDR_encode_shortints32((*it)->id, (*it)->int_value);
1561 break;
1562 }
1563 case TT_SHORT_FLOAT_1:
1564 {
1565 short value = get_scaled_short((*it)->float_value, 10.0);
1566 *ptr++ = XDR_encode_shortints32((*it)->id, value);
1567 break;
1568 }
1569 case TT_SHORT_FLOAT_2:
1570 {
1571 short value = get_scaled_short((*it)->float_value, 100.0);
1572 *ptr++ = XDR_encode_shortints32((*it)->id, value);
1573 break;
1574 }
1575 case TT_SHORT_FLOAT_3:
1576 {
1577 short value = get_scaled_short((*it)->float_value, 1000.0);
1578 *ptr++ = XDR_encode_shortints32((*it)->id, value);
1579 break;
1580 }
1581 case TT_SHORT_FLOAT_4:
1582 {
1583 short value = get_scaled_short((*it)->float_value, 10000.0);
1584 *ptr++ = XDR_encode_shortints32((*it)->id, value);
1585 break;
1586 }
1587
1589 {
1590 short value = get_scaled_short((*it)->float_value, 32767.0);
1591 *ptr++ = XDR_encode_shortints32((*it)->id, value);
1592 break;
1593 }
1594 case TT_BOOLARRAY:
1595 {
1596 int boolIdx = ((*it)->id - BOOLARRAY_BASE_1) / BOOLARRAY_BLOCKSIZE;
1597
1598 if (boolIdx < 0 || boolIdx >= MAX_BOOL_BUFFERS)
1599 {
1600 SG_LOG(SG_NETWORK, SG_WARN, "Unexpected prop id with type TT_BOOLARRAY: " << (*it)->id);
1601 break;
1602 }
1603
1604 if (!boolBuffer[boolIdx].propertyId)
1605 {
1606 // propertyId being unset indicates that this block was already written
1607 break;
1608 }
1609
1610 if (ptr + 2 >= msgEnd)
1611 {
1612 SG_LOG(SG_NETWORK, SG_ALERT, "Multiplayer packet truncated prop id: " << boolBuffer[boolIdx].propertyId << ": multiplay/generic/bools[" << boolIdx * 30 << "]");
1613 }
1614 *ptr++ = XDR_encode_int32(boolBuffer[boolIdx].propertyId);
1615 *ptr++ = XDR_encode_int32(boolBuffer[boolIdx].boolValue);
1616
1617 boolBuffer[boolIdx].propertyId = 0; // mark block as written
1618 break;
1619 }
1620 case simgear::props::INT:
1621 case simgear::props::BOOL:
1622 case simgear::props::LONG:
1623 *ptr++ = id;
1624 *ptr++ = XDR_encode_uint32((*it)->int_value);
1625 break;
1626 case simgear::props::FLOAT:
1627 case simgear::props::DOUBLE:
1628 *ptr++ = id;
1629 *ptr++ = XDR_encode_float((*it)->float_value);
1630 break;
1631 case simgear::props::STRING:
1632 case simgear::props::UNSPECIFIED:
1633 {
1634 if (protocolToUse > 1)
1635 {
1636 // New string encoding:
1637 // xdr[0] : ID length packed into 32 bit containing two shorts.
1638 // xdr[1..len/4] The string itself (char[length])
1639 const char* lcharptr = (*it)->string_value;
1640
1641 if (lcharptr != 0)
1642 {
1643 uint32_t len = strlen(lcharptr);
1644
1645 if (len >= MAX_TEXT_SIZE)
1646 {
1647 len = MAX_TEXT_SIZE - 1;
1648 SG_LOG(SG_NETWORK, SG_ALERT, "Multiplayer property truncated at MAX_TEXT_SIZE in string " << (*it)->id);
1649 }
1650
1651 char *encodeStart = (char*)ptr;
1652 char *msgEndbyte = (char*)msgEnd;
1653
1654 if (encodeStart + 2 + len >= msgEndbyte)
1655 {
1656 SG_LOG(SG_NETWORK, SG_ALERT, "Multiplayer property not sent (no room) string " << (*it)->id);
1657 goto escape;
1658 }
1659
1660 *ptr++ = XDR_encode_shortints32((*it)->id, len);
1661 encodeStart = (char*)ptr;
1662 if (len != 0)
1663 {
1664 int lcount = 0;
1665 while (*lcharptr && (lcount < MAX_TEXT_SIZE))
1666 {
1667 if (encodeStart + 2 >= msgEndbyte)
1668 {
1669 SG_LOG(SG_NETWORK, SG_ALERT, "Multiplayer packet truncated in string " << (*it)->id << " lcount " << lcount);
1670 break;
1671 }
1672 *encodeStart++ = *lcharptr++;
1673 lcount++;
1674 }
1675 }
1676 ptr = (xdr_data_t*)encodeStart;
1677 }
1678 else
1679 {
1680 // empty string, just send the id and a zero length
1681 *ptr++ = id;
1682 *ptr++ = XDR_encode_uint32(0);
1683 }
1684 }
1685 else {
1686
1687 // String is complicated. It consists of
1688 // The length of the string
1689 // The string itself
1690 // Padding to the nearest 4-bytes.
1691 const char* lcharptr = (*it)->string_value;
1692
1693 if (lcharptr != 0)
1694 {
1695 // Add the length
1697 uint32_t len = strlen(lcharptr);
1698 if (len >= MAX_TEXT_SIZE)
1699 {
1700 len = MAX_TEXT_SIZE - 1;
1701 SG_LOG(SG_NETWORK, SG_ALERT, "Multiplayer property truncated at MAX_TEXT_SIZE in string " << (*it)->id);
1702 }
1703
1704 // XXX This should not be using 4 bytes per character!
1705 // If there's not enough room for this property, drop it
1706 // on the floor.
1707 if (ptr + 2 + ((len + 3) & ~3) >= msgEnd)
1708 {
1709 SG_LOG(SG_NETWORK, SG_ALERT, "Multiplayer property not sent (no room) string " << (*it)->id);
1710 goto escape;
1711 }
1712 //cout << "String length unint32: " << len << "\n";
1713 *ptr++ = id;
1714 *ptr++ = XDR_encode_uint32(len);
1715 if (len != 0)
1716 {
1717 // Now the text itself
1718 // XXX This should not be using 4 bytes per character!
1719 int lcount = 0;
1720 while ((*lcharptr != '\0') && (lcount < MAX_TEXT_SIZE))
1721 {
1722 if (ptr + 2 >= msgEnd)
1723 {
1724 SG_LOG(SG_NETWORK, SG_ALERT, "Multiplayer packet truncated in string " << (*it)->id << " lcount " << lcount);
1725 break;
1726 }
1727 *ptr++ = XDR_encode_int8(*lcharptr);
1728 lcharptr++;
1729 lcount++;
1730 }
1731 // Now pad if required
1732 while ((lcount % 4) != 0)
1733 {
1734 if (ptr + 2 >= msgEnd)
1735 {
1736 SG_LOG(SG_NETWORK, SG_ALERT, "Multiplayer packet truncated in string " << (*it)->id << " lcount " << lcount);
1737 break;
1738 }
1739 *ptr++ = XDR_encode_int8(0);
1740 lcount++;
1741 }
1742 }
1743 }
1744 else
1745 {
1746 // Nothing to encode
1747 *ptr++ = id;
1748 *ptr++ = XDR_encode_uint32(0);
1749 }
1750 }
1751 }
1752 break;
1753
1754 default:
1755 *ptr++ = id;
1756 *ptr++ = XDR_encode_float((*it)->float_value);;
1757 break;
1758 }
1759 }
1760 }
1761 ++it;
1762 }
1763 }
1764 escape:
1765
1766 msgLen = reinterpret_cast<char*>(ptr) - msgBuf.Msg;
1767 FillMsgHdr(msgBuf.msgHdr(), POS_DATA_ID, msgLen);
1768
1769 /*
1770 * Informational:
1771 * Save the last packet length sent, and
1772 * if the property is set then dump the packet length to the console.
1773 * ----------------------------
1774 * This should be sufficient for rudimentary debugging (in order of useful ness)
1775 * 1. loopback your own craft. fantastic for resolving animations and property transmission issues.
1776 * 2. see what properties are being sent
1777 * 3. see how much space it takes up
1778 * 4. dump the packet as it goes out
1779 * 5. dump incoming packets
1780 */
1781 pXmitLen->setIntValue(msgLen); // 2. store the size of the properties as transmitted
1782
1783 if (pMultiPlayDebugLevel->getIntValue() & 2) // and dump it to the console
1784 {
1785 SG_LOG(SG_NETWORK, SG_INFO,
1786 "[SEND] Packet len " << msgLen);
1787 }
1788 if (pMultiPlayDebugLevel->getIntValue() & 4) // 4. hexdump the packet
1789 SG_LOG_HEXDUMP(SG_NETWORK, SG_INFO, data, (ptr - data) * sizeof(*ptr));
1790 /*
1791 * simple loopback of ourselves - to enable easy MP debug for model developers; see (1) above
1792 */
1793 if (pMultiPlayDebugLevel->getIntValue() & 1)
1794 {
1795 long stamp = SGTimeStamp::now().getSeconds();
1796 ProcessPosMsg(msgBuf, mServer, stamp);
1797 }
1798 }
1799 if (msgLen > 0)
1800 mSocket->sendto(msgBuf.Msg, msgLen, 0, &mServer);
1801 SG_LOG(SG_NETWORK, SG_BULK, "FGMultiplayMgr::SendMyPosition");
1802} // FGMultiplayMgr::SendMyPosition()
1803
1804short FGMultiplayMgr::get_scaled_short(double v, double scale)
1805{
1806 float nv = v * scale;
1807 if (nv >= 32767) return 32767;
1808 if (nv <= -32767) return -32767;
1809 short rv = (short)nv;
1810 return rv;
1811}
1813
1815//
1816// Name: SendTextMessage
1817// Description: Sends a message to the player. The message must
1818// contain a valid and correctly filled out header and optional
1819// message body.
1820//
1822void
1824{
1825 if (!mInitialised || !mHaveServer)
1826 return;
1827
1828 T_MsgHdr MsgHdr;
1829 FillMsgHdr(&MsgHdr, CHAT_MSG_ID);
1831 // Divide the text string into blocks that fit
1832 // in the message and send the blocks.
1834 unsigned iNextBlockPosition = 0;
1835 T_ChatMsg ChatMsg;
1836
1837 char Msg[sizeof(T_MsgHdr) + sizeof(T_ChatMsg)];
1838 while (iNextBlockPosition < MsgText.length()) {
1839 strncpy (ChatMsg.Text,
1840 MsgText.substr(iNextBlockPosition, MAX_CHAT_MSG_LEN - 1).c_str(),
1841 MAX_CHAT_MSG_LEN - 1);
1842 ChatMsg.Text[MAX_CHAT_MSG_LEN - 1] = '\0';
1843 memcpy (Msg, &MsgHdr, sizeof(T_MsgHdr));
1844 memcpy (Msg + sizeof(T_MsgHdr), &ChatMsg, sizeof(T_ChatMsg));
1845 mSocket->sendto (Msg, sizeof(T_MsgHdr) + sizeof(T_ChatMsg), 0, &mServer);
1846 iNextBlockPosition += MAX_CHAT_MSG_LEN - 1;
1847
1848 }
1849
1850
1851} // FGMultiplayMgr::SendTextMessage ()
1852
1853
1854
1855// If a message is available from mSocket, copies into <msgBuf>, converts
1856// endiness of the T_MsgHdr, and returns length.
1857//
1858// Otherwise returns 0.
1859//
1860int FGMultiplayMgr::GetMsgNetwork(MsgBuf& msgBuf, simgear::IPAddress& SenderAddress)
1861{
1863 // Although the recv call asks for
1864 // MAX_PACKET_SIZE of data, the number of bytes
1865 // returned will only be that of the next
1866 // packet waiting to be processed.
1868 if (!mSocket) {
1869 return 0;
1870 }
1871 int RecvStatus = mSocket->recvfrom(msgBuf.Msg, sizeof(msgBuf.Msg), 0,
1872 &SenderAddress);
1874 // no Data received
1876 if (RecvStatus == 0)
1877 return 0;
1878
1879 // socket error reported?
1880 // errno isn't thread-safe - so only check its value when
1881 // socket return status < 0 really indicates a failure.
1882 if ((RecvStatus < 0)&&
1883 ((errno == EAGAIN) || (errno == 0))) // MSVC output "NoError" otherwise
1884 {
1885 // ignore "normal" errors
1886 return 0;
1887 }
1888
1889 if (RecvStatus<0)
1890 {
1891 #ifdef _WIN32
1892 if (::WSAGetLastError() != WSAEWOULDBLOCK) // this is normal on a receive when there is no data
1893 {
1894 // with Winsock the error will not be the actual problem.
1895 SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr::MP_ProcessData - Unable to receive data. WSAGetLastError=" << ::WSAGetLastError());
1896 }
1897 #else
1898 SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - Unable to receive data. "
1899 << strerror(errno) << "(errno " << errno << ")");
1900 #endif
1901 return 0;
1902 }
1903
1904 T_MsgHdr* MsgHdr = msgBuf.msgHdr();
1905 MsgHdr->Magic = XDR_decode_uint32 (MsgHdr->Magic);
1906 MsgHdr->Version = XDR_decode_uint32 (MsgHdr->Version);
1907 MsgHdr->MsgId = XDR_decode_uint32 (MsgHdr->MsgId);
1908 MsgHdr->MsgLen = XDR_decode_uint32 (MsgHdr->MsgLen);
1909 MsgHdr->ReplyPort = XDR_decode_uint32 (MsgHdr->ReplyPort);
1910 MsgHdr->Callsign[MAX_CALLSIGN_LEN -1] = '\0';
1911
1912 return RecvStatus;
1913}
1914
1915// Returns message in msgBuf out-param.
1916//
1917// If we are in replay mode, we return recorded messages (omitting recorded
1918// chat messages), and live chat messages from mSocket.
1919//
1920int FGMultiplayMgr::GetMsg(MsgBuf& msgBuf, simgear::IPAddress& SenderAddress)
1921{
1922 if (pReplayState->getIntValue()) {
1923 // We are replaying, so return non-chat multiplayer messages from
1924 // mReplayMessageQueue and live chat messages from mSocket.
1925 //
1926 for(;;) {
1927
1928 if (mReplayMessageQueue.empty()) {
1929 // No recorded messages available, so look for live messages
1930 // from <mSocket>.
1931 //
1932 int RecvStatus = GetMsgNetwork(msgBuf, SenderAddress);
1933 if (RecvStatus == 0) {
1934 // No recorded messages, and no live messages, so return 0.
1935 return 0;
1936 }
1937
1938 // Always record all messages.
1939 //
1940 std::shared_ptr<std::vector<char>> data( new std::vector<char>(RecvStatus));
1941 memcpy( &data->front(), msgBuf.Msg, RecvStatus);
1942 mRecordMessageQueue.push_back(data);
1943
1944 if (msgBuf.Header.MsgId == CHAT_MSG_ID) {
1945 return RecvStatus;
1946 }
1947
1948 // If we get here, there is a live message but it is a
1949 // multiplayer aircraft position so we ignore it while
1950 // replaying.
1951 }
1952 else {
1953 // Replay recorded message, unless it is a chat message.
1954 //
1955 auto replayMessage = mReplayMessageQueue.front();
1956 mReplayMessageQueue.pop_front();
1957 assert(replayMessage->size() <= sizeof(msgBuf));
1958 int length = replayMessage->size();
1959 memcpy(&msgBuf.Msg, &replayMessage->front(), length);
1960 // Don't return recorded chat messages.
1961 if (msgBuf.Header.MsgId != CHAT_MSG_ID) {
1962 SG_LOG(SG_NETWORK, SG_BULK,
1963 "replaying message length=" << replayMessage->size()
1964 << ". num remaining messages=" << mReplayMessageQueue.size());
1965 return length;
1966 }
1967 }
1968 }
1969 }
1970 else {
1971 int length = GetMsgNetwork(msgBuf, SenderAddress);
1972
1973 // Make raw incoming packet available to recording code.
1974 if (length) {
1975 std::shared_ptr<std::vector<char>> data( new std::vector<char>(length));
1976 memcpy( &data->front(), msgBuf.Msg, length);
1977 mRecordMessageQueue.push_back(data);
1978 }
1979 return length;
1980 }
1981}
1982
1983
1985//
1986// Name: update
1987// Description: Processes data waiting at the receive socket. The
1988// processing ends when there is no more data at the socket.
1989//
1991void
1993{
1994 // We carry on even if !mInitialised, in case we are replaying a multiplayer
1995 // recording.
1996 //
1997
1999 long stamp = SGTimeStamp::now().getSeconds();
2000
2002 // Send if required
2004 //the mp protocol time is immune to pause, warp and time accel
2005 const double mpTime = globals->get_subsystem<TimeManager>()->getMPProtocolClockSec();
2006
2007 // the mpTime is not monotonic (adjustable offset), going back in time will
2008 // also trigger a send
2009 if ((mpTime >= mNextTransmitTime) || (mpTime < (mNextTransmitTime - 2.0 * mDt))) {
2010 Send(mpTime);
2011 }
2012
2014 // Read from receive socket and/or multiplayer
2015 // replay, and process any data.
2017 ssize_t bytes;
2018 do {
2019 MsgBuf msgBuf;
2020 simgear::IPAddress SenderAddress;
2021 int RecvStatus = GetMsg(msgBuf, SenderAddress);
2022 if (RecvStatus == 0) {
2023 break;
2024 }
2025 // status is positive: bytes received
2026 bytes = (ssize_t) RecvStatus;
2027 if (bytes <= static_cast<ssize_t>(sizeof(T_MsgHdr))) {
2028 SG_LOG( SG_NETWORK, SG_INFO, "FGMultiplayMgr::MP_ProcessData - "
2029 << "received message with insufficient data" );
2030 break;
2031 }
2032
2034 // Read header
2036 T_MsgHdr* MsgHdr = msgBuf.msgHdr();
2037 if (MsgHdr->Magic != MSG_MAGIC) {
2038 SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr::MP_ProcessData - "
2039 << "message has invalid magic number!" );
2040 break;
2041 }
2042 if (MsgHdr->Version != PROTO_VER) {
2043 SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr::MP_ProcessData - "
2044 << "message has invalid protocol number!" );
2045 break;
2046 }
2047 if (static_cast<ssize_t>(MsgHdr->MsgLen) != bytes) {
2048 SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr::MP_ProcessData - "
2049 << "message from " << MsgHdr->Callsign << " has invalid length!");
2050 break;
2051 }
2052 //hexdump the incoming packet
2053 if (pMultiPlayDebugLevel->getIntValue() & 16)
2054 SG_LOG_HEXDUMP(SG_NETWORK, SG_INFO, msgBuf.Msg, MsgHdr->MsgLen);
2055
2057 // Process messages
2059 switch (MsgHdr->MsgId) {
2060 case CHAT_MSG_ID:
2061 ProcessChatMsg(msgBuf, SenderAddress);
2062 break;
2063 case POS_DATA_ID:
2064 ProcessPosMsg(msgBuf, SenderAddress, stamp);
2065 break;
2068 case OLD_PROP_MSG_ID:
2069 case OLD_POS_DATA_ID:
2070 break;
2071 default:
2072 SG_LOG(SG_NETWORK, SG_INFO, "FGMultiplayMgr::MP_ProcessData - "
2073 << "Unknown message Id received: " << MsgHdr->MsgId );
2074 break;
2075 }
2076 } while (bytes > 0);
2077
2078 // check for expiry
2079 MultiPlayerMap::iterator it = mMultiPlayerMap.begin();
2080 while (it != mMultiPlayerMap.end()) {
2081 if (it->second->getLastTimestamp() + 10 < stamp) {
2082 std::string name = it->first;
2083 it->second->setDie(true);
2084 mMultiPlayerMap.erase(it);
2085 it = mMultiPlayerMap.upper_bound(name);
2086 } else
2087 ++it;
2088 }
2089
2090 if (_mpirc) {
2091 _mpirc->update();
2092 }
2093 if (_cpdlc) {
2094 _cpdlc->update();
2095 }
2096} // FGMultiplayMgr::update(void)
2097
2098
2100{
2101 SG_LOG(SG_NETWORK, SG_DEBUG, "Clearing all motion info");
2102 for (auto it: mMultiPlayerMap) {
2103 it.second->clearMotionInfo();
2104 }
2105}
2106
2107void FGMultiplayMgr::Send(double mpTime)
2108{
2109 using namespace simgear;
2110
2111 findProperties();
2112
2113 // Try to stick to a grid with the sending rate as step,
2114 // based on the MP protocol clock.
2115 mNextTransmitTime += floor(1.0 + (mpTime - mNextTransmitTime) / mDt) * mDt;
2116
2117 FlightProperties ifce;
2118
2119 // put together a motion info struct, you will get that later
2120 // from FGInterface directly ...
2121 FGExternalMotionData motionInfo;
2122
2123 // The current simulation time we need to update for,
2124 // note that the simulation time is updated before calling all the
2125 // update methods. Thus it contains the time intervals *end* time.
2126 // The FDM is already run, so the states belong to that time.
2127 motionInfo.time = mpTime;
2128 motionInfo.lag = mDt;
2129
2130 // These are for now converted from lat/lon/alt and euler angles.
2131 // But this should change in FGInterface ...
2132 double lon = ifce.get_Longitude();
2133 double lat = ifce.get_Latitude();
2134 // first the aprioriate structure for the geodetic one
2135 SGGeod geod = SGGeod::fromRadFt(lon, lat, ifce.get_Altitude());
2136 // Convert to cartesion coordinate
2137 motionInfo.position = SGVec3d::fromGeod(geod);
2138
2139 // The quaternion rotating from the earth centered frame to the
2140 // horizontal local frame
2141 SGQuatf qEc2Hl = SGQuatf::fromLonLatRad((float)lon, (float)lat);
2142 // The orientation wrt the horizontal local frame
2143 float heading = ifce.get_Psi();
2144 float pitch = ifce.get_Theta();
2145 float roll = ifce.get_Phi();
2146 SGQuatf hlOr = SGQuatf::fromYawPitchRoll(heading, pitch, roll);
2147 // The orientation of the vehicle wrt the earth centered frame
2148 motionInfo.orientation = qEc2Hl*hlOr;
2149
2150 if (!globals->get_subsystem<FDMShell>()->is_suspended()) {
2151 // velocities
2152 motionInfo.linearVel = SG_FEET_TO_METER*SGVec3f(ifce.get_uBody(),
2153 ifce.get_vBody(),
2154 ifce.get_wBody());
2155 motionInfo.angularVel = SGVec3f(ifce.get_P_body(),
2156 ifce.get_Q_body(),
2157 ifce.get_R_body());
2158
2159 // accels, set that to zero for now.
2160 // Angular accelerations are missing from the interface anyway,
2161 // linear accelerations are screwed up at least for JSBSim.
2162 // motionInfo.linearAccel = SG_FEET_TO_METER*SGVec3f(ifce.get_U_dot_body(),
2163 // ifce.get_V_dot_body(),
2164 // ifce.get_W_dot_body());
2165 motionInfo.linearAccel = SGVec3f::zeros();
2166 motionInfo.angularAccel = SGVec3f::zeros();
2167 }
2168 else {
2169 // if the interface is suspendend, prevent the client from
2170 // wild extrapolations
2171 motionInfo.linearVel = SGVec3f::zeros();
2172 motionInfo.angularVel = SGVec3f::zeros();
2173 motionInfo.linearAccel = SGVec3f::zeros();
2174 motionInfo.angularAccel = SGVec3f::zeros();
2175 }
2176
2177 PropertyMap::iterator it;
2178 for (it = mPropertyMap.begin(); it != mPropertyMap.end(); ++it) {
2179 FGPropertyData* pData = new FGPropertyData;
2180 pData->id = it->first;
2181 pData->type = findProperty(pData->id)->type;
2182
2183 switch (static_cast<int>(pData->type)) {
2184 case TT_SHORTINT:
2185 case TT_SHORT_FLOAT_1:
2186 case TT_SHORT_FLOAT_2:
2187 case TT_SHORT_FLOAT_3:
2188 case TT_SHORT_FLOAT_4:
2190 case props::INT:
2191 case props::LONG:
2192 case props::BOOL:
2193 pData->int_value = it->second->getIntValue();
2194 break;
2195 case props::FLOAT:
2196 case props::DOUBLE:
2197 pData->float_value = it->second->getFloatValue();
2198 break;
2199 case props::STRING:
2200 case props::UNSPECIFIED:
2201 {
2202 // FIXME: We assume unspecified are strings for the moment.
2203
2204 string cstr = it->second->getStringValue();
2205 size_t len = cstr.length();
2206
2207 if (len > 0)
2208 {
2209 pData->string_value = new char[len + 1];
2210 strcpy(pData->string_value, cstr.c_str());
2211 }
2212 else
2213 {
2214 // Size 0 - ignore
2215 pData->string_value = 0;
2216 }
2217
2218 //cout << " Sending property " << pData->id << " " << pData->type << " " << pData->string_value << "\n";
2219 break;
2220 }
2221 default:
2222 // FIXME Currently default to a float.
2223 //cout << "Unknown type when iterating through props: " << pData->type << "\n";
2224 pData->float_value = it->second->getFloatValue();
2225 break;
2226 }
2227 motionInfo.properties.push_back(pData);
2228 }
2229 SendMyPosition(motionInfo);
2230}
2231
2232
2234//
2235// handle a position message
2236//
2238void
2239FGMultiplayMgr::ProcessPosMsg(const FGMultiplayMgr::MsgBuf& Msg,
2240 const simgear::IPAddress& SenderAddress, long stamp)
2241{
2242 const T_MsgHdr* MsgHdr = Msg.msgHdr();
2243 if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + sizeof(T_PositionMsg)) {
2244 SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
2245 << "Position message received with insufficient data");
2246 return;
2247 }
2248 const T_PositionMsg* PosMsg = Msg.posMsg();
2249 FGExternalMotionData motionInfo;
2250 int fallback_model_index = 0;
2251 motionInfo.time = XDR_decode_double(PosMsg->time);
2252 motionInfo.lag = XDR_decode_double(PosMsg->lag);
2253 for (unsigned i = 0; i < 3; ++i)
2254 motionInfo.position(i) = XDR_decode_double(PosMsg->position[i]);
2255 SGVec3f angleAxis;
2256 for (unsigned i = 0; i < 3; ++i)
2257 angleAxis(i) = XDR_decode_float(PosMsg->orientation[i]);
2258 motionInfo.orientation = SGQuatf::fromAngleAxis(angleAxis);
2259 for (unsigned i = 0; i < 3; ++i)
2260 motionInfo.linearVel(i) = XDR_decode_float(PosMsg->linearVel[i]);
2261 for (unsigned i = 0; i < 3; ++i)
2262 motionInfo.angularVel(i) = XDR_decode_float(PosMsg->angularVel[i]);
2263 for (unsigned i = 0; i < 3; ++i)
2264 motionInfo.linearAccel(i) = XDR_decode_float(PosMsg->linearAccel[i]);
2265 for (unsigned i = 0; i < 3; ++i)
2266 motionInfo.angularAccel(i) = XDR_decode_float(PosMsg->angularAccel[i]);
2267
2268 // sanity check: do not allow injection of corrupted data (NaNs)
2269 if (!isSane(motionInfo))
2270 {
2271 // drop this message, keep old position until receiving valid data
2272 SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::ProcessPosMsg - "
2273 << "Position message with invalid data (NaN) received from "
2274 << MsgHdr->Callsign);
2275 return;
2276 }
2277
2278 //cout << "INPUT MESSAGE\n";
2279
2280 // There was a bug in 1.9.0 and before: T_PositionMsg was 196 bytes
2281 // on 32 bit architectures and 200 bytes on 64 bit, and this
2282 // structure is put directly on the wire. By looking at the padding,
2283 // we can sort through the mess, mostly:
2284 // If padding is 0 (which is not a valid property type), then the
2285 // message was produced by a new client or an old 64 bit client that
2286 // happened to have 0 on the stack;
2287 // Else if the property list starting with the padding word is
2288 // well-formed, then the client is probably an old 32 bit client and
2289 // we'll go with that;
2290 // Else it is an old 64-bit client and properties start after the
2291 // padding.
2292 // There is a chance that we could be fooled by garbage in the
2293 // padding looking like a valid property, so verifyProperties() is
2294 // strict about the validity of the property values.
2295 const xdr_data_t* xdr = Msg.properties();
2296 const xdr_data_t* data = xdr;
2297
2298 /*
2299 * with V2 we use the pad to forcefully invoke older clients to verify (and discard)
2300 * our new protocol.
2301 * This will preserve the position info but not transmit the properties; which is about
2302 * the most reasonable compromise we can have
2303 */
2304 if (PosMsg->pad != 0 && XDR_decode_int32(PosMsg->pad) != V2_PAD_MAGIC) {
2305 if (verifyProperties(&PosMsg->pad, Msg.propsRecvdEnd()))
2306 xdr = &PosMsg->pad;
2307 else if (!verifyProperties(xdr, Msg.propsRecvdEnd()))
2308 goto noprops;
2309 }
2310 while (xdr < Msg.propsRecvdEnd()) {
2311 // First element is always the ID
2312 unsigned id = XDR_decode_uint32(*xdr);
2313
2314 /*
2315 * As we can detect a short int encoded value (by the upper word being non-zero) we can
2316 * do the decode here; set the id correctly, extract the integer and set the flag.
2317 * This can then be picked up by the normal processing based on the flag
2318 */
2319 int int_value = 0;
2320 bool short_int_encoded = false;
2321 if (id & 0xffff0000)
2322 {
2323 int v1, v2;
2324 XDR_decode_shortints32(*xdr, v1, v2);
2325 int_value = v2;
2326 id = v1;
2327 short_int_encoded = true;
2328 }
2329
2330 if (pMultiPlayDebugLevel->getIntValue() & 8)
2331 SG_LOG(SG_NETWORK, SG_INFO,
2332 "[RECV] add " << std::hex << xdr
2333 << std::dec <<
2334 ": buf[" << ((char*)xdr) - ((char*)data)
2335 << "] id=" << id
2336 << " SIenc " << short_int_encoded);
2337
2338 // Check the ID actually exists and get the type
2339 const IdPropertyList* plist = findProperty(id);
2340
2341 if (plist)
2342 {
2343 FGPropertyData* pData = new FGPropertyData;
2344 if (plist->decode_received)
2345 {
2346 //
2347 // this needs the pointer prior to the extraction of the property id and possible shortint decode
2348 // too allow the method to redecode as it wishes
2349 xdr = (*plist->decode_received)(plist, xdr, pData);
2350 }
2351 else
2352 {
2353 pData->id = id;
2354 pData->type = plist->type;
2355 xdr++;
2356 // How we decode the remainder of the property depends on the type
2357 switch (pData->type) {
2358 case simgear::props::BOOL:
2359 /*
2360 * For 2017.2 we support boolean arrays transmitted as a single int for 30 bools.
2361 * this section handles the unpacking into the arrays.
2362 */
2363 if (pData->id >= BOOLARRAY_START_ID && pData->id <= BOOLARRAY_END_ID)
2364 {
2365 unsigned int val = XDR_decode_uint32(*xdr);
2366 bool first_bool = true;
2367 xdr++;
2368 for (int bitidx = 0; bitidx <= 30; bitidx++)
2369 {
2370 // ensure that this property is in the master list.
2371 const IdPropertyList* plistBool = findProperty(id + bitidx);
2372
2373 if (plistBool)
2374 {
2375 if (first_bool)
2376 first_bool = false;
2377 else
2378 pData = new FGPropertyData;
2379
2380 pData->id = id + bitidx;
2381 pData->int_value = (val & (1 << bitidx)) != 0;
2382 pData->type = simgear::props::BOOL;
2383 motionInfo.properties.push_back(pData);
2384
2385 // ensure that this is null because this section of code manages the property data and list directly
2386 // it has to be this way because one MP value results in multiple properties being set.
2387 pData = nullptr;
2388 }
2389 }
2390 break;
2391 }
2392 case simgear::props::INT:
2393 case simgear::props::LONG:
2394 if (short_int_encoded)
2395 {
2396 pData->int_value = int_value;
2397 pData->type = simgear::props::INT;
2398 }
2399 else
2400 {
2401 pData->int_value = XDR_decode_uint32(*xdr);
2402 xdr++;
2403 }
2404 //cout << pData->int_value << "\n";
2405 break;
2406 case simgear::props::FLOAT:
2407 case simgear::props::DOUBLE:
2408 if (short_int_encoded)
2409 {
2410 switch (plist->TransmitAs)
2411 {
2412 case TT_SHORT_FLOAT_1:
2413 pData->float_value = (double)int_value / 10.0;
2414 break;
2415 case TT_SHORT_FLOAT_2:
2416 pData->float_value = (double)int_value / 100.0;
2417 break;
2418 case TT_SHORT_FLOAT_3:
2419 pData->float_value = (double)int_value / 1000.0;
2420 break;
2421 case TT_SHORT_FLOAT_4:
2422 pData->float_value = (double)int_value / 10000.0;
2423 break;
2425 pData->float_value = (double)int_value / 32767.0;
2426 break;
2427 default:
2428 break;
2429 }
2430 }
2431 else
2432 {
2433 pData->float_value = XDR_decode_float(*xdr);
2434 xdr++;
2435 }
2436 break;
2437 case simgear::props::STRING:
2438 case simgear::props::UNSPECIFIED:
2439 {
2440 // if the string is using short int encoding then it is in the new format.
2441 if (short_int_encoded)
2442 {
2443 uint32_t length = int_value;
2444 pData->string_value = new char[length + 1];
2445
2446 char *cptr = (char*)xdr;
2447 for (unsigned i = 0; i < length; i++)
2448 {
2449 pData->string_value[i] = *cptr++;
2450 }
2451 pData->string_value[length] = '\0';
2452 xdr = (xdr_data_t*)cptr;
2453 }
2454 else {
2455 // String is complicated. It consists of
2456 // The length of the string
2457 // The string itself
2458 // Padding to the nearest 4-bytes.
2459 uint32_t length = XDR_decode_uint32(*xdr);
2460 xdr++;
2461 //cout << length << " ";
2462 // Old versions truncated the string but left the length unadjusted.
2463 if (length > MAX_TEXT_SIZE)
2464 length = MAX_TEXT_SIZE;
2465 pData->string_value = new char[length + 1];
2466 //cout << " String: ";
2467 for (unsigned i = 0; i < length; i++)
2468 {
2469 pData->string_value[i] = (char)XDR_decode_int8(*xdr);
2470 xdr++;
2471 }
2472
2473 pData->string_value[length] = '\0';
2474
2475 // Now handle the padding
2476 while ((length % 4) != 0)
2477 {
2478 xdr++;
2479 length++;
2480 //cout << "0";
2481 }
2482 //cout << "\n";
2483 }
2484 }
2485 break;
2486
2487 default:
2488 pData->float_value = XDR_decode_float(*xdr);
2489 SG_LOG(SG_NETWORK, SG_DEBUG, "Unknown Prop type " << pData->id << " " << pData->type);
2490 xdr++;
2491 break;
2492 }
2493 }
2494 if (pData) {
2495 motionInfo.properties.push_back(pData);
2496
2497 // Special case - we need the /sim/model/fallback-model-index to create
2498 // the MP model
2499 if (pData->id == FALLBACK_MODEL_ID) {
2500 fallback_model_index = pData->int_value;
2501 SG_LOG(SG_NETWORK, SG_DEBUG, "Found Fallback model index in message " << fallback_model_index);
2502 }
2503 }
2504 }
2505 else
2506 {
2507 // We failed to find the property. We'll try the next packet immediately.
2508 SG_LOG(SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::ProcessPosMsg - "
2509 "message from " << MsgHdr->Callsign << " has unknown property id "
2510 << id);
2511
2512 // At this point the packet must be considered to be unreadable
2513 // as we have no way of knowing the length of this property (it could be a string)
2514 break;
2515 }
2516 }
2517 noprops:
2518 FGAIMultiplayer* mp = getMultiplayer(MsgHdr->Callsign);
2519 if (!mp)
2520 mp = addMultiplayer(MsgHdr->Callsign, PosMsg->Model, fallback_model_index);
2521 mp->addMotionInfo(motionInfo, stamp);
2522
2523 // Optionally gather information about the raw speed of a selected
2524 // multiplayer aircraft. This is for scripts/python/recordreplay.py
2525 // --test-motion-mp.
2526 //
2527 {
2528 string callsign = pLogRawSpeedMultiplayer->getStringValue();
2529 if (!callsign.empty() && callsign == string(MsgHdr->Callsign)) {
2530 static SGVec3d s_pos_prev;
2531 static double s_simtime_prev = -1;
2532 SGVec3d pos = motionInfo.position;
2533 double dt = motionInfo.time - s_simtime_prev;
2534 if (s_simtime_prev != -1 && dt > 0) {
2535 double distance = length(pos - s_pos_prev);
2536 double speed = distance / dt;
2537 SGPropertyNode* n = fgGetNode("/sim/replay/log-raw-speed-multiplayer-values", true /*create*/);
2538 n = n->addChild("value");
2539 n->setDoubleValue(speed);
2540 SG_LOG(SG_GENERAL, SG_DEBUG, "Multiplayer aircraft callsign=" << callsign << ":"
2541 << " motionInfo.time=" << motionInfo.time
2542 << " dt=" << dt
2543 << " distance=" << distance
2544 << " speed=" << speed
2545 << " s_pos_prev=" << s_pos_prev
2546 << " pos=" << pos
2547 << " n->getPath()=" << n->getPath(true /*simplify*/)
2548 );
2549 }
2550 s_simtime_prev = motionInfo.time;
2551 s_pos_prev = pos;
2552 }
2553 }
2554} // FGMultiplayMgr::ProcessPosMsg()
2555
2556
2557std::shared_ptr<std::vector<char>> FGMultiplayMgr::popMessageHistory()
2558{
2559 if (mRecordMessageQueue.empty()) {
2560 return nullptr;
2561 }
2562
2563 auto ret = mRecordMessageQueue.front();
2564 mRecordMessageQueue.pop_front();
2565 return ret;
2566}
2567
2568void FGMultiplayMgr::pushMessageHistory(std::shared_ptr<std::vector<char>> message)
2569{
2570 mReplayMessageQueue.push_back(message);
2571}
2572
2574
2576//
2577// handle a chat message
2578// FIXME: display chat message within flightgear
2579//
2581void
2582FGMultiplayMgr::ProcessChatMsg(const MsgBuf& Msg,
2583 const simgear::IPAddress& SenderAddress)
2584{
2585 const T_MsgHdr* MsgHdr = Msg.msgHdr();
2586 if (MsgHdr->MsgLen < sizeof(T_MsgHdr) + 1) {
2587 SG_LOG( SG_NETWORK, SG_DEBUG, "FGMultiplayMgr::MP_ProcessData - "
2588 << "Chat message received with insufficient data" );
2589 return;
2590 }
2591
2592 char *chatStr = new char[MsgHdr->MsgLen - sizeof(T_MsgHdr)];
2593 const T_ChatMsg* ChatMsg
2594 = reinterpret_cast<const T_ChatMsg *>(Msg.Msg + sizeof(T_MsgHdr));
2595 strncpy(chatStr, ChatMsg->Text,
2596 MsgHdr->MsgLen - sizeof(T_MsgHdr));
2597 chatStr[MsgHdr->MsgLen - sizeof(T_MsgHdr) - 1] = '\0';
2598
2599 SG_LOG (SG_NETWORK, SG_WARN, "Chat [" << MsgHdr->Callsign << "]"
2600 << " " << chatStr);
2601
2602 delete [] chatStr;
2603} // FGMultiplayMgr::ProcessChatMsg ()
2605
2606void
2607FGMultiplayMgr::FillMsgHdr(T_MsgHdr *MsgHdr, int MsgId, unsigned _len)
2608{
2609 uint32_t len;
2610 switch (MsgId) {
2611 case CHAT_MSG_ID:
2612 len = sizeof(T_MsgHdr) + sizeof(T_ChatMsg);
2613 break;
2614 case POS_DATA_ID:
2615 len = _len;
2616 break;
2617 default:
2618 len = sizeof(T_MsgHdr);
2619 break;
2620 }
2623 MsgHdr->MsgId = XDR_encode_uint32(MsgId);
2624 MsgHdr->MsgLen = XDR_encode_uint32(len);
2625 MsgHdr->RequestedRangeNm = XDR_encode_shortints32(0,pMultiPlayRange->getIntValue());
2626 MsgHdr->ReplyPort = 0;
2627 strncpy(MsgHdr->Callsign, mCallsign.c_str(), MAX_CALLSIGN_LEN);
2628 MsgHdr->Callsign[MAX_CALLSIGN_LEN - 1] = '\0';
2629}
2630
2631
2632/* If <from>/<path> exists and <to>/<path> doesn't, copy the former to the
2633latter. */
2634static void copy_default(SGPropertyNode* from, const char* path, SGPropertyNode* to) {
2635 SGPropertyNode* from_ = from->getNode(path);
2636 if (from_) {
2637 if (!to->getNode(path)) {
2638 to->setDoubleValue(path, from_->getDoubleValue());
2639 }
2640 }
2641}
2642
2643static std::string makeStringPropertyNameSafe(const std::string& s)
2644{
2645 std::string ret;
2646 for (size_t i=0; i<s.size(); ++i) {
2647 char c = s[i];
2648 if (i==0 && !isalpha(c) && c!='_') c = '_';
2649 if (!isalnum(c) && c!='.' && c!='_' && c!='-') c = '_';
2650 ret += c;
2651 }
2652 return ret;
2653}
2654
2656FGMultiplayMgr::addMultiplayer(const std::string& callsign,
2657 const std::string& modelName,
2658 const int fallback_model_index)
2659{
2660 if (0 < mMultiPlayerMap.count(callsign))
2661 return mMultiPlayerMap[callsign].get();
2662
2663 FGAIMultiplayer* mp = new FGAIMultiplayer;
2664 mp->setPath(modelName.c_str());
2665 mp->setFallbackModelIndex(fallback_model_index);
2666 mp->setCallSign(callsign);
2667 mMultiPlayerMap[callsign] = mp;
2668
2669 auto aiMgr = globals->get_subsystem<FGAIManager>();
2670 if (aiMgr) {
2671 aiMgr->attach(mp);
2672
2674 for (unsigned i = 0; i < numProperties; ++i)
2676 }
2677
2678 /* Try to find a -set.xml for <modelName>, so that we can use its view
2679 parameters. If found, we install it into a 'set' property node.
2680
2681 If we are reusing an old entry in /ai/models/multiplayer[], there
2682 might be an old set/ node, so remove it.
2683
2684 todo: maybe we should cache the -set.xml nodes in memory and/or share them in
2685 properties?
2686 */
2687 mp->_getProps()->removeChildren("set");
2688
2689 SGPropertyNode_ptr set;
2690
2691 if (simgear::strutils::ends_with(modelName, ".xml")
2692 && simgear::strutils::starts_with(modelName, "Aircraft/")) {
2693
2694 std::string tail = modelName.substr(strlen("Aircraft/"));
2695
2697
2698 /* Need to append <fgdata>/Aircraft, otherwise we won't be able to find
2699 c172p. */
2700 SGPath fgdata_aircraft = globals->get_fg_root();
2701 fgdata_aircraft.append("Aircraft");
2702 dirs.push_back(fgdata_aircraft);
2703
2704 SGPath model_file;
2705 PathList::const_iterator it = std::find_if(dirs.begin(), dirs.end(),
2706 [&](SGPath dir) {
2707 model_file = dir;
2708 model_file.append(tail);
2709 return model_file.exists();
2710 });
2711
2712 if (it != dirs.end()) {
2713 /* We've found the model file.
2714
2715 Now try each -set.xml file in <modelName> aircraft directory. In theory
2716 an aircraft could have a -set.xml in an unrelated directory so we should
2717 scan all directories in globals->get_aircraft_paths(), but in practice
2718 most -set.xml files and models are in the same aircraft directory. */
2719 std::string model_file_head = it->str() + '/';
2720 std::string model_file_tail = model_file.str().substr(model_file_head.size());
2721 ssize_t p = model_file_tail.find('/');
2722 std::string aircraft_dir = model_file_head + model_file_tail.substr(0, p);
2723 simgear::Dir dir(aircraft_dir);
2724 std::vector<SGPath> dir_contents = dir.children(0 /*types*/, "-set.xml");
2725 /* simgear::Dir::children() claims that second param is glob, but
2726 actually it's just a suffix. */
2727
2728 for (auto path: dir_contents) {
2729 /* Load into a local SGPropertyNode.
2730
2731 As of 2020-03-08 we don't load directly into the global property tree
2732 because that appears to result in runtime-specific multiplayer values
2733 being written to autosave*.xml and reloaded next time fgfs is run,
2734 which results in lots of bogus properties within /ai/multiplayer. So
2735 instead we load into a local SGPropertyNode, then copy selected values
2736 into the global tree below. */
2737 set = new SGPropertyNode;
2738 bool ok = true;
2739 try {
2740 readProperties(path, set);
2741 }
2742 catch ( const std::exception & ) {
2743 ok = false;
2744 }
2745 if (ok) {
2746 SGPropertyNode* sim_model_path = set->getNode("sim/model/path");
2747 if (sim_model_path && sim_model_path->getStringValue() == modelName) {
2748 /* We've found (and loaded) a matching -set.xml. */
2749 break;
2750 }
2751 }
2752 set.reset();
2753 }
2754 }
2755 }
2756
2757 // Copy values from our local <set>/sim/view[]/config/* into global
2758 // /ai/models/multiplayer/set/sim/view[]/config/ so that we have view offsets
2759 // available for this multiplayer aircraft.
2760 SGPropertyNode* global_set = mp->_getProps()->addChild("set");
2761 SGPropertyNode* global_sim = global_set->addChild("sim");
2762 double sim_chase_distance_m = -25;
2763 if (set) {
2764 SGPropertyNode* sim = set->getChild("sim");
2765 if (sim) {
2766 /* Set <sim_chase_distance_m> to a default if not present. We
2767 also force the value to be negative - positive values reverse the
2768 affects of vertical mouse movements which isn't helpful; also see:
2769 https://sourceforge.net/p/flightgear/codetickets/2454/ */
2770 SGPropertyNode* sim_chase_distance_node = sim->getChild("chase-distance-m");
2771 if (sim_chase_distance_node) {
2772 sim_chase_distance_m = sim_chase_distance_node->getDoubleValue();
2773 if (sim_chase_distance_m > 0) {
2774 sim_chase_distance_m = -sim_chase_distance_m;
2775 SG_LOG(SG_VIEW, SG_ALERT,
2776 "Multiplayer aircraft's " << sim_chase_distance_node->getPath()
2777 << " is positive; correcting to: " << sim_chase_distance_m);
2778 }
2779 }
2780 SG_LOG(SG_VIEW, SG_DEBUG, "setting to " << sim_chase_distance_m << ": " << global_sim->getPath());
2781 global_sim->setDoubleValue("chase-distance-m", sim_chase_distance_m);
2782
2783 simgear::PropertyList views = sim->getChildren("view");
2784 for (auto view: views) {
2785 int view_index = view->getIndex();
2786 SGPropertyNode* global_view = global_sim->addChild("view", view_index, false /*append*/);
2787 assert(global_view->getIndex() == view_index);
2788 SGPropertyNode* config = view->getChild("config");
2789 SGPropertyNode* global_config = global_view->addChild("config");
2790 if (config) {
2791 int config_children_n = config->nChildren();
2792 for (int i=0; i<config_children_n; ++i) {
2793 SGPropertyNode* node = config->getChild(i);
2794 global_config->setDoubleValue(node->getNameString(), node->getDoubleValue());
2795 }
2796 }
2797 }
2798 }
2799 }
2800
2801 /* For views that are similar to Helicopter View, copy across Helicopter View
2802 target offsets if not specified. E.g. this allows Tower View AGL to work on
2803 aircraft that don't know about it but need non-zero target-*-offset-m values
2804 to centre the view on the middle of the aircraft.
2805
2806 This mimics what fgdata:Nasal/view.nas:manager does for the user aircraft's
2807 views.
2808 */
2809 SGPropertyNode* view_1 = global_sim->getNode("view", 1);
2810 std::initializer_list<int> views_with_default_z_offset_m = {1, 2, 3, 5, 7};
2811 for (int j: views_with_default_z_offset_m) {
2812 SGPropertyNode* v = global_sim->getChild("view", j);
2813 if (!v) {
2814 v = global_sim->addChild("view", j, false /*append*/);
2815 }
2816 SGPropertyNode* z_offset_m = v->getChild("config/z-offset-m");
2817 /* Setting config/z-offset-m default to <sim_chase_distance_m> here mimics
2818 what fgdata/defaults.xml does when it defines default views. */
2819 if (!z_offset_m) {
2820 v->setDoubleValue("config/z-offset-m", sim_chase_distance_m);
2821 }
2822 copy_default(view_1, "config/target-x-offset-m", v);
2823 copy_default(view_1, "config/target-y-offset-m", v);
2824 copy_default(view_1, "config/target-z-offset-m", v);
2825 }
2826
2827 /* Create a node /ai/models/callsigns/<callsign> containing the index of the
2828 callsign's aircraft's entry in /ai/models/multiplayer[]. This isn't strictly
2829 necessary, but simpifies debugging a lot and seems pretty lightweight. Note
2830 that we need to avoid special characters in the node name, otherwise the
2831 property system forces a fatal error. */
2832 std::string path = "/ai/models/callsigns/" + makeStringPropertyNameSafe(callsign);
2833 globals->get_props()->setIntValue(path, mp->_getProps()->getIndex());
2834
2835 return mp;
2836}
2837
2839FGMultiplayMgr::getMultiplayer(const std::string& callsign)
2840{
2841 if (0 < mMultiPlayerMap.count(callsign))
2842 return mMultiPlayerMap[callsign].get();
2843 else
2844 return 0;
2845}
2846
2847void
2848FGMultiplayMgr::findProperties()
2849{
2850 if (!mPropertiesChanged) {
2851 return;
2852 }
2853
2854 mPropertiesChanged = false;
2855
2856 for (unsigned i = 0; i < numProperties; ++i) {
2857 const char* name = sIdPropertyList[i].name;
2858 SGPropertyNode* pNode = globals->get_props()->getNode(name);
2859 if (!pNode) {
2860 continue;
2861 }
2862
2863 int id = sIdPropertyList[i].id;
2864 if (mPropertyMap.find(id) != mPropertyMap.end()) {
2865 continue; // already activated
2866 }
2867
2868 mPropertyMap[id] = pNode;
2869 mPropertyDefinition[id] = &sIdPropertyList[i];
2870 SG_LOG(SG_NETWORK, SG_DEBUG, "activating MP property:" << pNode->getPath());
2871 }
2872}
2873
2874
2875// Register the subsystem.
2876SGSubsystemMgr::Registrant<FGMultiplayMgr> registrantFGMultiplayMgr(
2877 SGSubsystemMgr::POST_FDM,
2878 {{"ai-model", SGSubsystemMgr::Dependency::HARD},
2879 {"flight", SGSubsystemMgr::Dependency::HARD},
2880 {"mp", SGSubsystemMgr::Dependency::HARD},
2881 {"time", SGSubsystemMgr::Dependency::HARD}});
static double scale(int center, int deadband, int min, int max, int value)
#define p(x)
#define i(x)
void getMessage()
Definition cpdlc.cxx:124
void disconnect()
Definition cpdlc.cxx:104
Wrap an FDM implementation in a subsystem with standard semantics Notably, deal with the various case...
Definition fdm_shell.hxx:44
void setFallbackModelIndex(const int i)
Definition AIBase.hxx:389
void setCallSign(const std::string &)
Definition AIBase.hxx:451
SGPropertyNode * _getProps() const
Definition AIBase.cxx:1040
void setPath(const char *model)
Definition AIBase.hxx:379
void addMotionInfo(FGExternalMotionData &motionInfo, long stamp)
void addPropertyId(unsigned id, const char *name)
PathList get_aircraft_paths() const
Definition globals.cxx:513
SGPropertyNode * get_props()
Definition globals.hxx:320
const SGPath & get_fg_root() const
Definition globals.hxx:189
friend class MPPropertyListener
void update(double dt) override
void shutdown() override
CPDLCManager * getCPDLC()
FGAIMultiplayer * getMultiplayer(const std::string &callsign)
std::shared_ptr< std::vector< char > > popMessageHistory()
void init() override
void reinit() override
void SendTextMessage(const std::string &sMsgText)
void pushMessageHistory(std::shared_ptr< std::vector< char > > message)
Encapsulate the FDM properties in some getter/setter helpers.
double get_wBody() const
double get_Theta() const
double get_uBody() const
double get_Q_body() const
double get_Psi() const
double get_Latitude() const
double get_vBody() const
double get_Longitude() const
double get_R_body() const
double get_P_body() const
double get_Altitude() const
double get_Phi() const
virtual void childAdded(SGPropertyNode *, SGPropertyNode *)
MPPropertyListener(FGMultiplayMgr *mp)
void setTarget(SGPropertyNode_ptr value)
Set the target property where the server-list gets stored.
void setService(const std::string &value)
Set the service name to use for the query.
void setDnsName(const std::string &value)
Set the dns domain name to query.
virtual void onSuccess()
Handler to be called if the resolver process finishes with success.
void setProtocol(const std::string &value)
Set the protocol name to use for the query.
virtual void onFailure()
Handler to be called if the resolver process terminates with an error.
const char * name
int fgGetInt(const char *name, int defaultValue)
Get an int value for a property.
Definition fg_props.cxx:532
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
Definition fg_props.cxx:556
bool fgSetInt(const char *name, int val)
Set an int value for a property.
Definition fg_props.cxx:568
FGGlobals * globals
Definition globals.cxx:142
std::vector< SGPath > PathList
Definition globals.hxx:37
const std::string IRC_DEFAULT_PORT
Definition mpirc.hxx:37
#define MAX_MODEL_NAME_LEN
#define OLD_PROP_MSG_ID
#define MAX_CALLSIGN_LEN
#define OLD_POS_DATA_ID
const uint32_t MSG_MAGIC
#define CHAT_MSG_ID
#define UNUSABLE_POS_DATA_ID
#define MAX_CHAT_MSG_LEN
const uint32_t PROTO_VER
#define POS_DATA_ID
#define OLD_OLD_POS_DATA_ID
const int MAX_PARTITIONS
const int BOOLARRAY_BLOCKSIZE
#define MAX_TEXT_SIZE
SGSubsystemMgr::Registrant< FGMultiplayMgr > registrantFGMultiplayMgr(SGSubsystemMgr::POST_FDM, {{"ai-model", SGSubsystemMgr::Dependency::HARD}, {"flight", SGSubsystemMgr::Dependency::HARD}, {"mp", SGSubsystemMgr::Dependency::HARD}, {"time", SGSubsystemMgr::Dependency::HARD}})
static xdr_data_t * encode_launchbar_state_for_transmission(const IdPropertyList *propDef, const xdr_data_t *_xdr, FGPropertyData *p)
const int BOOLARRAY_BASE_3
const int V2019_3_BASE
const int BOOLARRAY_START_ID
static const IdPropertyList sIdPropertyList[]
const int BOOLARRAY_BASE_2
static xdr_data_t * decode_received_launchbar_state(const IdPropertyList *propDef, const xdr_data_t *_xdr, FGPropertyData *p)
static const IdPropertyList * findProperty(unsigned id)
const int V1_1_PROP_ID
const int FALLBACK_MODEL_ID
const int BOOLARRAY_BASE_1
#define MAX_PACKET_SIZE
static bool do_multiplayer_refreshserverlist(const SGPropertyNode *arg, SGPropertyNode *root)
static bool do_cpdlc_connect(const SGPropertyNode *arg, SGPropertyNode *root)
const int V2018_3_BASE
static bool do_cpdlc_disconnect(const SGPropertyNode *arg, SGPropertyNode *root)
const int V2018_1_BASE
const int V2_PAD_MAGIC
const int V1_1_2_PROP_ID
static void copy_default(SGPropertyNode *from, const char *path, SGPropertyNode *to)
const int EMESARYBRIDGE_BASE
static std::string makeStringPropertyNameSafe(const std::string &s)
static bool do_cpdlc_next_msg(const SGPropertyNode *arg, SGPropertyNode *root)
const int EMESARYBRIDGETYPE_BASE
TransmissionType
@ TT_BOOL
@ TT_SHORT_FLOAT_1
@ TT_STRING
@ TT_BOOLARRAY
@ TT_FLOAT
@ TT_CHAR
@ TT_SHORT_FLOAT_2
@ TT_SHORT_FLOAT_3
@ TT_NOSEND
@ TT_SHORT_FLOAT_NORM
@ TT_INT
@ TT_SHORTINT
@ TT_ASIS
@ TT_SHORT_FLOAT_4
static bool do_multiplayer_connect(const SGPropertyNode *arg, SGPropertyNode *root)
const unsigned int numProperties
static bool do_cpdlc_send_msg(const SGPropertyNode *arg, SGPropertyNode *root)
const int BOOLARRAY_END_ID
const int V2020_4_BASE
const int MAX_BOOL_BUFFERS
static bool IsIncludedInPacket(int filter_base, int property_id)
static bool do_multiplayer_disconnect(const SGPropertyNode *arg, SGPropertyNode *root)
const int V2_PROP_ID_PROTOCOL
const std::string MPIRC_NICK_PREFIX
const std::string MPIRC_SERVER_HOST_PROPERTY
const std::string MPIRC_SERVER_PORT_PROPERTY
const std::string MPIRC_SERVER_HOST_DEFAULT
void addSentryTag(const char *, const char *)
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
Definition proptest.cpp:25
bool fgSetBool(char const *name, bool val)
Set a bool value for a property.
Definition proptest.cpp:24
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
Definition proptest.cpp:30
bool fgSetString(char const *name, char const *str)
Set a string value for a property.
Definition proptest.cpp:26
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27
static void replayMessage(FGReplayInternal &self, double time)
std::vector< FGPropertyData * > properties
simgear::props::Type type
TransmissionType TransmitAs
xdr_data_t *(* encode_for_transmit)(const IdPropertyList *propDef, const xdr_data_t *, FGPropertyData *)
simgear::props::Type type
const char * name
xdr_data_t *(* decode_received)(const IdPropertyList *propDef, const xdr_data_t *, FGPropertyData *)
char Text[256]
xdr_data_t RequestedRangeNm
char Callsign[8]
xdr_data_t Version
xdr_data_t MsgId
xdr_data_t ReplyPort
xdr_data_t Magic
xdr_data_t MsgLen
xdr_data_t linearVel[3]
xdr_data2_t lag
xdr_data2_t position[3]
xdr_data_t pad
xdr_data2_t time
xdr_data_t angularAccel[3]
xdr_data_t orientation[3]
char Model[96]
xdr_data_t angularVel[3]
xdr_data_t linearAccel[3]
xdr_data_t XDR_encode_int32(const int32_t &n_Val)
Definition tiny_xdr.cxx:71
xdr_data_t XDR_encode_shortints32(const int v1, const int v2)
Definition tiny_xdr.cxx:94
uint32_t XDR_decode_uint32(const xdr_data_t &n_Val)
Definition tiny_xdr.cxx:121
double XDR_decode_double(const xdr_data2_t &d_Val)
Definition tiny_xdr.cxx:192
xdr_data2_t XDR_encode_double(const double &d_Val)
Definition tiny_xdr.cxx:180
xdr_data_t XDR_encode_int8(const int8_t &n_Val)
Definition tiny_xdr.cxx:20
int8_t XDR_decode_int8(const xdr_data_t &n_Val)
Definition tiny_xdr.cxx:32
int32_t XDR_decode_int32(const xdr_data_t &n_Val)
Definition tiny_xdr.cxx:115
xdr_data_t XDR_encode_float(const float &f_Val)
Definition tiny_xdr.cxx:155
float XDR_decode_float(const xdr_data_t &f_Val)
Definition tiny_xdr.cxx:167
void XDR_decode_shortints32(const xdr_data_t &n_Val, int &v1, int &v2)
Definition tiny_xdr.cxx:99
xdr_data_t XDR_encode_uint32(const uint32_t &n_Val)
Definition tiny_xdr.cxx:109
uint32_t xdr_data_t
Definition tiny_xdr.hxx:30
uint64_t xdr_data2_t
Definition tiny_xdr.hxx:31
The buffer that holds a multi-player message, suitably aligned.
T_PositionMsg * posMsg()
const T_MsgHdr * msgHdr() const
const T_PositionMsg * posMsg() const
const xdr_data_t * propsRecvdEnd() const
xdr_data_t * propsRecvdEnd()
The end of properties actually in the buffer.
const xdr_data_t * propsEnd() const
xdr_data_t * propsEnd()
The end of the properties buffer.
const xdr_data_t * properties() const