Risk of Rain 2
Install with App

Details

Latest version
3.0.3
Last Updated
First Uploaded
Downloads
27
Likes
0
Size
70KB
Dependants

ClientPredictedProjectiles

Gives multiplayer clients full ownership of the projectiles they fire, for every projectile in the game (vanilla, DLC and modded survivors), so they feel as responsive as they do for the host.

In vanilla, every projectile a client fires is simulated and hit-checked by the host. The client only sees a "dumb" local preview that can't hit anything and doesn't home, stick or explode. As a result:

  • projectiles appear late, or not at all, until the host's copy arrives (many have no preview at all),
  • rockets visibly fly through the enemy they hit and explode a moment later,
  • homing, sticky and boomerang projectiles fly in a straight line, then snap to where the host has them,
  • the projectile visual snaps backwards when the host's copy arrives,
  • explosions and impact sounds play one full ping after the hit,
  • the host's copy starts behind (the game even calculates how far behind, then never uses the value).

What this mod changes about projectiles

When the host has the mod, for every projectile a modded client fires:

  1. Every projectile is predicted. Projectiles that vanilla never shows early now appear the instant you fire. For those, the host keeps network authority exactly as in vanilla. The client pairs its copy with the host's copy itself, so the host doesn't show up as a second projectile.

  2. Your copy runs the projectile's full behaviour locally. Logic the game only runs on the host (homing and target finding, sticking, boomerang returns, fuses, lifetimes, detonations, child projectiles) also runs on your copy, so it behaves correctly on your screen immediately. This works for modded projectiles too.

  3. Your copy does all of the hit detection. It decides:

    • what the projectile collides with (enemies, walls, bounces, triggers),
    • who is inside each explosion radius,
    • who each piercing, single-target or damage-zone hitbox touches.

    This is checked against enemies where you see them, at the moment it happens on your screen. Each result is sent to the host tagged with the projectile's age. Your copy never deals damage, heals, procs items, applies DoTs, fires orbs or spawns anything for real, and nothing else it does is sent over the network. Its effects and sounds play only on your machine.

  4. The host's copy replays your timeline. The host spawns its copy a small buffer (default 0.06 s) after your shot arrives. Your reports for every moment are then already waiting when its copy gets there:

    • your impacts are applied at the spot you saw, with your projectile's speed after the hit;
    • at each explosion or overlap attack, the host's own detection is swapped for your hit list.

    Hits are re-applied relative to where each enemy currently is on the host. Everything that follows (damage, crits, procs, DoTs, debuffs, the projectile's own on-hit behaviour) still runs through the normal game code on the host, so the host keeps authority over the results. Item procs that make their own explosions are never fed your hit list.

  5. Only shots your game confirms are client-decided. Right after you fire, your game tells the host it is simulating that shot. Shots it isn't simulating (local simulation off or turned off for that projectile type, passenger projectiles) stay fully host-decided, so a projectile can never end up with nobody detecting its hits. The host's copy ignores its own physics hits while it is in sync with yours. It only takes over (vanilla behaviour, and your visual blends to its copy) if one of your reports fails the sanity checks. Those checks allow for distance, projectile speed, enemy speed and ping. It also takes over if you disconnect or stop simulating that projectile.

  6. Fast-forward for the rest. Projectiles you don't simulate (passenger projectiles, or when local simulation is off) are instead fast-forwarded on the host by your one-way latency. This uses a collision sweep and also advances fuse and lifetime timers.

  7. The host doesn't echo your own effects back to you. Effects your projectile's own code creates (explosions, impacts, hit sparks, expiry effects, its sounds) aren't sent back, because you already played them. Damage numbers and proc effects still come from the host.

  8. Your projectile's visual follows your local path instead of snapping back to the host copy. It blends smoothly to the host's path only if the two genuinely disagree, or if the host rejected one of your impacts.

  9. Projectiles you didn't fire (enemies' and other players') are drawn where the host currently has them, not one ping behind, so what you dodge is what the host simulates. This is purely visual and is never drawn through walls.

Exception: projectiles that carry a passenger (VehicleSeat, ones that move your own character) keep vanilla prediction and don't run locally, because a second copy would grab your own character. They still get fast-forward and smoothed visuals.

Self-healing: if some projectile's code fails when run locally (for example a mod component that assumes it is on the host), the mod logs it once. After three failures it turns local simulation off for that projectile type for the rest of the session and tells the host.

Compatibility

  • Everyone should install it, but nothing breaks if someone doesn't. Clients and host shake hands when joining:
    • with a vanilla host, a modded client is 100% vanilla, apart from the purely visual item 9;
    • vanilla clients in a modded host's lobby play vanilla;
    • all modded clients get everything.
  • Doesn't add itself to the network mod list, so it doesn't block anyone from joining.
  • Only dependency: BepInExPack. No R2API or HookGenPatcher.
  • The host behaves exactly like vanilla for everything except client-fired projectiles.
  • Designed alongside these mods:
    • AttackDirectionFix: its visual offset still applies.
    • ItemQualities: local explosions run the real code, so its explosion radius changes apply.
    • PizzaClientLagFix, MiscFixes, RiskyFixes.
  • A vanilla host logs a single harmless Unknown message ID 21437 line when a modded client joins.

Configuration

BepInEx/config/daz.ClientPredictedProjectiles.cfg

  • [Host] settings only matter on the host: client-owned impacts, fast-forward and its cap, impact hold time, distance tolerance.
  • [Client] settings only matter on the player's own machine: predict all, simulate locally, predicted visuals, correction blend time, showing other projectiles where the host has them.
  • [Advanced]
    • Excluded Projectiles leaves specific projectiles completely vanilla.
    • Log Projectile Report lists how every projectile is handled.
    • Verbose Logging logs every impact report, rejection and sandbox block.

Limitations

  • Hitscan (bullets) and melee are already client-authoritative in vanilla, so they're unchanged.
  • Client-side detection covers the game's explosion (BlastAttack) and hitbox (OverlapAttack) systems, which is what nearly all projectiles use. A projectile that searches for targets some other way (homing lightning beams, a mod that calls damage directly) still has that part decided by the host.
  • Because the host replays your projectile a small buffer later, other players see it about that much later than you do. Item 9 on their machines makes up most of that.
  • Orb attacks (e.g. Huntress arrows) are created by the host and aren't projectiles, so they aren't changed.
  • Damage numbers and on-hit item procs still come from the host, one ping after the hit.
  • All players should run the same version (the handshake rejects mismatched versions, which then play vanilla).
Thunderstore development is made possible with ads. Please consider making an exception to your adblock.