ROUNDS
Install

Details

Last Updated
First Uploaded
Downloads
782
Likes
0
Size
187KB
Dependency string
CultOfTea-ReksikPerformance-0.3.1
Dependants

ReksikPerformance 0.3.1

ReksikPerformance treats Session Reliability and Squeeze FPS as equal product pillars for heavily modded, long-running ROUNDS sessions.

The performance side applies reversible client-local graphics and VFX budgets. The reliability side records bounded evidence, installs narrowly verified guards for known error storms, and offers an opt-in non-host hot-rejoin experiment. Experimental rejoin is disabled by default.

What this mod does

Area What it does
Local frame pacing Applies a target frame rate and VSync interval.
Local graphics Controls MSAA, shadows, shadow distance, pixel lights, anisotropic filtering, texture limit and LOD bias.
Local VFX budgeting Caps particle systems, scales particle emission and trail lifetime, and moves overflow lights off expensive pixel/shadow rendering paths.
Adaptive control Moves through T0-T3 only after sustained frame pressure and recovers one tier at a time after sustained headroom.
Live evidence Shows an optional FPS, 1% low, frame-time, managed-memory, GC and tracked-VFX overlay.
Session diagnostics Records bounded, privacy-safe Photon state, world counts, plugin inventory and error fingerprints.
Reliability guards Patches only exact, audited method signatures and leaves unknown versions untouched.
Experimental rejoin Makes at most two room-bound Photon rejoin attempts for an eligible non-host, then validates warm state at the next point boundary.

What this mod does not do

ReksikPerformance does not:

  • change damage, projectile simulation, card balance or authoritative game state to gain FPS;
  • promise higher FPS when the bottleneck is third-party gameplay code, physics, networking, the GPU driver or another external limiter;
  • pool arbitrary mod objects or rewrite every mod's update loops;
  • reduce ping, repair packet loss, prevent every disconnect or eliminate desync;
  • migrate a host, elect a replacement master client or reconnect the disconnected host;
  • save and restore an arbitrary mid-point world;
  • restore health from the pre-disconnect snapshot, running coroutines, card-owned transient components, spawned objects, current projectiles or pick-screen state;
  • silently patch unknown mod versions when an audited runtime contract no longer matches.

Requirements

  • BepInEx-BepInExPack_ROUNDS-5.4.1901
  • willis81808-UnboundLib-3.2.14

FPS profiles and diagnostics are client-local. Each player who wants those features installs the mod for themselves.

Experimental rejoin is different: the room creator and the reconnecting non-host client must both enable it before the room is created. A client cannot retrofit Photon TTLs onto an existing room.

In a lobby with additional peers, every peer that can run the audited player-left callbacks should use the same opt-in. There is no config handshake; an unconfigured third client can still perform destructive vanilla cleanup when it observes the inactive actor.

Quick start

  1. Open the UnboundLib mod settings menu.
  2. Open REKSIK PERFORMANCE.
  3. Keep Session Reliability enabled.
  4. Choose Balanced for a long modded session.
  5. Keep Adaptive VFX Pressure Control enabled.
  6. Enable the compact overlay only while tuning or troubleshooting.
  7. Leave experimental rejoin disabled unless you are deliberately testing with a host and a second client.

The menu is dark, refreshes from the real config values, and keeps every toggle visibly marked [ON] or [OFF].

Quick presets

These are recommended recipes, not additional values of the Profile enum.

