From f75ef55405d54ac2ea08445d1dceee1b2fa921af Mon Sep 17 00:00:00 2001 From: zznty <94796179+zznty@users.noreply.github.com> Date: Sun, 20 Oct 2024 07:40:34 +0700 Subject: [PATCH] add dynamic world config --- Torch.Server/Managers/InstanceManager.cs | 2 +- .../ViewModels/BlockLimitViewModel.cs | 8 +- .../ViewModels/ConfigDedicatedViewModel.cs | 4 +- Torch.Server/ViewModels/DynamicViewModel.cs | 115 +++++++ .../ViewModels/SessionSettingsViewModel.cs | 321 ------------------ .../ViewModels/WorldConfigurationViewModel.cs | 6 +- Torch.Server/Views/ConfigControl.xaml | 2 +- .../Views/WorldGeneratorDialog.xaml.cs | 2 +- Torch/Views/FlagsEditor.xaml.cs | 10 +- Torch/Views/PropertyGrid.xaml.cs | 17 +- 10 files changed, 145 insertions(+), 342 deletions(-) create mode 100644 Torch.Server/ViewModels/DynamicViewModel.cs delete mode 100644 Torch.Server/ViewModels/SessionSettingsViewModel.cs diff --git a/Torch.Server/Managers/InstanceManager.cs b/Torch.Server/Managers/InstanceManager.cs index 0a33755..9e5ca86 100644 --- a/Torch.Server/Managers/InstanceManager.cs +++ b/Torch.Server/Managers/InstanceManager.cs @@ -250,7 +250,7 @@ namespace Torch.Server.Managers Log.Debug("Loaded mod list from world"); if (!modsOnly) - DedicatedConfig.SessionSettings = new SessionSettingsViewModel(checkpoint.Settings); + DedicatedConfig.SessionSettings = new DynamicViewModel(checkpoint.Settings); } catch (Exception e) { diff --git a/Torch.Server/ViewModels/BlockLimitViewModel.cs b/Torch.Server/ViewModels/BlockLimitViewModel.cs index 025e99a..b4e25c7 100644 --- a/Torch.Server/ViewModels/BlockLimitViewModel.cs +++ b/Torch.Server/ViewModels/BlockLimitViewModel.cs @@ -1,15 +1,17 @@ -namespace Torch.Server.ViewModels +using VRage.Game; + +namespace Torch.Server.ViewModels { public class BlockLimitViewModel : ViewModel { - private SessionSettingsViewModel _sessionSettings; + private DynamicViewModel _sessionSettings; public string BlockType { get; set; } public short Limit { get; set; } //public CommandBinding Delete { get; } = new CommandBinding(new DeleteCommand()); - public BlockLimitViewModel(SessionSettingsViewModel sessionSettings, string blockType, short limit) + public BlockLimitViewModel(DynamicViewModel sessionSettings, string blockType, short limit) { _sessionSettings = sessionSettings; BlockType = blockType; diff --git a/Torch.Server/ViewModels/ConfigDedicatedViewModel.cs b/Torch.Server/ViewModels/ConfigDedicatedViewModel.cs index 5db7cb5..4a9908e 100644 --- a/Torch.Server/ViewModels/ConfigDedicatedViewModel.cs +++ b/Torch.Server/ViewModels/ConfigDedicatedViewModel.cs @@ -18,7 +18,7 @@ namespace Torch.Server.ViewModels { _config = configDedicated; _config.IgnoreLastSession = true; - SessionSettings = new SessionSettingsViewModel(_config.SessionSettings); + SessionSettings = new DynamicViewModel(_config.SessionSettings); Task.Run(() => UpdateAllModInfosAsync()); } @@ -48,7 +48,7 @@ namespace Torch.Server.ViewModels return true; } - public SessionSettingsViewModel SessionSettings { get; set; } + public DynamicViewModel SessionSettings { get; set; } public MtObservableList Worlds { get; } = new MtObservableList(); private WorldViewModel _selectedWorld; diff --git a/Torch.Server/ViewModels/DynamicViewModel.cs b/Torch.Server/ViewModels/DynamicViewModel.cs new file mode 100644 index 0000000..ce8ec3c --- /dev/null +++ b/Torch.Server/ViewModels/DynamicViewModel.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using HarmonyLib; +using JetBrains.Annotations; + +namespace Torch.Server.ViewModels; + +public class DynamicViewModel(T obj) : ViewModel +{ + private readonly T _obj = obj; + + public ViewModel Wrapper { get; } = CreateProxy(obj); + + public static implicit operator T(DynamicViewModel obj) => obj._obj; + + private static ViewModel CreateProxy(T obj) + { + if (DynamicViewModel.Proxies.TryGetValue(typeof(T), out var proxy)) + return (ViewModel)Activator.CreateInstance(proxy, obj); + + var viewModelSetMethod = AccessTools.GetDeclaredMethods(typeof(ViewModel)) + .First(b => b.Name == "SetValue" && b.GetParameters()[0].ParameterType.IsByRef); + + DynamicViewModel.ModuleBuilder ??= + AssemblyBuilder.DefineDynamicAssembly(new("Torch.Server.ViewModels.Generated"), AssemblyBuilderAccess.Run) + .DefineDynamicModule("Torch.Server.ViewModels.Generated"); + + var type = DynamicViewModel.ModuleBuilder.DefineType($"{typeof(T).FullName}ViewModel", + TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class, typeof(ViewModel)); + + var instanceField = type.DefineField("_instance", typeof(T), FieldAttributes.Private | FieldAttributes.InitOnly); + + var ctor = type.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, [typeof(T)]); + { + var il = ctor.GetILGenerator(); + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Stfld, instanceField); + il.Emit(OpCodes.Ret); + } + + foreach (var field in typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public)) + { + var prop = type.DefineProperty(field.Name, PropertyAttributes.None, field.FieldType, null); + + foreach (var customAttribute in field.GetCustomAttributesData()) + { + var hasCustomArgs = customAttribute.NamedArguments?.Any() ?? false; + var customArgsField = hasCustomArgs && customAttribute.NamedArguments[0].IsField; + + var constructorArgs = customAttribute.ConstructorArguments.Select(b => b.Value).ToArray(); + + CustomAttributeBuilder attributeBuilder; + if (!hasCustomArgs) + attributeBuilder = new CustomAttributeBuilder(customAttribute.Constructor, constructorArgs); + else if (customArgsField) + attributeBuilder = new CustomAttributeBuilder(customAttribute.Constructor, constructorArgs, + customAttribute.NamedArguments.Select(b => (FieldInfo)b.MemberInfo).ToArray(), + customAttribute.NamedArguments.Select(b => b.TypedValue.Value).ToArray()); + else + attributeBuilder = new CustomAttributeBuilder(customAttribute.Constructor, constructorArgs, + customAttribute.NamedArguments.Select(b => (PropertyInfo)b.MemberInfo).ToArray(), + customAttribute.NamedArguments.Select(b => b.TypedValue.Value).ToArray()); + + prop.SetCustomAttribute(attributeBuilder); + } + + var getMethod = type.DefineMethod($"get_{field.Name}", MethodAttributes.Public | MethodAttributes.HideBySig, field.FieldType, Type.EmptyTypes); + { + var il = getMethod.GetILGenerator(); + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, instanceField); + il.Emit(OpCodes.Ldfld, field); + il.Emit(OpCodes.Ret); + } + prop.SetGetMethod(getMethod); + + if (field.IsInitOnly) continue; + + var setMethod = type.DefineMethod($"set_{field.Name}", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(void), + [field.FieldType]); + { + var il = setMethod.GetILGenerator(); + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Dup); + il.Emit(OpCodes.Ldfld, instanceField); + il.Emit(OpCodes.Ldflda, field); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldstr, field.Name); + il.Emit(OpCodes.Callvirt, viewModelSetMethod.MakeGenericMethod(field.FieldType)); + il.Emit(OpCodes.Ret); + } + + prop.SetSetMethod(setMethod); + } + + proxy = type.CreateType(); + + DynamicViewModel.Proxies[typeof(T)] = proxy; + + return (ViewModel)Activator.CreateInstance(proxy, obj); + } +} + +file static class DynamicViewModel +{ + public static readonly Dictionary Proxies = []; + [CanBeNull] public static ModuleBuilder ModuleBuilder; +} \ No newline at end of file diff --git a/Torch.Server/ViewModels/SessionSettingsViewModel.cs b/Torch.Server/ViewModels/SessionSettingsViewModel.cs deleted file mode 100644 index 332a480..0000000 --- a/Torch.Server/ViewModels/SessionSettingsViewModel.cs +++ /dev/null @@ -1,321 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel; -using VRage.Game; -using VRage.Library.Utils; - -namespace Torch.Server.ViewModels -{ - public class SessionSettingsViewModel : ViewModel - { - private MyObjectBuilder_SessionSettings _settings; - - [Torch.Views.Display(Description = "The type of the game mode.", Name = "Game Mode", GroupName = "Others")] - public MyGameModeEnum GameMode { get => _settings.GameMode; set => SetValue(ref _settings.GameMode, value); } - [Torch.Views.Display(Description = "The type of the game online mode.", Name = "Online Mode", GroupName = "Others")] - public MyOnlineModeEnum OnlineMode { get => _settings.OnlineMode; set => SetValue(ref _settings.OnlineMode, value); } - - [Torch.Views.Display(Description = "The multiplier for character inventory size.", Name = "Character Inventory Size", GroupName = "Multipliers")] - public float CharacterInventorySizeMultiplier { get => _settings.InventorySizeMultiplier; set => SetValue(ref _settings.InventorySizeMultiplier, value); } - - [Torch.Views.Display(Description = "The multiplier for block inventory size.", Name = "Block Inventory Size", GroupName = "Multipliers")] - public float BlockInventorySizeMultiplier { get => _settings.BlocksInventorySizeMultiplier; set => SetValue(ref _settings.BlocksInventorySizeMultiplier, value); } - - [Torch.Views.Display(Description = "The multiplier for assembler speed.", Name = "Assembler Speed", GroupName = "Multipliers")] - public float AssemblerSpeedMultiplier { get => _settings.AssemblerSpeedMultiplier; set => SetValue(ref _settings.AssemblerSpeedMultiplier, value); } - - [Torch.Views.Display(Description = "The multiplier for assembler efficiency.", Name = "Assembler Efficiency", GroupName = "Multipliers")] - public float AssemblerEfficiencyMultiplier { get => _settings.AssemblerEfficiencyMultiplier; set => SetValue(ref _settings.AssemblerEfficiencyMultiplier, value); } - - [Torch.Views.Display(Description = "The multiplier for refinery speed.", Name = "Refinery Speed", GroupName = "Multipliers")] - public float RefinerySpeedMultiplier { get => _settings.RefinerySpeedMultiplier; set => SetValue(ref _settings.RefinerySpeedMultiplier, value); } - - [Torch.Views.Display(Description = "The maximum number of connected players.", Name = "Max Players", GroupName = "Players")] - public short MaxPlayers { get => _settings.MaxPlayers; set => SetValue(ref _settings.MaxPlayers, value); } - - [Torch.Views.Display(Description = "The maximum number of existing floating objects.", Name = "Max Floating Objects", GroupName = "Environment")] - public short MaxFloatingObjects { get => _settings.MaxFloatingObjects; set => SetValue(ref _settings.MaxFloatingObjects, value); } - - [Torch.Views.Display(Description = "The maximum number of backup saves.", Name = "Max Backup Saves", GroupName = "Others")] - public short MaxBackupSaves { get => _settings.MaxBackupSaves; set => SetValue(ref _settings.MaxBackupSaves, value); } - - [Torch.Views.Display(Description = "The maximum number of blocks in one grid.", Name = "Max Grid Blocks", GroupName = "Block Limits")] - public int MaxGridSize { get => _settings.MaxGridSize; set => SetValue(ref _settings.MaxGridSize, value); } - - [Torch.Views.Display(Description = "The maximum number of blocks per player.", Name = "Max Blocks per Player", GroupName = "Block Limits")] - public int MaxBlocksPerPlayer { get => _settings.MaxBlocksPerPlayer; set => SetValue(ref _settings.MaxBlocksPerPlayer, value); } - - [Torch.Views.Display(Description = "The total number of Performance Cost Units in the world.", Name = "World PCU", GroupName = "Block Limits")] - public int TotalPCU { get => _settings.TotalPCU; set => SetValue(ref _settings.TotalPCU, value); } - - [Torch.Views.Display(Description = "The maximum number of existing factions in the world.", Name = "Max Factions Count", GroupName = "Block Limits")] - public int MaxFactionsCount { get => _settings.MaxFactionsCount; set => SetValue(ref _settings.MaxFactionsCount, value); } - - [Torch.Views.Display(Description = "Defines block limits mode.", Name = "Block Limits Mode", GroupName = "Block Limits")] - public MyBlockLimitsEnabledEnum BlockLimitsEnabled { get => _settings.BlockLimitsEnabled; set => SetValue(ref _settings.BlockLimitsEnabled, value); } - - [Torch.Views.Display(Description = "Enables possibility to remove grid remotely from the world by an author.", Name = "Enable Remote Grid Removal", GroupName = "Others")] - public bool EnableRemoteBlockRemoval { get => _settings.EnableRemoteBlockRemoval; set => SetValue(ref _settings.EnableRemoteBlockRemoval, value); } - - [Torch.Views.Display(Description = "Defines hostility of the environment.", Name = "Environment Hostility", GroupName = "Environment")] - public MyEnvironmentHostilityEnum EnvironmentHostility { get => _settings.EnvironmentHostility; set => SetValue(ref _settings.EnvironmentHostility, value); } - - [Torch.Views.Display(Description = "Enables auto healing of the character.", Name = "Auto Healing", GroupName = "Players")] - public bool AutoHealing { get => _settings.AutoHealing; set => SetValue(ref _settings.AutoHealing, value); } - - [Torch.Views.Display(Description = "Enables copy and paste feature.", Name = "Enable Copy & Paste", GroupName = "Players")] - public bool EnableCopyPaste { get => _settings.EnableCopyPaste; set => SetValue(ref _settings.EnableCopyPaste, value); } - - [Torch.Views.Display(Description = "Enables weapons.", Name = "Enable Weapons", GroupName = "Others")] - public bool WeaponsEnabled { get => _settings.WeaponsEnabled; set => SetValue(ref _settings.WeaponsEnabled, value); } - - [Torch.Views.Display(Description = "", Name = "Show Player Names on HUD", GroupName = "Players")] - public bool ShowPlayerNamesOnHud { get => _settings.ShowPlayerNamesOnHud; set => SetValue(ref _settings.ShowPlayerNamesOnHud, value); } - - [Torch.Views.Display(Description = "Enables thruster damage.", Name = "Enable Thruster Damage", GroupName = "Others")] - public bool ThrusterDamage { get => _settings.ThrusterDamage; set => SetValue(ref _settings.ThrusterDamage, value); } - - [Torch.Views.Display(Description = "Enables spawning of cargo ships.", Name = "Enable Cargo Ships", GroupName = "NPCs")] - public bool CargoShipsEnabled { get => _settings.CargoShipsEnabled; set => SetValue(ref _settings.CargoShipsEnabled, value); } - - [Torch.Views.Display(Description = "Enables spectator camera.", Name = "Enable Spectator Camera", GroupName = "Others")] - public bool EnableSpectator { get => _settings.EnableSpectator; set => SetValue(ref _settings.EnableSpectator, value); } - - /// - /// Size of the edge of the world area cube. - /// Don't use directly, as it is error-prone (it's km instead of m and edge size instead of half-extent) - /// Rather use MyEntities.WorldHalfExtent() - /// - [Torch.Views.Display(Description = "Defines the size of the world.", Name = "World Size [km]", GroupName = "Environment")] - public int WorldSizeKm { get => _settings.WorldSizeKm; set => SetValue(ref _settings.WorldSizeKm, value); } - - [Torch.Views.Display(Description = "When enabled respawn ship is removed after player logout.", Name = "Remove Respawn Ships on Logoff", GroupName = "Others")] - public bool RespawnShipDelete { get => _settings.RespawnShipDelete; set => SetValue(ref _settings.RespawnShipDelete, value); } - - [Torch.Views.Display(Description = "", Name = "Reset Ownership", GroupName = "Players")] - public bool ResetOwnership { get => _settings.ResetOwnership; set => SetValue(ref _settings.ResetOwnership, value); } - - [Torch.Views.Display(Description = "The multiplier for welder speed.", Name = "Welder Speed", GroupName = "Multipliers")] - public float WelderSpeedMultiplier { get => _settings.WelderSpeedMultiplier; set => SetValue(ref _settings.WelderSpeedMultiplier, value); } - - [Torch.Views.Display(Description = "The multiplier for grinder speed.", Name = "Grinder Speed", GroupName = "Multipliers")] - public float GrinderSpeedMultiplier { get => _settings.GrinderSpeedMultiplier; set => SetValue(ref _settings.GrinderSpeedMultiplier, value); } - - [Torch.Views.Display(Description = "Enables realistic sounds.", Name = "Enable Realistic Sound", GroupName = "Environment")] - public bool RealisticSound { get => _settings.RealisticSound; set => SetValue(ref _settings.RealisticSound, value); } - - [Torch.Views.Display(Description = "The multiplier for hacking speed.", Name = "Hacking Speed", GroupName = "Multipliers")] - public float HackSpeedMultiplier { get => _settings.HackSpeedMultiplier; set => SetValue(ref _settings.HackSpeedMultiplier, value); } - - [Torch.Views.Display(Description = "Enables permanent death.", Name = "Permanent Death", GroupName = "Players")] - public bool? PermanentDeath { get => _settings.PermanentDeath; set => SetValue(ref _settings.PermanentDeath, value); } - - [Torch.Views.Display(Description = "Defines autosave interval.", Name = "Autosave Interval [mins]", GroupName = "Others")] - public uint AutoSaveInMinutes { get => _settings.AutoSaveInMinutes; set => SetValue(ref _settings.AutoSaveInMinutes, value); } - - [Torch.Views.Display(Description = "Enables saving from the menu.", Name = "Enable Saving from Menu", GroupName = "Others")] - public bool EnableSaving { get => _settings.EnableSaving; set => SetValue(ref _settings.EnableSaving, value); } - - [Torch.Views.Display(Description = "Enables respawn screen.", Name = "Enable Respawn Screen in the Game", GroupName = "Players")] - public bool StartInRespawnScreen { get => _settings.StartInRespawnScreen; set => SetValue(ref _settings.StartInRespawnScreen, value); } - - [Torch.Views.Display(Description = "Enables research.", Name = "Enable Research", GroupName = "Players")] - public bool EnableResearch { get => _settings.EnableResearch; set => SetValue(ref _settings.EnableResearch, value); } - - [Torch.Views.Display(Description = "Enables Good.bot hints.", Name = "Enable Good.bot hints", GroupName = "Players")] - public bool EnableGoodBotHints { get => _settings.EnableGoodBotHints; set => SetValue(ref _settings.EnableGoodBotHints, value); } - - [Torch.Views.Display(Description = "Enables infinite ammunition in survival game mode.", Name = "Enable Infinite Ammunition in Survival", GroupName = "Others")] - public bool InfiniteAmmo { get => _settings.InfiniteAmmo; set => SetValue(ref _settings.InfiniteAmmo, value); } - - [Torch.Views.Display(Description = "Enables drop containers (unknown signals).", Name = "Enable Drop Containers", GroupName = "Others")] - public bool EnableContainerDrops { get => _settings.EnableContainerDrops; set => SetValue(ref _settings.EnableContainerDrops, value); } - - [Torch.Views.Display(Description = "The multiplier for respawn ship timer.", Name = "Respawn Ship Time Multiplier", GroupName = "Players")] - public float SpawnShipTimeMultiplier { get => _settings.SpawnShipTimeMultiplier; set => SetValue(ref _settings.SpawnShipTimeMultiplier, value); } - - [Torch.Views.Display(Description = "Defines density of the procedurally generated content.", Name = "Procedural Density", GroupName = "Environment")] - public float ProceduralDensity { get => _settings.ProceduralDensity; set => SetValue(ref _settings.ProceduralDensity, value); } - - [Torch.Views.Display(Description = "Defines unique starting seed for the procedurally generated content.", Name = "Procedural Seed", GroupName = "Environment")] - public int ProceduralSeed { get => _settings.ProceduralSeed; set => SetValue(ref _settings.ProceduralSeed, value); } - - [Torch.Views.Display(Description = "Enables destruction feature for the blocks.", Name = "Enable Destructible Blocks", GroupName = "Environment")] - public bool DestructibleBlocks { get => _settings.DestructibleBlocks; set => SetValue(ref _settings.DestructibleBlocks, value); } - - [Torch.Views.Display(Description = "Enables in game scripts.", Name = "Enable Ingame Scripts", GroupName = "Others")] - public bool EnableIngameScripts { get => _settings.EnableIngameScripts; set => SetValue(ref _settings.EnableIngameScripts, value); } - - [Torch.Views.Display(Description = "", Name = "Flora Density Multiplier", GroupName = "Environment")] - public float FloraDensityMultiplier { get => _settings.FloraDensityMultiplier; set => SetValue(ref _settings.FloraDensityMultiplier, value); } - - [Torch.Views.Display(Description = "Enables tool shake feature.", Name = "Enable Tool Shake", GroupName = "Players")] - [DefaultValue(false)] - public bool EnableToolShake { get => _settings.EnableToolShake; set => SetValue(ref _settings.EnableToolShake, value); } - - [Torch.Views.Display(Description = "", Name = "Voxel Generator Version", GroupName = "Environment")] - public int VoxelGeneratorVersion { get => _settings.VoxelGeneratorVersion; set => SetValue(ref _settings.VoxelGeneratorVersion, value); } - - [Torch.Views.Display(Description = "Enables oxygen in the world.", Name = "Enable Oxygen", GroupName = "Environment")] - public bool EnableOxygen { get => _settings.EnableOxygen; set => SetValue(ref _settings.EnableOxygen, value); } - - [Torch.Views.Display(Description = "Enables airtightness in the world.", Name = "Enable Airtightness", GroupName = "Environment")] - public bool EnableOxygenPressurization { get => _settings.EnableOxygenPressurization; set => SetValue(ref _settings.EnableOxygenPressurization, value); } - - [Torch.Views.Display(Description = "Enables 3rd person camera.", Name = "Enable 3rd Person Camera", GroupName = "Players")] - public bool Enable3rdPersonView { get => _settings.Enable3rdPersonView; set => SetValue(ref _settings.Enable3rdPersonView, value); } - - [Torch.Views.Display(Description = "Enables random encounters in the world.", Name = "Enable Encounters", GroupName = "NPCs")] - public bool EnableEncounters { get => _settings.EnableEncounters; set => SetValue(ref _settings.EnableEncounters, value); } - - [Torch.Views.Display(Description = "Enables possibility of converting grid to station.", Name = "Enable Convert to Station", GroupName = "Others")] - public bool EnableConvertToStation { get => _settings.EnableConvertToStation; set => SetValue(ref _settings.EnableConvertToStation, value); } - - [Torch.Views.Display(Description = "Enables possibility of station grid inside voxel.", Name = "Enable Station Grid with Voxel", GroupName = "Environment")] - public bool StationVoxelSupport { get => _settings.StationVoxelSupport; set => SetValue(ref _settings.StationVoxelSupport, value); } - - [Torch.Views.Display(Description = "Enables sun rotation.", Name = "Enable Sun Rotation", GroupName = "Environment")] - public bool EnableSunRotation { get => _settings.EnableSunRotation; set => SetValue(ref _settings.EnableSunRotation, value); } - - [Torch.Views.Display(Description = "Enables respawn ships.", Name = "Enable Respawn Ships", GroupName = "Others")] - public bool EnableRespawnShips { get => _settings.EnableRespawnShips; set => SetValue(ref _settings.EnableRespawnShips, value); } - - [Torch.Views.Display(Description = "", Name = "Physics Iterations", GroupName = "Environment")] - public int PhysicsIterations { get => _settings.PhysicsIterations; set => SetValue(ref _settings.PhysicsIterations, value); } - - [Torch.Views.Display(Description = "Defines interval of one rotation of the sun.", Name = "Sun Rotation Interval", GroupName = "Environment")] - public float SunRotationIntervalMinutes { get => _settings.SunRotationIntervalMinutes; set => SetValue(ref _settings.SunRotationIntervalMinutes, value); } - - [Torch.Views.Display(Description = "Enables jetpack.", Name = "Enable Jetpack", GroupName = "Players")] - public bool EnableJetpack { get => _settings.EnableJetpack; set => SetValue(ref _settings.EnableJetpack, value); } - - [Torch.Views.Display(Description = "Enables spawning with tools in the inventory.", Name = "Spawn with Tools", GroupName = "Players")] - public bool SpawnWithTools { get => _settings.SpawnWithTools; set => SetValue(ref _settings.SpawnWithTools, value); } - - [Torch.Views.Display(Description = "Enables voxel destructions.", Name = "Enable Voxel Destruction", GroupName = "Environment")] - public bool EnableVoxelDestruction { get => _settings.EnableVoxelDestruction; set => SetValue(ref _settings.EnableVoxelDestruction, value); } - - [Torch.Views.Display(Description = "Enables spawning of drones in the world.", Name = "Enable Drones", GroupName = "NPCs")] - public bool EnableDrones { get => _settings.EnableDrones; set => SetValue(ref _settings.EnableDrones, value); } - - [Torch.Views.Display(Description = "Enables spawning of wolves in the world.", Name = "Enable Wolves", GroupName = "NPCs")] - public bool EnableWolfs { get => _settings.EnableWolfs; set => SetValue(ref _settings.EnableWolfs, value); } - - [Torch.Views.Display(Description = "Enables spawning of spiders in the world.", Name = "Enable Spiders", GroupName = "NPCs")] - public bool EnableSpiders { get => _settings.EnableSpiders; set => SetValue(ref _settings.EnableSpiders, value); } - - [Torch.Views.Display(Name = "Block Type World Limits", GroupName = "Block Limits")] - public Dictionary BlockTypeLimits { get => _settings.BlockTypeLimits.Dictionary; set => SetValue(x => _settings.BlockTypeLimits.Dictionary = x, value); } - - [Torch.Views.Display(Description = "Enables scripter role for administration.", Name = "Enable Scripter Role", GroupName = "Others")] - public bool EnableScripterRole { get => _settings.EnableScripterRole; set => SetValue(ref _settings.EnableScripterRole, value); } - - [Torch.Views.Display(Description = "Defines minimum respawn time for drop containers.", Name = "Min Drop Container Respawn Time", GroupName = "Others")] - public int MinDropContainerRespawnTime { get => _settings.MinDropContainerRespawnTime; set => SetValue(ref _settings.MinDropContainerRespawnTime, value); } - - [Torch.Views.Display(Description = "Defines maximum respawn time for drop containers.", Name = "Max Drop Container Respawn Time", GroupName = "Others")] - public int MaxDropContainerRespawnTime { get => _settings.MaxDropContainerRespawnTime; set => SetValue(ref _settings.MaxDropContainerRespawnTime, value); } - - [Torch.Views.Display(Description = "Enables friendly fire for turrets.", Name = "Enable Turrets Friendly Fire", GroupName = "Environment")] - public bool EnableTurretsFriendlyFire { get => _settings.EnableTurretsFriendlyFire; set => SetValue(ref _settings.EnableTurretsFriendlyFire, value); } - - [Torch.Views.Display(Description = "Enables sub-grid damage.", Name = "Enable Sub-Grid Damage", GroupName = "Environment")] - public bool EnableSubgridDamage { get => _settings.EnableSubgridDamage; set => SetValue(ref _settings.EnableSubgridDamage, value); } - - [Torch.Views.Display(Description = "Defines synchronization distance in multiplayer. High distance can slow down server drastically. Use with caution.", Name = "Sync Distance", GroupName = "Environment")] - public int SyncDistance { get => _settings.SyncDistance; set => SetValue(ref _settings.SyncDistance, value); } - - [Torch.Views.Display(Description = "Defines render distance for clients in multiplayer. High distance can slow down client FPS. Values larger than SyncDistance may not work as expected.", Name = "View Distance", GroupName = "Environment")] - public int ViewDistance { get => _settings.ViewDistance; set => SetValue(ref _settings.ViewDistance, value);} - - [Torch.Views.Display(Description = "Enables experimental mode.", Name = "Experimental Mode", GroupName = "Others")] - public bool ExperimentalMode { get => _settings.ExperimentalMode; set => SetValue(ref _settings.ExperimentalMode, value); } - - [Torch.Views.Display(Description = "Enables adaptive simulation quality system. This system is useful if you have a lot of voxel deformations in the world and low simulation speed.", Name = "Adaptive Simulation Quality", GroupName = "Others")] - public bool AdaptiveSimulationQuality { get => _settings.AdaptiveSimulationQuality; set => SetValue(ref _settings.AdaptiveSimulationQuality, value); } - - [Torch.Views.Display(Description = "Enables voxel hand.", Name = "Enable voxel hand", GroupName = "Others")] - public bool EnableVoxelHand { get => _settings.EnableVoxelHand; set => SetValue(ref _settings.EnableVoxelHand, value); } - - [Torch.Views.Display(Description = "Enables trash removal system.", Name = "Trash Removal Enabled", GroupName = "Trash Removal")] - public bool TrashRemovalEnabled { get => _settings.TrashRemovalEnabled; set => SetValue(ref _settings.TrashRemovalEnabled, value); } - - [Torch.Views.Display(Description = "Defines flags for trash removal system.", Name = "Trash Removal Flags", GroupName = "Trash Removal")] - public MyTrashRemovalFlags TrashFlagsValue { get => (MyTrashRemovalFlags)_settings.TrashFlagsValue; set => SetValue(ref _settings.TrashFlagsValue, (int)value); } - - [Torch.Views.Display(Description = "Defines block count threshold for trash removal system.", Name = "Block Count Threshold", GroupName = "Trash Removal")] - public int BlockCountThreshold { get => _settings.BlockCountThreshold; set => SetValue(ref _settings.BlockCountThreshold, value); } - - [Torch.Views.Display(Description = "Defines player distance threshold for trash removal system.", Name = "Player Distance Threshold [m]", GroupName = "Trash Removal")] - public float PlayerDistanceThreshold { get => _settings.PlayerDistanceThreshold; set => SetValue(ref _settings.PlayerDistanceThreshold, value); } - - [Torch.Views.Display(Description = "By setting this, server will keep number of grids around this value. \n !WARNING! It ignores Powered and Fixed flags, Block Count and lowers Distance from player.\n Set to 0 to disable.", Name = "Optimal Grid Count", GroupName = "Trash Removal")] - public int OptimalGridCount { get => _settings.OptimalGridCount; set => SetValue(ref _settings.OptimalGridCount, value); } - - [Torch.Views.Display(Description = "Defines player inactivity threshold for trash removal system. \n !WARNING! This will remove all grids of the player.\n Set to 0 to disable.", Name = "Player Inactivity Threshold [hours]", GroupName = "Trash Removal")] - public float PlayerInactivityThreshold { get => _settings.PlayerInactivityThreshold; set => SetValue(ref _settings.PlayerInactivityThreshold, value); } - - [Torch.Views.Display(Description = "Defines character removal threshold for trash removal system. If player disconnects it will remove his character after this time.\n Set to 0 to disable.", Name = "Character Removal Threshold [mins]", GroupName = "Trash Removal")] - public int PlayerCharacterRemovalThreshold { get => _settings.PlayerCharacterRemovalThreshold; set => SetValue(ref _settings.PlayerCharacterRemovalThreshold, value); } - - [Torch.Views.Display(Description = "Sets optimal distance in meters when spawning new players near others.", Name = "Optimal Spawn Distance", GroupName = "Players")] - public float OptimalSpawnDistance { get => _settings.OptimalSpawnDistance; set => SetValue(ref _settings.OptimalSpawnDistance, value); } - - [Torch.Views.Display(Description = "Enables automatic respawn at nearest available respawn point.", Name = "Enable Auto Respawn", GroupName = "Players")] - public bool EnableAutoRespawn { get => _settings.EnableAutorespawn; set => SetValue(ref _settings.EnableAutorespawn, value); } - - [Torch.Views.Display(Description = "The number of NPC factions generated on the start of the world.", Name = "NPC Factions Count", GroupName = "NPCs")] - public int TradeFactionsCount { get => _settings.TradeFactionsCount; set => SetValue(ref _settings.TradeFactionsCount, value); } - - [Torch.Views.Display(Description = "The inner radius [m] (center is in 0,0,0), where stations can spawn. Does not affect planet-bound stations (surface Outposts and Orbital stations).", Name = "Stations Inner Radius", GroupName = "NPCs")] - public double StationsDistanceInnerRadius { get => _settings.StationsDistanceInnerRadius; set => SetValue(ref _settings.StationsDistanceInnerRadius, value); } - - [Torch.Views.Display(Description = "The outer radius [m] (center is in 0,0,0), where stations can spawn. Does not affect planet-bound stations (surface Outposts and Orbital stations).", Name = "Stations Outer Radius Start", GroupName = "NPCs")] - public double StationsDistanceOuterRadiusStart { get => _settings.StationsDistanceOuterRadiusStart; set => SetValue(ref _settings.StationsDistanceOuterRadiusStart, value); } - - [Torch.Views.Display(Description = "The outer radius [m] (center is in 0,0,0), where stations can spawn. Does not affect planet-bound stations (surface Outposts and Orbital stations).", Name = "Stations Outer Radius End", GroupName = "NPCs")] - public double StationsDistanceOuterRadiusEnd { get => _settings.StationsDistanceOuterRadiusEnd; set => SetValue(ref _settings.StationsDistanceOuterRadiusEnd, value); } - - [Torch.Views.Display(Description = "Time period between two economy updates in seconds.", Name = "Economy tick time", GroupName = "NPCs")] - public int EconomyTickInSeconds { get => _settings.EconomyTickInSeconds; set => SetValue(ref _settings.EconomyTickInSeconds, value); } - - [Torch.Views.Display(Description = "If enabled bounty contracts will be available on stations.", Name = "Enable Bounty Contracts", GroupName = "Players")] - public bool EnableBountyContracts { get => _settings.EnableBountyContracts; set => SetValue(ref _settings.EnableBountyContracts, value); } - - [Torch.Views.Display(Description = "Resource deposits count coefficient for generated world content (voxel generator version > 2).", Name = "Deposits Count Coefficient", GroupName = "Environment")] - public float DepositsCountCoefficient { get => _settings.DepositsCountCoefficient; set => SetValue(ref _settings.DepositsCountCoefficient, value); } - - [Torch.Views.Display(Description = "Resource deposit size denominator for generated world content (voxel generator version > 2).", Name = "Deposit Size Denominator", GroupName = "Environment")] - public float DepositSideDenominator { get => _settings.DepositSizeDenominator; set => SetValue(ref _settings.DepositSizeDenominator, value); } - - [Torch.Views.Display(Description = "Enables economy features.", Name = "Enable Economy", GroupName = "NPCs")] - public bool EnableEconomy { get => _settings.EnableEconomy; set => SetValue(ref _settings.EnableEconomy, value); } - - [Torch.Views.Display(Description = "Enables system for voxel reverting.", Name = "Enable Voxel Reverting", GroupName = "Trash Removal")] - public bool VoxelTrashRemovalEnabled { get => _settings.VoxelTrashRemovalEnabled; set => SetValue(ref _settings.VoxelTrashRemovalEnabled, value); } - - [Torch.Views.Display(Description = "Allows super gridding exploit to be used.", Name = "Enable Supergridding", GroupName = "Others")] - public bool EnableSupergridding { get => _settings.EnableSupergridding; set => SetValue(ref _settings.EnableSupergridding, value); } - - [Torch.Views.Display(Description = "Enables Selective Physics", Name = "Enable Selective Physics", GroupName = "Others")] - public bool EnableSelectivePhysics { get => _settings.EnableSelectivePhysicsUpdates; set => SetValue(ref _settings.EnableSelectivePhysicsUpdates, value); } - - [Torch.Views.Display(Description = "Allows steam's family sharing", Name = "Enable Family Sharing", GroupName = "Players")] - public bool EnableFamilySharing { get => _settings.FamilySharing; set => SetValue(ref _settings.FamilySharing, value); } - - [Torch.Views.Display(Description = "Enables PCU trading", Name = "Enable PCU Trading", GroupName = "Block Limits")] - public bool EnablePCUTrading { get => _settings.EnablePcuTrading; set => SetValue(ref _settings.EnablePcuTrading, value); } - - [Torch.Views.Display(Description = "Enables system for weather", Name = "Enable Weather System", GroupName = "Others")] - public bool EnableWeatherSystem { get => _settings.WeatherSystem; set => SetValue(ref _settings.WeatherSystem, value); } - - public SessionSettingsViewModel(MyObjectBuilder_SessionSettings settings) - { - _settings = settings; - } - - public static implicit operator MyObjectBuilder_SessionSettings(SessionSettingsViewModel viewModel) - { - return viewModel._settings; - } - } -} diff --git a/Torch.Server/ViewModels/WorldConfigurationViewModel.cs b/Torch.Server/ViewModels/WorldConfigurationViewModel.cs index 40f704e..b42e15b 100644 --- a/Torch.Server/ViewModels/WorldConfigurationViewModel.cs +++ b/Torch.Server/ViewModels/WorldConfigurationViewModel.cs @@ -6,12 +6,12 @@ namespace Torch.Server.ViewModels public class WorldConfigurationViewModel : ViewModel { private readonly MyObjectBuilder_WorldConfiguration _worldConfiguration; - private SessionSettingsViewModel _sessionSettings; + private DynamicViewModel _sessionSettings; public WorldConfigurationViewModel(MyObjectBuilder_WorldConfiguration worldConfiguration) { _worldConfiguration = worldConfiguration; - _sessionSettings = new SessionSettingsViewModel(worldConfiguration.Settings); + _sessionSettings = new DynamicViewModel(worldConfiguration.Settings); } public static implicit operator MyObjectBuilder_WorldConfiguration(WorldConfigurationViewModel model) @@ -21,7 +21,7 @@ namespace Torch.Server.ViewModels public List Mods { get => _worldConfiguration.Mods; set => SetValue(ref _worldConfiguration.Mods, value); } - public SessionSettingsViewModel Settings + public DynamicViewModel Settings { get => _sessionSettings; set diff --git a/Torch.Server/Views/ConfigControl.xaml b/Torch.Server/Views/ConfigControl.xaml index 0cb4f02..3e3c182 100644 --- a/Torch.Server/Views/ConfigControl.xaml +++ b/Torch.Server/Views/ConfigControl.xaml @@ -130,7 +130,7 @@ - + diff --git a/Torch.Server/Views/WorldGeneratorDialog.xaml.cs b/Torch.Server/Views/WorldGeneratorDialog.xaml.cs index d6f00bb..033925f 100644 --- a/Torch.Server/Views/WorldGeneratorDialog.xaml.cs +++ b/Torch.Server/Views/WorldGeneratorDialog.xaml.cs @@ -101,7 +101,7 @@ namespace Torch.Server { var selected = (PremadeCheckpointItem)PremadeCheckpoints.SelectedItem; _currentItem = selected; - SettingsView.DataContext = new SessionSettingsViewModel(_currentItem.Checkpoint.Settings); + SettingsView.DataContext = new DynamicViewModel(_currentItem.Checkpoint.Settings).Wrapper; } } diff --git a/Torch/Views/FlagsEditor.xaml.cs b/Torch/Views/FlagsEditor.xaml.cs index d605ab1..1619758 100644 --- a/Torch/Views/FlagsEditor.xaml.cs +++ b/Torch/Views/FlagsEditor.xaml.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Reflection; using System.Windows; +using JetBrains.Annotations; namespace Torch.Views { @@ -19,21 +20,18 @@ namespace Torch.Views private PropertyInfo _property; private object _obj; - public void EditEnum(PropertyInfo prop, object obj) + public void EditEnum(PropertyInfo prop, object obj, [CanBeNull] Type propertyType = null) { - if (!prop.PropertyType.IsEnum || prop.PropertyType.GetCustomAttribute() == null) - throw new ArgumentException("Type is not a flags enum"); - _property = prop; _obj = obj; _flags = new List(); var initial = (int)Convert.ChangeType(prop.GetValue(obj), typeof(int)); - foreach (var value in Enum.GetValues(prop.PropertyType)) + foreach (var value in Enum.GetValues(propertyType ??= prop.PropertyType)) { var val = (int)Convert.ChangeType(value, typeof(int)); _flags.Add(new Flag { - Name = Enum.GetName(prop.PropertyType, value), + Name = Enum.GetName(propertyType, value), Value = val, IsChecked = (initial & val) > 0 }); diff --git a/Torch/Views/PropertyGrid.xaml.cs b/Torch/Views/PropertyGrid.xaml.cs index 7ef6fda..fed9b3b 100644 --- a/Torch/Views/PropertyGrid.xaml.cs +++ b/Torch/Views/PropertyGrid.xaml.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Reflection; using System.Windows; @@ -9,6 +10,7 @@ using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using VRage.Serialization; +using VRage.Utils; namespace Torch.Views { @@ -65,6 +67,11 @@ namespace Torch.Views foreach (var property in properties) { + var browsableAttribute = property.GetCustomAttribute(); + + if (browsableAttribute is { Browsable: false }) + continue; + //Attempt to load our custom DisplayAttribute var a = property.GetCustomAttribute(); //If not found and IgnoreDisplay is not set, fall back to system DisplayAttribute @@ -73,7 +80,7 @@ namespace Torch.Views if (!IgnoreDisplay && a == null || a?.Visible == false) continue; descriptors[property] = a; - string category = a?.GroupName ?? "Misc"; + string category = a?.GroupName ?? property.GetCustomAttribute()?.Category ?? "Misc"; if (!categories.TryGetValue(category, out List l)) { @@ -154,9 +161,9 @@ namespace Torch.Views valueControl = new CheckBox(); valueControl.SetBinding(CheckBox.IsCheckedProperty, property.Name); } - else if (propertyType.IsEnum) + else if (propertyType.IsEnum || property.HasAttribute()) { - var isFlags = propertyType.GetCustomAttribute() != null; + var isFlags = property.HasAttribute() || propertyType.HasAttribute(); if (isFlags) { @@ -298,12 +305,14 @@ namespace Torch.Views var obj = DataContext; var propName = btn.GetBindingExpression(DataContextProperty).ParentBinding.Path.Path; var propInfo = DataContext.GetType().GetProperty(propName); + + var propTypeOverride = propInfo?.GetCustomAttribute()?.EnumType; new FlagsEditorDialog { WindowStartupLocation = WindowStartupLocation.CenterOwner, Owner = Window.GetWindow(this) - }.EditEnum(propInfo, obj); + }.EditEnum(propInfo, obj, propTypeOverride); } private void EditDictionary(object dict)