diff --git a/NLog.config b/NLog.config index fb8235a..d1c94c7 100644 --- a/NLog.config +++ b/NLog.config @@ -9,7 +9,7 @@ - + diff --git a/Torch.Server/Initializer.cs b/Torch.Server/Initializer.cs index d8a07a1..a1066ff 100644 --- a/Torch.Server/Initializer.cs +++ b/Torch.Server/Initializer.cs @@ -23,6 +23,7 @@ namespace Torch.Server private const string STEAMCMD_ZIP = "temp.zip"; private static readonly string STEAMCMD_PATH = $"{STEAMCMD_DIR}\\steamcmd.exe"; private static readonly string RUNSCRIPT_PATH = $"{STEAMCMD_DIR}\\runscript.txt"; + private const string RUNSCRIPT = @"force_install_dir ../ login anonymous app_update 298740 @@ -69,7 +70,6 @@ quit"; Console.Write("."); Thread.Sleep(1000); } - } catch { @@ -86,17 +86,27 @@ quit"; _server = new TorchServer(_config); try { - _server.Init(); + var initTask = Task.Run(() => { _server.Init(); }); if (!_config.NoGui) { - var ui = new TorchUI(_server); - if (_config.Autostart) + _server.Init(); + + if (!_config.NoGui) + { + var ui = new TorchUI(_server); + if (_config.Autostart) + _server.Start(); + ui.ShowDialog(); + } + else _server.Start(); - ui.ShowDialog(); } else + { + initTask.Wait(); _server.Start(); + } } finally { @@ -106,100 +116,101 @@ quit"; } } - private TorchConfig InitConfig() - { - var configName = "Torch.cfg"; - var configPath = Path.Combine(Directory.GetCurrentDirectory(), configName); - if (File.Exists(configName)) + private TorchConfig InitConfig() { - Log.Info($"Loading config {configPath}"); - return TorchConfig.LoadFrom(configPath); - } - else - { - Log.Info($"Generating default config at {configPath}"); - var config = new TorchConfig { InstancePath = Path.GetFullPath("Instance") }; - config.Save(configPath); - return config; - } - } - - private static void RunSteamCmd() - { - var log = LogManager.GetLogger("SteamCMD"); - - if (!Directory.Exists(STEAMCMD_DIR)) - { - Directory.CreateDirectory(STEAMCMD_DIR); - } - - if (!File.Exists(RUNSCRIPT_PATH)) - File.WriteAllText(RUNSCRIPT_PATH, RUNSCRIPT); - - if (!File.Exists(STEAMCMD_PATH)) - { - try + var configName = "Torch.cfg"; + var configPath = Path.Combine(Directory.GetCurrentDirectory(), configName); + if (File.Exists(configName)) { - log.Info("Downloading SteamCMD."); - using (var client = new WebClient()) - client.DownloadFile("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip", STEAMCMD_ZIP); - - ZipFile.ExtractToDirectory(STEAMCMD_ZIP, STEAMCMD_DIR); - File.Delete(STEAMCMD_ZIP); - log.Info("SteamCMD downloaded successfully!"); + Log.Info($"Loading config {configPath}"); + return TorchConfig.LoadFrom(configPath); } - catch + else { - log.Error("Failed to download SteamCMD, unable to update the DS."); - return; + Log.Info($"Generating default config at {configPath}"); + var config = new TorchConfig {InstancePath = Path.GetFullPath("Instance")}; + config.Save(configPath); + return config; } } - log.Info("Checking for DS updates."); - var steamCmdProc = new ProcessStartInfo(STEAMCMD_PATH, "+runscript runscript.txt") + private static void RunSteamCmd() { - WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), STEAMCMD_DIR), - UseShellExecute = false, - RedirectStandardOutput = true, - StandardOutputEncoding = Encoding.ASCII - }; - var cmd = Process.Start(steamCmdProc); + var log = LogManager.GetLogger("SteamCMD"); - // ReSharper disable once PossibleNullReferenceException - while (!cmd.HasExited) + if (!Directory.Exists(STEAMCMD_DIR)) + { + Directory.CreateDirectory(STEAMCMD_DIR); + } + + if (!File.Exists(RUNSCRIPT_PATH)) + File.WriteAllText(RUNSCRIPT_PATH, RUNSCRIPT); + + if (!File.Exists(STEAMCMD_PATH)) + { + try + { + log.Info("Downloading SteamCMD."); + using (var client = new WebClient()) + client.DownloadFile("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip", STEAMCMD_ZIP); + + ZipFile.ExtractToDirectory(STEAMCMD_ZIP, STEAMCMD_DIR); + File.Delete(STEAMCMD_ZIP); + log.Info("SteamCMD downloaded successfully!"); + } + catch + { + log.Error("Failed to download SteamCMD, unable to update the DS."); + return; + } + } + + log.Info("Checking for DS updates."); + var steamCmdProc = new ProcessStartInfo(STEAMCMD_PATH, "+runscript runscript.txt") + { + WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), STEAMCMD_DIR), + UseShellExecute = false, + RedirectStandardOutput = true, + StandardOutputEncoding = Encoding.ASCII + }; + var cmd = Process.Start(steamCmdProc); + + // ReSharper disable once PossibleNullReferenceException + while (!cmd.HasExited) + { + log.Info(cmd.StandardOutput.ReadLine()); + Thread.Sleep(100); + } + } + + private void LogException(Exception ex) { - log.Info(cmd.StandardOutput.ReadLine()); - Thread.Sleep(100); + if (ex.InnerException != null) + { + LogException(ex.InnerException); + } + + Log.Fatal(ex); + if (ex is ReflectionTypeLoadException exti) + foreach (Exception exl in exti.LoaderExceptions) + LogException(exl); + } + + private void HandleException(object sender, UnhandledExceptionEventArgs e) + { + var ex = (Exception)e.ExceptionObject; + LogException(ex); + Console.WriteLine("Exiting in 5 seconds."); + Thread.Sleep(5000); + LogManager.Flush(); + if (_config.RestartOnCrash) + { + var exe = typeof(Program).Assembly.Location; + _config.WaitForPID = Process.GetCurrentProcess().Id.ToString(); + Process.Start(exe, _config.ToString()); + } + + Process.GetCurrentProcess().Kill(); } } - - private void LogException(Exception ex) - { - if (ex.InnerException != null) - { - LogException(ex.InnerException); - } - Log.Fatal(ex); - if (ex is ReflectionTypeLoadException exti) - foreach (Exception exl in exti.LoaderExceptions) - LogException(exl); - - } - - private void HandleException(object sender, UnhandledExceptionEventArgs e) - { - var ex = (Exception)e.ExceptionObject; - LogException(ex); - Console.WriteLine("Exiting in 5 seconds."); - Thread.Sleep(5000); - LogManager.Flush(); - if (_config.RestartOnCrash) - { - var exe = typeof(Program).Assembly.Location; - _config.WaitForPID = Process.GetCurrentProcess().Id.ToString(); - Process.Start(exe, _config.ToString()); - } - Process.GetCurrentProcess().Kill(); - } - } -} + } \ No newline at end of file diff --git a/Torch.Server/Managers/InstanceManager.cs b/Torch.Server/Managers/InstanceManager.cs index dfe9668..ea343a0 100644 --- a/Torch.Server/Managers/InstanceManager.cs +++ b/Torch.Server/Managers/InstanceManager.cs @@ -59,7 +59,7 @@ namespace Torch.Server.Managers var worldFolders = Directory.EnumerateDirectories(Path.Combine(Torch.Config.InstancePath, "Saves")); foreach (var f in worldFolders) - DedicatedConfig.Worlds.Add(new WorldViewModel(f, true)); + DedicatedConfig.Worlds.Add(new WorldViewModel(f)); if (DedicatedConfig.Worlds.Count == 0) { @@ -92,7 +92,7 @@ namespace Torch.Server.Managers foreach (var mod in world.Checkpoint.Mods) sb.AppendLine(mod.PublishedFileId.ToString()); - DedicatedConfig.Mods = sb.ToString(); + DedicatedConfig.Mods = world.Checkpoint.Mods.Select(x => x.PublishedFileId).ToList(); //sb.ToString(); Log.Debug("Loaded mod list from world"); @@ -115,7 +115,7 @@ namespace Torch.Server.Managers MyObjectBuilderSerializer.DeserializeXML(sandboxPath, out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes); if (checkpoint == null) { - Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {TorchBase.Instance.Config.InstancePath})"); + Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {Torch.Config.InstancePath})"); return; } @@ -123,7 +123,7 @@ namespace Torch.Server.Managers foreach (var mod in checkpoint.Mods) sb.AppendLine(mod.PublishedFileId.ToString()); - DedicatedConfig.Mods = sb.ToString(); + DedicatedConfig.Mods = checkpoint.Mods.Select(x => x.PublishedFileId).ToList(); //sb.ToString(); Log.Debug("Loaded mod list from world"); @@ -144,18 +144,23 @@ namespace Torch.Server.Managers try { - MyObjectBuilderSerializer.DeserializeXML(Path.Combine(DedicatedConfig.LoadWorld, "Sandbox.sbc"), out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes); + var sandboxPath = Path.Combine(DedicatedConfig.LoadWorld, "Sandbox.sbc"); + MyObjectBuilderSerializer.DeserializeXML(sandboxPath, out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes); if (checkpoint == null) { - Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {TorchBase.Instance.Config.InstancePath})"); + Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {Torch.Config.InstancePath})"); return; } + + checkpoint.SessionName = DedicatedConfig.WorldName; checkpoint.Settings = DedicatedConfig.SessionSettings; checkpoint.Mods.Clear(); foreach (var modId in DedicatedConfig.Model.Mods) checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(modId)); - MyLocalCache.SaveCheckpoint(checkpoint, DedicatedConfig.LoadWorld); + MyObjectBuilderSerializer.SerializeXML(sandboxPath, false, checkpoint); + + //MyLocalCache.SaveCheckpoint(checkpoint, DedicatedConfig.LoadWorld); Log.Info("Saved world config."); } catch (Exception e) @@ -185,15 +190,17 @@ namespace Torch.Server.Managers { public string FolderName { get; set; } public string WorldPath { get; } + public long WorldSizeKB { get; } private string _checkpointPath; public CheckpointViewModel Checkpoint { get; private set; } - public WorldViewModel(string worldPath, bool loadCheckpointAsync = false) + public WorldViewModel(string worldPath) { WorldPath = worldPath; + WorldSizeKB = new DirectoryInfo(worldPath).GetFiles().Sum(x => x.Length) / 1024; _checkpointPath = Path.Combine(WorldPath, "Sandbox.sbc"); FolderName = Path.GetFileName(worldPath); - LoadCheckpointAsync(); + BeginLoadCheckpoint(); } public async Task SaveCheckpointAsync() @@ -201,19 +208,18 @@ namespace Torch.Server.Managers await Task.Run(() => { using (var f = File.Open(_checkpointPath, FileMode.Create)) - MyObjectBuilderSerializer.SerializeXML(f, (MyObjectBuilder_Checkpoint)Checkpoint); + MyObjectBuilderSerializer.SerializeXML(f, Checkpoint); }); } - public async Task LoadCheckpointAsync() + private void BeginLoadCheckpoint() { - Checkpoint = await Task.Run(() => + Task.Run(() => { MyObjectBuilderSerializer.DeserializeXML(_checkpointPath, out MyObjectBuilder_Checkpoint checkpoint); - return new CheckpointViewModel(checkpoint); + Checkpoint = new CheckpointViewModel(checkpoint); + OnPropertyChanged(nameof(Checkpoint)); }); - OnPropertyChanged("Checkpoint"); - return Checkpoint; } } } diff --git a/Torch.Server/Managers/MultiplayerManagerDedicated.cs b/Torch.Server/Managers/MultiplayerManagerDedicated.cs index ec660a6..80d9592 100644 --- a/Torch.Server/Managers/MultiplayerManagerDedicated.cs +++ b/Torch.Server/Managers/MultiplayerManagerDedicated.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -9,11 +10,14 @@ using NLog.Fluent; using Sandbox; using Sandbox.Engine.Multiplayer; using Sandbox.Engine.Networking; +using Sandbox.Game.World; +using SteamSDK; using Torch.API; using Torch.API.Managers; using Torch.Managers; using Torch.Utils; using Torch.ViewModels; +using VRage.Game; using VRage.GameServices; using VRage.Network; using VRage.Steam; @@ -35,6 +39,9 @@ namespace Torch.Server.Managers private Dictionary _gameOwnerIds = new Dictionary(); + [Dependency] + private InstanceManager _instanceManager; + /// public MultiplayerManagerDedicated(ITorchBase torch) : base(torch) { @@ -135,22 +142,34 @@ namespace Torch.Server.Managers } //Largely copied from SE - private void ValidateAuthTicketResponse(ulong steamID, JoinResult response, ulong steamOwner) + private void ValidateAuthTicketResponse(ulong steamId, JoinResult response, ulong steamOwner) { - _log.Debug($"ValidateAuthTicketResponse(user={steamID}, response={response}, owner={steamOwner}"); - if (MySandboxGame.ConfigDedicated.GroupID == 0uL) - RunEvent(new ValidateAuthTicketEvent(steamID, steamOwner, response, 0, true, false)); + var state = new P2PSessionState(); + Peer2Peer.GetSessionState(steamId, ref state); + var ip = state.GetRemoteIP(); + + _log.Debug($"ValidateAuthTicketResponse(user={steamId}, response={response}, owner={steamOwner})"); + + _log.Info($"Connection attempt by {steamId} from {ip}"); + // TODO implement IP bans + if (Torch.CurrentSession.KeenSession.OnlineMode == MyOnlineModeEnum.OFFLINE && !Torch.CurrentSession.KeenSession.IsUserAdmin(steamId)) + { + _log.Warn($"Rejecting user {steamId}, world is set to offline and user is not admin."); + UserRejected(steamId, JoinResult.TicketCanceled); + } + else if (MySandboxGame.ConfigDedicated.GroupID == 0uL) + RunEvent(new ValidateAuthTicketEvent(steamId, steamOwner, response, 0, true, false)); else if (_getServerAccountType(MySandboxGame.ConfigDedicated.GroupID) != MyGameServiceAccountType.Clan) - UserRejected(steamID, JoinResult.GroupIdInvalid); - else if (MyGameService.GameServer.RequestGroupStatus(steamID, MySandboxGame.ConfigDedicated.GroupID)) + UserRejected(steamId, JoinResult.GroupIdInvalid); + else if (MyGameService.GameServer.RequestGroupStatus(steamId, MySandboxGame.ConfigDedicated.GroupID)) lock (_waitingForGroupLocal) { if (_waitingForGroupLocal.Count >= _waitListSize) _waitingForGroupLocal.RemoveAt(0); - _waitingForGroupLocal.Add(new WaitingForGroup(steamID, response, steamOwner)); + _waitingForGroupLocal.Add(new WaitingForGroup(steamId, response, steamOwner)); } else - UserRejected(steamID, JoinResult.SteamServersOffline); + UserRejected(steamId, JoinResult.SteamServersOffline); } private void RunEvent(ValidateAuthTicketEvent info) diff --git a/Torch.Server/Program.cs b/Torch.Server/Program.cs index ec745b2..401696a 100644 --- a/Torch.Server/Program.cs +++ b/Torch.Server/Program.cs @@ -1,33 +1,8 @@ using System; -using System.Collections; -using System.Collections.Generic; -using System.Configuration.Install; -using System.Diagnostics; using System.IO; -using System.Linq; using System.Reflection; using System.ServiceProcess; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Threading; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using NLog; -using Sandbox.Game.World; -using Sandbox.ModAPI; -using Torch; -using Torch.API; -using Torch.Server.Views; -using VRage.Game.ModAPI; -using System.IO.Compression; -using System.Net; -using System.Security.Policy; -using Torch.Server.Managers; using Torch.Utils; -using VRage.FileSystem; -using VRageRender; namespace Torch.Server { diff --git a/Torch.Server/Properties/Annotations.cs b/Torch.Server/Properties/Annotations.cs new file mode 100644 index 0000000..c00e554 --- /dev/null +++ b/Torch.Server/Properties/Annotations.cs @@ -0,0 +1,1065 @@ +/* MIT License + +Copyright (c) 2016 JetBrains http://www.jetbrains.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. */ + +using System; + +#pragma warning disable 1591 +// ReSharper disable UnusedMember.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable IntroduceOptionalParameters.Global +// ReSharper disable MemberCanBeProtected.Global +// ReSharper disable InconsistentNaming + +namespace Torch.Server.Annotations +{ + /// + /// Indicates that the value of the marked element could be null sometimes, + /// so the check for null is necessary before its usage. + /// + /// + /// [CanBeNull] object Test() => null; + /// + /// void UseTest() { + /// var p = Test(); + /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + public sealed class CanBeNullAttribute : Attribute { } + + /// + /// Indicates that the value of the marked element could never be null. + /// + /// + /// [NotNull] object Foo() { + /// return null; // Warning: Possible 'null' assignment + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + public sealed class NotNullAttribute : Attribute { } + + /// + /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can never be null. + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemNotNullAttribute : Attribute { } + + /// + /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can be null. + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemCanBeNullAttribute : Attribute { } + + /// + /// Indicates that the marked method builds string by format pattern and (optional) arguments. + /// Parameter, which contains format string, should be given in constructor. The format string + /// should be in -like form. + /// + /// + /// [StringFormatMethod("message")] + /// void ShowError(string message, params object[] args) { /* do something */ } + /// + /// void Foo() { + /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string + /// } + /// + [AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Method | + AttributeTargets.Property | AttributeTargets.Delegate)] + public sealed class StringFormatMethodAttribute : Attribute + { + /// + /// Specifies which parameter of an annotated method should be treated as format-string + /// + public StringFormatMethodAttribute([NotNull] string formatParameterName) + { + FormatParameterName = formatParameterName; + } + + [NotNull] public string FormatParameterName { get; private set; } + } + + /// + /// For a parameter that is expected to be one of the limited set of values. + /// Specify fields of which type should be used as values for this parameter. + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, + AllowMultiple = true)] + public sealed class ValueProviderAttribute : Attribute + { + public ValueProviderAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + /// + /// Indicates that the function argument should be string literal and match one + /// of the parameters of the caller function. For example, ReSharper annotates + /// the parameter of . + /// + /// + /// void Foo(string param) { + /// if (param == null) + /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InvokerParameterNameAttribute : Attribute { } + + /// + /// Indicates that the method is contained in a type that implements + /// System.ComponentModel.INotifyPropertyChanged interface and this method + /// is used to notify that some property value changed. + /// + /// + /// The method should be non-static and conform to one of the supported signatures: + /// + /// NotifyChanged(string) + /// NotifyChanged(params string[]) + /// NotifyChanged{T}(Expression{Func{T}}) + /// NotifyChanged{T,U}(Expression{Func{T,U}}) + /// SetProperty{T}(ref T, T, string) + /// + /// + /// + /// public class Foo : INotifyPropertyChanged { + /// public event PropertyChangedEventHandler PropertyChanged; + /// + /// [NotifyPropertyChangedInvocator] + /// protected virtual void NotifyChanged(string propertyName) { ... } + /// + /// string _name; + /// + /// public string Name { + /// get { return _name; } + /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } + /// } + /// } + /// + /// Examples of generated notifications: + /// + /// NotifyChanged("Property") + /// NotifyChanged(() => Property) + /// NotifyChanged((VM x) => x.Property) + /// SetProperty(ref myField, value, "Property") + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute + { + public NotifyPropertyChangedInvocatorAttribute() { } + public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) + { + ParameterName = parameterName; + } + + [CanBeNull] public string ParameterName { get; private set; } + } + + /// + /// Describes dependency between method input and output. + /// + /// + ///

