Brazil's biggest MU Online portal — since 2003
Tutorial Advanced Server

How to automate load testing on your MU Online server's GameServer

Build a load-testing pipeline that simulates hundreds of simultaneous connections to your MU Online server's ConnectServer and GameServer, measuring latency, CPU/memory usage, and the real limit before launch.

RO Rodrigo · Updated on Sep 28, 2017 · ⏱ 17 min read
Quick answer

Launching a MU Online server without knowing how many concurrent players it can actually handle is a blind bet: the first day of promotion, when the most people try to log in at once, is exactly the worst time to discover that the ConnectServer chokes at 200 connections or that the database saturate

Launching a MU Online server without knowing how many concurrent players it can actually handle is a blind bet: the first day of promotion, when the most people try to log in at once, is exactly the worst time to discover that the ConnectServer chokes at 200 connections or that the database saturates its pool sooner than expected. This tutorial shows how to build an automated load-testing pipeline, simulating simultaneous connections and actions on the ConnectServer and GameServer, measuring the right indicators, and identifying the real limit of your environment before your players do it for you.

Why test load before launch

Successful promotion (groups, YouTube, partner Discords) tends to generate a concentrated spike of traffic in the first hours after launch — well above the average the server will sustain afterward. If the environment breaks during that spike, players' first impression is terrible and many won't come back to try again. Testing load in advance lets you fix bottlenecks (database configuration, undersized hardware, OS connection limits) while there are still no real players at risk.

Differentiating test types

Not every load test needs to simulate the whole game. Splitting by layer makes it easier to isolate where the bottleneck is:

