Table of Contents

Class ClientDisplaySettings

Namespace
FishMMO.Client
Assembly
FishMMO.Client.dll

Everything the client knows about display modes: which ones the hardware offers, which one the player saved, and how to put one into effect.

public static class ClientDisplaySettings
Inheritance
ClientDisplaySettings
Inherited Members

Remarks

Split out of the options panel so the two callers that need it can share one implementation. The panel needs the option lists to build its dropdowns; the boot phase needs the resolve-and-apply half and nothing else. While this logic lived inside the panel, the boot phase had no way to reach it — which is why a saved resolution was never applied at start-up, only ever by pressing Apply in a panel the player had to open first.

Brightness is re-applied on every scene load. UnityEngine.RenderSettings.ambientLight is per-scene state baked into the lighting settings of whichever scene is active, so loading a scene discards it. The client loads several — launcher, login, world, and one per world scene transfer — so a brightness set once at boot survived only until the first load.

Brightness drives two properties, because which one is read depends on the scene. UnityEngine.RenderSettings.ambientLight is consulted only under AmbientMode.Flat. Every world scene in this project is authored AmbientMode.Skybox, where ambient comes from the skybox's spherical harmonics scaled by UnityEngine.RenderSettings.ambientIntensity and ambientLight is ignored outright — so a slider that wrote only ambientLight did nothing at all anywhere the player actually plays. It appeared to work while testing, because the login and preboot scenes are authored Flat. Both are written, so the setting has an effect whichever mode a scene uses and stays correct if one is re-authored.

This is an ambient control and not an exposure control: it scales indirect light, leaving direct lights and the skybox itself alone. A true gamma control would need a URP Volume carrying a Color Adjustments override, which is a rendering asset rather than a setting.

Fields

DefaultBrightness

Brightness used when the player has never chosen one.

public const float DefaultBrightness = 0.5

Field Value

float

Remarks

Mid-scale rather than maximum. This scales indirect light (see the class remarks), so 1.0 is the top of the slider with no headroom left to raise it — a player who finds the world too bright can only go down, and one who finds it too dark has nowhere to go. Half way leaves adjustment in both directions.

Only applies to a player who has never set a brightness; an existing saved value is read in preference to this and is not overwritten.

MaximumBrightness

Highest brightness the slider offers.

public const float MaximumBrightness = 1

Field Value

float

MinimumBrightness

Lowest brightness the slider offers.

public const float MinimumBrightness = 0

Field Value

float

Methods

ApplyBrightness(float)

Writes a brightness level into the scene's ambient lighting and remembers it.

public static void ApplyBrightness(float value)

Parameters

value float

Brightness in the range 0..1. Clamped.

ApplyQualityLevel(int, bool)

Switches to a quality level and puts the player's VSync preference back on top of it.

public static void ApplyQualityLevel(int index, bool applyExpensiveChanges)

Parameters

index int

Index into UnityEngine.QualitySettings.names.

applyExpensiveChanges bool

True to let Unity re-create render targets and reload textures. Worth it when the player is watching and waiting for the result; not during boot, where the stall reads as a hang.

Remarks

SetQualityLevel installs that level's own authored vSyncCount, which is a property of the level and has nothing to do with what the player chose — so the preference is re-applied afterwards. Doing that here rather than at each call site is what stops the two from being forgotten separately.

ApplySaved()

Applies every saved display setting: mode, VSync, frame-rate cap and brightness.

public static void ApplySaved()

Remarks

Order matters in one place only. VSync is written before the frame-rate cap because UnityEngine.Application.targetFrameRate is ignored outright whenever the active quality level has vSync enabled — so a cap applied first is silently discarded by a VSync value applied second.

ApplySavedBrightness()

Applies the saved brightness to the scene's ambient light.

public static void ApplySavedBrightness()

ApplySavedDisplayMode()

Puts the saved resolution, refresh rate and fullscreen mode into effect.

public static void ApplySavedDisplayMode()

Remarks

Nothing happens when no complete display mode has been saved. That distinction matters: a fresh install must keep whatever mode the player launched in — forcing one of the enumerated modes on first run would resize a window the player had already sized, and on a multi-monitor setup can move it to a different screen.

ApplySavedFrameRate()

Applies the saved render frame-rate cap.

public static void ApplySavedFrameRate()

Remarks