Goal Recommended settings
Long modded session Profile=Balanced, adaptive on, Session Reliability on, diagnostics on, guards on, experimental rejoin off.
Maximum uncapped local FPS Profile=MaxFps, adaptive on, overlay temporarily on.
Weak GPU or extreme visual clutter Profile=Potato, adaptive on. Potato uses a 90 FPS target but lowers texture and VFX cost more aggressively.
Competitive frame pacing Profile=Competitive, adaptive on; adjust TargetFrameRate to a sustainable value if 240 is unrealistic.
Preserve presentation quality Profile=Cinematic; adaptive can still lower VFX during sustained pressure unless disabled separately.
Observe a disconnect without interventions Session Reliability on, diagnostics on, CommonDisconnectGuards=false, experimental rejoin off, then restart.
Guarded reliability baseline Session Reliability on, diagnostics on, CommonDisconnectGuards=true, experimental rejoin off, then restart.
Two-client rejoin lab Enable experimental rejoin on the host and non-host before room creation; keep diagnostics and guards on; test only with a recoverable non-host transport interruption.

Built-in performance profiles

Selecting a profile writes the values shown below. It does not change Session Reliability, adaptive timing, overlay state, diagnostics retention or experimental-rejoin settings.

Profile FPS VSync MSAA Shadows / distance Pixel lights Aniso Texture limit LOD VFX Particles/system Trail multiplier
Vanilla Startup value Startup Startup Startup Startup Startup Startup Startup Restored Restored Restored
MaxFps -1 0 0 Off / 0 0 Off 0 0.65 Minimal 128 0.35
Balanced 120 0 2 On / 35 2 On 0 1.00 Reduced 512 0.75
Competitive 240 0 0 Off / 10 0 Off 0 0.75 Minimal 192 0.45
Potato 90 0 0 Off / 0 0 Off 1 0.50 Minimal 96 0.25
Cinematic 120 0 8 On / 100 8 On 0 2.00 Full 10000 1.00
Custom Config Config Config Config Config Config Config Config Config Config Config

-1 uses Unity's platform default rather than imposing a numerical cap. Vanilla restores the Unity values captured when the plugin started; it is not a hard-coded approximation of vanilla settings.

Changing one of the exposed graphics or VFX fields through the in-game menu selects Custom.

Adaptive T0-T3 budgets

Adaptive control operates only when:

  • the performance module is enabled;
  • the selected profile is not Vanilla;
  • a metrics snapshot exists;
  • Performance Improvements is not owning the overlapping VFX path.

The base particle cap and base trail multiplier come from the active profile/config. The effective particle limit is:

min(original maxParticles, configured ParticleLimitPerSystem, ceil(original maxParticles x tier retention))

An originally non-zero particle system always retains at least one particle slot.

Tier Particle retention Emission retention Trail lifetime Expensive non-directional lights
T0 100% 100% base x 1.00 Full: unlimited; Reduced: 24; Minimal: 12
T1 80% 90% base x 0.85 min(base, 20)
T2 60% 72% base x 0.65 min(base, 10)
T3 40% 55% base x 0.45 min(base, 5)

Overflow lights remain visible but are switched to vertex rendering with shadows disabled. Directional lights are not tracked by the VFX controller.

Pressure and recovery use hysteresis:

  • pressure: average FPS below 88% of target or 1% low below 68% of target;
  • headroom: average FPS at least 97% of target and 1% low at least 84% of target;
  • pressure delay: configurable, effective range 2-20 seconds;
  • recovery delay: configurable, effective range 5-60 seconds;
  • minimum hold after a tier change: 3.5 seconds;
  • movement: one tier per qualified pressure/recovery interval.

If TargetFrameRate is below 30, adaptive control uses the display refresh rate, falling back to 60 FPS.

VFX discovery is periodic, not per frame. The effective rescan interval is 10-30 seconds. Tier changes immediately reuse already tracked objects and schedule discovery only when budgeting is active.

Session Reliability

Session Reliability is independent of the selected FPS profile. Disabling General.Enabled does not disable the passive recorder.

The recorder:

  • samples lightweight connection state every second;
  • refreshes heavier world counts every 10 seconds;
  • keeps 30 recent runtime samples and 48 recent error fingerprints in bounded memory;
  • refreshes the in-memory summary every two seconds;
  • writes a snapshot on disconnect, room leave, shutdown and application quit;
  • continues in memory if disk writing fails.

Diagnostics v2

Disk records use JSON Lines with schema:

