Some people have been confused in what this is so Iâll try to explain what I know about FS2020 and what I have built. Some of this is well known.
FS2020 is built on ESP (FSX/P3D) which is why there are documents explaining the same concepts with slight differences around the web. The new FS uses CoherentGT, which is a system for enabling web technologies (HTML/CSS/JS) to be used within the sim, with a C++ interop layer so that the technologies can be mixed and matched and still have a high frame rate.
The engine within the sim maintains a set a variables that are available to most components. Things like INDICATED AIRSPEED
and also things like LIGHT TAXI ON
are maintained and communicated through these global variables. The aircraft variables in the SimConnect SDK are known as A:
variables, as they can be prefixed with A: when used in the xml gauge system. There are also L:
(local) variables like L:HUD_AP_SELECTED_ALTITUDE
that are still global, but created and used independently by a gauge or model xml. The real difference is that they are defined and used freely vs. being predefined and filled in by the system.
Within the sim, Coherent loads essentially one webpage per entry in panel.cfg
For example:
[VCockpit01]
size_mm=1280,800
pixel_size=1280,800
texture=$Touchpad
htmlgauge00=NavSystems/AS3X_Touch/AS3X_Touch.html,0,0,1280,800
The above instructs the game to load the gauge from NavSystems/AS3X_Touch/AS3X_Touch.htm
at 1280x800. pixel_size
and size_mm
are used for scaling the rendered web content into the texture in the game.
Inside the game they maintain a VFS, or virtual file system (see the MSFS SDK for further detail). The VFS is very simple, it is a layered data structure where all the packages share the same file paths, but last-writer-wins when there is a conflict. So if you install a package with a duplicate NavSystems/AS3X_Touch/AS3X_Touch.html
then it will load your version instead of whatever package was registered before you.
The game uses this VFS layout:
- Base packages (Packages folder within the game directory)
- Official packages (Downloaded the first time you ran the game)
- Community packages (Addons)
When we run the gauges externally, we add one additional package source to our VFS, which enables having a layer that is added ONLY for external use. The ExternalPackages
folder will always be loaded in Chrome but never loaded within the game itself.
The sim will navigate a browser to VCockpit.html
for each gauge. Asobo either uses or left us a hook in vcockpit.js
that automatically loads window.globalPanelData
. The global data has all the information in the [VCOCKPITXX]
section from the panel.cfg
.
Ok now we see that panel.cfg
instructs the sim to load VCockpit.html
on the sim VFS with some fixed data that tells it to load a component tree at NavSystems/AS3X_Touch/AS3X_Touch.html
with a size of 1280x800.
This will work, but first you need to do some fixes:
-
html imports
are deprecated. We need a polyfill. I used GitHub - webcomponents/html-imports: HTML Imports polyfill - this goes at the top of VCockpit.html. Without this all the text/html
templates wonât be loaded.
- common.js:
-
- It will try to load coui://html_ui/ URLs. These need to be cut off as the browser needs to get a regular relative path request instead, otherwise youâll see errors in the console about trying to load coui:// URLs.
-
- Name_Z::isValid would pop on transitions with an undefined
a.str
. Didnât investigate fully.
-
- Disable
fastToFixed
because it produces number like 0.-7
on Chrome/Windows. WorkingTitle mods also include this and thatâs why I have an edit for CommonPFD_MFD.js
in the external package, to nop itâs copy of fastToFixed
.
These changes are isolated in this commit, so it will be easier to redo them on the next sim update.
And now VCockpit.html with global data will display any gauge in the game! The VFS must be hosted on a webserver and with the above fixes and some static configuration data, you can load any gauge.
One last note here, is that all these paths will exceed Windowsâ MAX_PATH
limit, so I work around this by using SUBST
command to map a drive and chop off most of the path.
So now the gauge loads but we donât have any live sim data or real data at all. So we need to bridge in live sim data. All the sim data comes through these methods in the game, from simvar.js
in one of the base packages:
SimVar.GetSimVarValue
SimVar.SetSimVarValue
SimVar.GetSimVarArrayValues
SimVar.SetSimVarValue
SimVar.GetGlobalVarValue
SimVar.GetGameVarValue
SimVar.SetGameVarValue
You can use SimVar.GetSimVarValue
to read any A:
(without prefix) or L:
variable. SimVar.SetSimVarValue
can set L:
values and probably A:
's that are defined as readwrite in the SDK.
SimVar.GetGameVarValue
is a catch-all for things that are used like variables but arenât defined the usual way. Some of these read from cockpit.cfg
or some interpreted version of flight_model.cfg
.
All Iâve seen out of SimVar.GetGlobalVariable
is ZULU TIME
in seconds
.
SimVar.GetSimVarArrayValues
is very special, itâs used only for fs9gps calls, and this is currently not implemented due to the complexity of getting this data across the channel. This is used to set the current position an then query for nearest airports, VOR, etc. This drives the map and the flight planner.
So we need all these to work somehow. I replace the gameâs version of SimVar
.js with my own which points to SimVarBridge.js
which implements a table of âknown varsâ and makes requests through a websocket to the server to read/write values. The server continually sends updated data and thus the table of values is updated for the next calls to SimVar
.
The server serves the web resources and also uses SimConnect to connect to FS2020, then uses the client data regions to establish a connection with BridgeGauge.wasm
which is loaded via the community folder. The BridgeGauge.wasm
calls execute_calculator_code
to read and write L:vars. Right now this needs improvement:
- Source A: vars via external SimConnect directly.
- Replace with
get_named_variable_value
instead of execute_calculator_code
calls.
- Register strings instead of passing them over and over.
These changes will result in high fps.
The last part is InGameBridge.js
which gets loaded via the Community folder into the game, and continually copies some values into L:
vars so the bridge can pick them up in a predictable place. This is how the AIRCRAFT ORIENTATION AXIS
GameVar is coming through. The bridge can ONLY read L:
and A:
so anything else needs to be proxied through InGameBridge.js
first.
In summary this covers:
- How to get all the VFS package sources (Anzu)
- How to work around file paths being longer then MAX_PATH
- How the packages are composed
- Which files drive the HTML gauge system
- A set of bridge components into the FS that read/writes data