Skip to content

2.49 How It Works Dataref Encoding and Decoding

tngreene edited this page Sep 16, 2019 · 2 revisions

This represents, to the best of my research, how datarefs are encoded into aa lookup dictionary, bone names, and game properties, then decoded and used in .obj files.

Relevant Files

Not exhaustive, or sorted in any particular way. Lines may not match completely with official source.

  • XPlaneAnimObject.py
    • def doapply(evt,val)
    • def getvals(bonename, dataref, index)
  • XPlaneExport8_util.py
    • def getdataref(self, object, child, name, suffix, first, count) and it's uses in Anim.init
    • def getcustomdataref(object, child, thing, names)
  • XPlaneUtils.py
    • def getDatarefs()
    • def make_short_name(fullpath) #fullpath is a lie, it can be any text
    • def remove_vowels(s)
  • Datarefs.txt (which will not necessarily only have laminar research content.)

Definitions

  • Dataref, Dataref path*, full dataref: A full dataref, unshortened, and not a component of it. Has a tail and head. For example: sim/aircraft/acf_struct - acf_struct is the tail, sim/aircraft is the head (notice the lack of trailing '/')
  • Known Dataref: A dataref that came from Datarefs.txt
  • Unknown Dataref: A dataref that came from some other place, such a lua script
  • Ambiguous Dataref: A dataref which requires additional steps and bookkeeping to look up and to a bone names, and game properties. Known and unknown datarefs can be ambiguous with other datarefs
  • datarefs dictionary: The dictionary of datarefs from parsing Datarefs.txt. Annoyingly it is sometimes referred to in code as datarefs which is a pretty generic name for a very very important concept
  • sname, short name: A full dataref that has been run through make_short_name.
  • tail-name: The tail of a dataref, referring to its use as a type of encoding information for bone names and the datarefs dictionary
  • "[idx]", brackets, idx portion: bone names, game props, and outputted OBJ directives can include an array index as part of the text saying to X-Plane what index in the chosen dataref's array. A lot of string munging involves adding and removing this (as needed). "[1]" is the brackets portion of "my/example/ref[1]". There is one weird exception to this rule involving some of sim/weather props where a tail-name could include this.
  • Bone Name: Bone names are a short dataref to be looked up in the datarefs dict, or the tail-name to be looked up in a more complex manner (datarefs dict or in getcustomdataref)
  • Parent Armature: Each bone has a parent armature where game properties are stored
  • Anim-Value Game Properties: Game properties that connect animation values like v1,v2,...,loop to a dataref. Anim-Value refers to "Dataref Animation Value", not the value side of the property [key,value].
  • Disambiguating Key:For the lookup process, ambiguous and unknown values need a key-value pair that can reconstruct the full dataref without the help of Datarefs.txt (aka the contents of the datarefs dictionary)

One last example: my/example/ref int[20] y, and we're animating with my/example/ref[1]. We're assuming that it is found in Datarefs.txt

Text Name
my/example/ref Full Dataref
"my","example","ref" Components
my/example Head aka Head of Dataref
ref Tail of dataref, tail-name. Found in Datarefs Dictionary
ref[1] Tail + [idx]. Not found in Datarefs Dictionary. This is not tail-name.
ref[1] Bone name. Since "ref" is so short, we're using the tail-name + [idx]
me_ref sname (hint: look at make_short_name for how its made). Found in Datarefs Dictionary
me_ref[1] sname + [idx]
me_ref[1]_v2:.75 Anim-Value Game Prop on the parent armature: At frame 2 of my/example/ref[1] the dataref value will be .75
{ 'me_ref':('my/example/ref', 20),ref:('my/example/ref', 20)} The contents of the datarefs dictionary (excluding other keys). Two keys point to the same full dataref.

If my/other/example/ref int[20] y were also in the dictionary, we'd have duplicate tail-names representing an ambiguity.

{'moe_ref': ('my/other/example/ref', 20), 'me_ref': ('my/example/ref', 20), 'ref': None}

Notice both datarefs have sname keys pointing towards them, but there is only one tail-name key pointing to None.

If my/example/ref was not in Datarefs.txt we'd have a disambiguation key in any parent armature who had bones who used my/example/ref.

Text Name
ref:my/example Disambiguation Key, the tail component (never shortened) with Head value. Disambiguation Key != (Bone Name - [idx]) when Bone Name is of type sname! (This edge case is discussed later)

Got it? Try to keep up, we've got pages more of this.

The datarefs dictionary

Generated by XPlaneUtils.getDatarefs, this dictionary represents the contents of your chosen Datarefs.txt. In code it is usually called datarefs or lookup.

