diff --git a/.gitignore b/.gitignore index 222f5cc..2e77efc 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,8 @@ _ReSharper*/ *.DotSettings.user ## TeamCity add-in _TeamCity* + +# Other files +.deploy + +src/Debug.cs diff --git a/LICENSE.md b/LICENSE similarity index 95% rename from LICENSE.md rename to LICENSE index 02de197..4950b60 100644 --- a/LICENSE.md +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014-2018 Oxide Team and Contributors +Copyright (c) 2013-2020 Oxide and Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Oxide.CSharp.sln b/Oxide.CSharp.sln index ee887e3..eef57a2 100644 --- a/Oxide.CSharp.sln +++ b/Oxide.CSharp.sln @@ -1,8 +1,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27004.2008 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29613.14 MinimumVisualStudioVersion = 15.0 -Project("{73BB4989-CA7F-4148-8687-18760A25BC5E}") = "Oxide.CSharp", "src\Oxide.CSharp.csproj", "{9103D682-D1AA-4A95-A499-896F551AAA62}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oxide.CSharp", "src\Oxide.CSharp.csproj", "{9103D682-D1AA-4A95-A499-896F551AAA62}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,4 +15,10 @@ Global {9103D682-D1AA-4A95-A499-896F551AAA62}.Release|Any CPU.ActiveCfg = Release|Any CPU {9103D682-D1AA-4A95-A499-896F551AAA62}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9C9C46E6-F364-4D0B-8A9E-184ACD86D0BB} + EndGlobalSection EndGlobal diff --git a/netfx.props b/netfx.props new file mode 100644 index 0000000..f744186 --- /dev/null +++ b/netfx.props @@ -0,0 +1,31 @@ + + + + True + + + /Library/Frameworks/Mono.framework/Versions/Current/lib/mono + /usr/lib/mono + /usr/local/lib/mono + + + $(BaseFrameworkPathOverrideForMono)/2.0-api + $(BaseFrameworkPathOverrideForMono)/4.0-api + $(BaseFrameworkPathOverrideForMono)/4.5-api + $(BaseFrameworkPathOverrideForMono)/4.5.1-api + $(BaseFrameworkPathOverrideForMono)/4.5.2-api + $(BaseFrameworkPathOverrideForMono)/4.6-api + $(BaseFrameworkPathOverrideForMono)/4.6.1-api + $(BaseFrameworkPathOverrideForMono)/4.6.2-api + $(BaseFrameworkPathOverrideForMono)/4.7-api + $(BaseFrameworkPathOverrideForMono)/4.7.1-api + True + + + C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client + /Library/Frameworks/Mono.framework/Versions/Current/lib/mono/2.0-api + + + $(FrameworkPathOverride)/Facades;$(AssemblySearchPaths) + + diff --git a/nuget.config b/nuget.config index 63e3798..9098939 100644 --- a/nuget.config +++ b/nuget.config @@ -12,6 +12,5 @@ - diff --git a/resources/icon.png b/resources/icon.png new file mode 100644 index 0000000..f64c700 Binary files /dev/null and b/resources/icon.png differ diff --git a/src/CSharpExtension.cs b/src/CSharpExtension.cs index b757cba..86cfe1f 100644 --- a/src/CSharpExtension.cs +++ b/src/CSharpExtension.cs @@ -78,9 +78,12 @@ public CSharpExtension(ExtensionManager manager) : base(manager) Cleanup.Add(oldCompiler); } - var extDir = Interface.Oxide.ExtensionDirectory; - var configPath = Path.Combine(extDir, "Oxide.References.dll.config"); - if (File.Exists(configPath) && !(new[] { "target=\"x64", "target=\"./x64" }.Any(File.ReadAllText(configPath).Contains))) return; + string extDir = Interface.Oxide.ExtensionDirectory; + string configPath = Path.Combine(extDir, "Oxide.References.dll.config"); + if (File.Exists(configPath) && !(new[] { "target=\"x64", "target=\"./x64" }.Any(File.ReadAllText(configPath).Contains))) + { + return; + } File.WriteAllText(configPath, $"\n\n" + $"\n"); @@ -140,11 +143,14 @@ public override void OnShutdown() /// private void OnFrame(float delta) { - var args = new object[] { delta }; - foreach (var kv in loader.LoadedPlugins) + object[] args = new object[] { delta }; + foreach (System.Collections.Generic.KeyValuePair kv in loader.LoadedPlugins) { - var plugin = kv.Value as CSharpPlugin; - if (plugin != null && plugin.HookedOnFrame) plugin.CallHook("OnFrame", args); + CSharpPlugin plugin = kv.Value as CSharpPlugin; + if (plugin != null && plugin.HookedOnFrame) + { + plugin.CallHook("OnFrame", args); + } } } } diff --git a/src/CSharpPlugin.cs b/src/CSharpPlugin.cs index cb2132b..fe6293b 100644 --- a/src/CSharpPlugin.cs +++ b/src/CSharpPlugin.cs @@ -45,14 +45,25 @@ public InfoAttribute(string Title, string Author, double Version) private void SetVersion(string version) { - var versionParts = version.Split('.').Select(part => + List versionParts = version.Split('.').Select(part => { - ushort number; - if (!ushort.TryParse(part, out number)) number = 0; + if (!ushort.TryParse(part, out ushort number)) + { + number = 0; + } return number; }).ToList(); - while (versionParts.Count < 3) versionParts.Add(0); - if (versionParts.Count > 3) Interface.Oxide.LogWarning($"Version `{version}` is invalid for {Title}, should be `major.minor.patch`"); + + while (versionParts.Count < 3) + { + versionParts.Add(0); + } + + if (versionParts.Count > 3) + { + Interface.Oxide.LogWarning($"Version `{version}` is invalid for {Title}, should be `major.minor.patch`"); + } + Version = new VersionNumber(versionParts[0], versionParts[1], versionParts[2]); } } @@ -151,7 +162,7 @@ public PluginFieldInfo(Plugin plugin, FieldInfo field) public bool HasValidConstructor(params Type[] argument_types) { - var type = GenericArguments[1]; + Type type = GenericArguments[1]; return type.GetConstructor(new Type[0]) != null || type.GetConstructor(argument_types) != null; } @@ -159,21 +170,28 @@ public bool HasValidConstructor(params Type[] argument_types) public bool LookupMethod(string method_name, params Type[] argument_types) { - var method = FieldType.GetMethod(method_name, argument_types); - if (method == null) return false; + MethodInfo method = FieldType.GetMethod(method_name, argument_types); + if (method == null) + { + return false; + } + Methods[method_name] = method; return true; } public object Call(string method_name, params object[] args) { - MethodInfo method; - if (!Methods.TryGetValue(method_name, out method)) + if (!Methods.TryGetValue(method_name, out MethodInfo method)) { method = FieldType.GetMethod(method_name, BindingFlags.Instance | BindingFlags.Public); Methods[method_name] = method; } - if (method == null) throw new MissingMethodException(FieldType.Name, method_name); + if (method == null) + { + throw new MissingMethodException(FieldType.Name, method_name); + } + return method.Invoke(Value, args); } } @@ -201,24 +219,33 @@ public CSharpPlugin() { timer = new PluginTimers(this); - var type = GetType(); - foreach (var field in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)) + Type type = GetType(); + foreach (FieldInfo field in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)) { - var reference_attributes = field.GetCustomAttributes(typeof(PluginReferenceAttribute), true); + object[] reference_attributes = field.GetCustomAttributes(typeof(PluginReferenceAttribute), true); if (reference_attributes.Length > 0) { - var pluginReference = reference_attributes[0] as PluginReferenceAttribute; + PluginReferenceAttribute pluginReference = reference_attributes[0] as PluginReferenceAttribute; pluginReferenceFields[pluginReference.Name ?? field.Name] = field; } } - foreach (var method in type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)) + foreach (MethodInfo method in type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)) { - var info_attributes = method.GetCustomAttributes(typeof(HookMethodAttribute), true); - if (info_attributes.Length > 0) continue; + object[] info_attributes = method.GetCustomAttributes(typeof(HookMethodAttribute), true); + if (info_attributes.Length > 0) + { + continue; + } - if (method.Name.Equals("OnFrame")) HookedOnFrame = true; + if (method.Name.Equals("OnFrame")) + { + HookedOnFrame = true; + } // Assume all private instance methods which are not explicitly hooked could be hooks - if (method.DeclaringType.Name == type.Name) AddHookMethod(method.Name, method); + if (method.DeclaringType.Name == type.Name) + { + AddHookMethod(method.Name, method); + } } } @@ -227,10 +254,10 @@ public virtual bool SetPluginInfo(string name, string path) Name = name; Filename = path; - var infoAttributes = GetType().GetCustomAttributes(typeof(InfoAttribute), true); + object[] infoAttributes = GetType().GetCustomAttributes(typeof(InfoAttribute), true); if (infoAttributes.Length > 0) { - var info = infoAttributes[0] as InfoAttribute; + InfoAttribute info = infoAttributes[0] as InfoAttribute; Title = info.Title; Author = info.Author; Version = info.Version; @@ -242,17 +269,17 @@ public virtual bool SetPluginInfo(string name, string path) return false; } - var descriptionAttributes = GetType().GetCustomAttributes(typeof(DescriptionAttribute), true); + object[] descriptionAttributes = GetType().GetCustomAttributes(typeof(DescriptionAttribute), true); if (descriptionAttributes.Length > 0) { - var info = descriptionAttributes[0] as DescriptionAttribute; + DescriptionAttribute info = descriptionAttributes[0] as DescriptionAttribute; Description = info.Description; } - var config = GetType().GetMethod("LoadDefaultConfig", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + MethodInfo config = GetType().GetMethod("LoadDefaultConfig", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); HasConfig = config.DeclaringType != typeof(Plugin); - var messages = GetType().GetMethod("LoadDefaultMessages", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + MethodInfo messages = GetType().GetMethod("LoadDefaultMessages", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); HasMessages = messages.DeclaringType != typeof(Plugin); return true; @@ -262,9 +289,15 @@ public override void HandleAddedToManager(PluginManager manager) { base.HandleAddedToManager(manager); - if (Filename != null) Watcher.AddMapping(Name); + if (Filename != null) + { + Watcher.AddMapping(Name); + } - foreach (var name in pluginReferenceFields.Keys) pluginReferenceFields[name].SetValue(this, manager.GetPlugin(name)); + foreach (string name in pluginReferenceFields.Keys) + { + pluginReferenceFields[name].SetValue(this, manager.GetPlugin(name)); + } /*var compilable_plugin = CSharpPluginLoader.GetCompilablePlugin(Interface.Oxide.PluginDirectory, Name); if (compilable_plugin != null && compilable_plugin.CompiledAssembly != null) @@ -286,11 +319,17 @@ public override void HandleAddedToManager(PluginManager manager) public override void HandleRemovedFromManager(PluginManager manager) { - if (IsLoaded) CallHook("Unload", null); + if (IsLoaded) + { + CallHook("Unload", null); + } Watcher.RemoveMapping(Name); - foreach (var name in pluginReferenceFields.Keys) pluginReferenceFields[name].SetValue(this, null); + foreach (string name in pluginReferenceFields.Keys) + { + pluginReferenceFields[name].SetValue(this, null); + } base.HandleRemovedFromManager(manager); } @@ -308,28 +347,41 @@ protected override object InvokeMethod(HookMethod method, object[] args) { if (args != null && args.Length > 0) { - var parameters = method.Parameters; - for (var i = 0; i < args.Length; i++) + ParameterInfo[] parameters = method.Parameters; + for (int i = 0; i < args.Length; i++) { - var value = args[i]; - if (value == null) continue; - var parameter_type = parameters[i].ParameterType; - if (!parameter_type.IsValueType) continue; - var argument_type = value.GetType(); + object value = args[i]; + if (value == null) + { + continue; + } + + Type parameter_type = parameters[i].ParameterType; + if (!parameter_type.IsValueType) + { + continue; + } + + Type argument_type = value.GetType(); if (parameter_type != typeof(object) && argument_type != parameter_type) + { args[i] = Convert.ChangeType(value, parameter_type); + } } } try { - object ret; - if (DirectCallHook(method.Name, out ret, args)) return ret; + if (DirectCallHook(method.Name, out object ret, args)) + { + return ret; + } + PrintWarning("Unable to call hook directly: " + method.Name); } catch (InvalidProgramException ex) { Interface.Oxide.LogError("Hook dispatch failure detected, falling back to reflection based dispatch. " + ex); - var compilablePlugin = CSharpPluginLoader.GetCompilablePlugin(Interface.Oxide.PluginDirectory, Name); + CompilablePlugin compilablePlugin = CSharpPluginLoader.GetCompilablePlugin(Interface.Oxide.PluginDirectory, Name); if (compilablePlugin?.CompiledAssembly != null) { File.WriteAllBytes(Interface.Oxide.PluginDirectory + "\\" + Name + ".dump", compilablePlugin.CompiledAssembly.PatchedAssembly); @@ -354,15 +406,19 @@ public void SetFailState(string reason) [HookMethod("OnPluginLoaded")] private void base_OnPluginLoaded(Plugin plugin) { - FieldInfo field; - if (pluginReferenceFields.TryGetValue(plugin.Name, out field)) field.SetValue(this, plugin); + if (pluginReferenceFields.TryGetValue(plugin.Name, out FieldInfo field)) + { + field.SetValue(this, plugin); + } } [HookMethod("OnPluginUnloaded")] private void base_OnPluginUnloaded(Plugin plugin) { - FieldInfo field; - if (pluginReferenceFields.TryGetValue(plugin.Name, out field)) field.SetValue(this, null); + if (pluginReferenceFields.TryGetValue(plugin.Name, out FieldInfo field)) + { + field.SetValue(this, null); + } } /// @@ -404,10 +460,17 @@ protected void PrintError(string format, params object[] args) /// protected void LogToFile(string filename, string text, Plugin plugin, bool timeStamp = true) { - var path = Path.Combine(Interface.Oxide.LogDirectory, plugin.Name); - if (!Directory.Exists(path)) Directory.CreateDirectory(path); + string path = Path.Combine(Interface.Oxide.LogDirectory, plugin.Name); + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + filename = $"{plugin.Name.ToLower()}_{filename.ToLower()}{(timeStamp ? $"-{DateTime.Now:yyyy-MM-dd}" : "")}.txt"; - using (var writer = new StreamWriter(Path.Combine(path, Utility.CleanPath(filename)), true)) writer.WriteLine(text); + using (StreamWriter writer = new StreamWriter(Path.Combine(path, Utility.CleanPath(filename)), true)) + { + writer.WriteLine(text); + } } /// diff --git a/src/CSharpPluginLoader.cs b/src/CSharpPluginLoader.cs index 0ff4b63..7c469e7 100644 --- a/src/CSharpPluginLoader.cs +++ b/src/CSharpPluginLoader.cs @@ -19,9 +19,8 @@ public class CSharpPluginLoader : PluginLoader public static CompilablePlugin GetCompilablePlugin(string directory, string name) { - var className = Regex.Replace(name, "_", ""); - CompilablePlugin plugin; - if (!plugins.TryGetValue(className, out plugin)) + string className = Regex.Replace(name, "_", ""); + if (!plugins.TryGetValue(className, out CompilablePlugin plugin)) { plugin = new CompilablePlugin(extension, Instance, directory, name); plugins[className] = plugin; @@ -45,27 +44,44 @@ public CSharpPluginLoader(CSharpExtension extension) public void OnModLoaded() { // Include references to all loaded game extensions and any assemblies they reference - foreach (var extension in Interface.Oxide.GetAllExtensions()) + foreach (Core.Extensions.Extension extension in Interface.Oxide.GetAllExtensions()) { - if (extension == null || !extension.IsCoreExtension && !extension.IsGameExtension) continue; + if (extension == null || !extension.IsCoreExtension && !extension.IsGameExtension) + { + continue; + } - var assembly = extension.GetType().Assembly; - var assemblyName = assembly.GetName().Name; + System.Reflection.Assembly assembly = extension.GetType().Assembly; + string assemblyName = assembly.GetName().Name; - if (AssemblyBlacklist.Contains(assemblyName)) continue; + if (AssemblyBlacklist.Contains(assemblyName)) + { + continue; + } PluginReferences.Add(assemblyName); - foreach (var reference in assembly.GetReferencedAssemblies()) - if (reference != null) PluginReferences.Add(reference.Name); + foreach (System.Reflection.AssemblyName reference in assembly.GetReferencedAssemblies()) + { + if (reference != null) + { + PluginReferences.Add(reference.Name); + } + } } } public override IEnumerable ScanDirectory(string directory) { - if (PluginCompiler.BinaryPath == null) yield break; + if (PluginCompiler.BinaryPath == null) + { + yield break; + } - var enumerable = base.ScanDirectory(directory); - foreach (var file in enumerable) yield return file; + IEnumerable enumerable = base.ScanDirectory(directory); + foreach (string file in enumerable) + { + yield return file; + } } /// @@ -76,7 +92,7 @@ public override IEnumerable ScanDirectory(string directory) /// public override Plugin Load(string directory, string name) { - var compilablePlugin = GetCompilablePlugin(directory, name); + CompilablePlugin compilablePlugin = GetCompilablePlugin(directory, name); if (compilablePlugin.IsLoading) { Interface.Oxide.LogDebug($"Load requested for plugin which is already loading: {compilablePlugin.Name}"); @@ -99,9 +115,13 @@ public override void Reload(string directory, string name) if (Regex.Match(directory, @"\\include\b", RegexOptions.IgnoreCase).Success) { name = $"Oxide.{name}"; - foreach (var plugin in plugins.Values) + foreach (CompilablePlugin plugin in plugins.Values) { - if (!plugin.References.Contains(name)) continue; + if (!plugin.References.Contains(name)) + { + continue; + } + Interface.Oxide.LogInfo($"Reloading {plugin.Name} because it references updated include file: {name}"); plugin.LastModifiedAt = DateTime.Now; Load(plugin); @@ -109,7 +129,7 @@ public override void Reload(string directory, string name) return; } - var compilablePlugin = GetCompilablePlugin(directory, name); + CompilablePlugin compilablePlugin = GetCompilablePlugin(directory, name); if (compilablePlugin.IsLoading) { Interface.Oxide.LogDebug($"Reload requested for plugin which is already loading: {compilablePlugin.Name}"); @@ -126,14 +146,22 @@ public override void Reload(string directory, string name) /// public override void Unloading(Plugin pluginBase) { - var plugin = pluginBase as CSharpPlugin; - if (plugin == null) return; + CSharpPlugin plugin = pluginBase as CSharpPlugin; + if (plugin == null) + { + return; + } LoadedPlugins.Remove(plugin.Name); // Unload plugins which require this plugin first - foreach (var compilablePlugin in plugins.Values) - if (compilablePlugin.Requires.Contains(plugin.Name)) Interface.Oxide.UnloadPlugin(compilablePlugin.Name); + foreach (CompilablePlugin compilablePlugin in plugins.Values) + { + if (compilablePlugin.Requires.Contains(plugin.Name)) + { + Interface.Oxide.UnloadPlugin(compilablePlugin.Name); + } + } } public void Load(CompilablePlugin plugin) @@ -146,13 +174,16 @@ public void Load(CompilablePlugin plugin) return; } - var loadedLoadingRequirements = plugin.Requires.Where(r => LoadedPlugins.ContainsKey(r) && LoadingPlugins.Contains(r)); - foreach (var loadedPlugin in loadedLoadingRequirements) Interface.Oxide.UnloadPlugin(loadedPlugin); + IEnumerable loadedLoadingRequirements = plugin.Requires.Where(r => LoadedPlugins.ContainsKey(r) && LoadingPlugins.Contains(r)); + foreach (string loadedPlugin in loadedLoadingRequirements) + { + Interface.Oxide.UnloadPlugin(loadedPlugin); + } - var missingRequirements = plugin.Requires.Where(r => !LoadedPlugins.ContainsKey(r)); + IEnumerable missingRequirements = plugin.Requires.Where(r => !LoadedPlugins.ContainsKey(r)); if (missingRequirements.Any()) { - var loadingRequirements = plugin.Requires.Where(r => LoadingPlugins.Contains(r)); + IEnumerable loadingRequirements = plugin.Requires.Where(r => LoadingPlugins.Contains(r)); if (loadingRequirements.Any()) { Interface.Oxide.LogDebug($"{plugin.Name} plugin is waiting for requirements to be loaded: {loadingRequirements.ToSentence()}"); @@ -169,7 +200,11 @@ public void Load(CompilablePlugin plugin) Interface.Oxide.UnloadPlugin(plugin.Name); plugin.LoadPlugin(pl => { - if (pl != null) LoadedPlugins[pl.Name] = pl; + if (pl != null) + { + LoadedPlugins[pl.Name] = pl; + } + PluginLoadingCompleted(plugin); }); } @@ -210,11 +245,13 @@ private void PluginLoadingCompleted(CompilablePlugin plugin) { LoadingPlugins.Remove(plugin.Name); plugin.IsLoading = false; - foreach (var loadingName in LoadingPlugins.ToArray()) + foreach (string loadingName in LoadingPlugins.ToArray()) { - var loadingPlugin = GetCompilablePlugin(plugin.Directory, loadingName); + CompilablePlugin loadingPlugin = GetCompilablePlugin(plugin.Directory, loadingName); if (loadingPlugin.IsLoading && loadingPlugin.Requires.Contains(plugin.Name)) + { Load(loadingPlugin); + } } } @@ -224,7 +261,7 @@ private void CompileAssembly(CompilablePlugin[] plugins) { if (compilation.compiledAssembly == null) { - foreach (var plugin in compilation.plugins) + foreach (CompilablePlugin plugin in compilation.plugins) { plugin.OnCompilationFailed(); PluginErrors[plugin.Name] = $"Failed to compile: {plugin.CompilerErrors}"; @@ -236,12 +273,12 @@ private void CompileAssembly(CompilablePlugin[] plugins) { if (compilation.plugins.Count > 0) { - var compiledNames = compilation.plugins.Where(pl => string.IsNullOrEmpty(pl.CompilerErrors)).Select(pl => pl.Name).ToArray(); - var verb = compiledNames.Length > 1 ? "were" : "was"; + string[] compiledNames = compilation.plugins.Where(pl => string.IsNullOrEmpty(pl.CompilerErrors)).Select(pl => pl.Name).ToArray(); + string verb = compiledNames.Length > 1 ? "were" : "was"; Interface.Oxide.LogInfo($"{compiledNames.ToSentence()} {verb} compiled successfully in {Math.Round(compilation.duration * 1000f)}ms"); } - foreach (var plugin in compilation.plugins) + foreach (CompilablePlugin plugin in compilation.plugins) { if (plugin.CompilerErrors == null) { diff --git a/src/CompilableFile.cs b/src/CompilableFile.cs index abb935b..cf0eaa3 100644 --- a/src/CompilableFile.cs +++ b/src/CompilableFile.cs @@ -56,7 +56,7 @@ internal void Compile(Action callback) { if (CompilationQueuedAt > 0f) { - var ago = Interface.Oxide.Now - CompilationQueuedAt; + float ago = Interface.Oxide.Now - CompilationQueuedAt; Interface.Oxide.LogDebug($"Plugin compilation is already queued: {ScriptName} ({ago:0.000} ago)"); //RemoteLogger.Debug($"Plugin compilation is already queued: {ScriptName} ({ago:0.000} ago)"); return; @@ -130,7 +130,7 @@ internal void OnCompilationTimeout() internal bool HasBeenModified() { - var lastModifiedAt = LastModifiedAt; + DateTime lastModifiedAt = LastModifiedAt; CheckLastModificationTime(); return LastModifiedAt != lastModifiedAt; } @@ -142,8 +142,11 @@ internal void CheckLastModificationTime() LastModifiedAt = default(DateTime); return; } - var modifiedTime = GetLastModificationTime(); - if (modifiedTime != default(DateTime)) LastModifiedAt = modifiedTime; + DateTime modifiedTime = GetLastModificationTime(); + if (modifiedTime != default(DateTime)) + { + LastModifiedAt = modifiedTime; + } } internal DateTime GetLastModificationTime() @@ -169,7 +172,11 @@ protected virtual void OnCompilationRequested() protected virtual void InitFailed(string message = null) { - if (message != null) Interface.Oxide.LogError(message); + if (message != null) + { + Interface.Oxide.LogError(message); + } + LoadCallback?.Invoke(null); } } diff --git a/src/CompilablePlugin.cs b/src/CompilablePlugin.cs index e0a3795..3e1dc70 100644 --- a/src/CompilablePlugin.cs +++ b/src/CompilablePlugin.cs @@ -44,7 +44,7 @@ internal void LoadPlugin(Action callback = null) return; } - var type = CompiledAssembly.LoadedAssembly.GetType($"Oxide.Plugins.{Name}"); + Type type = CompiledAssembly.LoadedAssembly.GetType($"Oxide.Plugins.{Name}"); if (type == null) { InitFailed($"Unable to find main plugin class: {Name}"); @@ -63,7 +63,7 @@ internal void LoadPlugin(Action callback = null) } catch (TargetInvocationException invocationException) { - var ex = invocationException.InnerException; + Exception ex = invocationException.InnerException; InitFailed($"Unable to load {ScriptName}. {ex.ToString()}"); return; } @@ -95,7 +95,11 @@ internal void LoadPlugin(Action callback = null) return; } - if (!CompiledAssembly.IsBatch) LastGoodAssembly = CompiledAssembly; + if (!CompiledAssembly.IsBatch) + { + LastGoodAssembly = CompiledAssembly; + } + callback?.Invoke(plugin); }); } @@ -105,11 +109,19 @@ internal override void OnCompilationStarted() base.OnCompilationStarted(); // Enqueue compilation of any plugins which depend on this plugin - foreach (var plugin in Interface.Oxide.RootPluginManager.GetPlugins()) + foreach (Core.Plugins.Plugin plugin in Interface.Oxide.RootPluginManager.GetPlugins()) { - if (!(plugin is CSharpPlugin)) continue; - var compilablePlugin = CSharpPluginLoader.GetCompilablePlugin(Directory, plugin.Name); - if (!compilablePlugin.Requires.Contains(Name)) continue; + if (!(plugin is CSharpPlugin)) + { + continue; + } + + CompilablePlugin compilablePlugin = CSharpPluginLoader.GetCompilablePlugin(Directory, plugin.Name); + if (!compilablePlugin.Requires.Contains(Name)) + { + continue; + } + compilablePlugin.CompiledAssembly = null; Loader.Load(compilablePlugin); } diff --git a/src/Compilation.cs b/src/Compilation.cs index 938d4ff..a9ea977 100644 --- a/src/Compilation.cs +++ b/src/Compilation.cs @@ -38,9 +38,12 @@ internal Compilation(int id, Action callback, CompilablePlugin[] pl this.callback = callback; this.queuedPlugins = new ConcurrentHashSet(plugins); - if (Current == null) Current = this; + if (Current == null) + { + Current = this; + } - foreach (var plugin in plugins) + foreach (CompilablePlugin plugin in plugins) { plugin.CompilerErrors = null; plugin.OnCompilationStarted(); @@ -48,7 +51,7 @@ internal Compilation(int id, Action callback, CompilablePlugin[] pl includePath = Path.Combine(Interface.Oxide.PluginDirectory, "include"); extensionNames = Interface.Oxide.GetAllExtensions().Select(ext => ext.Name).ToArray(); - var gameExtension = Interface.Oxide.GetAllExtensions().SingleOrDefault(ext => ext.IsGameExtension); + Core.Extensions.Extension gameExtension = Interface.Oxide.GetAllExtensions().SingleOrDefault(ext => ext.IsGameExtension); gameExtensionName = gameExtension?.Name.ToUpper(); gameExtensionNamespace = gameExtension?.GetType().Namespace; gameExtensionBranch = gameExtension?.Branch?.ToUpper(); @@ -64,22 +67,31 @@ internal void Completed(byte[] rawAssembly = null) { endedAt = Interface.Oxide.Now; if (plugins.Count > 0 && rawAssembly != null) + { compiledAssembly = new CompiledAssembly(name, plugins.ToArray(), rawAssembly, duration); + } + Interface.Oxide.NextTick(() => callback(this)); } internal void Add(CompilablePlugin plugin) { - if (!queuedPlugins.Add(plugin)) return; + if (!queuedPlugins.Add(plugin)) + { + return; + } plugin.Loader.PluginLoadingStarted(plugin); plugin.CompilerErrors = null; plugin.OnCompilationStarted(); - foreach (var pl in Interface.Oxide.RootPluginManager.GetPlugins().Where(pl => pl is CSharpPlugin)) + foreach (Core.Plugins.Plugin pl in Interface.Oxide.RootPluginManager.GetPlugins().Where(pl => pl is CSharpPlugin)) { - var loadedPlugin = CSharpPluginLoader.GetCompilablePlugin(plugin.Directory, pl.Name); - if (!loadedPlugin.Requires.Contains(plugin.Name)) continue; + CompilablePlugin loadedPlugin = CSharpPluginLoader.GetCompilablePlugin(plugin.Directory, pl.Name); + if (!loadedPlugin.Requires.Contains(plugin.Name)) + { + continue; + } AddDependency(loadedPlugin); } @@ -87,9 +99,12 @@ internal void Add(CompilablePlugin plugin) internal bool IncludesRequiredPlugin(string name) { - if (referencedPlugins.Contains(name)) return true; + if (referencedPlugins.Contains(name)) + { + return true; + } - var compilablePlugin = plugins.SingleOrDefault(pl => pl.Name == name); + CompilablePlugin compilablePlugin = plugins.SingleOrDefault(pl => pl.Name == name); return compilablePlugin != null && compilablePlugin.CompilerErrors == null; } @@ -103,20 +118,27 @@ internal void Prepare(Action callback) references.Clear(); // Include references made by the CSharpPlugins project - foreach (var filename in CSharpPluginLoader.PluginReferences) + foreach (string filename in CSharpPluginLoader.PluginReferences) { if (File.Exists(Path.Combine(Interface.Oxide.ExtensionDirectory, filename + ".dll"))) + { references[filename + ".dll"] = new CompilerFile(Interface.Oxide.ExtensionDirectory, filename + ".dll"); + } + if (File.Exists(Path.Combine(Interface.Oxide.ExtensionDirectory, filename + ".exe"))) + { references[filename + ".exe"] = new CompilerFile(Interface.Oxide.ExtensionDirectory, filename + ".exe"); + } } //Interface.Oxide.LogDebug("Preparing compilation"); - CompilablePlugin plugin; - while (queuedPlugins.TryDequeue(out plugin)) + while (queuedPlugins.TryDequeue(out CompilablePlugin plugin)) { - if (Current == null) Current = this; + if (Current == null) + { + Current = this; + } if (!CacheScriptLines(plugin) || plugin.ScriptLines.Length < 1) { @@ -165,28 +187,40 @@ private void PreparseScript(CompilablePlugin plugin) plugin.IncludePaths.Clear(); plugin.Requires.Clear(); - var parsingNamespace = false; - for (var i = 0; i < plugin.ScriptLines.Length; i++) + bool parsingNamespace = false; + for (int i = 0; i < plugin.ScriptLines.Length; i++) { - var line = plugin.ScriptLines[i].Trim(); - if (line.Length < 1) continue; + string line = plugin.ScriptLines[i].Trim(); + if (line.Length < 1) + { + continue; + } Match match; if (parsingNamespace) { // Skip blank lines and opening brace at the top of the namespace block match = Regex.Match(line, @"^\s*\{?\s*$", RegexOptions.IgnoreCase); - if (match.Success) continue; + if (match.Success) + { + continue; + } // Skip class custom attributes match = Regex.Match(line, @"^\s*\[", RegexOptions.IgnoreCase); - if (match.Success) continue; + if (match.Success) + { + continue; + } // Detect main plugin class name match = Regex.Match(line, @"^\s*(?:public|private|protected|internal)?\s*class\s+(\S+)\s+\:\s+\S+Plugin\s*$", RegexOptions.IgnoreCase); - if (!match.Success) break; + if (!match.Success) + { + break; + } - var className = match.Groups[1].Value; + string className = match.Groups[1].Value; if (className != plugin.Name) { Interface.Oxide.LogError($"Plugin filename {plugin.ScriptName}.cs must match the main class {className} (should be {className}.cs)"); @@ -201,7 +235,7 @@ private void PreparseScript(CompilablePlugin plugin) match = Regex.Match(line, @"^//\s*Requires:\s*(\S+?)(\.cs)?\s*$", RegexOptions.IgnoreCase); if (match.Success) { - var dependencyName = match.Groups[1].Value; + string dependencyName = match.Groups[1].Value; plugin.Requires.Add(dependencyName); if (!File.Exists(Path.Combine(plugin.Directory, dependencyName + ".cs"))) { @@ -212,7 +246,7 @@ private void PreparseScript(CompilablePlugin plugin) } //Interface.Oxide.LogDebug(plugin.Name + " plugin requires dependency: " + dependency_name); - var dependencyPlugin = CSharpPluginLoader.GetCompilablePlugin(plugin.Directory, dependencyName); + CompilablePlugin dependencyPlugin = CSharpPluginLoader.GetCompilablePlugin(plugin.Directory, dependencyName); AddDependency(dependencyPlugin); continue; } @@ -221,14 +255,17 @@ private void PreparseScript(CompilablePlugin plugin) match = Regex.Match(line, @"^//\s*Reference:\s*(\S+)\s*$", RegexOptions.IgnoreCase); if (match.Success) { - var result = match.Groups[1].Value; + string result = match.Groups[1].Value; if (!result.StartsWith("Oxide.") && !result.StartsWith("Newtonsoft.Json") && !result.StartsWith("protobuf-net") && !result.StartsWith("Rust.")) { AddReference(plugin, result); Interface.Oxide.LogInfo("Added '// Reference: {0}' in plugin '{1}'", result, plugin.Name); } else + { Interface.Oxide.LogWarning("Ignored unnecessary '// Reference: {0}' in plugin '{1}'", result, plugin.Name); + } + continue; } @@ -236,35 +273,49 @@ private void PreparseScript(CompilablePlugin plugin) match = Regex.Match(line, @"^\s*using\s+(Oxide\.(?:Core|Ext|Game)\.(?:[^\.]+))[^;]*;.*$", RegexOptions.IgnoreCase); if (match.Success) { - var result = match.Groups[1].Value; - var newResult = Regex.Replace(result, @"Oxide\.[\w]+\.([\w]+)", "Oxide.$1"); + string result = match.Groups[1].Value; + string newResult = Regex.Replace(result, @"Oxide\.[\w]+\.([\w]+)", "Oxide.$1"); if (!string.IsNullOrEmpty(newResult) && File.Exists(Path.Combine(Interface.Oxide.ExtensionDirectory, newResult + ".dll"))) + { AddReference(plugin, newResult); + } else + { AddReference(plugin, result); + } + continue; } // Start parsing the Oxide.Plugins namespace contents match = Regex.Match(line, @"^\s*namespace Oxide\.Plugins\s*(\{\s*)?$", RegexOptions.IgnoreCase); - if (match.Success) parsingNamespace = true; + if (match.Success) + { + parsingNamespace = true; + } } } private void ResolveReferences(CompilablePlugin plugin) { - foreach (var reference in plugin.References) + foreach (string reference in plugin.References) { - var match = Regex.Match(reference, @"^(Oxide\.(?:Ext|Game)\.(.+))$", RegexOptions.IgnoreCase); - if (!match.Success) continue; + Match match = Regex.Match(reference, @"^(Oxide\.(?:Ext|Game)\.(.+))$", RegexOptions.IgnoreCase); + if (!match.Success) + { + continue; + } - var fullName = match.Groups[1].Value; - var name = match.Groups[2].Value; - if (extensionNames.Contains(name)) continue; + string fullName = match.Groups[1].Value; + string name = match.Groups[2].Value; + if (extensionNames.Contains(name)) + { + continue; + } if (Directory.Exists(includePath)) { - var includeFilePath = Path.Combine(includePath, $"Ext.{name}.cs"); + string includeFilePath = Path.Combine(includePath, $"Ext.{name}.cs"); if (File.Exists(includeFilePath)) { plugin.IncludePaths.Add(includeFilePath); @@ -272,7 +323,7 @@ private void ResolveReferences(CompilablePlugin plugin) } } - var message = $"{fullName} is referenced by {plugin.Name} plugin but is not loaded! An appropriate include file needs to be saved to plugins\\include\\Ext.{name}.cs if this extension is not required."; + string message = $"{fullName} is referenced by {plugin.Name} plugin but is not loaded! An appropriate include file needs to be saved to plugins\\include\\Ext.{name}.cs if this extension is not required."; Interface.Oxide.LogError(message); plugin.CompilerErrors = message; RemovePlugin(plugin); @@ -281,15 +332,20 @@ private void ResolveReferences(CompilablePlugin plugin) private void AddDependency(CompilablePlugin plugin) { - if (plugin.IsLoading || plugins.Contains(plugin) || queuedPlugins.Contains(plugin)) return; + if (plugin.IsLoading || plugins.Contains(plugin) || queuedPlugins.Contains(plugin)) + { + return; + } - var compiledDependency = plugin.CompiledAssembly; + CompiledAssembly compiledDependency = plugin.CompiledAssembly; if (compiledDependency != null && !compiledDependency.IsOutdated()) { // The dependency already has a compiled assembly which is up to date referencedPlugins.Add(plugin.Name); if (!references.ContainsKey(compiledDependency.Name)) + { references[compiledDependency.Name] = new CompilerFile(compiledDependency.Name, compiledDependency.RawAssembly); + } } else { @@ -300,7 +356,7 @@ private void AddDependency(CompilablePlugin plugin) private void AddReference(CompilablePlugin plugin, string assemblyName) { - var path = Path.Combine(Interface.Oxide.ExtensionDirectory, assemblyName + ".dll"); + string path = Path.Combine(Interface.Oxide.ExtensionDirectory, assemblyName + ".dll"); if (!File.Exists(path)) { if (assemblyName.StartsWith("Oxide.")) @@ -331,12 +387,15 @@ private void AddReference(CompilablePlugin plugin, string assemblyName) AddReference(plugin, assembly.GetName()); // Include references made by the referenced assembly - foreach (var reference in assembly.GetReferencedAssemblies()) + foreach (AssemblyName reference in assembly.GetReferencedAssemblies()) { // TODO: Fix Oxide.References to avoid these and other dependency conflicts - if (reference.Name.StartsWith("Newtonsoft.Json") || reference.Name.StartsWith("Rust.Workshop")) continue; + if (reference.Name.StartsWith("Newtonsoft.Json") || reference.Name.StartsWith("Rust.Workshop")) + { + continue; + } - var referencePath = Path.Combine(Interface.Oxide.ExtensionDirectory, reference.Name + ".dll"); + string referencePath = Path.Combine(Interface.Oxide.ExtensionDirectory, reference.Name + ".dll"); if (!File.Exists(referencePath)) { Interface.Oxide.LogWarning($"Reference {reference.Name}.dll from {assembly.GetName().Name}.dll not found"); @@ -349,14 +408,18 @@ private void AddReference(CompilablePlugin plugin, string assemblyName) private void AddReference(CompilablePlugin plugin, AssemblyName reference) { - var filename = reference.Name + ".dll"; - if (!references.ContainsKey(filename)) references[filename] = new CompilerFile(Interface.Oxide.ExtensionDirectory, filename); + string filename = reference.Name + ".dll"; + if (!references.ContainsKey(filename)) + { + references[filename] = new CompilerFile(Interface.Oxide.ExtensionDirectory, filename); + } + plugin.References.Add(reference.Name); } private bool CacheScriptLines(CompilablePlugin plugin) { - var waitingForAccess = false; + bool waitingForAccess = false; while (true) { try @@ -372,11 +435,18 @@ private bool CacheScriptLines(CompilablePlugin plugin) plugin.CheckLastModificationTime(); if (plugin.LastCachedScriptAt != plugin.LastModifiedAt) { - using (var reader = File.OpenText(plugin.ScriptPath)) + using (StreamReader reader = File.OpenText(plugin.ScriptPath)) { - var lines = new List(); - while (!reader.EndOfStream) lines.Add(reader.ReadLine()); - if (!string.IsNullOrEmpty(gameExtensionName)) lines.Insert(0, $"#define {gameExtensionName}"); + List lines = new List(); + while (!reader.EndOfStream) + { + lines.Add(reader.ReadLine()); + } + + if (!string.IsNullOrEmpty(gameExtensionName)) + { + lines.Insert(0, $"#define {gameExtensionName}"); + } if (!string.IsNullOrEmpty(gameExtensionName)) { @@ -393,7 +463,9 @@ private bool CacheScriptLines(CompilablePlugin plugin) } plugin.LastCachedScriptAt = plugin.LastModifiedAt; if (plugins.Remove(plugin)) + { queuedPlugins.Add(plugin); + } } return true; } @@ -411,25 +483,40 @@ private bool CacheScriptLines(CompilablePlugin plugin) private void CacheModifiedScripts() { - var modifiedPlugins = plugins.Where(pl => pl.ScriptLines == null || pl.HasBeenModified() || pl.LastCachedScriptAt != pl.LastModifiedAt).ToArray(); - if (modifiedPlugins.Length < 1) return; + CompilablePlugin[] modifiedPlugins = plugins.Where(pl => pl.ScriptLines == null || pl.HasBeenModified() || pl.LastCachedScriptAt != pl.LastModifiedAt).ToArray(); + if (modifiedPlugins.Length < 1) + { + return; + } + + foreach (CompilablePlugin plugin in modifiedPlugins) + { + CacheScriptLines(plugin); + } - foreach (var plugin in modifiedPlugins) CacheScriptLines(plugin); Thread.Sleep(100); CacheModifiedScripts(); } private void RemovePlugin(CompilablePlugin plugin) { - if (plugin.LastCompiledAt == default(DateTime)) return; + if (plugin.LastCompiledAt == default(DateTime)) + { + return; + } queuedPlugins.Remove(plugin); plugins.Remove(plugin); plugin.OnCompilationFailed(); // Remove plugins which are required by this plugin if they are only being compiled for this requirement - foreach (var requiredPlugin in plugins.Where(pl => !pl.IsCompilationNeeded && plugin.Requires.Contains(pl.Name)).ToArray()) - if (!plugins.Any(pl => pl.Requires.Contains(requiredPlugin.Name))) RemovePlugin(requiredPlugin); + foreach (CompilablePlugin requiredPlugin in plugins.Where(pl => !pl.IsCompilationNeeded && plugin.Requires.Contains(pl.Name)).ToArray()) + { + if (!plugins.Any(pl => pl.Requires.Contains(requiredPlugin.Name))) + { + RemovePlugin(requiredPlugin); + } + } } } } diff --git a/src/CompiledAssembly.cs b/src/CompiledAssembly.cs index bd950b5..b369dff 100644 --- a/src/CompiledAssembly.cs +++ b/src/CompiledAssembly.cs @@ -64,13 +64,20 @@ public void LoadAssembly(Action callback) IsLoading = true; loadCallbacks.Add(callback); - if (isPatching) return; + if (isPatching) + { + return; + } PatchAssembly(rawAssembly => { if (rawAssembly == null) { - foreach (var cb in loadCallbacks) cb(true); + foreach (Action cb in loadCallbacks) + { + cb(true); + } + loadCallbacks.Clear(); IsLoading = false; return; @@ -79,7 +86,11 @@ public void LoadAssembly(Action callback) LoadedAssembly = Assembly.Load(rawAssembly); isLoaded = true; - foreach (var cb in loadCallbacks) cb(true); + foreach (Action cb in loadCallbacks) + { + cb(true); + } + loadCallbacks.Clear(); IsLoading = false; @@ -95,7 +106,7 @@ private void PatchAssembly(Action callback) return; } - var startedAt = Interface.Oxide.Now; + float startedAt = Interface.Oxide.Now; isPatching = true; ThreadPool.QueueUserWorkItem(_ => @@ -103,25 +114,27 @@ private void PatchAssembly(Action callback) try { AssemblyDefinition definition; - using (var stream = new MemoryStream(RawAssembly)) + using (MemoryStream stream = new MemoryStream(RawAssembly)) + { definition = AssemblyDefinition.ReadAssembly(stream); + } - var exceptionConstructor = typeof(UnauthorizedAccessException).GetConstructor(new[] { typeof(string) }); - var securityException = definition.MainModule.Import(exceptionConstructor); + ConstructorInfo exceptionConstructor = typeof(UnauthorizedAccessException).GetConstructor(new[] { typeof(string) }); + MethodReference securityException = definition.MainModule.Import(exceptionConstructor); Action patchModuleType = null; patchModuleType = type => { - foreach (var method in type.Methods) + foreach (MethodDefinition method in type.Methods) { - var changedMethod = false; + bool changedMethod = false; if (method.Body == null) { if (method.HasPInvokeInfo) { method.Attributes &= ~MethodAttributes.PInvokeImpl; - var body = new MethodBody(method); + MethodBody body = new MethodBody(method); body.Instructions.Add(Instruction.Create(OpCodes.Ldstr, "PInvoke access is restricted, you are not allowed to use PInvoke")); body.Instructions.Add(Instruction.Create(OpCodes.Newobj, securityException)); body.Instructions.Add(Instruction.Create(OpCodes.Throw)); @@ -130,12 +143,15 @@ private void PatchAssembly(Action callback) } else { - var replacedMethod = false; - foreach (var variable in method.Body.Variables) + bool replacedMethod = false; + foreach (VariableDefinition variable in method.Body.Variables) { - if (!IsNamespaceBlacklisted(variable.VariableType.FullName)) continue; + if (!IsNamespaceBlacklisted(variable.VariableType.FullName)) + { + continue; + } - var body = new MethodBody(method); + MethodBody body = new MethodBody(method); body.Instructions.Add(Instruction.Create(OpCodes.Ldstr, $"System access is restricted, you are not allowed to use {variable.VariableType.FullName}")); body.Instructions.Add(Instruction.Create(OpCodes.Newobj, securityException)); body.Instructions.Add(Instruction.Create(OpCodes.Throw)); @@ -143,21 +159,28 @@ private void PatchAssembly(Action callback) replacedMethod = true; break; } - if (replacedMethod) continue; + if (replacedMethod) + { + continue; + } - var instructions = method.Body.Instructions; - var ilProcessor = method.Body.GetILProcessor(); - var first = instructions.First(); + References::Mono.Collections.Generic.Collection instructions = method.Body.Instructions; + ILProcessor ilProcessor = method.Body.GetILProcessor(); + Instruction first = instructions.First(); - var i = 0; + int i = 0; while (i < instructions.Count) { - if (changedMethod) break; - var instruction = instructions[i]; + if (changedMethod) + { + break; + } + + Instruction instruction = instructions[i]; if (instruction.OpCode == OpCodes.Ldtoken) { - var operand = instruction.Operand as IMetadataTokenProvider; - var token = operand?.ToString(); + IMetadataTokenProvider operand = instruction.Operand as IMetadataTokenProvider; + string token = operand?.ToString(); if (IsNamespaceBlacklisted(token)) { ilProcessor.InsertBefore(first, Instruction.Create(OpCodes.Ldstr, $"System access is restricted, you are not allowed to use {token}")); @@ -168,8 +191,8 @@ private void PatchAssembly(Action callback) } else if (instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Calli || instruction.OpCode == OpCodes.Callvirt || instruction.OpCode == OpCodes.Ldftn) { - var methodCall = instruction.Operand as MethodReference; - var fullNamespace = methodCall?.DeclaringType.FullName; + MethodReference methodCall = instruction.Operand as MethodReference; + string fullNamespace = methodCall?.DeclaringType.FullName; if ((fullNamespace == "System.Type" && methodCall.Name == "GetType") || IsNamespaceBlacklisted(fullNamespace)) { @@ -181,8 +204,8 @@ private void PatchAssembly(Action callback) } else if (instruction.OpCode == OpCodes.Ldfld) { - var fieldType = instruction.Operand as FieldReference; - var fullNamespace = fieldType?.FieldType.FullName; + FieldReference fieldType = instruction.Operand as FieldReference; + string fullNamespace = fieldType?.FieldType.FullName; if (IsNamespaceBlacklisted(fullNamespace)) { ilProcessor.InsertBefore(first, Instruction.Create(OpCodes.Ldstr, $"System access is restricted, you are not allowed to use {fullNamespace}")); @@ -211,28 +234,35 @@ private void PatchAssembly(Action callback) }*/ } } - foreach (var nestedType in type.NestedTypes) + foreach (TypeDefinition nestedType in type.NestedTypes) + { patchModuleType(nestedType); + } }; - foreach (var type in definition.MainModule.Types) + foreach (TypeDefinition type in definition.MainModule.Types) { patchModuleType(type); - if (IsCompilerGenerated(type)) continue; + if (IsCompilerGenerated(type)) + { + continue; + } if (type.Namespace == "Oxide.Plugins") { if (PluginNames.Contains(type.Name)) { - var constructor = + MethodDefinition constructor = type.Methods.FirstOrDefault( m => !m.IsStatic && m.IsConstructor && !m.HasParameters && !m.IsPublic); if (constructor != null) { - var plugin = CompilablePlugins.SingleOrDefault(p => p.Name == type.Name); + CompilablePlugin plugin = CompilablePlugins.SingleOrDefault(p => p.Name == type.Name); if (plugin != null) + { plugin.CompilerErrors = "Primary constructor in main class must be public"; + } } else { @@ -250,21 +280,27 @@ private void PatchAssembly(Action callback) else if (type.FullName != "") { if (!PluginNames.Any(plugin => type.FullName.StartsWith($"Oxide.Plugins.{plugin}"))) + { Interface.Oxide.LogWarning(PluginNames.Length == 1 ? $"{PluginNames[0]} has polluted the global namespace by defining {type.FullName}" : $"A plugin has polluted the global namespace by defining {type.FullName}"); + } } } // TODO: Why is there no error on boot using this? - foreach (var type in definition.MainModule.Types) + foreach (TypeDefinition type in definition.MainModule.Types) { - if (type.Namespace != "Oxide.Plugins" || !PluginNames.Contains(type.Name)) continue; - foreach (var m in type.Methods.Where(m => !m.IsStatic && !m.HasGenericParameters && !m.ReturnType.IsGenericParameter && !m.IsSetter && !m.IsGetter)) + if (type.Namespace != "Oxide.Plugins" || !PluginNames.Contains(type.Name)) { - foreach (var parameter in m.Parameters) + continue; + } + + foreach (MethodDefinition m in type.Methods.Where(m => !m.IsStatic && !m.HasGenericParameters && !m.ReturnType.IsGenericParameter && !m.IsSetter && !m.IsGetter)) + { + foreach (ParameterDefinition parameter in m.Parameters) { - foreach (var attribute in parameter.CustomAttributes) + foreach (CustomAttribute attribute in parameter.CustomAttributes) { //Interface.Oxide.LogInfo($"{m.FullName} - {parameter.Name} - {attribute.Constructor.FullName}"); } @@ -272,7 +308,7 @@ private void PatchAssembly(Action callback) } } - using (var stream = new MemoryStream()) + using (MemoryStream stream = new MemoryStream()) { definition.Write(stream); PatchedAssembly = stream.ToArray(); @@ -304,10 +340,18 @@ private void PatchAssembly(Action callback) private static bool IsNamespaceBlacklisted(string fullNamespace) { - foreach (var namespaceName in BlacklistedNamespaces) + foreach (string namespaceName in BlacklistedNamespaces) { - if (!fullNamespace.StartsWith(namespaceName)) continue; - if (WhitelistedNamespaces.Any(fullNamespace.StartsWith)) continue; + if (!fullNamespace.StartsWith(namespaceName)) + { + continue; + } + + if (WhitelistedNamespaces.Any(fullNamespace.StartsWith)) + { + continue; + } + return true; } return false; diff --git a/src/Covalence/Plugin.cs b/src/Covalence/Plugin.cs index a5d1ac9..224152f 100644 --- a/src/Covalence/Plugin.cs +++ b/src/Covalence/Plugin.cs @@ -78,15 +78,22 @@ protected void LogError(string format, params object[] args) /// public override void HandleAddedToManager(PluginManager manager) { - foreach (var method in GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)) + foreach (MethodInfo method in GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)) { - var commandAttribute = method.GetCustomAttributes(typeof(CommandAttribute), true); - var permissionAttribute = method.GetCustomAttributes(typeof(PermissionAttribute), true); - if (commandAttribute.Length <= 0) continue; + object[] commandAttribute = method.GetCustomAttributes(typeof(CommandAttribute), true); + object[] permissionAttribute = method.GetCustomAttributes(typeof(PermissionAttribute), true); + if (commandAttribute.Length <= 0) + { + continue; + } + + CommandAttribute cmd = commandAttribute[0] as CommandAttribute; + PermissionAttribute perm = permissionAttribute.Length <= 0 ? null : permissionAttribute[0] as PermissionAttribute; + if (cmd == null) + { + continue; + } - var cmd = commandAttribute[0] as CommandAttribute; - var perm = permissionAttribute.Length <= 0 ? null : permissionAttribute[0] as PermissionAttribute; - if (cmd == null) continue; AddCovalenceCommand(cmd.Commands, perm?.Permission, (caller, command, args) => { CallHook(method.Name, caller, command, args); diff --git a/src/DirectCallMethod.cs b/src/DirectCallMethod.cs index 6e031fc..be8b6db 100644 --- a/src/DirectCallMethod.cs +++ b/src/DirectCallMethod.cs @@ -51,29 +51,34 @@ public DirectCallMethod(ModuleDefinition module, TypeDefinition type) stringEquals = module.Import(typeof(string).GetMethod("Equals", new[] { typeof(string) })); // Copy method definition from base class - var base_assembly = AssemblyDefinition.ReadAssembly(Path.Combine(Interface.Oxide.ExtensionDirectory, "Oxide.CSharp.dll")); - var base_module = base_assembly.MainModule; - var base_type = module.Import(base_assembly.MainModule.GetType("Oxide.Plugins.CSharpPlugin")).Resolve(); - var base_method = module.Import(base_type.Methods.First(method => method.Name == "DirectCallHook")).Resolve(); + AssemblyDefinition base_assembly = AssemblyDefinition.ReadAssembly(Path.Combine(Interface.Oxide.ExtensionDirectory, "Oxide.CSharp.dll")); + ModuleDefinition base_module = base_assembly.MainModule; + TypeDefinition base_type = module.Import(base_assembly.MainModule.GetType("Oxide.Plugins.CSharpPlugin")).Resolve(); + MethodDefinition base_method = module.Import(base_type.Methods.First(method => method.Name == "DirectCallHook")).Resolve(); // Create method override based on virtual method signature method = new MethodDefinition(base_method.Name, base_method.Attributes, base_module.Import(base_method.ReturnType)) { DeclaringType = type }; - foreach (var parameter in base_method.Parameters) + foreach (ParameterDefinition parameter in base_method.Parameters) { - var new_param = new ParameterDefinition(parameter.Name, parameter.Attributes, base_module.Import(parameter.ParameterType)) + ParameterDefinition new_param = new ParameterDefinition(parameter.Name, parameter.Attributes, base_module.Import(parameter.ParameterType)) { IsOut = parameter.IsOut, Constant = parameter.Constant, MarshalInfo = parameter.MarshalInfo, IsReturnValue = parameter.IsReturnValue }; - foreach (var attribute in parameter.CustomAttributes) + foreach (CustomAttribute attribute in parameter.CustomAttributes) + { new_param.CustomAttributes.Add(new CustomAttribute(module.Import(attribute.Constructor))); + } + method.Parameters.Add(new_param); } - foreach (var attribute in base_method.CustomAttributes) + foreach (CustomAttribute attribute in base_method.CustomAttributes) + { method.CustomAttributes.Add(new CustomAttribute(module.Import(attribute.Constructor))); + } method.ImplAttributes = base_method.ImplAttributes; method.SemanticsAttributes = base_method.SemanticsAttributes; @@ -100,7 +105,7 @@ public DirectCallMethod(ModuleDefinition module, TypeDefinition type) // Check for name null or empty AddInstruction(OpCodes.Ldarg_1); AddInstruction(OpCodes.Call, isNullOrEmpty); - var empty = AddInstruction(OpCodes.Brfalse, body.Instructions[0]); + Instruction empty = AddInstruction(OpCodes.Brfalse, body.Instructions[0]); Return(false); // Get method name length @@ -113,51 +118,64 @@ public DirectCallMethod(ModuleDefinition module, TypeDefinition type) AddInstruction(OpCodes.Stloc_1); // Find all hook methods defined by the plugin - foreach (var m in type.Methods.Where(m => !m.IsStatic && (m.IsPrivate || IsHookMethod(m)) && !m.HasGenericParameters && !m.ReturnType.IsGenericParameter && m.DeclaringType == type && !m.IsSetter && !m.IsGetter)) + foreach (MethodDefinition m in type.Methods.Where(m => !m.IsStatic && (m.IsPrivate || IsHookMethod(m)) && !m.HasGenericParameters && !m.ReturnType.IsGenericParameter && m.DeclaringType == type && !m.IsSetter && !m.IsGetter)) { //ignore compiler generated - if (m.Name.Contains("<")) continue; + if (m.Name.Contains("<")) + { + continue; + } - var name = m.Name; + string name = m.Name; if (m.Parameters.Count > 0) + { name += $"({string.Join(", ", m.Parameters.Select(x => x.ParameterType.ToString().Replace("/", "+").Replace("<", "[").Replace(">", "]")).ToArray())})"; + } - if (!hookMethods.ContainsKey(name)) hookMethods[name] = m; + if (!hookMethods.ContainsKey(name)) + { + hookMethods[name] = m; + } } // Build a hook method name trie - var root_node = new Node(); - foreach (var method_name in hookMethods.Keys) + Node root_node = new Node(); + foreach (string method_name in hookMethods.Keys) { - var current_node = root_node; - for (var i = 1; i <= method_name.Length; i++) + Node current_node = root_node; + for (int i = 1; i <= method_name.Length; i++) { - var letter = method_name[i - 1]; - Node next_node; - if (!current_node.Edges.TryGetValue(letter, out next_node)) + char letter = method_name[i - 1]; + if (!current_node.Edges.TryGetValue(letter, out Node next_node)) { next_node = new Node { Parent = current_node, Char = letter }; current_node.Edges[letter] = next_node; } - if (i == method_name.Length) next_node.Name = method_name; + if (i == method_name.Length) + { + next_node.Name = method_name; + } + current_node = next_node; } } // Build conditional method call logic from trie nodes - var n = 1; - foreach (var edge in root_node.Edges.Keys) + int n = 1; + foreach (char edge in root_node.Edges.Keys) + { BuildNode(root_node.Edges[edge], n++); + } // No valid method was found endInstruction = Return(false); - foreach (var instruction in jumpToEdgePlaceholderTargets.Keys) + foreach (Instruction instruction in jumpToEdgePlaceholderTargets.Keys) { instruction.Operand = jumpToEdgePlaceholderTargets[instruction].FirstInstruction; } - foreach (var instruction in jumpToEndPlaceholders) + foreach (Instruction instruction in jumpToEndPlaceholders) { instruction.Operand = endInstruction; } @@ -167,10 +185,12 @@ public DirectCallMethod(ModuleDefinition module, TypeDefinition type) private bool IsHookMethod(MethodDefinition method) { - foreach (var attribute in method.CustomAttributes) + foreach (CustomAttribute attribute in method.CustomAttributes) { if (attribute.AttributeType.FullName == hook_attribute) + { return true; + } } return false; } @@ -187,9 +207,14 @@ private void BuildNode(Node node, int edge_number) // Check the char at the current position if (edge_number == 1) + { AddInstruction(OpCodes.Ldarg_1); //method_name + } else + { node.FirstInstruction = AddInstruction(OpCodes.Ldarg_1); + } + AddInstruction(OpCodes.Ldloc_1); // i AddInstruction(OpCodes.Callvirt, getChars); // method_name[i] AddInstruction(Ldc_I4_n(node.Char)); @@ -207,9 +232,12 @@ private void BuildNode(Node node, int edge_number) if (node.Edges.Count == 1 && node.Name == null) { - var last_edge = node; + Node last_edge = node; while (last_edge.Edges.Count == 1 && last_edge.Name == null) + { last_edge = last_edge.Edges.Values.First(); + } + if (last_edge.Edges.Count == 0 && last_edge.Name != null) { // There is only one remaining possible hook on this path @@ -240,31 +268,37 @@ private void BuildNode(Node node, int edge_number) AddInstruction(OpCodes.Ldloc_0); // If the method name is longer than the current position if (node.Edges.Count > 0) + { JumpToEdge(node.Edges.Values.First()); + } else + { JumpToEnd(); + } // Method has been found CallMethod(hookMethods[node.Name]); Return(true); } - var n = 1; - foreach (var edge in node.Edges.Keys) + int n = 1; + foreach (char edge in node.Edges.Keys) + { BuildNode(node.Edges[edge], n++); + } } private void CallMethod(MethodDefinition method) { - var paramDict = new Dictionary(); + Dictionary paramDict = new Dictionary(); //check for ref/out param - for (var i = 0; i < method.Parameters.Count; i++) + for (int i = 0; i < method.Parameters.Count; i++) { - var parameter = method.Parameters[i]; - var param = parameter.ParameterType as ByReferenceType; + ParameterDefinition parameter = method.Parameters[i]; + ByReferenceType param = parameter.ParameterType as ByReferenceType; if (param != null) { - var refParam = AddVariable(module.Import(param.ElementType)); + VariableDefinition refParam = AddVariable(module.Import(param.ElementType)); AddInstruction(OpCodes.Ldarg_3); // object[] params AddInstruction(Ldc_I4_n(i)); // param_number AddInstruction(OpCodes.Ldelem_Ref); @@ -274,13 +308,16 @@ private void CallMethod(MethodDefinition method) } } - if (method.ReturnType.Name != "Void") AddInstruction(OpCodes.Ldarg_2); // out object ret + if (method.ReturnType.Name != "Void") + { + AddInstruction(OpCodes.Ldarg_2); // out object ret + } AddInstruction(OpCodes.Ldarg_0); // this - for (var i = 0; i < method.Parameters.Count; i++) + for (int i = 0; i < method.Parameters.Count; i++) { - var parameter = method.Parameters[i]; - var param = parameter.ParameterType as ByReferenceType; + ParameterDefinition parameter = method.Parameters[i]; + ByReferenceType param = parameter.ParameterType as ByReferenceType; if (param != null) { AddInstruction(OpCodes.Ldloca, paramDict[parameter]); @@ -297,10 +334,10 @@ private void CallMethod(MethodDefinition method) AddInstruction(OpCodes.Call, module.Import(method)); //handle ref/out params - for (var i = 0; i < method.Parameters.Count; i++) + for (int i = 0; i < method.Parameters.Count; i++) { - var parameter = method.Parameters[i]; - var param = parameter.ParameterType as ByReferenceType; + ParameterDefinition parameter = method.Parameters[i]; + ByReferenceType param = parameter.ParameterType as ByReferenceType; if (param != null) { AddInstruction(OpCodes.Ldarg_3); // object[] params @@ -313,21 +350,25 @@ private void CallMethod(MethodDefinition method) if (method.ReturnType.Name != "Void") { - if (method.ReturnType.Name != "Object") AddInstruction(OpCodes.Box, module.Import(method.ReturnType)); + if (method.ReturnType.Name != "Object") + { + AddInstruction(OpCodes.Box, module.Import(method.ReturnType)); + } + AddInstruction(OpCodes.Stind_Ref); } } private Instruction Return(bool value) { - var instruction = AddInstruction(Ldc_I4_n(value ? 1 : 0)); + Instruction instruction = AddInstruction(Ldc_I4_n(value ? 1 : 0)); AddInstruction(OpCodes.Ret); return instruction; } private void JumpToEdge(Node node) { - var instruction = AddInstruction(OpCodes.Bne_Un, body.Instructions[1]); + Instruction instruction = AddInstruction(OpCodes.Bne_Un, body.Instructions[1]); jumpToEdgePlaceholderTargets[instruction] = node; } @@ -374,22 +415,58 @@ private Instruction AddInstruction(Instruction instruction) public VariableDefinition AddVariable(TypeReference typeRef, string name = "") { - var def = new VariableDefinition(name, typeRef); + VariableDefinition def = new VariableDefinition(name, typeRef); body.Variables.Add(def); return def; } private Instruction Ldc_I4_n(int n) { - if (n == 0) return Instruction.Create(OpCodes.Ldc_I4_0); - if (n == 1) return Instruction.Create(OpCodes.Ldc_I4_1); - if (n == 2) return Instruction.Create(OpCodes.Ldc_I4_2); - if (n == 3) return Instruction.Create(OpCodes.Ldc_I4_3); - if (n == 4) return Instruction.Create(OpCodes.Ldc_I4_4); - if (n == 5) return Instruction.Create(OpCodes.Ldc_I4_5); - if (n == 6) return Instruction.Create(OpCodes.Ldc_I4_6); - if (n == 7) return Instruction.Create(OpCodes.Ldc_I4_7); - if (n == 8) return Instruction.Create(OpCodes.Ldc_I4_8); + if (n == 0) + { + return Instruction.Create(OpCodes.Ldc_I4_0); + } + + if (n == 1) + { + return Instruction.Create(OpCodes.Ldc_I4_1); + } + + if (n == 2) + { + return Instruction.Create(OpCodes.Ldc_I4_2); + } + + if (n == 3) + { + return Instruction.Create(OpCodes.Ldc_I4_3); + } + + if (n == 4) + { + return Instruction.Create(OpCodes.Ldc_I4_4); + } + + if (n == 5) + { + return Instruction.Create(OpCodes.Ldc_I4_5); + } + + if (n == 6) + { + return Instruction.Create(OpCodes.Ldc_I4_6); + } + + if (n == 7) + { + return Instruction.Create(OpCodes.Ldc_I4_7); + } + + if (n == 8) + { + return Instruction.Create(OpCodes.Ldc_I4_8); + } + return Instruction.Create(OpCodes.Ldc_I4_S, (sbyte)n); } } diff --git a/src/ObjectStream/Data/CompilerData.cs b/src/ObjectStream/Data/CompilerData.cs index 8b259b8..d96604a 100644 --- a/src/ObjectStream/Data/CompilerData.cs +++ b/src/ObjectStream/Data/CompilerData.cs @@ -14,6 +14,7 @@ public CompilerData() LoadDefaultReferences = false; SdkVersion = "2"; } + public bool LoadDefaultReferences { get; set; } public string OutputFile { get; set; } public CompilerPlatform Platform { get; set; } diff --git a/src/ObjectStream/IO/BindChanger.cs b/src/ObjectStream/IO/BindChanger.cs index 2c91c02..e1b0253 100644 --- a/src/ObjectStream/IO/BindChanger.cs +++ b/src/ObjectStream/IO/BindChanger.cs @@ -8,12 +8,16 @@ public class BindChanger : SerializationBinder { public override Type BindToType(string assemblyName, string typeName) { - /*if (typeName.Contains(".")) typeName = typeName.Substring(typeName.LastIndexOf(".", StringComparison.Ordinal) + 1); - if (typeName.Contains("+")) typeName = typeName.Substring(typeName.LastIndexOf("+", StringComparison.Ordinal) + 1); + /*if (typeName.Contains(".")) + { + typeName = typeName.Substring(typeName.LastIndexOf(".", StringComparison.Ordinal) + 1); + } + if (typeName.Contains("+")) + { + typeName = typeName.Substring(typeName.LastIndexOf("+", StringComparison.Ordinal) + 1); + } - var type = AppDomain.CurrentDomain.GetAssemblies() - .SelectMany(x => x.GetTypes()) - .FirstOrDefault(x => x.Name.Equals(typeName)); + Type type = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()).FirstOrDefault(x => x.Name.Equals(typeName)); return type != null ? Type.GetType(string.Format("{0}, {1}", type.FullName, Assembly.GetExecutingAssembly().FullName)) : null;*/ return Type.GetType(string.Format("{0}, {1}", typeName, Assembly.GetExecutingAssembly().FullName)); diff --git a/src/ObjectStream/IO/ObjectStreamWrapper.cs b/src/ObjectStream/IO/ObjectStreamWrapper.cs index 2a3c34a..a93722e 100644 --- a/src/ObjectStream/IO/ObjectStreamWrapper.cs +++ b/src/ObjectStream/IO/ObjectStreamWrapper.cs @@ -39,7 +39,11 @@ public ObjectStreamWrapper(Stream inStream, Stream outStream) public void Close() { - if (!_run) return; + if (!_run) + { + return; + } + _run = false; try { @@ -55,7 +59,7 @@ public void Close() public TRead ReadObject() { - var len = ReadLength(); + int len = ReadLength(); return len == 0 ? null : ReadObject(len); } @@ -64,14 +68,22 @@ public TRead ReadObject() private int ReadLength() { const int lensize = sizeof(int); - var lenbuf = new byte[lensize]; - var bytesRead = _inStream.Read(lenbuf, 0, lensize); - if (bytesRead == 0) return 0; + byte[] lenbuf = new byte[lensize]; + int bytesRead = _inStream.Read(lenbuf, 0, lensize); + if (bytesRead == 0) + { + return 0; + } + if (bytesRead != lensize) { // TODO: Hack to ignore BOM Array.Resize(ref lenbuf, Encoding.UTF8.GetPreamble().Length); - if (Encoding.UTF8.GetPreamble().SequenceEqual(lenbuf)) return ReadLength(); + if (Encoding.UTF8.GetPreamble().SequenceEqual(lenbuf)) + { + return ReadLength(); + } + throw new IOException(string.Format("Expected {0} bytes but read {1}", lensize, bytesRead)); } return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(lenbuf, 0)); @@ -79,12 +91,15 @@ private int ReadLength() private TRead ReadObject(int len) { - var data = new byte[len]; + byte[] data = new byte[len]; int count; int sum = 0; while (len - sum > 0 && (count = _inStream.Read(data, sum, len - sum)) > 0) + { sum += count; - using (var memoryStream = new MemoryStream(data)) + } + + using (MemoryStream memoryStream = new MemoryStream(data)) { return (TRead)_binaryFormatter.Deserialize(memoryStream); } @@ -94,7 +109,7 @@ private TRead ReadObject(int len) public void WriteObject(TWrite obj) { - var data = Serialize(obj); + byte[] data = Serialize(obj); WriteLength(data.Length); WriteObject(data); Flush(); @@ -104,7 +119,7 @@ public void WriteObject(TWrite obj) private byte[] Serialize(TWrite obj) { - using (var memoryStream = new MemoryStream()) + using (MemoryStream memoryStream = new MemoryStream()) { _binaryFormatter.Serialize(memoryStream, obj); return memoryStream.ToArray(); @@ -113,7 +128,7 @@ private byte[] Serialize(TWrite obj) private void WriteLength(int len) { - var lenbuf = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(len)); + byte[] lenbuf = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(len)); _outStream.Write(lenbuf, 0, lenbuf.Length); } diff --git a/src/ObjectStream/ObjectStreamClient.cs b/src/ObjectStream/ObjectStreamClient.cs index 06167a1..beb6c14 100644 --- a/src/ObjectStream/ObjectStreamClient.cs +++ b/src/ObjectStream/ObjectStreamClient.cs @@ -34,7 +34,7 @@ public ObjectStreamClient(Stream inStream, Stream outStream) public void Start() { - var worker = new Worker(); + Worker worker = new Worker(); worker.Error += OnError; worker.DoWork(ListenSync); } @@ -42,13 +42,17 @@ public void Start() public void PushMessage(TWrite message) { if (_connection != null) + { _connection.PushMessage(message); + } } public void Stop() { if (_connection != null) + { _connection.Close(); + } } #region Private methods @@ -65,7 +69,9 @@ private void ListenSync() private void OnReceiveMessage(ObjectStreamConnection connection, TRead message) { if (Message != null) + { Message(connection, message); + } } private void ConnectionOnError(ObjectStreamConnection connection, Exception exception) @@ -76,7 +82,9 @@ private void ConnectionOnError(ObjectStreamConnection connection, private void OnError(Exception exception) { if (Error != null) + { Error(exception); + } } #endregion Private methods diff --git a/src/ObjectStream/ObjectStreamConnection.cs b/src/ObjectStream/ObjectStreamConnection.cs index f9eb44d..5aa6a72 100644 --- a/src/ObjectStream/ObjectStreamConnection.cs +++ b/src/ObjectStream/ObjectStreamConnection.cs @@ -26,11 +26,11 @@ internal ObjectStreamConnection(Stream inStream, Stream outStream) public void Open() { - var readWorker = new Worker(); + Worker readWorker = new Worker(); readWorker.Error += OnError; readWorker.DoWork(ReadStream); - var writeWorker = new Worker(); + Worker writeWorker = new Worker(); writeWorker.Error += OnError; writeWorker.DoWork(WriteStream); } @@ -53,16 +53,22 @@ private void CloseImpl() private void OnError(Exception exception) { if (Error != null) + { Error(this, exception); + } } private void ReadStream() { while (_streamWrapper.CanRead) { - var obj = _streamWrapper.ReadObject(); + TRead obj = _streamWrapper.ReadObject(); ReceiveMessage?.Invoke(this, obj); - if (obj != null) continue; + if (obj != null) + { + continue; + } + CloseImpl(); return; } @@ -74,7 +80,9 @@ private void WriteStream() { _writeSignal.WaitOne(); while (_writeQueue.Count > 0) + { _streamWrapper.WriteObject(_writeQueue.Dequeue()); + } } } } diff --git a/src/ObjectStream/Threading/Worker.cs b/src/ObjectStream/Threading/Worker.cs index f052d87..b9f2e8d 100644 --- a/src/ObjectStream/Threading/Worker.cs +++ b/src/ObjectStream/Threading/Worker.cs @@ -19,7 +19,7 @@ public void DoWork(Action action) private void DoWorkImpl(object oAction) { - var action = (Action)oAction; + Action action = (Action)oAction; try { action(); @@ -33,7 +33,9 @@ private void DoWorkImpl(object oAction) private void Fail(Exception exception) { if (Error != null) + { Error(exception); + } } private void Callback(Action action) diff --git a/src/Oxide.CSharp.csproj b/src/Oxide.CSharp.csproj index 30bfaa0..c3144f6 100644 --- a/src/Oxide.CSharp.csproj +++ b/src/Oxide.CSharp.csproj @@ -1,31 +1,25 @@  + 2.0.0 Oxide.CSharp Oxide and Contributors + (c) 2013-$([System.DateTime]::Now.Year) $(Authors) C#/CSharp (.cs) plugin support for the Oxide modding framework https://github.com/OxideMod/Oxide.CSharp - https://github.com/OxideMod/Oxide.CSharp/blob/develop/LICENSE.md - https://github.com/OxideMod/Oxide.CSharp - https://avatars1.githubusercontent.com/u/10712027?s=64 - Copyright (c) 2014-$([System.DateTime]::Now.Year) $(Authors) - api framework gaming modding plugins csharp c# - net46;net45;net40;net35 - True - - C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client - /Library/Frameworks/Mono.framework/Versions/Current/lib/mono/2.0-api - Oxide.Core.CSharp + icon.png + MIT + $(RepositoryUrl) + net35;net40;net45;net46 - - + + contentfiles;analyzers;build + + diff --git a/src/PluginCompiler.cs b/src/PluginCompiler.cs index 947e737..af1af09 100644 --- a/src/PluginCompiler.cs +++ b/src/PluginCompiler.cs @@ -31,8 +31,8 @@ public class PluginCompiler public static void CheckCompilerBinary() { BinaryPath = null; - var rootDirectory = Interface.Oxide.RootDirectory; - var binaryPath = Path.Combine(rootDirectory, FileName); + string rootDirectory = Interface.Oxide.RootDirectory; + string binaryPath = Path.Combine(rootDirectory, FileName); if (File.Exists(binaryPath)) { @@ -57,7 +57,10 @@ public static void CheckCompilerBinary() UpdateCheck(); // TODO: Only check once on server startup try { - if (Syscall.access(binaryPath, AccessModes.X_OK) == 0) break; + if (Syscall.access(binaryPath, AccessModes.X_OK) == 0) + { + break; + } } catch (Exception ex) { @@ -82,12 +85,15 @@ public static void CheckCompilerBinary() private void DependencyTrace() { - if (TraceRan || Environment.OSVersion.Platform != PlatformID.Unix) return; + if (TraceRan || Environment.OSVersion.Platform != PlatformID.Unix) + { + return; + } try { Interface.Oxide.LogWarning($"Running dependency trace for {FileName}"); - var trace = new Process + Process trace = new Process { StartInfo = { @@ -125,15 +131,19 @@ private static void DownloadCompiler(WebResponse response, string remoteHash) { Interface.Oxide.LogInfo($"Downloading {FileName} for .cs (C#) plugin compilation"); - var stream = response.GetResponseStream(); - var fs = new FileStream(FileName, FileMode.Create, FileAccess.Write, FileShare.None); - var bufferSize = 10000; - var buffer = new byte[bufferSize]; + Stream stream = response.GetResponseStream(); + FileStream fs = new FileStream(FileName, FileMode.Create, FileAccess.Write, FileShare.None); + int bufferSize = 10000; + byte[] buffer = new byte[bufferSize]; while (true) { - var result = stream.Read(buffer, 0, bufferSize); - if (result == -1 || result == 0) break; + int result = stream.Read(buffer, 0, bufferSize); + if (result == -1 || result == 0) + { + break; + } + fs.Write(buffer, 0, result); } fs.Flush(); @@ -147,7 +157,7 @@ private static void DownloadCompiler(WebResponse response, string remoteHash) return; } - var localHash = File.Exists(BinaryPath) ? GetHash(BinaryPath, Algorithms.MD5) : "0"; + string localHash = File.Exists(BinaryPath) ? GetHash(BinaryPath, Algorithms.MD5) : "0"; if (remoteHash != localHash) { Interface.Oxide.LogInfo($"Local hash did not match remote hash for {FileName}, attempting download again"); @@ -169,13 +179,17 @@ private static void UpdateCheck() { try { - var filePath = Path.Combine(Interface.Oxide.RootDirectory, FileName); - var request = (HttpWebRequest)WebRequest.Create($"https://umod-01.nyc3.digitaloceanspaces.com/{FileName}"); - var response = (HttpWebResponse)request.GetResponse(); - var statusCode = (int)response.StatusCode; - if (statusCode != 200) Interface.Oxide.LogWarning($"Status code from download location was not okay (code {statusCode})"); - var remoteHash = response.Headers[HttpResponseHeader.ETag].Trim('"'); - var localHash = File.Exists(filePath) ? GetHash(filePath, Algorithms.MD5) : "0"; + string filePath = Path.Combine(Interface.Oxide.RootDirectory, FileName); + HttpWebRequest request = (HttpWebRequest)WebRequest.Create($"https://umod-01.nyc3.digitaloceanspaces.com/{FileName}"); + HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + int statusCode = (int)response.StatusCode; + if (statusCode != 200) + { + Interface.Oxide.LogWarning($"Status code from download location was not okay (code {statusCode})"); + } + + string remoteHash = response.Headers[HttpResponseHeader.ETag].Trim('"'); + string localHash = File.Exists(filePath) ? GetHash(filePath, Algorithms.MD5) : "0"; Interface.Oxide.LogInfo($"Latest compiler MD5: {remoteHash}"); Interface.Oxide.LogInfo($"Local compiler MD5: {localHash}"); if (remoteHash != localHash) @@ -214,8 +228,8 @@ public PluginCompiler() internal void Compile(CompilablePlugin[] plugins, Action callback) { - var id = lastId++; - var compilation = new Compilation(id, callback, plugins); + int id = lastId++; + Compilation compilation = new Compilation(id, callback, plugins); compilations[id] = compilation; compilation.Prepare(() => EnqueueCompilation(compilation)); } @@ -223,23 +237,36 @@ internal void Compile(CompilablePlugin[] plugins, Action callback) public void Shutdown() { ready = false; - var endedProcess = process; - if (endedProcess != null) endedProcess.Exited -= OnProcessExited; + Process endedProcess = process; + if (endedProcess != null) + { + endedProcess.Exited -= OnProcessExited; + } + process = null; - if (client == null) return; + if (client == null) + { + return; + } client.Message -= OnMessage; client.Error -= OnError; client.PushMessage(new CompilerMessage { Type = CompilerMessageType.Exit }); client.Stop(); client = null; - if (endedProcess == null) return; + if (endedProcess == null) + { + return; + } ThreadPool.QueueUserWorkItem(_ => { Thread.Sleep(5000); // Calling Close can block up to 60 seconds on certain machines - if (!endedProcess.HasExited) endedProcess.Close(); + if (!endedProcess.HasExited) + { + endedProcess.Close(); + } }); } @@ -259,20 +286,24 @@ private void EnqueueCompilation(Compilation compilation) compilation.Started(); //Interface.Oxide.LogDebug("Compiling with references: {0}", compilation.references.Keys.ToSentence()); - var sourceFiles = compilation.plugins.SelectMany(plugin => plugin.IncludePaths).Distinct().Select(path => new CompilerFile(path)).ToList(); + List sourceFiles = compilation.plugins.SelectMany(plugin => plugin.IncludePaths).Distinct().Select(path => new CompilerFile(path)).ToList(); sourceFiles.AddRange(compilation.plugins.Select(plugin => new CompilerFile($"{plugin.ScriptName}.cs", plugin.ScriptSource))); //Interface.Oxide.LogDebug("Compiling files: {0}", sourceFiles.Select(f => f.Name).ToSentence()); - var data = new CompilerData + CompilerData data = new CompilerData { OutputFile = compilation.name, SourceFiles = sourceFiles.ToArray(), ReferenceFiles = compilation.references.Values.ToArray() }; - var message = new CompilerMessage { Id = compilation.id, Data = data, Type = CompilerMessageType.Compile }; + CompilerMessage message = new CompilerMessage { Id = compilation.id, Data = data, Type = CompilerMessageType.Compile }; if (ready) + { client.PushMessage(message); + } else + { messageQueue.Enqueue(message); + } } private void OnMessage(ObjectStreamConnection connection, CompilerMessage message) @@ -291,36 +322,44 @@ private void OnMessage(ObjectStreamConnection switch (message.Type) { case CompilerMessageType.Assembly: - var compilation = compilations[message.Id]; + Compilation compilation = compilations[message.Id]; if (compilation == null) { Interface.Oxide.LogWarning("Compiler compiled an unknown assembly"); // TODO: Any way to clarify this? return; } compilation.endedAt = Interface.Oxide.Now; - var stdOutput = (string)message.ExtraData; + string stdOutput = (string)message.ExtraData; if (stdOutput != null) { - foreach (var line in stdOutput.Split('\r', '\n')) + foreach (string line in stdOutput.Split('\r', '\n')) { - var match = fileErrorRegex.Match(line.Trim()); - for (var i = 1; i < match.Groups.Count; i++) + Match match = fileErrorRegex.Match(line.Trim()); + for (int i = 1; i < match.Groups.Count; i++) { - var value = match.Groups[i].Value; - if (value.Trim() == string.Empty) continue; - var fileName = value.Basename(); - var scriptName = fileName.Substring(0, fileName.Length - 3); - var compilablePlugin = compilation.plugins.SingleOrDefault(pl => pl.ScriptName == scriptName); + string value = match.Groups[i].Value; + if (value.Trim() == string.Empty) + { + continue; + } + + string fileName = value.Basename(); + string scriptName = fileName.Substring(0, fileName.Length - 3); + CompilablePlugin compilablePlugin = compilation.plugins.SingleOrDefault(pl => pl.ScriptName == scriptName); if (compilablePlugin == null) { Interface.Oxide.LogError($"Unable to resolve script error to plugin: {line}"); continue; } - var missingRequirements = compilablePlugin.Requires.Where(name => !compilation.IncludesRequiredPlugin(name)); + IEnumerable missingRequirements = compilablePlugin.Requires.Where(name => !compilation.IncludesRequiredPlugin(name)); if (missingRequirements.Any()) + { compilablePlugin.CompilerErrors = $"Missing dependencies: {missingRequirements.ToSentence()}"; + } else + { compilablePlugin.CompilerErrors = line.Trim().Replace(Interface.Oxide.PluginDirectory + Path.DirectorySeparatorChar, string.Empty); + } } } } @@ -332,7 +371,10 @@ private void OnMessage(ObjectStreamConnection Interface.Oxide.NextTick(() => { idleTimer?.Destroy(); - if (AutoShutdown) idleTimer = Interface.Oxide.GetLibrary().Once(60, Shutdown); + if (AutoShutdown) + { + idleTimer = Interface.Oxide.GetLibrary().Once(60, Shutdown); + } }); } break; @@ -357,7 +399,10 @@ private void OnMessage(ObjectStreamConnection if (!ready) { ready = true; - while (messageQueue.Count > 0) connection.PushMessage(messageQueue.Dequeue()); + while (messageQueue.Count > 0) + { + connection.PushMessage(messageQueue.Dequeue()); + } } break; } @@ -370,14 +415,21 @@ private bool CheckCompiler() CheckCompilerBinary(); idleTimer?.Destroy(); - if (BinaryPath == null) return false; - if (process != null && process.Handle != IntPtr.Zero && !process.HasExited) return true; + if (BinaryPath == null) + { + return false; + } + + if (process != null && process.Handle != IntPtr.Zero && !process.HasExited) + { + return true; + } SetCompilerVersion(); PurgeOldLogs(); Shutdown(); - var args = new[] { "/service", "/logPath:" + EscapePath(Interface.Oxide.LogDirectory) }; + string[] args = new[] { "/service", "/logPath:" + EscapePath(Interface.Oxide.LogDirectory) }; try { process = new Process @@ -418,14 +470,31 @@ private bool CheckCompiler() process?.Dispose(); process = null; Interface.Oxide.LogException($"Exception while starting compiler version {CompilerVersion}: ", ex); - if (BinaryPath.Contains("'")) Interface.Oxide.LogWarning("Server directory path contains an apostrophe, compiler will not work until path is renamed"); - else if (Environment.OSVersion.Platform == PlatformID.Unix) Interface.Oxide.LogWarning("Compiler may not be set as executable; chmod +x or 0744/0755 required"); - if (ex.GetBaseException() != ex) Interface.Oxide.LogException("BaseException: ", ex.GetBaseException()); - var win32 = ex as Win32Exception; - if (win32 != null) Interface.Oxide.LogError("Win32 NativeErrorCode: {0} ErrorCode: {1} HelpLink: {2}", win32.NativeErrorCode, win32.ErrorCode, win32.HelpLink); + if (BinaryPath.Contains("'")) + { + Interface.Oxide.LogWarning("Server directory path contains an apostrophe, compiler will not work until path is renamed"); + } + else if (Environment.OSVersion.Platform == PlatformID.Unix) + { + Interface.Oxide.LogWarning("Compiler may not be set as executable; chmod +x or 0744/0755 required"); + } + + if (ex.GetBaseException() != ex) + { + Interface.Oxide.LogException("BaseException: ", ex.GetBaseException()); + } + + Win32Exception win32 = ex as Win32Exception; + if (win32 != null) + { + Interface.Oxide.LogError("Win32 NativeErrorCode: {0} ErrorCode: {1} HelpLink: {2}", win32.NativeErrorCode, win32.ErrorCode, win32.HelpLink); + } } - if (process == null) return false; + if (process == null) + { + return false; + } client = new ObjectStreamClient(process.StandardOutput.BaseStream, process.StandardInput.BaseStream); client.Message += OnMessage; @@ -499,9 +568,13 @@ private void OnProcessExited(object sender, EventArgs eventArgs) private void OnCompilerFailed(string reason) { - foreach (var compilation in compilations.Values) + foreach (Compilation compilation in compilations.Values) { - foreach (var plugin in compilation.plugins) plugin.CompilerErrors = reason; + foreach (CompilablePlugin plugin in compilation.plugins) + { + plugin.CompilerErrors = reason; + } + compilation.Completed(); } compilations.Clear(); @@ -511,12 +584,15 @@ private static void PurgeOldLogs() { try { - var filePaths = Directory.GetFiles(Interface.Oxide.LogDirectory, "*.txt").Where(f => + IEnumerable filePaths = Directory.GetFiles(Interface.Oxide.LogDirectory, "*.txt").Where(f => { - var fileName = Path.GetFileName(f); + string fileName = Path.GetFileName(f); return fileName != null && fileName.StartsWith("compiler_"); }); - foreach (var filePath in filePaths) File.Delete(filePath); + foreach (string filePath in filePaths) + { + File.Delete(filePath); + } } catch (Exception) { @@ -526,7 +602,10 @@ private static void PurgeOldLogs() private static string EscapePath(string path) { - if (string.IsNullOrEmpty(path)) return "\"\""; + if (string.IsNullOrEmpty(path)) + { + return "\"\""; + } path = Regex.Replace(path, @"(\\*)" + "\"", @"$1\$0"); path = Regex.Replace(path, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\""); @@ -545,9 +624,9 @@ private static class Algorithms private static string GetHash(string filePath, HashAlgorithm algorithm) { - using (var stream = new BufferedStream(File.OpenRead(filePath), 100000)) + using (BufferedStream stream = new BufferedStream(File.OpenRead(filePath), 100000)) { - var hash = algorithm.ComputeHash(stream); + byte[] hash = algorithm.ComputeHash(stream); return BitConverter.ToString(hash).Replace("-", string.Empty).ToLower(); } } diff --git a/src/Utility.cs b/src/Utility.cs index 80ce3de..51706f9 100644 --- a/src/Utility.cs +++ b/src/Utility.cs @@ -22,20 +22,29 @@ public TValue this[TKey key] { get { - TValue value; - if (TryGetValue(key, out value)) + if (TryGetValue(key, out TValue value)) + { return value; + } + if (typeof(TValue).IsValueType) + { return (TValue)Activator.CreateInstance(typeof(TValue)); + } + return default(TValue); } set { if (value == null) + { dictionary.Remove(key); + } else + { dictionary[key] = value; + } } } @@ -45,14 +54,23 @@ public TValue this[TKey key] public bool IsReadOnly => dictionary.IsReadOnly; public IEnumerator> GetEnumerator() => dictionary.GetEnumerator(); + public bool ContainsKey(TKey key) => dictionary.ContainsKey(key); + public bool Contains(KeyValuePair item) => dictionary.Contains(item); + public void CopyTo(KeyValuePair[] array, int index) => dictionary.CopyTo(array, index); + public bool TryGetValue(TKey key, out TValue value) => dictionary.TryGetValue(key, out value); + public void Add(TKey key, TValue value) => dictionary.Add(key, value); + public void Add(KeyValuePair item) => dictionary.Add(item); + public bool Remove(TKey key) => dictionary.Remove(key); + public bool Remove(KeyValuePair item) => dictionary.Remove(item); + public void Clear() => dictionary.Clear(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();