rnp
This tool converts XML code developed in RNP scripting language into RPN language used by Microsoft Flight Simulator.
Aircraft instruments, model behaviors and other parts of MSFS use a scripting language based on Reverse Polish Notation (RPN), which is notoriously difficult to work with, because it uses a mathematical notation that is declared in reverse. For example, 3 + 9
is expressed as 3 9 +
and 2 * 4 + 5
is expressed as 2 4 * 5 +
. This makes it faster for computers to process, but much harder for humans to read and write, particularly when complexity grows beyond mere additions and multiplications.
RNP is an alternative scripting language that uses standard notation. Its name is a pun on RPN – it’s the reverse of Reverse Polish Notation.
This tool lets you use RNP scripting in instrument and model behavior XML files as if it was RPN and converts such XML files into RPN scripting language for MSFS. All you have to do is wrap RNP code inside <!--RPN-->
start tag and <!--RPN END-->
end tag and run your files through the tool.
RNP scripting language was originally created by FlyByWire Simulations. This version is a further development with many improvements, but remains fully compatible with code written for the original compiler.
Usage
msfs rnp [-help] [-nobackup] file
- -nobackup
- Skip automatic backup of the XML file before overwriting it. Backup copies are stored in Windows’ temporary folder.
- file
- Path to XML file containing RNP code.
Examples
Process “cubes.xml” file:
msfs rnp "cubes.xml"
Contents of “cubes.xml”:
<ModelInfo guid="{2905f7da-3367-4c5b-a459-0fc8cbbd83ee}" version="1.1">
<LODS>
<LOD ModelFile="cubes.gltf"/>
</LODS>
<Behaviors>
<Include ModelBehaviorFile="Asobo\Generic.xml"/>
<Component ID="cube_red" Node="cube_red">
<UseTemplate Name="ASOBO_GT_Visibility_Code">
<VISIBILITY_CODE>
<!--RNP bool-->
let is_visible = false;
if (E:TIME OF DAY) == 0 {
is_visible = true;
}
is_visible
<!--RNP END-->
</VISIBILITY_CODE>
</UseTemplate>
</Component>
<Component ID="cube_green" Node="cube_green">
<UseTemplate Name="ASOBO_GT_Visibility_Code">
<VISIBILITY_CODE>
<!--RNP bool-->
let is_visible = (E:TIME OF DAY) == 1;
let is_even_second = near((E:ABSOLUTE TIME, seconds)) % 2 == 0;
is_visible and is_even_second
<!--RNP END-->
</VISIBILITY_CODE>
</UseTemplate>
</Component>
<Component ID="cube_blue" Node="cube_blue">
<UseTemplate Name="ASOBO_GT_Visibility_Code">
<VISIBILITY_CODE>
<![CDATA[
<!--RNP bool-->
(A:AMBIENT TEMPERATURE, celsius) < 0
<!--RNP END-->
]]>
</VISIBILITY_CODE>
</UseTemplate>
</Component>
</Behaviors>
</ModelInfo>
This model configuration file belongs to a simple SimObject that has red, green and blue cubes:
- red – visible during dawn,
- green – visible on even seconds (blinks) during daytime,
- blue – visible when ambient temperature falls below 0°C.
Red cube initializes is_visible
local variable to false
and using if
block, sets it true
when the time of day is dawn (value 0
). The last line references is_visible
without semicolon at the end to use it as the return value.
Green cube sets is_visible
and is_even_second
local variables inline and returns their combined value. Both have to be true
for the return expression to evaluate to true
.
Blue cube is similar to red cube, but checks ambient temperature and makes the cube visibile when it’s below freezing. It does not use a if
-structure nor local variables. Instead, the comparison expression is directly set as return value. In addition, code for the blue cube is wrapped inside a CDATA block, which allows to use <
operator without having to escape it as <
(see Formatting).
Tip
You can get full MSFS project with the described Simobject by downloading simobject-cubes example from Examples.
Syntax
Start and end tags
RNP code blocks must have <!--RNP-->
tag at the start of the block and <!--RNP END-->
at the end:
<UseTemplate Name="ASOBO_GT_Update">
<FREQUENCY>1</FREQUENCY>
<UPDATE_CODE>
<![CDATA[
<!--RNP-->
if (E:TIME OF DAY) != 1 and (A:GENERAL ENG COMBUSTION SOUND PERCENT, percent) > 1 {
(A:LIGHT HEADLIGHT INTENSITY, percent) = 100;
} else {
(A:LIGHT HEADLIGHT INTENSITY, percent) = 0;
}
<!--RNP END-->
]]>
</UPDATE_CODE>
</UseTemplate>
This code sets vehicle headlights to 100% when it’s not daytime and its engine is producing sound (running).
Return values
If the code block returns a value, its type must be added to the starting tag:
<!--RNP bool-->
Supported return types:
void
– no value (default)any
– unspecified typebool
– true or falsenumber
– double-precision floating point numberstring
– text string
Comments
Single line comment:
// Simple comment
Multi-line comments:
/*
This multi-line comment
spans multiple lines
*/
Local variables
Declare a local variable:
let a = 1.0;
You can add aliases to simplify working with long variable names:
alias alt = (A:PLANE ALTITUDE, feet);
if alt < 50.0 {
// ... do something
}
Assignments
Set value to a local variable:
a = 20;
Set a simulation variable:
(A:LIGHT BEACON, bool) = true;
Expressions
Boolean true and false literals:
true;
false;
Numbers, hex and binary values:
1.0;
0xFF;
0b101010;
Strings – enclosed in single quotes:
'Remove before flight';
Simulation variables:
(A:LIGHT LANDING, bool);
(L:GPU_ATTACHED);
If you do not declare units (eg bool
) for a simulation variable, then number
is assumed for E:
, I:
, L:
, O:
, P:
, Z:
variables by default and they will be treated as holding generic floating-point numbers.
Templating
XML template substitutions:
let count = #COUNT#;
let vol = #VOLUME, number#;
If template variable is defined without unit, then it is assumed to be a floating-point number. Declared units are used only internally to check for potential errors.
Formatting
All expressions (except return values) must end with semicolon ;
:
let a = 25.0;
let b = 75.0;
let c = a + b;
c
You can wrap your code inside XML CDATA sections to use special XML characters &
, <
, >
directly without having to write them as &
, <
, >
:
<UseTemplate Name="ASOBO_GT_Update">
<FREQUENCY>1</FREQUENCY>
<UPDATE_CODE>
<![CDATA[
<!--RNP-->
// ... code here
<!--RNP END-->
]]>
</UPDATE_CODE>
</UseTemplate>
During processing, rnp tool automatically unpacks CDATA sections and escapes special characters.
Arithmetic operators
Arithmetic operators are very straightforward:
let a = 2 + 5;
You can also use parenthesis:
(L:MYVARIABLE) = 1 + 2 * 3;
(L:MYVARIABLE) = (1 + 2) * 3;
The examples above result in different order of operations:
1 2 3 * + (>L:MYVARIABLE)
1 2 + 3 * (>L:MYVARIABLE)
Supported operators:
Operator | Description |
---|---|
+ | Addition |
- | Subtraction |
/ | Floating-point division (eg 3 / 2 = 1.5 ) |
idiv | Integer division (eg 3 idiv 2 = 1 ), also available as div() function |
* | Multiplication |
% | Modulo |
** | Power, also available as pow() function |
Bit operators
Conditional logic with bit operators:
let nav_lights = 1;
let landing_lights = 4;
if ((A:LIGHT STATES, number) & (nav_lights | landing_lights)) != 0 {
// Nav and landing lights are on
}
Supported operators:
Operator | Description |
---|---|
& | Bitwise AND |
| | Bitwise OR |
^ | Bitwise XOR |
~ | Bitwise NOT |
>> | Shift bits to the right |
<< | Shift bits to the left |
Comparison operators
Basic comparisons:
if (E:TIME OF DAY) >= 2 {
// Dusk or night
} else {
// Dawn or day
}
Supported operators:
Operator | Description |
---|---|
== | Left side equals right side |
!= | Left side does not equal right side |
> | Left side is larger than right side |
< | Right side is larger than left side |
>= | Left side is larger or equal to right side |
<= | Right side is larger or equal to left side |
Conditionals
Conditional logic follows standard convention:
if val {
// do something
}
if val1 == val2 {
// do something
} else {
// do something else
}
if (E:LOCAL MONTH OF YEAR) == 1 {
// January
} else if (E:LOCAL MONTH OF YEAR) == 12 {
// December
} else {
// other months
}
Compact inline form:
let visible = if (E:LOCAL DAY OF WEEK) == 3 { 1 } else { 0 };
Functions
All functions listed in MSFS SDK are available in RNP:
let x = dgrd(90.0);
This converts 90 degrees to radians, resulting in x = 1.57
.
Refer to the SDK for detailed descriptions and examples of each function.
The table below summarizes available functions in the same order as they are listed in the SDK.
Function | Description |
---|---|
pmod(a, b) | Absolute modulo |
neg(val) | Negate a number |
abs(val) | Absolute value |
int(val) | Round down to nearest integer |
flr(val) | Round down to nearest integer |
rng(min, max, val) | True if val is within min .. max range |
cos(rad) | Cosine, input in radians |
lg(val) | Logarithm to base 10 |
min(a, b) | Smallest value of a and b |
sin(rad) | Sine, input in radians |
acos(val) | Arc cosine, input in radians |
ctg(rad) | Cotangent, input in radians |
ln(val) | Natural logarithm |
sqr(val) | Take square |
asin(val) | Arc sine |
eps(val) | Floating-point relative accuracy |
log(a, b) | Logarithm of a to the base of b |
pi() | PI value |
sqrt(val) | Square root |
atg2(y, x) | Arc tangent with two inputs, both in radians |
exp(val) | Exponent e to the power of val |
max(a, b) | Largest value of a and b |
pow(a, b) | Power of a to the value of b |
tg(val) | Tangent, input in radians |
atg(val) | Arc tangent with one input, in radians |
sign(val) | Sign of the value: -1 when negative, 1 when zero or positive |
dec(val) | Decimal part of floating-point value |
div(a, b) | Divide a by b and round down to nearest integer |
ceil(val) | Round up to nearest integer |
near(val) | Round up or down to nearest integer |
dnor(val) | Normalize angle expressed in degrees to 0 .. 360 range |
d360(val) | Normalize angle expressed in degrees to 0 .. 360 range |
rdeg(val) | Normalize angle expressed in degrees to 0 .. 360 range |
rddg(rad) | Convert radians to degrees |
dgrd(deg) | Convert degrees to radians |
rnor(rad) | Normalize angle expressed in radians to 0 .. 2*PI range |
seed(val) | Set seed for rand() function |
rand() | Generate a random number in 0.0 .. 1.0 range |
lc(str) | Convert string to lowercase |
uc(str) | Convert string to uppercase |
cap(str) | Convrt string to uppercase |
chr(int) | Convert integer into ASCII character |
ord(str) | Convert ASCII character into integer |
scat(str1, str2) | Concatenate two strings |
schr(str, char) | Finds the position of the specific character in a string |
scmp(str1, str2) | Case sensitive string comparison |
scmi(str1, str2) | Case insensitive string comparison |
sstr(str, sub) | Find the position of substring in a string |
ssub(str, start, end) | Extract substring from a string |
symb(str, pos) | Extract a single character from a string |
Stack operators
Stack operators are used internally for local variables and are not directly exposed.