Use at your own risk. Everything here is shared by the community and is not tested or security-checked by rA Vault. Always try it on a sandbox / test server first.Not security-checked. Test on a sandbox server first. Read the disclaimer

Population Engine (Advance Fake Players)

A drop-in fake-population system for rAthena that fills your Ragnarok Online server with persistent, AI driven shell players making every zone feel alive without a single real player online.

by Louis T Steinhil Β· listed Oct 29, 2026

Population Engine

- A drop-in fake-population system for rAthena that fills your Ragnarok Online server with persistent, AI driven shell players making every zone feel alive without a single real player online.

What Is It?

- Population Engine (PE) spawns virtual player characters (called "shells") that look, walk, fight, sit, and vend exactly like real players to any connected client. They are not monsters they appear in the player list, have names, equipment, classes, and behaviors. They require zero client modifications and work with any standard rAthena compatible RO client.

src/map/
β”œβ”€β”€ population_engine.cpp          ← Core engine (spawning, timers, vendors, arena)
β”œβ”€β”€ population_engine.hpp          ← Public API
└── population_engine/
    β”œβ”€β”€ core/
    β”‚   β”œβ”€β”€ population_engine_core.hpp    ← All types, enums, constants
    β”‚   β”œβ”€β”€ population_shell_state.hpp    ← Per-shell runtime struct (sd->pop)
    β”‚   β”œβ”€β”€ pe_perf.hpp / pe_perf.cpp     ← RAII performance profiler
    β”œβ”€β”€ config/
    β”‚   β”œβ”€β”€ population_config.hpp/.cpp    ← YAML DB loaders and lookup functions
    └── runtime/
        β”œβ”€β”€ population_engine_combat.cpp  ← Combat AI tick
        β”œβ”€β”€ population_engine_path.cpp    ← Wander/movement AI
        └── population_shell_runtime.cpp  ← Target tracking, hotspot seeding

db/
β”œβ”€β”€ population_engine.yml      ← Job profiles, behaviors, gear, skills
β”œβ”€β”€ population_spawn.yml       ← Map lists and population targets per zone
β”œβ”€β”€ population_vendor_pop.yml  ← Jobs used for vendor shells
β”œβ”€β”€ population_vendors.yml     ← Vendor inventories + map placement rules
β”œβ”€β”€ population_names.yml       ← Name generation profiles
β”œβ”€β”€ population_gear_sets.yml   ← Shared gear pools referenced by profiles
β”œβ”€β”€ population_chat.yml        ← Chat phrases per behavior
β”œβ”€β”€ population_pvp.yml         ← Arena team definitions
└── population_skill_db.yml    ← Skill usage rules per job

conf/battle/
└── population_engine.conf     ← Runtime tuning (counts, timing, features)

Setup & Configuration

- Build

- Add population_engine.cpp (unity build root) to your map-server project. It pulls in all subfiles via #include. No CMake changes required beyond adding the one .cpp.
- Database

- Copy all db/population_*.yml files into your server's db folder.
- Battle Config

- Edit population_engine.conf:
- Setting Default Description

population_engine_enable on Master on/off switch

population_engine_max_count 500 Global shell cap

population_engine_autosummon_batch_size 100 Shells spawned per 10s tick (0 = unlimited)

population_engine_vending_enable on Vendor shells open real stalls

population_engine_name_bot_fallback off Use syllable names instead of Bot_<id>
- Configure Populations

- Edit population_spawn.yml β€” define which maps get which shell profiles and how many:
-
- Configure Vendors (optional)

- Edit population_vendors.yml to define VendorPlacement entries restricting vendors to specific maps with bounding boxes, spacing, and caps.
- In-Game Management

-

Shell Behaviors

- Behavior can be overridden per map category β€” e.g. a Combat job becomes Vendor when it spawns in a town.
- Behavior Description

Combat Actively seeks and fights monsters using skill rotation

Guard Stays near spawn point, only attacks if approached

Wander Roams the map aimlessly (progressive-vector algorithm)

Support Follows and buffs nearby shells/players

Social Walks around, emotes, chats

Vendor Sits and opens a real vending stall with configurable stock

Sit Stationary decoration shell

AI Skill Condition System