Deliberately runs after the bootstrap system has installed its own menu-time cap. That cap is the default for a client with no preference — and ResolveSavedFrameRate(List<int>) resolves to the same number, so running afterwards re-applies it rather than replacing it. A player who has chosen a cap gets theirs, and getting the order backwards is indistinguishable from the setting not being saved at all.

ApplySavedQualityLevel()

Applies the saved quality level, matched by name.

public static void ApplySavedQualityLevel()

ApplySavedVSync()

Applies the saved VSync preference.

public static void ApplySavedVSync()

ApplyVSync(bool)

Writes a VSync preference into the active quality level.

public static void ApplyVSync(bool enabled)

Parameters

enabled bool

True to wait for the display's refresh.

Remarks

Every VSync write goes through here so it cannot happen without the editor safeguard in FishMMO.Client.ClientDisplaySettings.CaptureAuthoredQuality() having run first.

BuildFrameRateChoices()

The selectable frame-rate caps for this machine, ascending.

public static List<int> BuildFrameRateChoices()

Returns

List<int>

Remarks

The floor is the network tick rate. FishNet derives ticks from the update loop, so a frame rate below the tick rate cannot deliver them on schedule and the client falls behind the server's timeline — offering such a value lets a player break their own connection from a settings menu.

The ceiling is the display's fastest mode; frames produced faster than the panel can present them are discarded at scan-out.

The display's own rate is always included even when it is not a ladder value — 165 Hz and 59.94 Hz panels both exist.

BuildFullscreenOptions()

The fullscreen modes this platform offers, in the order they are presented.

public static List<FullScreenMode> BuildFullscreenOptions()

Returns

List<FullScreenMode>

Remarks

The list is built conditionally, so an index into it is not the UnityEngine.FullScreenMode value — on a build without exclusive fullscreen the second entry is MaximizedWindow. The stored key is always the enum value.

BuildRefreshRateOptions(Vector2Int)

The refresh rates this display offers at a given resolution, slowest first.

public static List<RefreshRate> BuildRefreshRateOptions(Vector2Int size)

Parameters

size Vector2Int

Returns

List<RefreshRate>

BuildResolutionOptions()

The distinct width/height pairs this display supports, smallest first.

public static List<Vector2Int> BuildResolutionOptions()

Returns

List<Vector2Int>

Remarks

Deduplicated. Screen.resolutions returns one entry per width/height/refresh-rate combination, so a monitor offering three refresh rates lists every resolution three times.

ResolveSavedFrameRate(List<int>)

The frame-rate cap to apply, given the caps this machine offers.

public static int ResolveSavedFrameRate(List<int> choices)

Parameters

choices List<int>

Returns

int

Remarks

Three cases, and they are deliberately different from one another.

No preference at all — a fresh install — keeps the boot-time menu cap of BootstrapTargetFrameRate. This used to return the display's fastest mode instead, which made the bootstrap cap dead on arrival: it was installed before the first frame and replaced microseconds later by the settings apply, so a fresh install rendered its launcher and login screens as fast as the panel allowed and pegged a core drawing a static menu. Somebody with no opinion about frame rate should get the modest default, not the maximum.

A saved value this machine offers is honoured exactly.

A saved value it does not offer falls back to the fastest available, and not to the default: that is the player who moved the game to a different monitor, and they did express a preference — the old number is just meaningless on the new hardware, so the closest thing to "as fast as I asked for" is the most this display can do.

ToHz(RefreshRate)

Converts a refresh-rate ratio to hertz.

public static float ToHz(RefreshRate rate)

Parameters

rate RefreshRate

Returns

float

TryResolveSavedDisplayMode(out Vector2Int, out RefreshRate, out FullScreenMode)

Reads the saved display mode, rejecting anything this display cannot present.

public static bool TryResolveSavedDisplayMode(out Vector2Int size, out RefreshRate rate, out FullScreenMode mode)

Parameters

size Vector2Int

The saved resolution.

rate RefreshRate

The saved refresh rate, or the display's own when none was saved.

mode FullScreenMode

The saved fullscreen mode.

Returns

bool

True when a complete, supported mode was found.

Remarks

A saved resolution the display no longer offers is refused rather than approximated. The most common way to get one is moving the install to a different machine, and forcing an unsupported mode there is exactly the failure the options panel's confirmation countdown exists to protect against — except at boot, where there is no countdown and no panel.