reksik-session-reliability-v2

Each record can contain:

  • UTC timestamp and capture reason;
  • previous/current Photon client state and sanitized disconnect cause;
  • connected, ready, in-room and master-client flags;
  • Photon probe status and bounded probe issue identifiers;
  • per-process anonymous room fingerprint;
  • room player/max-player/open state when available;
  • counts for players, Photon views, projectiles, gameplay entities, particle systems and trails;
  • total exception/error counts and events in the last minute;
  • sorted plugin GUID/version inventory;
  • up to 30 recent connection/world-count samples;
  • up to 48 recent error categories and salted fingerprints.

Default disk bounds are five files of at most 2 MiB each. Rotation and deletion of old diagnostic files are best-effort; a locked file never stops the live monitor.

Diagnostics deliberately exclude:

  • player names;
  • raw room names;
  • IP addresses and endpoints;
  • authentication values;
  • raw exception messages;
  • stack traces;
  • filesystem paths.

Room and exception fingerprints use random per-process salts, so they are useful within one game launch but cannot reliably correlate identities across launches.

Fail-closed reliability guards

Session Reliability.CommonDisconnectGuards is enabled by default and requires a restart when changed. Every guard first verifies the expected plugin/assembly, type, method signature and required members. Missing or changed contracts are skipped; patch failure leaves the original method intact.

Guard target Narrow intervention
EntityLagFixerUpper.RoundEnd Suppresses the audited unsafe local bullet-cleanup coroutine.
NetworkPhysicsObject.OnCollisionEnter2D Skips only collisions missing required Photon/collision state.
FollowPlayer.LateUpdate Stops updates whose followed player disappeared and can disable the stale component.
SetPlayerSpriteLayer.Start Skips only the incomplete cosmetic hierarchy proven to abort RWF player-leave cleanup.
RWF.UI.FollowPlayer.Update Disables an orphaned RWF spotlight before it dereferences a departed player.
ModdingUtils PlayerPatchFullReset.Prefix Skips effect cleanup only for an already-destroyed Player; live players keep full cleanup.
ProjectileHit.RPCA_DoHit Installs only after proving both exact target lookups and terminal vanilla DestroyMe. Valid resolved targets and the audited targetless sentinel pair stay vanilla; a missing, invalid or throwing runtime target is cleaned up through DestroyMe best-effort before the unsafe RPC is suppressed.
GearUp OrbLifeforceDualityModifier.Update Makes the audited terminal orb-destroy path owner-only and one-shot.
GearUp ArcaneConversionModifier.DoHitEffect Ignores only the audited hit with no target transform.

The menu reports guard active/off state, patched count and intervention count. Skipped/failed patch counts remain available through startup logs and the status API. These guards target confirmed error signatures; they are not a general promise to make every mod combination safe.

Experimental non-host room-bound hot rejoin

Safety status

Experimental rejoin is OFF by default and all of its config fields are read at startup. Restart after changing them.

It is designed for a short recoverable transport interruption affecting a non-host. It is not host migration and it is not a save-game system.

Eligibility contract

A new attempt starts only when all of these client-side conditions are true:

  1. the reconnecting client enabled the feature before startup;
  2. the client was previously observed in that room as a non-host;
  3. exact Photon, ROUNDS and UnboundLib critical hooks were resolved;
  4. a locally owned player produced a room-bound warm-state receipt no more than one second before the disconnect;
  5. the receipt has a valid room name and Photon actor number;
  6. the disconnect cause is exactly Exception, ServerTimeout or ClientTimeout.

For actor retention to work, the host must also have created the room with the experiment enabled. There is currently no peer config handshake and no per-attempt proof that the room has the expected TTLs; mismatched setup therefore reaches Photon and normally fails or falls back within the bounded attempt.

Kicks, authentication failures, explicit client disconnects, operation limits, unknown causes and missing/stale room receipts keep the vanilla disconnect path.