Properties of a Datarefs Dictionary:

Each dataref in your Datarefs.txt will have two keys (shortened full dataref and unshortened tail) attempting to point at a record of the full dataref and it's array size (1 if scalar).

There will always be exactly 1 sname and 1 tail-name attempting to pointing at the same full dataref.

Using the datarefs dictionary with an sname is never None, using it with a tail-name is possibly None.

Any attempts to use a duplicate sname will be ignored and the current parsed value won't be stored.

Any attempts to use a duplicate tail-name will set lookup[tail_name] to None (representing an ambiguity).

The Datarefs Dictionary as a Python 3.4 type annotation might be getDatarefs() -> Dict[Union[ShortenedDataref,tail-name],Optional[Tuple[FullDataref,ArraySize]]]

Looking Up Full Datarefs With Datarefs Dictionary

The idea of the lookup dictionary is: "Given some input (sname, or tail-name) we can find the full dataref or say that there is an ambiguity. All of a valid Datarefs.txt file will be read in and perfectly usable, even trying to handle datarefs that resolve to duplicate snames or have duplicate tail-names".

Messy Parts Of The Datarefs Dictionary Lookup System

  • Only dataref path is considered in detecting dictionary collisions. Other data like type, array size, units, or description are ignored.

    • sim/whatever int n
    • sim/whatever int[250] n

    In this example, you will get { "whatever": (sim/whatever, 1), whatever:None }, not two datarefs as expected. A warning is given about ambiguous short names.

  • sname collision is particularly dangerous

    • "sim/bad_choice/mychoice"
    • "sim/boring_choice/mychoice"

    In this example, you will get {sb_mychoice:(sim/bad_choice/mychoice),mychoice:None}, not two datarefs as expected.

  • Array indexes in dataref path are kept

    • There are several known datarefs (sim/weather/cloud_type, sim/weather/wind_altitude) which have the array index in their names, as in sim/weather/wind_altitude_msl_m[0]. This is legal. While laminar datarefs do not appear to have this AND be an array type like int[25], it could be possible. The spec here (which there isn't one) is undefined. While not breaking, it is a large potential for bugs and confusion, as there is ambiguity between which is the right answer: (sim/weather/wind_altitude_msl_m[2],1) vs (sim/weather/wind_altitude_msl_m[2],3). According the the dataref as defined, it has no array. "[2]" is simply text in the path.
    • An .obj with a dataref like this gets exported fine. The game properties used are sw_cldbsmslm[0]_v1 and v2, bone name is cloud_base_msl_m[0]. Even with an ambiguous tail with "[idx]" still works.
    • This is the point of getdataref's use of seq=[ref, name] we think, to check "wind_altitude_msl_m" and "wind_altitude_msl_m[0]". The later seq.append(make_short_name(dataref)) is actually important! ref could come from a bone whose name came from an sname or a tail-name.
  • We fail on 'sim/weather/wind_direction_degt', 'sim/weather/wind_direction_degt[0]' and bone name is wind_direction_degt[0]

    This fails for us because our implementation's lookup won't know how to choose between the options. Currently there is a manual hard-coded fix for this one edge case. Other user's custom DataRefs.txt that have datarefs like this will be told to change it or they can open the source themselves and add an exception. See 249: sim/weather/wind_direction_degt vs sim/weather/wind_direction_degt[n] is a problem for more details.

Bone Name,Datarefs Dictionary,Game Properties Soup

Matching a bone name to game properties is done with getdataref and getcustomdataref.

  1. Using the bone name, attempt to get the full dataref path.

    a.) If it is a known unambiguous key ((bone_name in datarefs and datarefs[bone_name]) is True,

         use datarefs[bone_name].*
    

    b.) If it is a known but ambiguous key ((bone_name in datarefs and datarefs[bone_name] is None) is True) or unknown**, search the properties for the disambiguating key (using bone_name - "[idx]")***.

         Combine tail-name + value to get dataref.
    
  2. Using your dataref, make an sname of it (to make sure we can access Dataref Value For Frame and Loop properties). Using [bone_name, bone_name + "[idx]" (if needed), short_name_of_dataref], iterate and match each property to its dataref.

*If the datarefs dictionary is queried using a bone name that is of type tail-name, and datarefs[bone_name] is None it is counted as ambiguous even if the sname lookup would have worked

**In the unknown case, you'd better hope that the bone name used tail-name form and NOT sname, because the disambiguating key is always tail-name:head! If the unknown key's bone name is using sname you'll never be able to match the bone name to any disambiguating key! There is no validation for this

