Parcourir la source

Merge pull request #37 from KyleThmpsn/feature/account-preference-replication

Replicate FOV, VSync, and keybinding source from settings.json
stan il y a 3 semaines
Parent
commit
d0fe8886fa

+ 3 - 0
Sunrise/resources/default_settings.json

@@ -237,6 +237,8 @@
           "brightness": 3,
           "show_fps": false,
           "hdr_mode": 0,
+          "vertical_sync_interval": 0,
+          "field_of_view": 85,
           "calibration_primary": 10000.0,
           "calibration_alpha": 0.0
         },
@@ -269,6 +271,7 @@
           "clan_chat_join_mode": 1,
           "chat_auto_hide_mode": 1
         },
+        "key_binding_source": "computer",
         "key_bindings": {
           "fire": { "primary": "left mouse button", "secondary": null },
           "toggle_zoom": { "primary": null, "secondary": null },

+ 14 - 0
Sunrise/src/core/settings/state/account_settings_parser.cpp

@@ -25,6 +25,7 @@ bool Parser::account_settings(state::account::settings::AccountSettings& output)
         return false;
     }
     std::bitset<static_cast<std::size_t>(Group::count)> supplied;
+    bool hasKeyBindingSource = false;
     const auto mark = [&supplied](Group group) noexcept {
         const std::size_t index = static_cast<std::size_t>(group);
         if (supplied.test(index)) {
@@ -61,6 +62,19 @@ bool Parser::account_settings(state::account::settings::AccountSettings& output)
             if (!mark(Group::social) || !social_settings(output.social)) {
                 return false;
             }
+        } else if (key == "key_binding_source") {
+            std::string_view source;
+            if (hasKeyBindingSource || !string(source)) {
+                return false;
+            }
+            if (source == "account") {
+                output.keyBindingSource = state::account::settings::KeyBindingSource::account;
+            } else if (source == "computer") {
+                output.keyBindingSource = state::account::settings::KeyBindingSource::computer;
+            } else {
+                return false;
+            }
+            hasKeyBindingSource = true;
         } else if (key == "key_bindings") {
             if (!mark(Group::keyBindings) || !key_bindings(output.keyBindings)) {
                 return false;

+ 21 - 0
Sunrise/src/core/settings/state/display_parser.cpp

@@ -3,6 +3,12 @@
 #include "../parser.h"
 
 namespace sunrise::core::settings::parser {
+namespace {
+
+/** DXGI accepts sync intervals from immediate presentation through every fourth refresh. */
+constexpr std::uint64_t kMaximumVerticalSyncInterval = 4;
+
+} // namespace
 
 /** Parses screen and renderer settings under stable Sunrise-owned names. */
 bool Parser::display_settings(state::account::settings::Display& output) noexcept {
@@ -20,6 +26,8 @@ bool Parser::display_settings(state::account::settings::Display& output) noexcep
         return false;
     }
     std::bitset<static_cast<std::size_t>(Field::count)> supplied;
+    bool hasVerticalSyncInterval = false;
+    bool hasFieldOfView = false;
     const auto mark = [&supplied](Field field) noexcept {
         const std::size_t index = static_cast<std::size_t>(field);
         if (supplied.test(index)) {
@@ -48,6 +56,19 @@ bool Parser::display_settings(state::account::settings::Display& output) noexcep
             if (!mark(Field::hdrMode) || !signed_byte(output.hdrMode)) {
                 return false;
             }
+        } else if (key == "vertical_sync_interval") {
+            std::uint64_t value = 0;
+            if (hasVerticalSyncInterval || !unsigned_integer(value)
+                || value > kMaximumVerticalSyncInterval) {
+                return false;
+            }
+            output.verticalSyncInterval = static_cast<std::uint8_t>(value);
+            hasVerticalSyncInterval = true;
+        } else if (key == "field_of_view") {
+            if (hasFieldOfView || !signed_32(output.fieldOfView)) {
+                return false;
+            }
+            hasFieldOfView = true;
         } else if (key == "calibration_primary") {
             if (!mark(Field::calibrationPrimary) || !floating_point(output.calibrationPrimary)) {
                 return false;

+ 1 - 0
Sunrise/src/middleware/datagen/family4/account/preferences/layout.h

@@ -104,6 +104,7 @@ struct BindingsRecord {
     std::uint8_t voiceChatMirror{};
     std::uint8_t verticalSyncMirror{};
     std::array<std::byte, kFieldOfViewPaddingSize> fieldOfViewPadding{};
+    /** Stores the absolute FOV menu value used by the target client. */
     std::int32_t fieldOfViewAdjustment{};
     /** 0 picks this replicated array after the client's initial seed pass. */
     std::uint8_t sourceSelector{};

+ 12 - 4
Sunrise/src/middleware/datagen/family4/account/preferences/preferences_encoder.cpp

@@ -11,12 +11,15 @@ constexpr std::uint16_t kUnboundInputCode = 0x0074;
 constexpr std::int32_t kOpenSeedVersion = 0;
 /**
  * Seed version 1 closes a gate so the client keeps the replicated values behind it.
- * The keybinding gate stays open so the client seeds its own defaults. The post-processing gate
- * stays closed, or local cvars would overwrite its 3 replicated fields on every sign-in.
+ * The keybinding gate now stays closed because Sunrise supplies the modeled preferences behind it.
+ * The post-processing gate stays closed, or local cvars would overwrite its 3 replicated fields on
+ * every sign-in.
  */
 constexpr std::int32_t kClosedSeedVersion = 1;
 /** Source 0 makes later input reads use the replicated keybinding array. */
 constexpr std::uint8_t kReplicatedBindingSource = 0;
+/** Source 1 makes later input reads use the computer-local keybindings. */
+constexpr std::uint8_t kComputerBindingSource = 1;
 
 /**
  * Converts a semantic boolean to the native 1-byte form.
@@ -53,8 +56,11 @@ bool encode(const state::account::settings::AccountSettings& settings,
     record = {};
     bindingsRecord = {};
     record.postProcessingSeedVersion = kClosedSeedVersion;
-    bindingsRecord.accountSeedVersion = kOpenSeedVersion;
-    bindingsRecord.sourceSelector = kReplicatedBindingSource;
+    bindingsRecord.accountSeedVersion = kClosedSeedVersion;
+    bindingsRecord.sourceSelector =
+        settings.keyBindingSource == state::account::settings::KeyBindingSource::account
+            ? kReplicatedBindingSource
+            : kComputerBindingSource;
 
     const auto& controls = settings.controls;
     record.buttonLayout = controls.buttonLayout;
@@ -88,6 +94,8 @@ bool encode(const state::account::settings::AccountSettings& settings,
     record.brightness = display.brightness;
     record.showFps = native_boolean(display.showFps);
     record.hdrMode = display.hdrMode;
+    bindingsRecord.verticalSyncMirror = display.verticalSyncInterval;
+    bindingsRecord.fieldOfViewAdjustment = display.fieldOfView;
     record.calibrationPrimary = display.calibrationPrimary;
     record.calibrationAlpha = display.calibrationAlpha;
 

+ 12 - 3
Sunrise/src/state/account/settings/settings_state.cpp

@@ -35,6 +35,10 @@ constexpr Range<std::int8_t> kAudioVolume{0, 10};
 
 /** Brightness stores the 7 menu choices, 0 to 6. */
 constexpr Range<std::int8_t> kBrightness{0, 6};
+/** VSync stores the DXGI presentation interval from 0 to 4. */
+constexpr Range<std::uint8_t> kVerticalSyncInterval{0, 4};
+/** Field of view exposes the 55 through 105 degree range. */
+constexpr Range<std::int32_t> kFieldOfView{55, 105};
 /** The first unidentified calibration field uses the working renderer fallback. */
 constexpr float kCalibrationPrimary = 10000.0F;
 /** The second unidentified calibration field uses the working renderer alpha. */
@@ -116,6 +120,8 @@ template <typename Value, std::size_t Count>
  */
 [[nodiscard]] bool valid_display(const Display& value) noexcept {
     return within(value.brightness, kBrightness) && within(value.hdrMode, kTwoChoiceSelector)
+           && within(value.verticalSyncInterval, kVerticalSyncInterval)
+           && within(value.fieldOfView, kFieldOfView)
            && value.calibrationPrimary == kCalibrationPrimary
            && value.calibrationAlpha == kCalibrationAlpha;
 }
@@ -155,9 +161,12 @@ template <typename Value, std::size_t Count>
 
 /** Checks a whole account-settings object against the supported menu domains. */
 bool valid(const AccountSettings& value) noexcept {
-    return value.configured && value.keyBindings.configured && valid_controls(value.controls)
-           && valid_audio(value.audio) && valid_display(value.display)
-           && valid_interface(value.interface) && valid_social(value.social);
+    const bool validBindingSource = value.keyBindingSource == KeyBindingSource::account
+                                    || value.keyBindingSource == KeyBindingSource::computer;
+    return value.configured && value.keyBindings.configured && validBindingSource
+           && valid_controls(value.controls) && valid_audio(value.audio)
+           && valid_display(value.display) && valid_interface(value.interface)
+           && valid_social(value.social);
 }
 
 } // namespace sunrise::state::account::settings

+ 11 - 0
Sunrise/src/state/account/settings/settings_state.h

@@ -9,6 +9,12 @@ namespace sunrise::state::account::settings {
 /** The account record's one-time audio migration treats version 8 as finished. */
 inline constexpr std::int8_t kCompletedAudioMigrationVersion = 8;
 
+/** Selects account-replicated or computer-local keyboard and mouse bindings. */
+enum class KeyBindingSource : std::uint8_t {
+    account,
+    computer,
+};
+
 /** Authored controller and mouse input preferences. */
 struct Controls {
     std::int8_t buttonLayout{};
@@ -48,6 +54,10 @@ struct Display {
     std::int8_t brightness{};
     bool showFps{};
     std::int8_t hdrMode{};
+    /** Presentation interval: 0 disables VSync, 1 to 4 wait that many refreshes. */
+    std::uint8_t verticalSyncInterval{};
+    /** Horizontal field of view in degrees. */
+    std::int32_t fieldOfView{85};
     /** First unidentified renderer-calibration scalar. */
     float calibrationPrimary{};
     /** Second unidentified renderer-calibration scalar. */
@@ -95,6 +105,7 @@ struct AccountSettings {
     Display display;
     Interface interface;
     Social social;
+    KeyBindingSource keyBindingSource{KeyBindingSource::computer};
     bindings::KeyBindings keyBindings;
     /** True only when a settings object was supplied by configuration. */
     bool configured{};