What happens

  1. Room creation patches set PlayerTtl and EmptyRoomTtl on a non-null Photon RoomOptions.
  2. While Photon marks an actor IsInactive, the audited ROUNDS, UnboundLib and optional RWF player-left callbacks are temporarily suppressed.
  3. The client calls ReconnectAndRejoin() once and waits up to ReconnectTimeoutSeconds.
  4. If needed, it resets the transport, waits up to three seconds for disconnect, waits RetryDelaySeconds, then performs one final attempt.
  5. A successful transport attempt must return to an in-room, connected-and-ready state.
  6. The result remains RejoinedWarmStatePending until the next PointStart.
  7. At PointStart, the controller waits up to eight frames for the locally owned player and validates the frozen receipt.

The point-boundary validation requires:

  • the same Photon room;
  • the same ROUNDS player ID;
  • the same Photon actor number;
  • a locally owned reconstructed player;
  • finite, positive health and max-health values;
  • an exact multiset match of internal/display card identities and duplicate counts;
  • a frozen receipt no older than 300 seconds.

On a successful validation, ammo is reloaded when the pre-disconnect player had a GunAmmo state and the warm-state warning is acknowledged. The normal point lifecycle may recreate some transient card state, but this controller neither verifies nor guarantees that.

Health is validated but not overwritten from the snapshot.

Exact guarantees

When the feature reports a completed point-boundary restore, it has verified same-room ownership, player ID, actor number and exact deck composition, and it has normalized ammo at a new point. It has not reconstructed the interrupted point.

There are at most two transport attempts: one initial attempt and one retry. Each attempt has a bounded timeout. Exhaustion calls the audited vanilla NetworkConnectionHandler.NetworkRestart() fallback.

Limitations

  • The host is never auto-rejoined.
  • EmptyRoomTtl does not provide host migration or elect a new master client.
  • A host enabling the feature only prepares rooms; it does not make the host recoverable.
  • A client enabling it after room creation cannot add TTL retention retroactively.
  • Health, coroutine progress, temporary card counters, arbitrary card-owned MonoBehaviours, spawned objects, projectiles and mid-point world state are not restored.
  • Exact deck mismatch, actor mismatch, room mismatch, expired receipt, invalid health or missing owned player rejects warm-state acknowledgement.
  • A rejected point-boundary validation never fabricates old state and does not call NetworkRestart(). At that safe boundary it clears the pending receipt so fresh stable-state receipts can arm future disconnect attempts.
  • The allowed timeout/TTL ranges are independent. A very short PlayerTtl can be incompatible with a long two-attempt envelope.
  • Only the first locally owned ROUNDS player found by the controller is covered; couch/multiple-local-player restoration is not guaranteed.
  • Third-party mods may destroy player state through callbacks outside the audited baseline.
  • Harmony patches with unknown IDs/order and optional callback drift outside the mandatory baseline are not covered.
  • RWF integration is optional and signature-checked; the base ROUNDS and UnboundLib inactive-player hooks remain mandatory.
  • If a required symbol or critical hook is missing, state becomes FailedClosed and vanilla disconnect handling remains active.

Compatibility

Performance Improvements

When pykess.rounds.plugins.performanceimprovements is detected:

Detection uses that exact GUID. A renamed fork with another GUID is not automatically recognized.

  • ReksikPerformance disables its overlapping VFX controller and adaptive VFX tiers;
  • shadow, shadow-distance and pixel-light controls are deferred;
  • frame cap, VSync, MSAA, texture limit, LOD bias and anisotropic filtering can still apply;
  • Session Reliability, diagnostics and compatible reliability guards remain available.

This prevents two mods from repeatedly fighting over the same visual resources.

Other mods

  • Reliability guards are version/signature aware and skip unknown contracts.
  • Optional Rounds With Friends inactive-player handling is used only when its exact callback exists.
  • Networking guards do not guess authority. Unclear ownership keeps the original/vanilla path.
  • VFX controls restore the values captured before ReksikPerformance touched each tracked object.

