LV
4
- Joined
- Jan 29, 2026
- Messages
- 124
- Reaction score
- 117
- Points
- 88
- Age
- 41
GameServer memory optimization: what we did and why
Previously the game server kept every monster in memory as “heavy” as a player: full slots for skills, inventory, appearance, and effects — even when a mob barely used any of that. On a typical config (pool of 24,000 characters, about 11,500 live mobs on the maps), the process used roughly 2.1 GB of RAM after the world loaded. The character pool alone accounted for more than 1.3 GB.
The goal was to cut server memory use without changing what players see: mobs, combat, loot, and appearance stay the same. Nothing special was changed for the client — only the server side got thinner.
Phase B: short step-by-step
The idea on every step is the same: do not keep huge empty tables inside every monster up front. Memory is allocated when needed, and mobs without inventory or a complex look carry almost no extras.
B1 — skills (SkillBag)
Previously every character in the pool had a large fixed skill table “for a thousand slots.” For a mob it was almost empty, but still took space.
We switched to store-by-use: only as many skills as actually exist.
Why: one of the fattest chunks in every slot.
Measure: process memory after maps loaded fell from about 2100 MB to 1635 MB.
B2a — inventory (Kitbag)
Previously everyone had a full “backpack” with dozens of slots at once. Mobs do not need a backpack.
We sized it by real capacity: a mob starts with zero slots; a player gets the normal size. The player pool also got lighter (banks, etc.).
Why: the second big chunk of “empty air” in every character.
Measure: process — about 1065 MB.
B2b — appearance (Look)
Previously the look data (equipment, body parts) lived fully inside every pool slot — including mobs.
We changed it so players and boats create look data when needed; a normal mob has none in the object.
Why: almost another 10 KB of waste per slot.
Measure: process — about 824 MB.
B3 — status effects / buffs (SkillState)
Previously everyone had a large table of possible states (buffs, debuffs, and so on).
We made storage compact: only states that are actually active.
Why: closed the last large “empty array” in the character.
Measure: process — about 530 MB.
Per monster in the pool: from about 58 KB down to about 4 KB (~14× lighter). For players, inventory, look, and skills still work — they just no longer inflate every mob in the world.
Takeaway: how much memory we saved
Same server (GS00, Win32, zero players online, same maps, same ~11,500 mobs):
The process is about 4× lighter (−75%). The character pool is about 14× lighter (−93%).
So the main RAM wall for the current config is gone: the server no longer hits memory limits because every mob copied a player’s layout. Next bottlenecks are more likely tick cost, mob density near players.


What do you think about this?
P.S. The experiments were conducted on the x86 source code of Tales of Pirate 2022 DX9 by Mothanna.
Previously the game server kept every monster in memory as “heavy” as a player: full slots for skills, inventory, appearance, and effects — even when a mob barely used any of that. On a typical config (pool of 24,000 characters, about 11,500 live mobs on the maps), the process used roughly 2.1 GB of RAM after the world loaded. The character pool alone accounted for more than 1.3 GB.
The goal was to cut server memory use without changing what players see: mobs, combat, loot, and appearance stay the same. Nothing special was changed for the client — only the server side got thinner.
Phase B: short step-by-step
The idea on every step is the same: do not keep huge empty tables inside every monster up front. Memory is allocated when needed, and mobs without inventory or a complex look carry almost no extras.
B1 — skills (SkillBag)
Previously every character in the pool had a large fixed skill table “for a thousand slots.” For a mob it was almost empty, but still took space.
We switched to store-by-use: only as many skills as actually exist.
Why: one of the fattest chunks in every slot.
Measure: process memory after maps loaded fell from about 2100 MB to 1635 MB.
B2a — inventory (Kitbag)
Previously everyone had a full “backpack” with dozens of slots at once. Mobs do not need a backpack.
We sized it by real capacity: a mob starts with zero slots; a player gets the normal size. The player pool also got lighter (banks, etc.).
Why: the second big chunk of “empty air” in every character.
Measure: process — about 1065 MB.
B2b — appearance (Look)
Previously the look data (equipment, body parts) lived fully inside every pool slot — including mobs.
We changed it so players and boats create look data when needed; a normal mob has none in the object.
Why: almost another 10 KB of waste per slot.
Measure: process — about 824 MB.
B3 — status effects / buffs (SkillState)
Previously everyone had a large table of possible states (buffs, debuffs, and so on).
We made storage compact: only states that are actually active.
Why: closed the last large “empty array” in the character.
Measure: process — about 530 MB.
Per monster in the pool: from about 58 KB down to about 4 KB (~14× lighter). For players, inventory, look, and skills still work — they just no longer inflate every mob in the world.
Takeaway: how much memory we saved
Same server (GS00, Win32, zero players online, same maps, same ~11,500 mobs):
| Before optimization | After all B steps | |
| Process memory (after maps loaded) | ~2100 MB | ~530 MB |
| Character pool (24,000 slots) | ~1336 MB | ~93 MB |
The process is about 4× lighter (−75%). The character pool is about 14× lighter (−93%).
So the main RAM wall for the current config is gone: the server no longer hits memory limits because every mob copied a player’s layout. Next bottlenecks are more likely tick cost, mob density near players.


What do you think about this?
P.S. The experiments were conducted on the x86 source code of Tales of Pirate 2022 DX9 by Mothanna.
