Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/kenzz55/ue5-iocp-mmo-server/llms.txt

Use this file to discover all available pages before exploring further.

Combat is entirely server-authoritative. When a player swings, the client sends a lightweight intent packet; the server performs a capsule-sweep hit test against all monsters in the area, applies damage and knockback, and broadcasts the results to every client in the Area of Interest (AoI). Monster AI runs as part of the game-simulation tick (every 33 ms by default) inside GameWorld::StepMonsters(), advancing a per-monster state machine that transitions between idle patrolling, chasing, attacking, and returning to the spawn point.

Player Attack Flow

1

C_AttackReq (0x0231) — client sends attack intent

The client sends this packet when the player initiates an attack. No hit information is included; the server computes all hits independently.
message C_AttackReq {
  uint32 attack_sequence = 1;
  uint32 combo_step      = 2;
  uint64 client_time_ms  = 3;
}
attack_sequence
uint32
required
Monotonically increasing counter per session; used to detect dropped or out-of-order attack packets.
combo_step
uint32
required
Current step in the combo chain (1-indexed). The server tracks lastComboStep on PlayerEntity to validate sequencing.
client_time_ms
uint64
required
Client-side timestamp; stored for latency logging and debug packets.
2

S_AttackAck (0x0232) — server acknowledges

Sent back to the attacking client only. The client uses server_time_ms to calibrate its lag-compensated animation.
message S_AttackAck {
  bool   success         = 1;
  string message         = 2;
  uint32 attack_sequence = 3;
  uint32 combo_step      = 4;
  uint64 server_time_ms  = 5;
}
3

Hit detection — ApplyDamageToMonstersInSweep

After acknowledging the attack, the server calls GameWorld::ApplyDamageToMonstersInSweep() to determine which monsters were struck. The sweep is a capsule test along the player’s facing direction.
// GameWorld.h
std::vector<HpChange> ApplyDamageToMonstersInSweep(
    EntityId attackerId,
    float    sweepStartX,
    float    sweepStartY,
    float    sweepEndX,
    float    sweepEndY,
    float    sweepRadius,
    std::size_t maxTargets,
    int32    damage,
    float    knockbackDistance);
For each monster hit, a HpChange result is produced and packaged into S_EntityHpChanged.
4

S_AttackNotify (0x0233) — broadcast to AoI peers

Broadcast to all players within AoI of the attacker so they can play the attack animation on the correct character.
5

S_EntityHpChanged (0x0244) — HP update

Sent to all AoI observers whenever a hit lands on a monster (or a monster hits a player).
entity_type
EntityType
Player or Monster.
entity_id
uint64
Entity that was hit.
current_hp
int32
HP after this change.
max_hp
int32
Maximum HP of the entity.
delta_hp
int32
Negative for damage, positive for healing.
dead
bool
true if the entity’s HP reached 0.

MonsterEntity

Each live monster is represented by a MonsterEntity struct held inside GameWorld. The fields below are the most operationally significant:
// Game/GameWorld.h
struct MonsterEntity
{
    EntityId id          = 0;
    uint32 spawnPointId  = 0;
    uint32 templateId    = 0;

    // Position and velocity
    float x = 0.0f, y = 0.0f, z = 0.0f;
    float yaw = 0.0f;
    float spawnX = 0.0f, spawnY = 0.0f, spawnZ = 0.0f;
    float velocityX = 0.0f, velocityY = 0.0f, velocityZ = 0.0f;

    // Movement parameters
    float moveSpeed       = 250.0f;
    float detectionRadius = 600.0f;
    float leashRadius     = 1500.0f;

    // Attack range parameters
    float rangedAttackRadius = 400.0f;
    float meleeAttackRadius  = 140.0f;

    // Cooldowns (seconds)
    float rangedAttackCooldownSeconds = 1.5f;
    float meleeAttackCooldownSeconds  = 1.0f;

    // Attack animation timing
    float rangedAttackActionSeconds   = 40.0f / 30.0f;  // ~1.33 s
    float meleeAttackActionSeconds    = 38.0f / 30.0f;  // ~1.27 s
    float rangedAttackHitSeconds      = 17.0f / 30.0f;  // ~0.57 s
    float meleeAttackHitSeconds       = 13.0f / 30.0f;  // ~0.43 s

    // Damage
    int32 rangedAttackDamage = 10;
    int32 meleeAttackDamage  = 10;