Complete config reference

Config file: BepInEx/config/com.reksikperformance.rounds.cfg

Ranges below are effective runtime ranges for finite values. Out-of-range values are normalized when applied; use finite numbers in manually edited config files.

Section Key Type Default Effective values/range Meaning
General Enabled bool true true/false Apply the selected local performance profile. Does not disable Session Reliability.
General Profile enum Balanced Vanilla, MaxFps, Balanced, Competitive, Potato, Cinematic, Custom Select a built-in profile or use config values.
Session Reliability Enabled bool true true/false Run passive session monitoring independently of the FPS profile.
Session Reliability WriteDiagnosticsToDisk bool true true/false Write bounded diagnostics v2 JSONL files.
Session Reliability RunInBackground bool true true/false Keep this Unity client active while unfocused to reduce Alt-Tab stalls.
Session Reliability CommonDisconnectGuards bool true true/false Install exact-signature reliability guards. Restart required.
Experimental Rejoin Enabled bool false true/false Enable the non-host room-bound experiment. Host and client opt-in plus restart required.
Experimental Rejoin PlayerTtlMilliseconds int 60000 5000-300000 Time Photon retains an inactive actor slot. Restart required.
Experimental Rejoin EmptyRoomTtlMilliseconds int 30000 0-300000 Time Photon retains an empty room allocation. Not host migration. Restart required.
Experimental Rejoin ReconnectTimeoutSeconds float 12 3-30 Timeout for each of the two transport attempts. Restart required.
Experimental Rejoin RetryDelaySeconds float 1 0.25-5 Delay before the single retry. Restart required.
Diagnostics ShowOverlay bool false true/false Show the local metrics overlay.
Frame Pacing TargetFrameRate int 120 -1, or 30-1000 Unity target FPS; -1 uses the platform default.
Frame Pacing VSyncCount int 0 0-4 Unity VSync interval.
Adaptive Enabled bool true true/false Enable adaptive VFX pressure control.
Adaptive PressureSeconds float 4 2-20 Sustained pressure required before moving up one tier.
Adaptive RecoverySeconds float 10 5-60 Sustained headroom required before recovering one tier.
Graphics AntiAliasing int 2 0, 2, 4, 8 Local MSAA samples; other values normalize downward to a supported step.
Graphics Shadows bool true true/false Enable local real-time shadows unless compatibility mode defers them.
Graphics ShadowDistance float 35 0-500 Local shadow draw distance unless compatibility mode defers it.
Graphics PixelLightCount int 2 0-32 Unity pixel-light count unless compatibility mode defers it.
Graphics AnisotropicFiltering bool true true/false Force-enable or disable anisotropic filtering.
Graphics TextureLimit int 0 0-3 Unity master texture downscale level; 0 is full resolution.
Graphics LodBias float 1 0.1-5 Local level-of-detail bias.
VFX Budget enum Reduced Full, Reduced, Minimal Select the static VFX/light budget.
VFX ParticleLimitPerSystem int 512 16-10000 Base maximum particles for each tracked system.
VFX TrailLifetimeMultiplier float 0.75 0-1.5 Base multiplier applied to tracked trail lifetimes.
VFX RescanSeconds float 12 10-30 Delay between active VFX discovery passes.

Paths

Purpose Path
Config BepInEx/config/com.reksikperformance.rounds.cfg
Diagnostics v2 BepInEx/config/ReksikPerformance/diagnostics/session-reliability-*.jsonl
Main BepInEx log BepInEx/LogOutput.log
In-game controls UnboundLib mod settings -> REKSIK PERFORMANCE

When reporting a disconnect, include the matching diagnostics JSONL and BepInEx log. Review files before sharing them even though the writer intentionally excludes direct personal/network identifiers.

Testing checklist

