diff --git a/SboxAnalyzers/SboxAnalyzers.csproj b/SboxAnalyzers/SboxAnalyzers.csproj
index 5b10dd1..2845d79 100644
--- a/SboxAnalyzers/SboxAnalyzers.csproj
+++ b/SboxAnalyzers/SboxAnalyzers.csproj
@@ -26,9 +26,11 @@
+
+
diff --git a/SboxAnalyzers/ServerCmdAnalyzer.cs b/SboxAnalyzers/ServerCmdAnalyzer.cs
new file mode 100644
index 0000000..155f85b
--- /dev/null
+++ b/SboxAnalyzers/ServerCmdAnalyzer.cs
@@ -0,0 +1,54 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+using SboxAnalyzers.Extensions;
+using System.Collections.Immutable;
+using System.Linq;
+
+namespace SboxAnalyzers;
+
+///
+/// A Roslyn analyzer for checking event method arguments.
+///
+[DiagnosticAnalyzer( LanguageNames.CSharp )]
+public class ServerCmdAnalyzer : DiagnosticAnalyzer
+{
+ ///
+ public override ImmutableArray SupportedDiagnostics => Diagnostics.ServerCmd.AllRules;
+
+ ///
+ public override void Initialize( AnalysisContext context )
+ {
+ context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.None );
+ context.EnableConcurrentExecution();
+
+ context.RegisterSyntaxNodeAction( AnalyzeMethodDeclaration, SyntaxKind.MethodDeclaration );
+ }
+
+ ///
+ /// Analyzes a to check if it is a correctly implemented server command.
+ ///
+ /// The context relating to the syntax node being analyzed.
+ private static void AnalyzeMethodDeclaration( SyntaxNodeAnalysisContext context )
+ {
+ var methodDeclaration = (MethodDeclarationSyntax)context.Node;
+ if ( methodDeclaration.ParameterList is null )
+ return;
+
+ // Check that all parameters are networkable in server commands.
+ foreach ( var parameter in methodDeclaration.ParameterList.Parameters )
+ {
+ if ( parameter.Type is null )
+ continue;
+
+ if ( parameter.Type.IsServerCommandSupported( context.SemanticModel ) )
+ continue;
+
+ var diagnostic = Diagnostic.Create( Diagnostics.ServerCmd.NetworkableRule,
+ parameter.Type.GetLocation(),
+ parameter.Type.ToNameString( true, context.SemanticModel ) );
+ context.ReportDiagnostic( diagnostic );
+ }
+ }
+}
diff --git a/SboxAnalyzers/ServerCmdResources.Designer.cs b/SboxAnalyzers/ServerCmdResources.Designer.cs
new file mode 100644
index 0000000..acfa0f8
--- /dev/null
+++ b/SboxAnalyzers/ServerCmdResources.Designer.cs
@@ -0,0 +1,252 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace SboxAnalyzers {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class ServerCmdResources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal ServerCmdResources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SboxAnalyzers.ServerCmdResources", typeof(ServerCmdResources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Networked properties must be implemented as an auto property for them to function. Init setters do not count..
+ ///
+ internal static string AutoPropertyDescription {
+ get {
+ return ResourceManager.GetString("AutoPropertyDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Networked properties must be implemented as an auto property { get; set; }.
+ ///
+ internal static string AutoPropertyMessageFormat {
+ get {
+ return ResourceManager.GetString("AutoPropertyMessageFormat", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Networked properties must be auto-implemented { get; set; }.
+ ///
+ internal static string AutoPropertyTitle {
+ get {
+ return ResourceManager.GetString("AutoPropertyTitle", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The change callback method must exist on the type as the name is defined or defaulted to. If default, it will look for "On[PropertyName]Changed"..
+ ///
+ internal static string ChangeMissingDescription {
+ get {
+ return ResourceManager.GetString("ChangeMissingDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The change callback method '{0}' does not exist.
+ ///
+ internal static string ChangeMissingMessageFormat {
+ get {
+ return ResourceManager.GetString("ChangeMissingMessageFormat", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Change callback method is missing.
+ ///
+ internal static string ChangeMissingTitle {
+ get {
+ return ResourceManager.GetString("ChangeMissingTitle", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The change callback must have two parameters. The first containing the old value; the second containing the new value..
+ ///
+ internal static string ChangeParameterCountDescription {
+ get {
+ return ResourceManager.GetString("ChangeParameterCountDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The change callback needs two parameters, not {0}.
+ ///
+ internal static string ChangeParameterCountMessageFormat {
+ get {
+ return ResourceManager.GetString("ChangeParameterCountMessageFormat", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Change callback parameter count mismatch.
+ ///
+ internal static string ChangeParameterCountTitle {
+ get {
+ return ResourceManager.GetString("ChangeParameterCountTitle", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The change callback method must have the parameter types be the same as the properties type..
+ ///
+ internal static string ChangeParameterTypeDescription {
+ get {
+ return ResourceManager.GetString("ChangeParameterTypeDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The change callback parameter type must be '{0}'.
+ ///
+ internal static string ChangeParameterTypeMessageFormat {
+ get {
+ return ResourceManager.GetString("ChangeParameterTypeMessageFormat", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Change callback parameter type mismatch.
+ ///
+ internal static string ChangeParameterTypeTitle {
+ get {
+ return ResourceManager.GetString("ChangeParameterTypeTitle", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The local attribute is currently not implemented. See issue #2169 for updates..
+ ///
+ internal static string LocalDescription {
+ get {
+ return ResourceManager.GetString("LocalDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The local attribute is not implemented.
+ ///
+ internal static string LocalMessageFormat {
+ get {
+ return ResourceManager.GetString("LocalMessageFormat", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Local attribute is not implemented.
+ ///
+ internal static string LocalTitle {
+ get {
+ return ResourceManager.GetString("LocalTitle", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Not all types can be networked in S&box. See https://wiki.facepunch.com/sbox/Networked_Types for currently networkable types..
+ ///
+ internal static string NetworkableDescription {
+ get {
+ return ResourceManager.GetString("NetworkableDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The type '{0}' is not networkable.
+ ///
+ internal static string NetworkableMessageFormat {
+ get {
+ return ResourceManager.GetString("NetworkableMessageFormat", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The type is not networkable.
+ ///
+ internal static string NetworkableTitle {
+ get {
+ return ResourceManager.GetString("NetworkableTitle", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Static properties cannot be networked..
+ ///
+ internal static string StaticDescription {
+ get {
+ return ResourceManager.GetString("StaticDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Static properties cannot be networked.
+ ///
+ internal static string StaticMessageFormat {
+ get {
+ return ResourceManager.GetString("StaticMessageFormat", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Static properties cannot be networked.
+ ///
+ internal static string StaticTitle {
+ get {
+ return ResourceManager.GetString("StaticTitle", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/SboxAnalyzers/ServerCmdResources.resx b/SboxAnalyzers/ServerCmdResources.resx
new file mode 100644
index 0000000..4bcde9c
--- /dev/null
+++ b/SboxAnalyzers/ServerCmdResources.resx
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Not all types can be used in S&box server commands. See https://wiki.facepunch.com/sbox/Commands#parametertypes for currently usable types.
+ An optional longer localizable description of the diagnostic.
+
+
+ The type '{0}' is not supported for server commands
+ The format-able message the diagnostic displays.
+
+
+ The type is not supported for server commands
+ The title of the diagnostic.
+
+
\ No newline at end of file