    // Knockback on hit
    float knockbackDistance        = 60.0f;
    float knockbackDurationSeconds = 0.18f;

    // Hit reaction
    float hitReactionDurationSeconds = 30.0f / 30.0f;  // 1.0 s

    // HP
    int32 currentHp = 100;
    int32 maxHp     = 100;

    // AI state
    EntityId       targetEntityId = 0;
    MonsterAiState aiState        = MonsterAiState::Idle;
    uint32         attackSequence = 0;
};

Monster AI State Machine

The MonsterAiState enum drives every monster’s behaviour each simulation tick:
enum class MonsterAiState : uint8
{
    Idle        = 0,
    Chase       = 1,
    RangedAttack = 2,
    Return      = 3,
    MeleeAttack = 4
};

Idle → Chase

When a player enters detectionRadius (600 units) the monster picks that player as its target and transitions to Chase, beginning to move toward them at moveSpeed (250 units/s).

Chase → RangedAttack

While chasing, if the player is within rangedAttackRadius (400 units) and the ranged cooldown has expired, the monster transitions to RangedAttack. It plays the ranged attack animation (rangedAttackActionSeconds ≈ 1.33 s) and deals rangedAttackDamage (10) at the hit frame (rangedAttackHitSeconds ≈ 0.57 s).

Chase → MeleeAttack

If the player is within meleeAttackRadius (140 units) the monster transitions to MeleeAttack. The melee animation takes meleeAttackActionSeconds ≈ 1.27 s with the hit at meleeAttackHitSeconds ≈ 0.43 s dealing meleeAttackDamage (10).

Chase → Return

If the monster moves farther than leashRadius (1500 units) from its spawn point, it abandons its target and transitions to Return, moving back to (spawnX, spawnY, spawnZ).

Return → Idle

When the monster reaches within MonsterReturnArrivalTolerance (1 unit) of the spawn point it transitions back to Idle and clears its target.

RangedAttack / MeleeAttack → Chase

After the attack action completes (attackActionRemainingSeconds reaches 0) the monster returns to Chase to re-evaluate range.
Attack types
enum class MonsterAttackType : uint8
{
    Ranged = 1,
    Melee  = 2
};

AoI Broadcasting

All monster events are broadcast only to players within the configured AoI (MonsterAoiViewDistance, which equals MovementAoiViewDistance = 3000 units). AoI membership is refreshed every MonsterAoiRefreshIntervalSeconds (0.2 s).
Packet IDNameTrigger
0x0241S_SpawnMonsterMonster enters a player’s AoI
0x0242S_DespawnMonsterMonster leaves a player’s AoI or is dead
0x0243S_MonsterMoveNotifyPosition update during Chase/Return
0x0244S_EntityHpChangedAny HP change (player or monster)
0x0246S_MonsterAttackNotifyMelee attack started
0x0248S_MonsterRangedShotNotifyRanged projectile fired

Knockback and Hit Reaction

When a player’s sweep hits a monster the server applies knockback using the following parameters from MonsterEntity:
ParameterValue
knockbackDistance60 units
knockbackDurationSeconds0.18 s
hitReactionDurationSeconds30/30 = 1.0 s
During hitReactionRemainingSeconds > 0 the monster is in a stunned state and cannot initiate new attacks. Knockback movement is resolved by the server and broadcast via S_MonsterMoveNotify.

Monster Respawn

When a monster’s HP reaches 0, the server removes it from GameWorld, broadcasts S_DespawnMonster to all observers, and schedules a respawn after MonsterRespawnDelaySeconds (30 s). After the delay the monster is re-created at its original spawn point (identified by spawnPointId) with full HP and aiState = Idle.
// Common/Config.h
constexpr int MonsterRespawnDelaySeconds = 30;
Setting EnableCombatDebugPackets = true in Common/Config.h (the compiled-in default) causes the server to emit two additional packets per attack event:
  • 0x0245 S_CombatDebugNotify — sent to the attacking player; includes sweep geometry (start/end/radius), the list of hit monster IDs, and the server-computed damage values.
  • 0x0247 S_MonsterCombatDebugNotify — broadcast to all AoI observers; includes the monster’s AI state, target ID, and current cooldown timers at the moment of the attack.
Disable this flag for production builds to reduce per-frame bandwidth.

Build docs developers (and LLMs) love