***In the known but ambiguous case, you are guaranteed, to be using a tail-name (otherwise, you'd be following code path a!).

****You had also better hope that the tail is short enough because the prop keys also have a size limit of 31 characters and will silently cut off extra characters. "AB_CDFGHJKLMNPQRSTVWXYZ012345" only has room for "_v".

Anim-Value Game Property: sname or tail-name based key?

Game properties are used to represent the saved input from the AnimObject menu. The process involves appending extra text to an sname or tail-name. The 2.49 automatically trims space around property and bone names, remove .001,.002, etc from bone names, and will ignore a trailing / on the value of a disambiguating key. Any key that is too long gets truncated without warning.

Anim-Value Encoding Type Pattern Notes
Disambiguating Key tail-name tail-name:head See XPlaneAnimObject, line 503
Dataref Value for Frame N sname + any "[idx]" "name_vN" or "name[idx]_vN" where N is the frame number Unless specified, the first two values are assumed to be 0.0 and 1.0 and not necessary to define.
Loop sname + any "[idx]" "name_loop" or "name[idx]_loop"
Hide/Show tail-name + any "[idx]" "name_hide/show_vN" or "name[idx]_hide/show_vN" Unlike Dataref Value for Frame N, there are always two properties per Hide/Show. Also, it is not tied to a bone's name!

2.49 Python vs 2.78 Python Types

Concept 2.49 2.78
Action Dict[channel_name,Ipo] List[FCurves] but also has groups of channels to organize it
Channel Uses bone name, semanti List[FCurves], semantics, subsection of Action's list
Group Optional Mandatory, List[Channels], name is bone name
Curve IPOCurve FCurve
Anim Data in bezierPoints in keyframe_points

During read, 2.7x turns channels into groups and IPOCurves into FCurves.

Automatic Dataref Value Filling

During export, 2.49 replaces missing dataref values with 0 (if unspecified) and 1 for deleted middle keys and the last key (if unspecified) so that every Blender keyframe has a pair.

An ex 7 frame animation:

nth Frame Value In Game Props? As In OBJ
1 0 No ..._key 0
2 0.0 Yes ..._key 0.0
3 2001.0 Yes ..._key 2001.0
4 -3999.0 Yes ..._key -3999.0
5 1 No (deleted) ..._key 1
6 1.0 Yes ..._key 1.0
7 1.0 No ..._key 1

Appendix: Useful Datarefs To Know About

Known Ambiguous SNames

(List from first read to last. None of these appear in the list of ambiguous tail-names) Notice the difference between engine vs enigne (Misspelling is marked as deprecated)

SName Dataref
scs_ntcnlthtprngn sim/cockpit/switches/anti_ice_inlet_heat_per_enigne
scs_ntcnlthtprngn sim/cockpit/switches/anti_ice_inlet_heat_per_engine

Notice the difference between _value and _avail! What a coincidence! Fortunately, we can use tail-name to look them up, and they won't be shortened

SName Dataref
sj_jymppdxsvl sim/joystick/joy_mapped_axis_avail
sj_jymppdxsvl sim/joystick/joy_mapped_axis_value

Notice the difference between failures and failures

SName Dataref
sof_nblrndmflrs sim/operation/failures/enable_random_falures
sof_nblrndmflrs sim/operation/failures/enable_random_failures

Known Ambiguous Tailname

  • sim/joystick/yoke_roll_ratio and sim/cockpit2/controls/yoke_roll_ratio

Known With Long Tail

  • sim/flightmodel2/engines/engine_rotation_speed_rad_sec (Can't be used with Show/Hide because tail is so long)

Formerly Known, Now Removed

  • sim/aircraft/acf_struct int[10] n

Unknown And Testable

  • laminar/c172/fuel/fuel_tank_selector, has values at 1, 2, 3, and 4
  • `laminar/c172/knob_OATS, has values at 0 and 1

Unknown With Long Tail

  • A/B/CDEFGHIJKLMNOPQRSTUVWXYZ123 (Unknown dataref of length 30, with tail of 27. sname can be a bone name, the tail triggers shortening causing a later comparison of , AB_CDFGHJKLMNPQRSTVWXYZ123 and CDEFGHIJKLMNOPQRSTUVWXYZ123)

Weird [idx] cases

  • sim/weather/cloud_type[0], easily testable with changing the weather
  • sim/weather/cloud_base_msl_m[0]
  • sim/weather/wind_altitude_msl_m[1]

The double trouble double/trouble/idx[0] int[10] y case will not export, saying it is deformed.

Magic Numbers

  • If a name is > 26 characters long, it must be shortened
Clone this wiki locally