Test typeWhat it simulatesTypical tool
Connection test (login)Hundreds of simultaneous handshakes on the ConnectServerCustom script (Python/C#) emulating the protocol
Authentication testLogin + character selection on the GameServerScript using the emulator's login protocol
Gameplay testMovement, attack, map change, NPC useBots that actually play (more expensive)
Database testRead/write concurrency on MEMB_INFO, Charactersysbench or a direct SQL script
Network/infra testBandwidth, latency, packet lossiperf3, interface monitoring

Preparing the staging environment

Never run the load test against the production database/server — the risk of corrupting real data or crashing the server during a promotion is too high. Clone the configuration (same emulator versions, same database configuration, hardware as close as possible) in an isolated environment, with synthetic test data:

CREATE DATABASE mu_staging;
-- Restore a clean/synthetic dump, never real player data

Simulating connections to the ConnectServer

The ConnectServer is the first bottleneck during a traffic spike — it resolves which GameServer the client should use and holds a connection queue. A simple Python script using raw sockets can simulate hundreds of handshakes:

import socket
import threading
import time

def simulate_connection(host, port, results, i):
    start = time.time()
    try:
        s = socket.create_connection((host, port), timeout=5)
        s.close()
        results.append(time.time() - start)
    except Exception as e:
        results.append(None)

results = []
threads = []
for i in range(500):
    t = threading.Thread(target=simulate_connection, args=("staging.myserver.com", 44405, results, i))
    threads.append(t)
    t.start()
    time.sleep(0.01)  # slight spacing to simulate gradual arrival

for t in threads:
    t.join()

success = [r for r in results if r is not None]
print(f"Successful connections: {len(success)}/{len(results)}")
print(f"Average latency: {sum(success)/len(success):.3f}s")

This basic test already reveals whether the ConnectServer accepts connections quickly or starts queuing/refusing above a certain volume.

Simulating the full login flow

To measure the real authentication bottleneck (which involves an account database lookup), you need to emulate your emulator's packet protocol — the structure varies between IGCN, MuEMU, and X-Team, but the principle is the same: build the binary login packet and read the response:

import struct

def build_login_packet(account, password):
    # Simplified structure — adjust to your emulator's real offsets
    body = account.encode('ascii').ljust(10, b'\x00') + password.encode('ascii').ljust(10, b'\x00')
    size = len(body) + 4
    return struct.pack('BBB', 0xC1, size, 0xF1) + body

Running this packet concurrently against the staging GameServer, measuring the time until a success/failure response, shows the real concurrent authentication capacity — the bottleneck usually shows up here first, not in the raw TCP connection.

Testing database concurrency

Regardless of the network test, the database is typically the real scaling limit. Use sysbench to simulate read/write load equivalent to the server's access pattern (many character reads, few position writes every few seconds):

sysbench oltp_read_write \
  --db-driver=mysql --mysql-db=mu_staging \
  --mysql-user=test --mysql-password=password \
  --tables=10 --table-size=100000 \
  --threads=200 --time=120 \
  run

Compare the sysbench latency results (p95, p99) with the expected number of concurrent players — if p99 already degrades with 200 simulated threads and your target is 500 CCU, the database (not the GameServer) is the bottleneck to solve first.

Monitoring resources during the test

While the test runs, collect system metrics in parallel to correlate load with resource consumption:

# In a separate window, throughout the whole test:
top -b -n 100 -d 5 | grep -E "GameServer|ConnectServer|mysqld" >> /tmp/load_monitor.log
vmstat 5 100 >> /tmp/load_monitor.log
MetricToolWhat it indicates
GameServer process CPUtop/htopGame logic processing bottleneck
Resident memorytop/psMemory leak under sustained load
Active MySQL connectionsSHOW PROCESSLIST;Connection pool saturation
Disk latencyiostat -x 5I/O bottleneck on large databases
Packet loss/network latencyiperf3, ping -fBandwidth or network configuration limit

Defining test scenarios (gradual ramp-up)

Instead of firing all "simulated players" at once (which doesn't reflect the real arrival pattern), structure the test as a ramp-up — gradually increasing load and observing at which point each metric starts to degrade:

PhaseSimulated CCUDurationGoal
Warm-up502 minConfirm baseline with no load
Ramp 11505 minMinimum launch target
Ramp 23005 minExpected peak target
Ramp 35005 minStress above target (safety margin)
Ramp 4700+until it breaksIdentify the real breaking point

Automating the full pipeline execution

A simple orchestrator script chains the steps and saves the timestamped results, so you can compare runs over time (e.g., before and after a configuration optimization):

#!/bin/bash
DATE=$(date +%F_%H%M)
mkdir -p /tests/$DATE

python3 connection_test.py > /tests/$DATE/connection.log &
sysbench oltp_read_write --threads=200 --time=300 run > /tests/$DATE/database.log &
top -b -d 5 -n 60 > /tests/$DATE/resources.log &

wait
echo "Load test $DATE completed. Results in /tests/$DATE/"

Interpreting results and acting on them

The end goal isn't just generating numbers, it's making concrete decisions. A typical analysis result:

  • If login latency degrades before the GameServer's CPU saturates, the bottleneck is the database connection pool — increase max_connections in MySQL/MSSQL and adjust the emulator's pool.
  • If GameServer CPU saturates first, the hardware is undersized for the CCU target — consider a vCPU upgrade or optimizing event logic that runs in a loop.
  • If the network degrades with just a few hundred connections, the contracted link may be insufficient for the expected volume — review the plan with your provider.

Common errors and fixes

SymptomLikely causeFix
Test crashes the staging environment quicklyRamp-up too aggressive from the startUse a gradual ramp (50 → 150 → 300 → 500)
Results aren't reproducibleStaging environment has different hardware than productionMatch CPU/RAM/disk specs between staging and production
High latency but CPU/memory are fineDatabase bottleneck (pool or index)Run sysbench in isolation and review SHOW PROCESSLIST
Simulation script crashes with a socket errorOS connection limit (ulimit) reached on the load-generating machineIncrease ulimit -n on the load-generating machine
Test reveals nothing usefulSystem metrics not collected during the runAlways run top/vmstat/iostat in parallel
Production breaks even after an "approved" testTest only covered the connection layer, not real gameplayComplement with a bot test simulating real actions

Load testing checklist

  • Isolated staging environment, with specs equivalent to production.
  • ConnectServer connection simulation script working.
  • Login/authentication simulation script tested.
  • Database concurrency test (sysbench) run and analyzed.
  • CPU, memory, connection, and disk monitoring collected during the test.
  • Gradual load ramp executed until identifying the breaking point.
  • Corrective actions defined based on the bottlenecks found.
  • Retest performed after adjustments to confirm improvement.

With the real limit of your environment mapped out before launch, you trade production surprises for sizing decisions backed by data. If your server's foundation is still in the initial configuration stage, start with the MU Online server creation tutorial before investing time in advanced load testing.

Frequently asked questions

Do I need real bots logging into the game, or can I just simulate the protocol?

For connection load tests (ConnectServer and login), simulating the network protocol directly with a script is more efficient and scalable than opening hundreds of real clients. To test gameplay behavior (movement, combat, drops), bots that emulate the GameServer protocol are needed, but they're still lighter than the full client.

How many concurrent players should I simulate in the test?

Start with your realistic launch target (e.g., 300 CCU) and go 50-100% beyond it to find the real safety margin. If your goal is 300 online, test up to 450-600 to find out where the system actually breaks.

Can the load test be run on the production server?

Not recommended. Use a staging environment with hardware and configuration as close to production as possible. Testing in production risks corrupting real player data or causing a visible outage during the test.

What exactly should I measure during the test?

At minimum: CPU and memory usage of the GameServer/ConnectServer process, login response time, map-change response time, packet loss/network latency, and database connection usage.

What should I do with the results afterward?

Document the exact point where performance degrades (e.g., 'above 400 CCU, map-change latency exceeds 2s') and use it to size hardware, tune database settings (connection pool), or define a login queue limit before the official launch.

RO
Founder & editor-in-chief

Rodrigo has run ViciadosMU since the portal's early days. A specialist in MU Online server creation and administration, game history and the evolution of the seasons — he wrote much of the archive before 2024.

Keep reading

Related articles