Feat: add game-specific server presets#22
Conversation
Yeah i thought about the same initially, drop down menu where one of the options would be to open a window to manage presets. But if you want to keep everything simple and in once place, you can replace safe/delete with icons of diskette and trash bin right next to the presets drop down. Seems intuitive that it's for save/delete when it's right next to the presets drop down |
- Persist presets and last-selected preset names per game in JsonSetting - Add ServerPresetModel plus preset name dialog for save/create flow - Add preset picker and save/delete preset actions to the main window - Store clustered state and blocked server keys in each preset - Restore the last applied preset for the current game on load and game switch when the state is still clean - Apply presets through a reset-first flow: clear current rules, switch clustered state if needed, then apply the preset's blocked set - Track blocked server keys in-session so the current state can be saved without reading firewall rules back from the OS - Clear the active preset selection and persisted last-selected preset when manual block/unblock or manual cluster changes dirty the current state - Keep the last preset name as an in-session save suggestion so updating an existing preset does not require retyping it - Use the full current ServerModels list for internal reset paths during preset apply, game switching, and cluster/uncluster transitions while keeping the user-facing Unblock All action filtered - Update the cluster/uncluster button text after preset apply and manual view switches so it matches the actual active clustered state - Add localized preset strings and a disabled empty-state UI when the current game has no presets
|
Think it would be better to move presets to a separate window with its own view model. If it requires access to the main window view model then just pass the view model as constructor parameter when initializing the window after clicking |
|
Sure, I'll move the preset controls to a separate window. Just want to clarify the scope: Should the preset window be a simple management panel that operates on the main window's current state? As in:
The main window would keep the server list, cluster toggle, and block/unblock controls as-is, and just get a single "Presets" button that opens this window. I'm asking because presets store the clustered/unclustered state and the blocked server set for that view. If the preset window needed its own server list, search, and cluster controls to let users build presets independently, it would end up being a near-duplicate of the main window. I'm assuming that's not the intent and that preset creation is just "configure your state in the main window, then save it as a preset." Edit: |
If you can make it similar to the cs2-server-picker style, where the presets window contains the CRUD operations with 2 datagrid, 1 for the preset names and the other is the preset servers. |
|
If I were to approach this I'll just take the previous design from previous cs2 server picker app and convert it to a modern mvvm approach. |
236242a to
ef096e8
Compare
|
I pushed some of the fixes we discussed earlier around preset pruning and a few behavior cleanups. I'll work on the new view when I can. |
Well if you are to take previous approach hopefully the separate window is just for managing presets, but the new dropdown for quick change of presets stays, because that is actually quite cool. Opening separate window to then pick if i want to block except preset, or block preset seems kinda like useless nesting. |
Keep the dropdown in the main window. Separate window for managing presets to prevent a bit complicated UI/UX design. |
|
So here is what I settled on. I still need to iron out a few things before pushing. Any feedback? Carnac_Gtf2sQV6W1.mp4 |
|
Yeah, that seems damn good. I'm curious if it's possible to add a whitelist option instead for a preset, but that seems to be out of scope. Otherwise looks good |
Looks good to me! |
Sorry, this is not yet pushed right and still in WIP? |
Sorry for the delay, just pushed the final changes (hopefully). Sorting works on both the preset list and the blocked servers columns, country flags included. Had to write a custom NaturalStringComparer for preset name so something like "New Preset (10)" doesn't end up before "New Preset (1)". I've tested as much as I could, let me know if I missed anything. |
Great! I'll take time reviewing and will test it out on linux distros (Ubuntu, Arch Manjaro). Thanks. |
There was a problem hiding this comment.
It might be better to extract PresetListItemViewModel and PresetServerSelectionItem inner class inside PresetManagerWindowViewModel.cs with their own dedicated model classes in Models directory
Also PresetListItemViewModel can be renamed to PresetItemModel since it doesn't get binded as context to a view and same rename for PresetServerSelectionItem to PresetServerItemModel? Correct me if I'm wrong with this.
I'm not an MVVM expert, but both classes are more like wrappers around existing domain models with their own UI state on top (
|
|
Sorry! That's deadcode. I had a selection bug for the preset rename and I tried to transform TextBlock->TextBox manually (outside of DataGrid lifecycle) to see if the edit field still bugged or not. I didn't fully remove the code tied to that. For rename, the relevant code is: What do you think about splitting the logic into two classes
|
I think its better splitting them into their own model classes since they are used as models inside |
|
Just pushed the requested changes. Hopefully this is what you wanted. |
Thanks. I'll take time reviewing the changes since its a huge PR. |
|
I refactored some stuffs for simplicity. Not sure if I broke something but I tried my best to test the new changes. If you have time, would appreciate a test to verify things still work as they should be. |
Found one last leftover unused reference. I tested the code with your changes, everything seems to work fine! |
- Remove in-memory blocked-server tracking and simplify cluster/preset handling. - Deleted the _blockedServerKeys field and TrackBlockedServerKeys/ReplaceBlockedServerKeysFromPreset methods; SetClusterStateAsync signature simplified (removed shouldUpdatePresetSelection) and callers updated. - Preset selection is now marked dirty by clearing saved last-selected preset (MarkPresetSelectionDirtyAsync) and resetting SelectedPreset. - Use JsonSetting methods directly for preset lookups (GetPresetsByGameMode/GetPresetByGameMode). - Also normalize PerformOperationAsync calls to pass ServerModels directly.




This PR adds the requested server presets feature (#17). I had to make a few assumptions you might not agree with, but I tried to keep the behavior intuitive and consistent with the existing block/unblock flow. Let me know if anything seems off or unclear. I should note that I did not test these changes on Linux.
Each preset stores:
Preset Application
Preset application is reset-first and does not diff against the current firewall state. Applying a preset clears the current server list for the active game/view, switches clustered state if needed, then applies the preset's blocked server set.
Dirty State
If the user manually blocks or unblocks servers after applying a preset, the app treats the state as dirty:
Save Presetretains the previous preset name as an in-session suggestion, so saving back to the same preset doesn't require retyping the nameIf the app is closed while dirty, no preset is auto-restored on next launch. It just loads the last selected game/view.
Clustered State
Clustered state is part of the persisted preset. A clustered preset restores the clustered view and blocked clusters; an unclustered preset restores the unclustered view and blocked individual servers. Manually toggling cluster/uncluster still follows the original reset-first behavior and clears the active preset selection, since switching representation discards the currently applied view-specific state.
Internal Reset Behavior /
UnblockAllAsync()The original code reused
UnblockAllAsync()for internal flows like cluster/uncluster, but that method operates onFilteredServerModels. In practice, an internal reset can be silently limited by the active search filter, even though the surrounding code describes it as a full "unblock all" step.The split now is:
Unblock Allaction still operates on the filtered listServerModelscollectionThis prevents hidden stale rules from surviving cluster/uncluster transitions, game-mode switches, and preset apply resets. If that's not the original intent, I can revert that part independently.
Preset Saving
Preset saving uses in-session blocked-key tracking rather than reading firewall rules back from the OS:
TrackBlockedServerKeys(...)Deleting a preset only removes the saved entry. It does not modify currently applied firewall rules.
Performance
This PR does not depend on the firewall refactor (#21) and works correctly on current
main.It does however make the existing Windows firewall cost more visible, since preset apply, game switching, and cluster/uncluster transitions all go through reset-first rule changes.
I recorded a side-by-side comparison, left is this PR with the refactor, right is this PR on top of current
main:ServerPickerPresetsFirewallComp.mp4
Demo was recorded before I fixed the width change bug on the preset dropdown so don't mind that.