- Each shell job has a full skill ruleset defined in population_skill_db.yml. Every skill entry has a condition gate that controls when the shell is eligible to use it β€” evaluated every combat tick with zero scripting overhead.
- credits to Singe Horizontal: For Expanded AI Conditions (https://rathena.org/board/topic/130266-mod-expanded-ai-conditions/)

Flat (Scalar) Conditions
32 named predicates cover the full range of tactical decisions:

- Predicate Description

hp_below / hp_above Self HP% threshold

sp_below / sp_above Self SP% threshold

enemy_hp_below / enemy_hp_above Target HP% threshold

enemy_status / not_enemy_status Target has/lacks a status effect (SC name)

self_status / not_self_status Self has/lacks a status effect

ally_hp_below Any ally in range below HP%

ally_status / not_ally_status Any ally in range has/lacks a status effect

distance_below / distance_above Range to target in cells

melee_attacked A melee attacker is in range and targeting this shell

range_attacked A ranged attacker is targeting this shell

self_targeted Any enemy is specifically targeting this shell

self_being_cast_on A skill is being cast at this shell (proactive dodge window)

enemy_casting Target is mid-cast

enemy_casting_ground Target is casting a ground AoE

enemy_hidden A hidden/cloaked enemy is in detection range

enemy_count_nearby Enemy count in range meets threshold

ally_count_nearby Ally count in range meets threshold

enemy_element Target's element matches (Water/Fire/Wind/Earth/Holy/etc.)

enemy_race Target's race matches (Brute/DemiHuman/Undead/etc.)

enemy_is_boss Target is an MVP or boss-class monster

map_zone Shell is in a town / field / dungeon

has_sphere Shell has β‰₯ N spirit spheres

cell_has_skill_unit Shell's current cell has an active skill unit

after_skill Shell's previous successful cast was a specific skill enables combos

damaged_gt Shell received a single hit exceeding N% of max HP enables burst reactions

skill_used A specific skill was used against this shell in the last 3 seconds mirrors mob_skill_db skillused

Expanded Conditions Boolean Logic Trees

For complex decisions, Condition: accepts a full boolean expression tree written in YAML:

Gates: AND, OR, NAND, NOR, XOR, NXOR

Leaf token grammar:

- [not_]<target>_<predicate>[_<cmp><value>]

Targets: self, enemy, ally
Comparators: lt, le, gt, ge, eq
Predicates: any status name (without SC_ prefix), hp_pct, sp_pct, distance, or any named predicate from the flat list

Examples:

-

Sequence form is sugar for AND: β€” so a simple list of tokens is an implicit all-must-pass check. Each leaf can be individually prefixed with not_ to invert it; entire subtrees can be inverted with NAND/NOR.

Invalid or unparseable trees evaluate to false (skill is skipped) and the error is logged at startup β€” no silent misfires.

Per-Skill Extra Controls

- Field Description

Rate 1–10000 chance weight this entry fires when conditions pass

State any / has_target / no_target β€” further narrows when the entry is eligible

Target current / self / ally β€” who the skill is cast on

Cooldown Per-entry cooldown in ms β€” prevents individual skill spam (e.g. Devotion: 30000)

AroundRange Randomize ground/AoE placement within N cells of the shell

Vendor System

- Static stock: YAML-defined item list (e.g. potions, arrows)
- Dynamic stock: Automatically derived from mob drop tables of source maps vendors in Morroc sell Pyramid drops, vendors in Izlude sell Byalan drops
- VendorPlacement: Hard restrict vendors to specific maps, bounding boxes, minimum spacing, and maximum count
- Price control: PriceMultiplier on dynamic stock, per-item Price override on static stock

Arena System

Shells can be assigned to PvP arenas via script (population_engine_arena_start). Inside an arena:

- Ally shells buff and heal each other (and real ally players)
- Enemy shells fight opposing team shells and real enemy players
- battle_check_target is hooked to return correct BCT flags for all combinations

Pros

- Zero client mods: works with vanilla RO clients
- No real DB saves: shells never touch char-server, registry, or achievement tables; safe
- Hot-reload: all YAML databases reload live via @populate reload
- YAML-driven: everything configurable without recompiling
- Map-aware behavior: town vs field vs dungeon behavior per job
- Real vending: vendor shells open actual vending_openvending stalls; players can buy
- Progressive vector wander: shells find walkable paths even in complex geometry
- Per-map party ID: shells on the same map auto-party for skill interactions
- Performance profiler built-in: PE_PERF_SCOPE macros with @populate timing
- FluxCP integration: live population count written to cp_population_stats SQL table (you need to edit fluxcp if you want population reflect on your website)
- Drift correction: shells that get teleported off their map auto-warp back
- Arena combat: real PvP arena simulation between shell teams + real players

Cons

- Single-threaded: rAthena's map-server is inherently single-threaded; shells are ticked only when real players are nearby (active AI range). Very high counts on very large player bases may cause frame-time pressure
- Memory-heavy at scale: each shell is a full map_session_data; see capacity table below
- No persistence between restarts: shells despawn and respawn fresh on every server boot (intentional)
- Combat AI is basic: it uses population_skill_db rules, not a full decision tree; don't expect pro-level PvP behavior
- Vendor stock is static per-spawn: stock does not deplete or restock dynamically after spawn (reloading YAML respawns them)

How Many Shells Can One Server Hold?
Memory Per Shell

- Each shell allocates a full map_session_data. In rAthena this struct contains inventory arrays, status blocks, skill data, and the custom s_population state:
- Component Approx Size

Core struct + status ~50 KB

Inventory (100 slots) ~6 KB

Cart (500 slots, vendor only) ~28 KB

Skill arrays ~10 KB

Misc pointers / linked lists ~5 KB

Total (combat shell) ~70–80 KB

Total (vendor shell with cart) ~100–110 KB

Capacity by RAM

- RAM Safe Shell Cap Notes

4 GB ~5,000 Shared with OS, MySQL, char-server, login-server

8 GB ~15,000 Comfortable for a full mid-pop server

16 GB ~35,000 High-density; tick budget becomes the limit before…

Known issues

No known issues yet.

Having trouble with Population Engine (Advance Fake Players)? Ask about it β†’