Performance

  1. Enable the overlay and note FPS, 1% low and frame time at T0.
  2. Create a repeatable VFX-heavy round.
  3. Confirm pressure persists for the configured delay before the tier rises.
  4. Confirm the tier holds for at least 3.5 seconds.
  5. Remove pressure and confirm recovery happens one tier at a time.
  6. Select Vanilla or disable the performance module and confirm captured graphics/VFX values are restored.

Diagnostics and guards

  1. Start a room and confirm the menu shows a Session Reliability sample.
  2. Check active/patched/intervention counts in the menu and skipped/failed details in LogOutput.log.
  3. Produce a controlled room leave or client disconnect.
  4. Confirm a v2 JSONL record appears in the diagnostics directory.
  5. Confirm the file contains no raw room/player names, address, exception message or stack trace.

Experimental rejoin

Use two machines or two isolated clients.

  1. Enable the experiment on host and non-host; restart both.
  2. Let the opted-in host create a new room.
  3. Confirm the non-host has a fresh SnapshotReady warm-state receipt.
  4. Interrupt only the non-host transport briefly.
  5. Confirm the actor becomes inactive rather than being destructively removed.
  6. Confirm ReconnectAndRejoin returns the same actor to the same room.
  7. Do not treat mid-point transport success as gameplay recovery.
  8. Continue to the next point and confirm same-room/player/actor/deck validation succeeds.
  9. Repeat with a deck mismatch or expired receipt and confirm restoration is rejected.
  10. Test attempt exhaustion and confirm vanilla NetworkRestart() fallback.
  11. Test an unconfigured third peer in a mixed lobby and confirm the limitation is understood.
  12. Test a room-creation path with null/unprepared RoomOptions.
  13. Test an optional RWF callback mismatch without confusing it with mandatory-hook FailedClosed.
  14. Test a deliberately incompatible short TTL / long retry envelope.
  15. After a rejected warm validation, confirm no old state is fabricated, no automatic restart occurs, and a later healthy point can produce a fresh SnapshotReady receipt.

FAQ

Does every player need the same FPS profile?

No. Performance profiles are local rendering choices.

Why did VFX or shadow settings do nothing?

Performance Improvements may be installed. In compatibility mode, ReksikPerformance deliberately defers overlapping VFX, shadow and pixel-light ownership.

Why did FPS not improve?

The active bottleneck may be gameplay scripts, physics, networking, the driver or a cap outside Unity. Use the overlay and compare a repeatable scene rather than assuming every low-FPS case is VFX-bound.

Can the host hot-rejoin?

No. The rejoin subsystem rejects previously observed host context. EmptyRoomTtl is room retention, not host migration.

Does successful transport rejoin mean I can continue the interrupted point?

No. Wait for the next point-boundary validation. Transport success alone leaves NeedsWarmStateRestore=true.

Does point-boundary restore copy my old health?

No. It verifies that reconstructed health is finite and positive, but never overwrites it from the receipt. It verifies exact deck composition and reloads ammo when applicable.

Why did rejoin use vanilla behavior instead?

The BepInEx log now records a parseable [warm-receipt=...] reason such as host, no-room, no-owned-player or stale-receipt. Other likely reasons include a non-recoverable cause, an unprepared/expired actor slot, or missing/changed critical runtime hooks. Missing critical contracts fail closed; host/TTL/setup mismatches use the bounded Photon attempt or vanilla fallback described above.

Are diagnostics safe to upload publicly?

They are privacy-minimized, not magically risk-free. Direct player/room names, IPs, auth data, raw messages and stacks are excluded. Plugin GUID/version inventory and anonymous per-launch fingerprints remain, so review files before sharing.

Rejoin/warm-state transitions are not embedded in diagnostics v2 JSONL. Use the live menu and BepInEx/LogOutput.log for that state machine.

Which settings require a restart?

CommonDisconnectGuards and all Experimental Rejoin fields are startup-installed behavior. Restart after changing them.

Dependencies

  • BepInExPack_ROUNDS
  • UnboundLib
Thunderstore development is made possible with ads. Please consider making an exception to your adblock.