Hand-Crafting Saveable Filters (Preview)
This page is for power users who want to author saveable animal filters by hand. It documents the on-disk XML format, every available field and comparator, the rules the evaluator follows, and a few real examples.
Status: Most players should use the in-game filter editor (RL Menu -> Settings -> Filters) - see the Saved Filters guide. This page remains a reference for the on-disk XML format and power-user hand-editing, and is intentionally kept out of the site navigation.
Note: This documentation was generated with AI assistance and may contain inaccuracies. If you spot an error, please open an issue.
Where the file lives
Filters are stored together with your RL settings, in your savegame folder:
~/Library/Application Support/FarmingSimulator2025/savegame<N>/rm_RlSettings.xml
(or the equivalent on your platform)
Edit only when the game is closed for that savegame. The host writes this file out on every settings change and on every game save, so live edits will be overwritten. Always make a backup before editing.
If you have not yet opened the savegame after installing the version that
moved filter persistence into rm_RlSettings.xml, the file may exist with
only your settings and no <filters> block. Add the block as shown below.
Top-level structure
The file root is <rm_RlSettings>. Filters live in a single <filters>
block as a sibling of the settings entries:
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<rm_RlSettings version="1">
<deathEnabled value="2"/>
<diseasesEnabled value="2"/>
<!-- ... other settings ... -->
<filters>
<filter id="my_first_filter" name="My first filter" animalType="COW" farmId="1" version="1">
<group op="AND">
<condition field="age" cmp=">=" value="60.000000"/>
<condition field="isPregnant" cmp="==" value="false"/>
</group>
</filter>
<!-- more <filter> entries ... -->
</filters>
</rm_RlSettings>
Every filter has exactly one root <group>. Group operators can be nested
arbitrarily deep.
<filter> attributes
| Attribute | Required | Type | Notes |
|---|---|---|---|
id |
yes | string | Stable unique identifier. Any string works; production filters created via the in-game flow use the prefix rlFilter_. Hand-authored ids can be any short slug, e.g. cow_butcher. Must be unique within the file. |
name |
recommended | string | Display name shown in the in-game cycle chip and lists. Optional: the loader defaults a missing name to an empty string. Only id and the .group subtree are hard requirements. |
animalType |
optional | string | Scope to a single animal type: COW, SHEEP, PIG, CHICKEN, HORSE and bridge-mod types (e.g. RABBIT on Witcombe). Omit the attribute for global scope (filter shows on every animal-type tab). |
farmId |
optional | integer | Scope to a single farm by id (1, 2, ...). Omit for global scope (visible to every farm). |
usage |
optional | string | Controls which screens show the filter: ANY (default), OWNED (owned-herd screens), DEALER (buy screen). Unknown values load as ANY with a warning. |
version |
optional | integer | Schema version for forward compatibility. Default 1. Leave at 1 unless future docs say otherwise. |
A filter with animalType="COW" farmId="1" only appears on Farm 1's cow tab.
A filter with both attributes omitted appears everywhere. The attributes are
immutable once a filter is created through the in-game flow; for
hand-authored filters they are honoured as-written each load.
Groups: AND / OR
Every filter has exactly one root <group>. A group has one attribute,
op, which is either AND or OR. A group's children are any mix of
<condition> and <group> elements:
<group op="AND">
<condition .../>
<group op="OR">
<condition .../>
<condition .../>
</group>
</group>
Vacuous truth (important):
- An empty
ANDgroup (<group op="AND"/>with no children) evaluates to true for every animal. Useful as a "match-all" placeholder. - An empty
ORgroup evaluates to false for every animal. - Any other (non-AND, non-OR) op is treated as false to fail-closed.
There is no NOT operator in the current schema. Negation lives at the
condition level via !=, notin, notcontains, or by inverting a bool
condition (isPregnant == false).
Conditions
A condition tests a single field against one or more values. There are two shapes depending on the comparator:
Scalar form (one value)
<condition field="age" cmp=">=" value="60.000000"/>
<condition field="gender" cmp="==" value="male"/>
<condition field="isPregnant" cmp="==" value="false"/>
<condition field="name" cmp="notcontains" value="keep"/>
The value attribute is interpreted per the field's type - see the
Field reference below.
List form (multiple values, only for in / notin)
<condition field="subType" cmp="in">
<value value="BULL_HOLSTEIN"/>
<value value="BULL_REDHOLSTEIN"/>
<value value="BULL_JERSEY"/>
</condition>
Use the list form for in and notin comparators. Each <value> element
has a single value attribute. An empty list (no <value> children):
inagainst an empty list -> false (the animal can't be "in" nothing)notinagainst an empty list -> true (vacuously not-in)
Mixing forms (a value= attribute and <value> children on the same
condition) is undefined; the engine reads the form that matches the
comparator's expected shape.
Field reference
| Field | Type | Comparators | Scope | Notes |
|---|---|---|---|---|
age |
number | < <= == != >= > in notin |
all | Months, integer in practice. |
gender |
enum | == != in notin |
all | Values: male, female (lowercase, internal keys). |
isCastrated |
bool | == |
all | true / false. |
isPregnant |
bool | == |
all | true / false. |
isLactating |
bool | == |
COW only | Other species ignore this field. Goats (a SHEEP subtype, subType="GOAT") do track lactation internally, but this filter field is scoped to COW, so an isLactating condition always evaluates false for goats. |
hasName |
bool | == |
all | True if the animal has a non-empty player-set name. |
hasAnyDisease |
bool | == |
all | True if at least one disease is currently active. |
hasAnyMark |
bool | == |
all | True if any mark is active (player-set or AI-manager). |
name |
string | contains notcontains |
all | Case-insensitive substring match. Unnamed animals are treated as empty string, so notcontains anything is true for them. |
weight |
number | < <= == != >= > in notin |
all | Monitor-gated. Returns no value when the animal's monitor is not active; the condition then evaluates to false. |
health |
number | same as weight | all | Monitor-gated. Same as weight. |
genetics.metabolism |
number (0-99) | < <= == != >= > in notin |
all | Scaled to 0-99. |
genetics.health |
number (0-99) | same | all | Scaled to 0-99. |
genetics.fertility |
number (0-99) | same | all | Scaled to 0-99. |
genetics.quality |
number (0-99) | same | all | Scaled to 0-99. |
genetics.productivity |
number (0-99) | same | COW, SHEEP, CHICKEN | Scaled to 0-99. Other species don't carry productivity. |
genetics.overall |
number (0-99) | same | all | Average of the present genetics traits, then scaled to 0-99. Matches the on-screen overall genetics number. |
subType |
enum | == != in notin |
all | The internal breed/variant string. Examples (vary by map/bridge): COW_HOLSTEIN, BULL_HOLSTEIN, SHEEP_TEXEL, RAM_TEXEL, CHICKEN, CHICKEN_ROOSTER, RABBIT, RABBIT_MALE. To find the exact string for a breed on your map, open rm_RlAnimalSystem.xml and look at the subType="..." attribute on any <animal> entry. |
Type rules at a glance:
- number comparators require numeric values on both sides. Integer or float literals both work in the XML; ages are integer in practice, genetics/weight/health are floats.
- bool values are the literal strings
true/false. - enum values are stable lowercase / uppercase internal keys (see notes per field). Enum keys are case-sensitive.
- string comparators (
contains/notcontains) are case-insensitive substring matches.
== and != enforce type strictness: a number-vs-string comparison
returns false rather than fail-open, so a non-numeric typo like value="abc"
on the age field yields nil and will silently exclude every animal.
Comparators by type
| Type | Comparators |
|---|---|
| number | < <= == != >= > in notin |
| bool | == only |
| enum | == != in notin |
| string | contains notcontains |
!= is intentionally absent for bool (use == against the negated value).
== and != are intentionally absent for string (the substring matcher
is the only intended path; for an exact match, use contains <fullName>
on a sufficiently-unique substring).
There is no between operator. Express ranges as
AND(field >= low, field <= high).
Behaviours that catch authors out
Monitor-gated fields silently fail. weight and health only return
a value when the animal's monitor is active. If you write
<condition field="weight" cmp=">=" value="200"/>, animals without an
active monitor never match. Combine with another field if you want a
fallback.
name on unnamed animals is the empty string, not nil. This means
name notcontains "keep" is true for every unnamed animal - they
vacuously do not contain "keep". This is intentional so that herds with
mostly-unnamed animals still get filtered sensibly.
hasName and name are independent. hasName == false filters to
unnamed animals; name contains "betty" filters to animals named with
"betty" anywhere in their name. Use the one that matches your intent.
Empty AND matches everything. A condition that fails to load
(unknown field, unknown comparator, etc.) is dropped during load, but
the group is preserved. If every condition in an AND group is
dropped, you end up with an empty AND that matches every animal.
Watch the game log for RLFilterSerialization warnings on first load
to catch this.
Genetics are 0-99, not raw multipliers. The engine stores genetics
internally as 0.25-1.75 multipliers but every filter field exposes them
on the same 0-99 scale you see on the in-game info card.
genetics.health >= 75 does what you expect.
Filter id collisions silently overwrite. If two <filter> entries
share the same id, the second one wins on load. Pick distinct ids.
Validation behaviour
The filter loader is fail-closed: a malformed condition is dropped with a warning, not silently passed through. On first load after editing, watch the game log for warnings like:
| Log line | Meaning | Fix |
|---|---|---|
RLFilterSerialization.readCondition: unknown field 'X' ... |
Field name doesn't exist in the catalog. | Check spelling against the Field reference. |
RLFilterSerialization.readCondition: cmp 'X' is not in whitelist for field 'Y' ... |
Comparator isn't valid for that field's type. | Pick a comparator from the field's row in the table. |
RLFilterSerialization.readCondition: missing field/cmp at ... |
One of the required attributes is absent. | Add the missing attribute. |
RLFilterSerialization.readFilter: missing id |
<filter> lacks an id attribute. |
Add a unique id. |
RLFilterSerialization.readFilter: filter id=... has no .group subtree; skipping (corrupt save?) |
<filter> has no <group> child. |
Wrap conditions in a <group op="AND">...</group>. |
A filter that loads cleanly but matches every animal usually means an
empty AND group - see Empty AND matches everything above.
Examples
The filters below are real working examples. Adapt the id, name,
animalType, farmId, and the breed strings (subType values) to your
own savegame.
1. Cull old or non-pregnant cows
<filter id="cow_cull" name="Cow cull" animalType="COW" farmId="1" version="1">
<group op="OR">
<condition field="genetics.overall" cmp="<" value="75.000000"/>
<group op="AND">
<condition field="age" cmp=">=" value="48.000000"/>
<condition field="isPregnant" cmp="==" value="false"/>
</group>
</group>
</filter>
Selects any cow with overall genetics below 75, or any cow that is at
least 48 months old and not currently pregnant. Note < - the <
character must be entity-encoded inside an XML attribute value.
2. Sell chickens for profit, but spare marked birds
<filter id="chicken_sell_profit" name="Sell chickens for profit" animalType="CHICKEN" farmId="1" version="1">
<group op="AND">
<condition field="subType" cmp="in">
<value value="CHICKEN"/>
<value value="CHICKEN_ROOSTER"/>
</condition>
<group op="OR">
<condition field="age" cmp=">=" value="60.000000"/>
<group op="AND">
<condition field="age" cmp=">=" value="6.000000"/>
<condition field="hasAnyMark" cmp="==" value="false"/>
</group>
</group>
</group>
</filter>
Restricts to base chicken subtypes, then matches anything 60+ months old, or 6+ months old without any mark.
3. Move "any animal called 'keep' should NOT match" to a butcher list
<filter id="cow_butcher" name="Move to butcher" animalType="COW" farmId="1" version="1">
<group op="AND">
<condition field="age" cmp=">=" value="12.000000"/>
<condition field="isPregnant" cmp="==" value="false"/>
<condition field="hasAnyMark" cmp="==" value="false"/>
<condition field="name" cmp="notcontains" value="keep"/>
</group>
</filter>
Cows aged 12+ months, not pregnant, no active marks, with no "keep" substring in their name (case-insensitive). Unnamed cows pass the name check vacuously, so the filter is still useful in mostly-unnamed herds.
4. Find dairy breeding bulls in a specific genetics window
<filter id="dairy_bull_pick" name="Dairy breeding bull" animalType="COW" farmId="1" version="1">
<group op="AND">
<condition field="gender" cmp="==" value="male"/>
<condition field="genetics.overall" cmp=">=" value="80.000000"/>
<condition field="subType" cmp="in">
<value value="BULL_HOLSTEIN"/>
<value value="BULL_REDHOLSTEIN"/>
<value value="BULL_JERSEY"/>
</condition>
</group>
</filter>
Bulls (male cows) with overall genetics 80+ and a dairy-breed subtype.
5. Match-all (placeholder while you draft)
<filter id="match_all" name="All animals" version="1">
<group op="AND"/>
</filter>
Empty AND is vacuously true, so this filter matches every animal in
scope. Useful as a starting point when iterating: edit the group, reload,
test, repeat.
Limitations and notes
- No
NOToperator. Use!=,notin,notcontains, or invert a bool condition. - No
betweenoperator. UseAND(field >= low, field <= high). - No date-based fields.
ageis months only. - Per-mark-kind filtering (player vs. AI-manager mark) is not in this
schema; only the generic
hasAnyMarkis exposed. - The
subTypestrings depend on which mods/maps you have installed. When in doubt, copy the exact string from an existing<animal subType="..."/>line inrm_RlAnimalSystem.xml. - Multiplayer: filters are server-authoritative. Hand-edits on the host propagate to clients automatically on next reconnect or filter mutation.
Quick reference card
<rm_RlSettings>
<filters>
<filter id="<unique>" name="<display>" animalType="<TYPE>" farmId="<N>" usage="ANY|OWNED|DEALER" version="1">
<group op="AND|OR">
<condition field="<key>" cmp="<op>" value="<scalar>"/>
<condition field="<key>" cmp="in|notin">
<value value="<v1>"/>
<value value="<v2>"/>
</condition>
<group op="AND|OR"> ... </group>
</group>
</filter>
</filters>
</rm_RlSettings>
XML entity reminders inside attribute values:
- < -> <
- > -> >
- & -> &
- " -> "