Function Definition Table syntax:

+ /// + /// FDT ::= FDTRow [;FDTRow]* + /// FDTRow ::= Input => Output | Output <= Input + /// Input ::= ParameterName: Value [, Input]* + /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} + /// Value ::= true | false | null | notnull | canbenull + /// + /// If method has single input parameter, it's name could be omitted.
+ /// Using halt (or void/nothing, which is the same) for method output + /// means that the methos doesn't return normally (throws or terminates the process).
+ /// Value canbenull is only applicable for output parameters.
+ /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute + /// with rows separated by semicolon. There is no notion of order rows, all rows are checked + /// for applicability and applied per each program state tracked by R# analysis.
+ ///
+ /// + /// + /// [ContractAnnotation("=> halt")] + /// public void TerminationMethod() + /// + /// + /// [ContractAnnotation("halt <= condition: false")] + /// public void Assert(bool condition, string text) // regular assertion method + /// + /// + /// [ContractAnnotation("s:null => true")] + /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() + /// + /// + /// // A method that returns null if the parameter is null, + /// // and not null if the parameter is not null + /// [ContractAnnotation("null => null; notnull => notnull")] + /// public object Transform(object data) + /// + /// + /// [ContractAnnotation("=> true, result: notnull; => false, result: null")] + /// public bool TryParse(string s, out Person result) + /// + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public sealed class ContractAnnotationAttribute : Attribute + { + public ContractAnnotationAttribute([NotNull] string contract) + : this(contract, false) { } + + public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) + { + Contract = contract; + ForceFullStates = forceFullStates; + } + + [NotNull] public string Contract { get; private set; } + + public bool ForceFullStates { get; private set; } + } + + /// + /// Indicates that marked element should be localized or not. + /// + /// + /// [LocalizationRequiredAttribute(true)] + /// class Foo { + /// string str = "my string"; // Warning: Localizable string + /// } + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class LocalizationRequiredAttribute : Attribute + { + public LocalizationRequiredAttribute() : this(true) { } + + public LocalizationRequiredAttribute(bool required) + { + Required = required; + } + + public bool Required { get; private set; } + } + + /// + /// Indicates that the value of the marked type (or its derivatives) + /// cannot be compared using '==' or '!=' operators and Equals() + /// should be used instead. However, using '==' or '!=' for comparison + /// with null is always permitted. + /// + /// + /// [CannotApplyEqualityOperator] + /// class NoEquality { } + /// + /// class UsesNoEquality { + /// void Test() { + /// var ca1 = new NoEquality(); + /// var ca2 = new NoEquality(); + /// if (ca1 != null) { // OK + /// bool condition = ca1 == ca2; // Warning + /// } + /// } + /// } + /// + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] + public sealed class CannotApplyEqualityOperatorAttribute : Attribute { } + + /// + /// When applied to a target attribute, specifies a requirement for any type marked + /// with the target attribute to implement or inherit specific type or types. + /// + /// + /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement + /// class ComponentAttribute : Attribute { } + /// + /// [Component] // ComponentAttribute requires implementing IComponent interface + /// class MyComponent : IComponent { } + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + [BaseTypeRequired(typeof(Attribute))] + public sealed class BaseTypeRequiredAttribute : Attribute + { + public BaseTypeRequiredAttribute([NotNull] Type baseType) + { + BaseType = baseType; + } + + [NotNull] public Type BaseType { get; private set; } + } + + /// + /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), + /// so this symbol will not be marked as unused (as well as by other usage inspections). + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class UsedImplicitlyAttribute : Attribute + { + public UsedImplicitlyAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + public ImplicitUseKindFlags UseKindFlags { get; private set; } + + public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + /// + /// Should be used on attributes and causes ReSharper to not mark symbols marked with such attributes + /// as unused (as well as by other usage inspections) + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)] + public sealed class MeansImplicitUseAttribute : Attribute + { + public MeansImplicitUseAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; } + + [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + [Flags] + public enum ImplicitUseKindFlags + { + Default = Access | Assign | InstantiatedWithFixedConstructorSignature, + /// Only entity marked with attribute considered used. + Access = 1, + /// Indicates implicit assignment to a member. + Assign = 2, + /// + /// Indicates implicit instantiation of a type with fixed constructor signature. + /// That means any unused constructor parameters won't be reported as such. + /// + InstantiatedWithFixedConstructorSignature = 4, + /// Indicates implicit instantiation of a type. + InstantiatedNoFixedConstructorSignature = 8, + } + + /// + /// Specify what is considered used implicitly when marked + /// with or . + /// + [Flags] + public enum ImplicitUseTargetFlags + { + Default = Itself, + Itself = 1, + /// Members of entity marked with attribute are considered used. + Members = 2, + /// Entity marked with attribute and all its members considered used. + WithMembers = Itself | Members + } + + /// + /// This attribute is intended to mark publicly available API + /// which should not be removed and so is treated as used. + /// + [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] + public sealed class PublicAPIAttribute : Attribute + { + public PublicAPIAttribute() { } + + public PublicAPIAttribute([NotNull] string comment) + { + Comment = comment; + } + + [CanBeNull] public string Comment { get; private set; } + } + + /// + /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. + /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. + /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InstantHandleAttribute : Attribute { } + + /// + /// Indicates that a method does not make any observable state changes. + /// The same as System.Diagnostics.Contracts.PureAttribute. + /// + /// + /// [Pure] int Multiply(int x, int y) => x * y; + /// + /// void M() { + /// Multiply(123, 42); // Waring: Return value of pure method is not used + /// } + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class PureAttribute : Attribute { } + + /// + /// Indicates that the return value of method invocation must be used. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class MustUseReturnValueAttribute : Attribute + { + public MustUseReturnValueAttribute() { } + + public MustUseReturnValueAttribute([NotNull] string justification) + { + Justification = justification; + } + + [CanBeNull] public string Justification { get; private set; } + } + + /// + /// Indicates the type member or parameter of some type, that should be used instead of all other ways + /// to get the value that type. This annotation is useful when you have some "context" value evaluated + /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. + /// + /// + /// class Foo { + /// [ProvidesContext] IBarService _barService = ...; + /// + /// void ProcessNode(INode node) { + /// DoSomething(node, node.GetGlobalServices().Bar); + /// // ^ Warning: use value of '_barService' field + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] + public sealed class ProvidesContextAttribute : Attribute { } + + /// + /// Indicates that a parameter is a path to a file or a folder within a web project. + /// Path can be relative or absolute, starting from web root (~). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class PathReferenceAttribute : Attribute + { + public PathReferenceAttribute() { } + + public PathReferenceAttribute([NotNull, PathReference] string basePath) + { + BasePath = basePath; + } + + [CanBeNull] public string BasePath { get; private set; } + } + + /// + /// An extension method marked with this attribute is processed by ReSharper code completion + /// as a 'Source Template'. When extension method is completed over some expression, it's source code + /// is automatically expanded like a template at call site. + /// + /// + /// Template method body can contain valid source code and/or special comments starting with '$'. + /// Text inside these comments is added as source code when the template is applied. Template parameters + /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. + /// Use the attribute to specify macros for parameters. + /// + /// + /// In this example, the 'forEach' method is a source template available over all values + /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: + /// + /// [SourceTemplate] + /// public static void forEach<T>(this IEnumerable<T> xs) { + /// foreach (var x in xs) { + /// //$ $END$ + /// } + /// } + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class SourceTemplateAttribute : Attribute { } + + /// + /// Allows specifying a macro for a parameter of a source template. + /// + /// + /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression + /// is defined in the property. When applied on a method, the target + /// template parameter is defined in the property. To apply the macro silently + /// for the parameter, set the property value = -1. + /// + /// + /// Applying the attribute on a source template method: + /// + /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] + /// public static void forEach<T>(this IEnumerable<T> collection) { + /// foreach (var item in collection) { + /// //$ $END$ + /// } + /// } + /// + /// Applying the attribute on a template method parameter: + /// + /// [SourceTemplate] + /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { + /// /*$ var $x$Id = "$newguid$" + x.ToString(); + /// x.DoSomething($x$Id); */ + /// } + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] + public sealed class MacroAttribute : Attribute + { + /// + /// Allows specifying a macro that will be executed for a source template + /// parameter when the template is expanded. + /// + [CanBeNull] public string Expression { get; set; } + + /// + /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. + /// + /// + /// If the target parameter is used several times in the template, only one occurrence becomes editable; + /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, + /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. + /// > + public int Editable { get; set; } + + /// + /// Identifies the target parameter of a source template if the + /// is applied on a template method. + /// + [CanBeNull] public string Target { get; set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute + { + public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute + { + public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute + { + public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcMasterLocationFormatAttribute : Attribute + { + public AspMvcMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute + { + public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcViewLocationFormatAttribute : Attribute + { + public AspMvcViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC action. If applied to a method, the MVC action name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcActionAttribute : Attribute + { + public AspMvcActionAttribute() { } + + public AspMvcActionAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC area. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcAreaAttribute : Attribute + { + public AspMvcAreaAttribute() { } + + public AspMvcAreaAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is + /// an MVC controller. If applied to a method, the MVC controller name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcControllerAttribute : Attribute + { + public AspMvcControllerAttribute() { } + + public AspMvcControllerAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcMasterAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcModelTypeAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC + /// partial view. If applied to a method, the MVC partial view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcPartialViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public sealed class AspMvcSuppressViewErrorAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcDisplayTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcEditorTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC template. + /// Use this attribute for custom wrappers similar to + /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Controller.View(Object). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component name. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcViewComponentAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component view. If applied to a method, the MVC view component view name is default. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcViewComponentViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. When applied to a parameter of an attribute, + /// indicates that this parameter is an MVC action name. + /// + /// + /// [ActionName("Foo")] + /// public ActionResult Login(string returnUrl) { + /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK + /// return RedirectToAction("Bar"); // Error: Cannot resolve action + /// } + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] + public sealed class AspMvcActionSelectorAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] + public sealed class HtmlElementAttributesAttribute : Attribute + { + public HtmlElementAttributesAttribute() { } + + public HtmlElementAttributesAttribute([NotNull] string name) + { + Name = name; + } + + [CanBeNull] public string Name { get; private set; } + } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class HtmlAttributeValueAttribute : Attribute + { + public HtmlAttributeValueAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + /// + /// Razor attribute. Indicates that a parameter or a method is a Razor section. + /// Use this attribute for custom wrappers similar to + /// System.Web.WebPages.WebPageBase.RenderSection(String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class RazorSectionAttribute : Attribute { } + + /// + /// Indicates how method, constructor invocation or property access + /// over collection type affects content of the collection. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] + public sealed class CollectionAccessAttribute : Attribute + { + public CollectionAccessAttribute(CollectionAccessType collectionAccessType) + { + CollectionAccessType = collectionAccessType; + } + + public CollectionAccessType CollectionAccessType { get; private set; } + } + + [Flags] + public enum CollectionAccessType + { + /// Method does not use or modify content of the collection. + None = 0, + /// Method only reads content of the collection but does not modify it. + Read = 1, + /// Method can change content of the collection but does not add new elements. + ModifyExistingContent = 2, + /// Method can add new elements to the collection. + UpdatedContent = ModifyExistingContent | 4 + } + + /// + /// Indicates that the marked method is assertion method, i.e. it halts control flow if + /// one of the conditions is satisfied. To set the condition, mark one of the parameters with + /// attribute. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class AssertionMethodAttribute : Attribute { } + + /// + /// Indicates the condition parameter of the assertion method. The method itself should be + /// marked by attribute. The mandatory argument of + /// the attribute is the assertion type. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AssertionConditionAttribute : Attribute + { + public AssertionConditionAttribute(AssertionConditionType conditionType) + { + ConditionType = conditionType; + } + + public AssertionConditionType ConditionType { get; private set; } + } + + /// + /// Specifies assertion type. If the assertion method argument satisfies the condition, + /// then the execution continues. Otherwise, execution is assumed to be halted. + /// + public enum AssertionConditionType + { + /// Marked parameter should be evaluated to true. + IS_TRUE = 0, + /// Marked parameter should be evaluated to false. + IS_FALSE = 1, + /// Marked parameter should be evaluated to null value. + IS_NULL = 2, + /// Marked parameter should be evaluated to not null value. + IS_NOT_NULL = 3, + } + + /// + /// Indicates that the marked method unconditionally terminates control flow execution. + /// For example, it could unconditionally throw exception. + /// + [Obsolete("Use [ContractAnnotation('=> halt')] instead")] + [AttributeUsage(AttributeTargets.Method)] + public sealed class TerminatesProgramAttribute : Attribute { } + + /// + /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, + /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters + /// of delegate type by analyzing LINQ method chains. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class LinqTunnelAttribute : Attribute { } + + /// + /// Indicates that IEnumerable, passed as parameter, is not enumerated. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class NoEnumerationAttribute : Attribute { } + + /// + /// Indicates that parameter is regular expression pattern. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class RegexPatternAttribute : Attribute { } + + /// + /// Prevents the Member Reordering feature from tossing members of the marked class. + /// + /// + /// The attribute must be mentioned in your member reordering patterns + /// + [AttributeUsage( + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] + public sealed class NoReorderAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the type that has ItemsSource property and should be treated + /// as ItemsControl-derived type, to enable inner items DataContext type resolve. + /// + [AttributeUsage(AttributeTargets.Class)] + public sealed class XamlItemsControlAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the property of some BindingBase-derived type, that + /// is used to bind some item of ItemsControl-derived type. This annotation will + /// enable the DataContext type resolve for XAML bindings for such properties. + /// + /// + /// Property should have the tree ancestor of the ItemsControl type or + /// marked with the attribute. + /// + [AttributeUsage(AttributeTargets.Property)] + public sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspChildControlTypeAttribute : Attribute + { + public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) + { + TagName = tagName; + ControlType = controlType; + } + + [NotNull] public string TagName { get; private set; } + + [NotNull] public Type ControlType { get; private set; } + } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldsAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspMethodPropertyAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspRequiredAttributeAttribute : Attribute + { + public AspRequiredAttributeAttribute([NotNull] string attribute) + { + Attribute = attribute; + } + + [NotNull] public string Attribute { get; private set; } + } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspTypePropertyAttribute : Attribute + { + public bool CreateConstructorReferences { get; private set; } + + public AspTypePropertyAttribute(bool createConstructorReferences) + { + CreateConstructorReferences = createConstructorReferences; + } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorImportNamespaceAttribute : Attribute + { + public RazorImportNamespaceAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorInjectionAttribute : Attribute + { + public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) + { + Type = type; + FieldName = fieldName; + } + + [NotNull] public string Type { get; private set; } + + [NotNull] public string FieldName { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorDirectiveAttribute : Attribute + { + public RazorDirectiveAttribute([NotNull] string directive) + { + Directive = directive; + } + + [NotNull] public string Directive { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorPageBaseTypeAttribute : Attribute + { + public RazorPageBaseTypeAttribute([NotNull] string baseType) + { + BaseType = baseType; + } + public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) + { + BaseType = baseType; + PageName = pageName; + } + + [NotNull] public string BaseType { get; private set; } + [CanBeNull] public string PageName { get; private set; } + } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorHelperCommonAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class RazorLayoutAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteLiteralMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class RazorWriteMethodParameterAttribute : Attribute { } +} \ No newline at end of file diff --git a/Torch.Server/ServerStatistics.cs b/Torch.Server/ServerStatistics.cs deleted file mode 100644 index c3cd6a2..0000000 --- a/Torch.Server/ServerStatistics.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Torch.Collections; - -namespace Torch.Server -{ - public class ServerStatistics - { - public RollingAverage SimSpeed { get; } = new RollingAverage(30); - } -} diff --git a/Torch.Server/Torch.Server.csproj b/Torch.Server/Torch.Server.csproj index e999ad5..b215745 100644 --- a/Torch.Server/Torch.Server.csproj +++ b/Torch.Server/Torch.Server.csproj @@ -102,6 +102,7 @@ False + @@ -201,8 +202,8 @@ + - Component @@ -224,9 +225,9 @@ True SessionSettingsViewModel.tt - - DynamicView.xaml - + + + EntityControlHost.xaml @@ -287,6 +288,7 @@ + WorldGeneratorDialog.xaml @@ -326,10 +328,6 @@ - - Designer - MSBuild:Compile - Designer MSBuild:Compile @@ -378,6 +376,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/Torch.Server/TorchServer.cs b/Torch.Server/TorchServer.cs index 7d18a50..7e0ae9f 100644 --- a/Torch.Server/TorchServer.cs +++ b/Torch.Server/TorchServer.cs @@ -1,40 +1,20 @@ -using Sandbox; -using Sandbox.Engine.Utils; -using Sandbox.Game; +using NLog; +using Sandbox; +using Sandbox.Game.Multiplayer; using Sandbox.Game.World; using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; -using System.IO; using System.Reflection; -using System.Runtime.CompilerServices; -using System.Security.Principal; using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.Xml.Serialization.GeneratedAssembly; -using NLog; -using Sandbox.Engine.Analytics; -using Sandbox.Game.Multiplayer; -using Sandbox.ModAPI; -using SteamSDK; using Torch.API; using Torch.API.Managers; using Torch.API.Session; -using Torch.Managers; using Torch.Server.Managers; using Torch.Utils; -using VRage.Dedicated; -using VRage.FileSystem; using VRage.Game; -using VRage.Game.ModAPI; -using VRage.Game.ObjectBuilder; -using VRage.Game.SessionComponents; -using VRage.Library; -using VRage.ObjectBuilders; -using VRage.Plugins; -using VRage.Utils; #pragma warning disable 618 @@ -131,7 +111,6 @@ namespace Torch.Server Sandbox.Engine.Platform.Game.IsDedicated = true; base.Init(); - Managers.GetManager().SessionStateChanged += OnSessionStateChanged; GetManager().LoadInstance(Config.InstancePath); } diff --git a/Torch.Server/ViewModels/ConfigDedicatedViewModel.cs b/Torch.Server/ViewModels/ConfigDedicatedViewModel.cs index ab63510..fd32cf2 100644 --- a/Torch.Server/ViewModels/ConfigDedicatedViewModel.cs +++ b/Torch.Server/ViewModels/ConfigDedicatedViewModel.cs @@ -29,32 +29,10 @@ namespace Torch.Server.ViewModels _config = configDedicated; _config.IgnoreLastSession = true; SessionSettings = new SessionSettingsViewModel(_config.SessionSettings); - Administrators = string.Join(Environment.NewLine, _config.Administrators); - Banned = string.Join(Environment.NewLine, _config.Banned); - Mods = string.Join(Environment.NewLine, _config.Mods); } public void Save(string path = null) { - var newline = new [] {Environment.NewLine}; - - _config.Administrators.Clear(); - foreach (var admin in Administrators.Split(newline, StringSplitOptions.RemoveEmptyEntries)) - _config.Administrators.Add(admin); - - _config.Banned.Clear(); - foreach (var banned in Banned.Split(newline, StringSplitOptions.RemoveEmptyEntries)) - _config.Banned.Add(ulong.Parse(banned)); - - _config.Mods.Clear(); - foreach (var mod in Mods.Split(newline, StringSplitOptions.RemoveEmptyEntries)) - { - if (ulong.TryParse(mod, out ulong modId)) - _config.Mods.Add(modId); - else - Log.Warn($"'{mod}' is not a valid mod ID."); - } - // Never ever _config.IgnoreLastSession = true; _config.Save(path); @@ -75,71 +53,30 @@ namespace Torch.Server.ViewModels } } - private string _administrators; - public string Administrators { get => _administrators; set { _administrators = value; OnPropertyChanged(); } } - private string _banned; - public string Banned { get => _banned; set { _banned = value; OnPropertyChanged(); } } - private string _mods; - public string Mods { get => _mods; set { _mods = value; OnPropertyChanged(); } } + public List Administrators { get => _config.Administrators; set => SetValue(x => _config.Administrators = x, value); } - public int AsteroidAmount - { - get { return _config.AsteroidAmount; } - set { _config.AsteroidAmount = value; OnPropertyChanged(); } - } + public List Banned { get => _config.Banned; set => SetValue(x => _config.Banned = x, value); } - public ulong GroupId - { - get { return _config.GroupID; } - set { _config.GroupID = value; OnPropertyChanged(); } - } + public List Mods { get => _config.Mods; set => SetValue(x => _config.Mods = x, value); } - public string IP - { - get { return _config.IP; } - set { _config.IP = value; OnPropertyChanged(); } - } + public int AsteroidAmount { get => _config.AsteroidAmount; set => SetValue(x => _config.AsteroidAmount = x, value); } - public int Port - { - get { return _config.ServerPort; } - set { _config.ServerPort = value; OnPropertyChanged(); } - } + public ulong GroupId { get => _config.GroupID; set => SetValue(x => _config.GroupID = x, value); } - public string ServerName - { - get { return _config.ServerName; } - set { _config.ServerName = value; OnPropertyChanged(); } - } + public string IP { get => _config.IP; set => SetValue(x => _config.IP = x, value); } - public bool PauseGameWhenEmpty - { - get { return _config.PauseGameWhenEmpty; } - set { _config.PauseGameWhenEmpty = value; OnPropertyChanged(); } - } + public int Port { get => _config.ServerPort; set => SetValue(x => _config.ServerPort = x, value); } - public string PremadeCheckpointPath - { - get { return _config.PremadeCheckpointPath; } - set { _config.PremadeCheckpointPath = value; OnPropertyChanged(); } - } + public string ServerName { get => _config.ServerName; set => SetValue(x => _config.ServerName = x, value); } - public string LoadWorld - { - get { return _config.LoadWorld; } - set { _config.LoadWorld = value; OnPropertyChanged(); } - } + public bool PauseGameWhenEmpty { get => _config.PauseGameWhenEmpty; set => SetValue(x => _config.PauseGameWhenEmpty = x, value); } - public int SteamPort - { - get { return _config.SteamPort; } - set { _config.SteamPort = value; OnPropertyChanged(); } - } + public string PremadeCheckpointPath { get => _config.PremadeCheckpointPath; set => SetValue(x => _config.PremadeCheckpointPath = x, value); } - public string WorldName - { - get { return _config.WorldName; } - set { _config.WorldName = value; OnPropertyChanged(); } - } + public string LoadWorld { get => _config.LoadWorld; set => SetValue(x => _config.LoadWorld = x, value); } + + public int SteamPort { get => _config.SteamPort; set => SetValue(x => _config.SteamPort = x, value); } + + public string WorldName { get => _config.WorldName; set => SetValue(x => _config.WorldName = x, value); } } } diff --git a/Torch.Server/ViewModels/SessionSettingsViewModel.cs b/Torch.Server/ViewModels/SessionSettingsViewModel.cs index 401969e..486e4e5 100644 --- a/Torch.Server/ViewModels/SessionSettingsViewModel.cs +++ b/Torch.Server/ViewModels/SessionSettingsViewModel.cs @@ -8,235 +8,279 @@ using Torch.Collections; using VRage.Game; using VRage.Library.Utils; using VRage.Serialization; +using System.ComponentModel.DataAnnotations; namespace Torch.Server.ViewModels { public class SessionSettingsViewModel : ViewModel { private MyObjectBuilder_SessionSettings _settings; - /// - public string GameMode { get => _settings.GameMode.ToString(); set { Enum.TryParse(value, true, out VRage.Library.Utils.MyGameModeEnum parsedVal); SetValue(ref _settings.GameMode, parsedVal); } } - public List GameModeValues { get; } = new List {"Creative", "Survival"}; + /// + [Display(Name = "Game mode")] + public VRage.Library.Utils.MyGameModeEnum GameMode { get => _settings.GameMode; set => SetValue(ref _settings.GameMode, value); } - /// + /// + [Display(Name = "Inventory size multiplier")] public System.Single InventorySizeMultiplier { get => _settings.InventorySizeMultiplier; set => SetValue(ref _settings.InventorySizeMultiplier, value); } - /// + /// + [Display(Name = "Assembler speed multiplier")] public System.Single AssemblerSpeedMultiplier { get => _settings.AssemblerSpeedMultiplier; set => SetValue(ref _settings.AssemblerSpeedMultiplier, value); } - /// + /// + [Display(Name = "Assembler efficiency multiplier")] public System.Single AssemblerEfficiencyMultiplier { get => _settings.AssemblerEfficiencyMultiplier; set => SetValue(ref _settings.AssemblerEfficiencyMultiplier, value); } - /// + /// + [Display(Name = "Refinery speed multiplier")] public System.Single RefinerySpeedMultiplier { get => _settings.RefinerySpeedMultiplier; set => SetValue(ref _settings.RefinerySpeedMultiplier, value); } - /// - public string OnlineMode { get => _settings.OnlineMode.ToString(); set { Enum.TryParse(value, true, out VRage.Game.MyOnlineModeEnum parsedVal); SetValue(ref _settings.OnlineMode, parsedVal); } } - public List OnlineModeValues { get; } = new List {"OFFLINE", "PUBLIC", "FRIENDS", "PRIVATE"}; + /// + [Display(Name = "OnlineMode")] + public VRage.Game.MyOnlineModeEnum OnlineMode { get => _settings.OnlineMode; set => SetValue(ref _settings.OnlineMode, value); } - /// + /// + [Display(Name = "Max players")] public System.Int16 MaxPlayers { get => _settings.MaxPlayers; set => SetValue(ref _settings.MaxPlayers, value); } - /// + /// + [Display(Name = "Max floating objects")] public System.Int16 MaxFloatingObjects { get => _settings.MaxFloatingObjects; set => SetValue(ref _settings.MaxFloatingObjects, value); } - /// + /// + [Display(Name = "Max Backup Saves")] public System.Int16 MaxBackupSaves { get => _settings.MaxBackupSaves; set => SetValue(ref _settings.MaxBackupSaves, value); } - /// + /// + [Display(Name = "Max grid size")] public System.Int32 MaxGridSize { get => _settings.MaxGridSize; set => SetValue(ref _settings.MaxGridSize, value); } - /// + /// + [Display(Name = "Max blocks per player")] public System.Int32 MaxBlocksPerPlayer { get => _settings.MaxBlocksPerPlayer; set => SetValue(ref _settings.MaxBlocksPerPlayer, value); } - /// + /// + [Display(Name = "Enable block limits")] public System.Boolean EnableBlockLimits { get => _settings.EnableBlockLimits; set => SetValue(ref _settings.EnableBlockLimits, value); } - /// + /// + [Display(Name = "Enable remote removal of owned blocks")] public System.Boolean EnableRemoteBlockRemoval { get => _settings.EnableRemoteBlockRemoval; set => SetValue(ref _settings.EnableRemoteBlockRemoval, value); } - /// - public string EnvironmentHostility { get => _settings.EnvironmentHostility.ToString(); set { Enum.TryParse(value, true, out VRage.Game.MyEnvironmentHostilityEnum parsedVal); SetValue(ref _settings.EnvironmentHostility, parsedVal); } } - public List EnvironmentHostilityValues { get; } = new List {"SAFE", "NORMAL", "CATACLYSM", "CATACLYSM_UNREAL"}; + /// + [Display(Name = "Environment hostility")] + public VRage.Game.MyEnvironmentHostilityEnum EnvironmentHostility { get => _settings.EnvironmentHostility; set => SetValue(ref _settings.EnvironmentHostility, value); } - /// + /// + [Display(Name = "Auto healing")] public System.Boolean AutoHealing { get => _settings.AutoHealing; set => SetValue(ref _settings.AutoHealing, value); } - /// + /// + [Display(Name = "Enable Copy&Paste")] public System.Boolean EnableCopyPaste { get => _settings.EnableCopyPaste; set => SetValue(ref _settings.EnableCopyPaste, value); } - /// + /// + [Display(Name = "Weapons enabled")] public System.Boolean WeaponsEnabled { get => _settings.WeaponsEnabled; set => SetValue(ref _settings.WeaponsEnabled, value); } - /// + /// + [Display(Name = "Show player names on HUD")] public System.Boolean ShowPlayerNamesOnHud { get => _settings.ShowPlayerNamesOnHud; set => SetValue(ref _settings.ShowPlayerNamesOnHud, value); } - /// + /// + [Display(Name = "Thruster damage")] public System.Boolean ThrusterDamage { get => _settings.ThrusterDamage; set => SetValue(ref _settings.ThrusterDamage, value); } - /// + /// + [Display(Name = "Cargo ships enabled")] public System.Boolean CargoShipsEnabled { get => _settings.CargoShipsEnabled; set => SetValue(ref _settings.CargoShipsEnabled, value); } - /// + /// + [Display(Name = "Enable spectator")] public System.Boolean EnableSpectator { get => _settings.EnableSpectator; set => SetValue(ref _settings.EnableSpectator, value); } - /// + /// + [Display(Name = "World size in Km")] public System.Int32 WorldSizeKm { get => _settings.WorldSizeKm; set => SetValue(ref _settings.WorldSizeKm, value); } - /// + /// + [Display(Name = "Respawn ship delete")] public System.Boolean RespawnShipDelete { get => _settings.RespawnShipDelete; set => SetValue(ref _settings.RespawnShipDelete, value); } - /// + /// + [Display(Name = "Reset ownership")] public System.Boolean ResetOwnership { get => _settings.ResetOwnership; set => SetValue(ref _settings.ResetOwnership, value); } - /// + /// + [Display(Name = "Welder speed multiplier")] public System.Single WelderSpeedMultiplier { get => _settings.WelderSpeedMultiplier; set => SetValue(ref _settings.WelderSpeedMultiplier, value); } - /// + /// + [Display(Name = "Grinder speed multiplier")] public System.Single GrinderSpeedMultiplier { get => _settings.GrinderSpeedMultiplier; set => SetValue(ref _settings.GrinderSpeedMultiplier, value); } - /// + /// + [Display(Name = "Realistic sound")] public System.Boolean RealisticSound { get => _settings.RealisticSound; set => SetValue(ref _settings.RealisticSound, value); } - /// + /// + [Display(Name = "Hack speed multiplier")] public System.Single HackSpeedMultiplier { get => _settings.HackSpeedMultiplier; set => SetValue(ref _settings.HackSpeedMultiplier, value); } - /// + /// + [Display(Name = "Permanent death")] public System.Nullable PermanentDeath { get => _settings.PermanentDeath; set => SetValue(ref _settings.PermanentDeath, value); } - /// + /// + [Display(Name = "AutoSave in minutes")] public System.UInt32 AutoSaveInMinutes { get => _settings.AutoSaveInMinutes; set => SetValue(ref _settings.AutoSaveInMinutes, value); } - /// + /// + [Display(Name = "Enable saving from menu")] public System.Boolean EnableSaving { get => _settings.EnableSaving; set => SetValue(ref _settings.EnableSaving, value); } - /// + /// + [Display(Name = "Enable respawn screen in the game")] public System.Boolean EnableRespawnScreen { get => _settings.EnableRespawnScreen; set => SetValue(ref _settings.EnableRespawnScreen, value); } - /// + /// + [Display(Name = "Enable infinite ammunition in survival")] public System.Boolean InfiniteAmmo { get => _settings.InfiniteAmmo; set => SetValue(ref _settings.InfiniteAmmo, value); } - /// + /// + [Display(Name = "Enable drop containers")] public System.Boolean EnableContainerDrops { get => _settings.EnableContainerDrops; set => SetValue(ref _settings.EnableContainerDrops, value); } - /// + /// + [Display(Name = "Spawnship time multiplier")] public System.Single SpawnShipTimeMultiplier { get => _settings.SpawnShipTimeMultiplier; set => SetValue(ref _settings.SpawnShipTimeMultiplier, value); } - /// + /// + [Display(Name = "Procedural density")] public System.Single ProceduralDensity { get => _settings.ProceduralDensity; set => SetValue(ref _settings.ProceduralDensity, value); } - /// + /// + [Display(Name = "Procedural seed")] public System.Int32 ProceduralSeed { get => _settings.ProceduralSeed; set => SetValue(ref _settings.ProceduralSeed, value); } - /// + /// + [Display(Name = "Destructible blocks")] public System.Boolean DestructibleBlocks { get => _settings.DestructibleBlocks; set => SetValue(ref _settings.DestructibleBlocks, value); } - /// + /// + [Display(Name = "Enable ingame scripts")] public System.Boolean EnableIngameScripts { get => _settings.EnableIngameScripts; set => SetValue(ref _settings.EnableIngameScripts, value); } - /// + /// + [Display(Name = "View distance")] public System.Int32 ViewDistance { get => _settings.ViewDistance; set => SetValue(ref _settings.ViewDistance, value); } - /// + /// + [Display(Name = "Flora density")] public System.Int32 FloraDensity { get => _settings.FloraDensity; set => SetValue(ref _settings.FloraDensity, value); } - /// + /// + [Display(Name = "Enable tool shake")] public System.Boolean EnableToolShake { get => _settings.EnableToolShake; set => SetValue(ref _settings.EnableToolShake, value); } - /// + /// + [Display(Name = "Voxel generator version")] public System.Int32 VoxelGeneratorVersion { get => _settings.VoxelGeneratorVersion; set => SetValue(ref _settings.VoxelGeneratorVersion, value); } - /// + /// + [Display(Name = "Enable oxygen")] public System.Boolean EnableOxygen { get => _settings.EnableOxygen; set => SetValue(ref _settings.EnableOxygen, value); } - /// + /// + [Display(Name = "Enable airtightness")] public System.Boolean EnableOxygenPressurization { get => _settings.EnableOxygenPressurization; set => SetValue(ref _settings.EnableOxygenPressurization, value); } - /// + /// + [Display(Name = "Enable 3rd person view")] public System.Boolean Enable3rdPersonView { get => _settings.Enable3rdPersonView; set => SetValue(ref _settings.Enable3rdPersonView, value); } - /// + /// + [Display(Name = "Enable encounters")] public System.Boolean EnableEncounters { get => _settings.EnableEncounters; set => SetValue(ref _settings.EnableEncounters, value); } - /// + /// + [Display(Name = "Enable flora")] public System.Boolean EnableFlora { get => _settings.EnableFlora; set => SetValue(ref _settings.EnableFlora, value); } - /// + /// + [Display(Name = "Enable convert to station")] public System.Boolean EnableConvertToStation { get => _settings.EnableConvertToStation; set => SetValue(ref _settings.EnableConvertToStation, value); } - /// + /// + [Display(Name = "Enable station grid with voxel")] public System.Boolean StationVoxelSupport { get => _settings.StationVoxelSupport; set => SetValue(ref _settings.StationVoxelSupport, value); } - /// + /// + [Display(Name = "Enable sun rotation")] public System.Boolean EnableSunRotation { get => _settings.EnableSunRotation; set => SetValue(ref _settings.EnableSunRotation, value); } - /// + /// + [Display(Name = "Enable respawn ships / carts")] public System.Boolean EnableRespawnShips { get => _settings.EnableRespawnShips; set => SetValue(ref _settings.EnableRespawnShips, value); } - /// - public System.Boolean ScenarioEditMode { get => _settings.ScenarioEditMode; set => SetValue(ref _settings.ScenarioEditMode, value); } - - /// - public System.Boolean Scenario { get => _settings.Scenario; set => SetValue(ref _settings.Scenario, value); } - - /// - public System.Boolean CanJoinRunning { get => _settings.CanJoinRunning; set => SetValue(ref _settings.CanJoinRunning, value); } - - /// + /// + [Display(Name = "PhysicsIterations")] public System.Int32 PhysicsIterations { get => _settings.PhysicsIterations; set => SetValue(ref _settings.PhysicsIterations, value); } - /// + /// + [Display(Name = "Sun rotation interval")] public System.Single SunRotationIntervalMinutes { get => _settings.SunRotationIntervalMinutes; set => SetValue(ref _settings.SunRotationIntervalMinutes, value); } - /// + /// + [Display(Name = "Enable jetpack")] public System.Boolean EnableJetpack { get => _settings.EnableJetpack; set => SetValue(ref _settings.EnableJetpack, value); } - /// + /// + [Display(Name = "Spawn with tools")] public System.Boolean SpawnWithTools { get => _settings.SpawnWithTools; set => SetValue(ref _settings.SpawnWithTools, value); } - /// - public System.Boolean StartInRespawnScreen { get => _settings.StartInRespawnScreen; set => SetValue(ref _settings.StartInRespawnScreen, value); } - - /// + /// + [Display(Name = "Enable voxel destruction")] public System.Boolean EnableVoxelDestruction { get => _settings.EnableVoxelDestruction; set => SetValue(ref _settings.EnableVoxelDestruction, value); } - /// - public System.Int32 MaxDrones { get => _settings.MaxDrones; set => SetValue(ref _settings.MaxDrones, value); } - - /// + /// + [Display(Name = "Enable drones")] public System.Boolean EnableDrones { get => _settings.EnableDrones; set => SetValue(ref _settings.EnableDrones, value); } - /// + /// + [Display(Name = "Enable wolfs")] public System.Boolean EnableWolfs { get => _settings.EnableWolfs; set => SetValue(ref _settings.EnableWolfs, value); } - /// + /// + [Display(Name = "Enable spiders")] public System.Boolean EnableSpiders { get => _settings.EnableSpiders; set => SetValue(ref _settings.EnableSpiders, value); } - /// + /// + [Display(Name = "Flora density multiplier")] public System.Single FloraDensityMultiplier { get => _settings.FloraDensityMultiplier; set => SetValue(ref _settings.FloraDensityMultiplier, value); } - /// - public System.Boolean EnableStructuralSimulation { get => _settings.EnableStructuralSimulation; set => SetValue(ref _settings.EnableStructuralSimulation, value); } - - /// - public System.Int32 MaxActiveFracturePieces { get => _settings.MaxActiveFracturePieces; set => SetValue(ref _settings.MaxActiveFracturePieces, value); } - - /// + /// + [Display(Name = "Block type limits")] public VRage.Serialization.SerializableDictionary BlockTypeLimits { get => _settings.BlockTypeLimits; set => SetValue(ref _settings.BlockTypeLimits, value); } - /// + /// + [Display(Name = "Enable Scripter role")] public System.Boolean EnableScripterRole { get => _settings.EnableScripterRole; set => SetValue(ref _settings.EnableScripterRole, value); } - /// + /// + [Display(Name = "Min Drop Container Respawn Time")] public System.Int32 MinDropContainerRespawnTime { get => _settings.MinDropContainerRespawnTime; set => SetValue(ref _settings.MinDropContainerRespawnTime, value); } - /// + /// + [Display(Name = "Max Drop Container Respawn Time")] public System.Int32 MaxDropContainerRespawnTime { get => _settings.MaxDropContainerRespawnTime; set => SetValue(ref _settings.MaxDropContainerRespawnTime, value); } - /// + /// + [Display(Name = "Enable Turrets Friendly Fire")] public System.Boolean EnableTurretsFriendlyFire { get => _settings.EnableTurretsFriendlyFire; set => SetValue(ref _settings.EnableTurretsFriendlyFire, value); } - /// + /// + [Display(Name = "Enable Sub-Grid damage")] public System.Boolean EnableSubgridDamage { get => _settings.EnableSubgridDamage; set => SetValue(ref _settings.EnableSubgridDamage, value); } diff --git a/Torch.Server/ViewModels/SessionSettingsViewModel.tt b/Torch.Server/ViewModels/SessionSettingsViewModel.tt index f399fc0..766008d 100644 --- a/Torch.Server/ViewModels/SessionSettingsViewModel.tt +++ b/Torch.Server/ViewModels/SessionSettingsViewModel.tt @@ -1,5 +1,6 @@ <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> +<#@ assembly name="System.ComponentModel.DataAnnotations" #> <#@ assembly name="$(SolutionDir)\GameBinaries\VRage.Game.dll" #> <#@ assembly name="$(SolutionDir)\GameBinaries\VRage.Library.dll" #> <#@ import namespace="System.Collections" #> @@ -9,6 +10,7 @@ <#@ import namespace="System.Reflection" #> <#@ import namespace="VRage.Game" #> <#@ import namespace="VRage.Serialization" #> +<#@ import namespace="System.ComponentModel.DataAnnotations" #> <#@ output extension=".cs" #> // This file is generated automatically! Any changes will be overwritten. @@ -20,6 +22,7 @@ using Torch.Collections; using VRage.Game; using VRage.Library.Utils; using VRage.Serialization; +using System.ComponentModel.DataAnnotations; namespace Torch.Server.ViewModels { @@ -32,9 +35,17 @@ namespace Torch.Server.ViewModels PushIndent(" "); foreach (var field in typeFields) { + if (field.GetCustomAttribute()?.RelatedTo == Game.MedievalEngineers) + continue; + + var displayName = field.GetCustomAttribute()?.Name ?? field.Name; + if (string.IsNullOrEmpty(displayName)) + continue; + var getSet = ""; WriteLine(GetPropertySummary(field)); - if (field.FieldType.IsEnum) + WriteLine($"[Display(Name = \"{displayName}\")]"); + if (false)//field.FieldType.IsEnum) { Write($"public string {field.Name} "); WriteLine($"{{ get => _settings.{field.Name}.ToString(); set {{ Enum.TryParse(value, true, out {field.FieldType} parsedVal); SetValue(ref _settings.{field.Name}, parsedVal); }} }}"); @@ -65,7 +76,7 @@ namespace Torch.Server.ViewModels string GetPropertySummary(FieldInfo info) { - return $"/// "; + return $"/// "; } string GetPropertyName(FieldInfo info) diff --git a/Torch.Server/Views/AddWorkshopItemsDialog.xaml b/Torch.Server/Views/AddWorkshopItemsDialog.xaml index 5d2daf2..f08c51d 100644 --- a/Torch.Server/Views/AddWorkshopItemsDialog.xaml +++ b/Torch.Server/Views/AddWorkshopItemsDialog.xaml @@ -3,7 +3,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:local="clr-namespace:Torch.Server" mc:Ignorable="d" Title="Add Workshop Item" Height="200" Width="400"> diff --git a/Torch.Server/Views/ChatControl.xaml b/Torch.Server/Views/ChatControl.xaml index 3f6ea01..7bf6782 100644 --- a/Torch.Server/Views/ChatControl.xaml +++ b/Torch.Server/Views/ChatControl.xaml @@ -3,7 +3,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:local="clr-namespace:Torch.Server" mc:Ignorable="d"> diff --git a/Torch.Server/Views/ConfigControl.xaml b/Torch.Server/Views/ConfigControl.xaml index d6fe3d3..22fa24b 100644 --- a/Torch.Server/Views/ConfigControl.xaml +++ b/Torch.Server/Views/ConfigControl.xaml @@ -3,33 +3,45 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:local="clr-namespace:Torch.Server.Views" xmlns:viewModels="clr-namespace:Torch.Server.ViewModels" xmlns:managers="clr-namespace:Torch.Server.Managers" + xmlns:system="clr-namespace:System;assembly=mscorlib" + xmlns:validationRules="clr-namespace:Torch.Server.Views.ValidationRules" + xmlns:views="clr-namespace:Torch.Views;assembly=Torch" mc:Ignorable="d" Background="White"> + + + + + + + - - + +