Targets .NET Core 3.0 (#39)
This commit is contained in:
parent
0993c03b70
commit
08bf44af9a
125 changed files with 1473 additions and 14244 deletions
|
|
@ -19,538 +19,37 @@
|
|||
***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
#if NETCORE
|
||||
using System.Runtime.Loader;
|
||||
#endif
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public static class ScriptCompiler
|
||||
{
|
||||
private static List<string> m_AdditionalReferences = new List<string>();
|
||||
|
||||
private static Dictionary<Assembly, TypeCache> m_TypeCaches = new Dictionary<Assembly, TypeCache>();
|
||||
private static TypeCache m_NullCache;
|
||||
public static Assembly[] Assemblies{ get; set; }
|
||||
public static Assembly[] Assemblies { get; set; }
|
||||
|
||||
public static string[] GetReferenceAssemblies()
|
||||
public static string ScriptsPath = EnsureDirectory("Assemblies");
|
||||
|
||||
public static bool LoadScripts(string path = null)
|
||||
{
|
||||
List<string> list = new List<string>();
|
||||
string[] files = Directory.GetFiles(path ?? ScriptsPath, "*.dll");
|
||||
|
||||
string path = Path.Combine(Core.BaseDirectory, "Data/Assemblies.cfg");
|
||||
Assemblies = new Assembly[files.Length];
|
||||
|
||||
if (File.Exists(path))
|
||||
using (StreamReader ip = new StreamReader(path))
|
||||
{
|
||||
string line;
|
||||
|
||||
while ((line = ip.ReadLine()) != null)
|
||||
if (line.Length > 0 && !line.StartsWith("#"))
|
||||
list.Add(line);
|
||||
}
|
||||
|
||||
list.Add(Core.ExePath);
|
||||
|
||||
list.AddRange(m_AdditionalReferences);
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public static string GetCompilerOptions(bool debug)
|
||||
{
|
||||
StringBuilder sb = null;
|
||||
|
||||
AppendCompilerOption(ref sb, "/unsafe");
|
||||
|
||||
if (!debug)
|
||||
AppendCompilerOption(ref sb, "/optimize");
|
||||
|
||||
#if MONO
|
||||
AppendCompilerOption( ref sb, "/d:MONO" );
|
||||
for (int i = 0; i < files.Length; i++)
|
||||
#if NETCORE
|
||||
Assemblies[i] = AssemblyLoadContext.Default.LoadFromAssemblyPath(files[i]);
|
||||
#else
|
||||
Assemblies[i] = Assembly.LoadFrom(files[i]);
|
||||
#endif
|
||||
|
||||
if (Core.Is64Bit)
|
||||
AppendCompilerOption(ref sb, "/d:x64");
|
||||
|
||||
#if NEWTIMERS
|
||||
AppendCompilerOption(ref sb, "/d:NEWTIMERS");
|
||||
#endif
|
||||
|
||||
return sb?.ToString();
|
||||
}
|
||||
|
||||
private static void AppendCompilerOption(ref StringBuilder sb, string define)
|
||||
{
|
||||
if (sb == null)
|
||||
sb = new StringBuilder();
|
||||
else
|
||||
sb.Append(' ');
|
||||
|
||||
sb.Append(define);
|
||||
}
|
||||
|
||||
private static byte[] GetHashCode(string compiledFile, string[] scriptFiles, bool debug)
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
using (BinaryWriter bin = new BinaryWriter(ms))
|
||||
{
|
||||
FileInfo fileInfo = new FileInfo(compiledFile);
|
||||
|
||||
bin.Write(fileInfo.LastWriteTimeUtc.Ticks);
|
||||
|
||||
foreach (string scriptFile in scriptFiles)
|
||||
{
|
||||
fileInfo = new FileInfo(scriptFile);
|
||||
|
||||
bin.Write(fileInfo.LastWriteTimeUtc.Ticks);
|
||||
}
|
||||
|
||||
bin.Write(debug);
|
||||
bin.Write(Core.Version.ToString());
|
||||
|
||||
ms.Position = 0;
|
||||
|
||||
using (SHA1 sha1 = SHA1.Create())
|
||||
{
|
||||
return sha1.ComputeHash(ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CompileCSScripts(out Assembly assembly, bool cache = true, bool debug = false)
|
||||
{
|
||||
Console.Write("Scripts: Compiling C# scripts...");
|
||||
string[] files = GetScripts("*.cs");
|
||||
|
||||
if (files.Length == 0)
|
||||
{
|
||||
Console.WriteLine("no files found.");
|
||||
assembly = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (File.Exists("Scripts/Output/Scripts.CS.dll"))
|
||||
if (cache && File.Exists("Scripts/Output/Scripts.CS.hash"))
|
||||
try
|
||||
{
|
||||
byte[] hashCode = GetHashCode("Scripts/Output/Scripts.CS.dll", files, debug);
|
||||
|
||||
using (FileStream fs = new FileStream("Scripts/Output/Scripts.CS.hash", FileMode.Open,
|
||||
FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
using (BinaryReader bin = new BinaryReader(fs))
|
||||
{
|
||||
byte[] bytes = bin.ReadBytes(hashCode.Length);
|
||||
|
||||
if (bytes.Length == hashCode.Length)
|
||||
{
|
||||
bool valid = true;
|
||||
|
||||
for (int i = 0; i < bytes.Length; ++i)
|
||||
if (bytes[i] != hashCode[i])
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (valid)
|
||||
{
|
||||
assembly = Assembly.LoadFrom("Scripts/Output/Scripts.CS.dll");
|
||||
|
||||
if (!m_AdditionalReferences.Contains(assembly.Location))
|
||||
m_AdditionalReferences.Add(assembly.Location);
|
||||
|
||||
Console.WriteLine("done (cached)");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
DeleteFiles("Scripts.CS*.dll");
|
||||
|
||||
using (CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"))
|
||||
{
|
||||
string path = GetUnusedPath("Scripts.CS");
|
||||
|
||||
CompilerParameters parms = new CompilerParameters(GetReferenceAssemblies(), path, debug);
|
||||
|
||||
string options = GetCompilerOptions(debug);
|
||||
|
||||
if (options != null)
|
||||
parms.CompilerOptions = options;
|
||||
|
||||
if (Core.HaltOnWarning)
|
||||
parms.WarningLevel = 4;
|
||||
|
||||
if (Core.Unix)
|
||||
{
|
||||
parms.CompilerOptions = $"{parms.CompilerOptions} /nowarn:169,219,414 /recurse:Scripts/*.cs";
|
||||
files = new string[0];
|
||||
}
|
||||
|
||||
CompilerResults results = provider.CompileAssemblyFromFile(parms, files);
|
||||
m_AdditionalReferences.Add(path);
|
||||
|
||||
Display(results);
|
||||
|
||||
if (results.Errors.Count > 0)
|
||||
{
|
||||
if (!Core.Unix)
|
||||
{
|
||||
assembly = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (CompilerError err in results.Errors)
|
||||
if (!err.IsWarning)
|
||||
{
|
||||
assembly = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (cache && Path.GetFileName(path) == "Scripts.CS.dll")
|
||||
try
|
||||
{
|
||||
byte[] hashCode = GetHashCode(path, files, debug);
|
||||
|
||||
using (FileStream fs = new FileStream("Scripts/Output/Scripts.CS.hash", FileMode.Create,
|
||||
FileAccess.Write, FileShare.None))
|
||||
{
|
||||
using (BinaryWriter bin = new BinaryWriter(fs))
|
||||
{
|
||||
bin.Write(hashCode, 0, hashCode.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
assembly = results.CompiledAssembly;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CompileVBScripts(out Assembly assembly, bool cache = true, bool debug = false)
|
||||
{
|
||||
Console.Write("Scripts: Compiling VB.NET scripts...");
|
||||
string[] files = GetScripts("*.vb");
|
||||
|
||||
if (files.Length == 0)
|
||||
{
|
||||
Console.WriteLine("no files found.");
|
||||
assembly = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (File.Exists("Scripts/Output/Scripts.VB.dll"))
|
||||
if (cache && File.Exists("Scripts/Output/Scripts.VB.hash"))
|
||||
{
|
||||
byte[] hashCode = GetHashCode("Scripts/Output/Scripts.VB.dll", files, debug);
|
||||
|
||||
try
|
||||
{
|
||||
using (FileStream fs = new FileStream("Scripts/Output/Scripts.VB.hash", FileMode.Open,
|
||||
FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
using (BinaryReader bin = new BinaryReader(fs))
|
||||
{
|
||||
byte[] bytes = bin.ReadBytes(hashCode.Length);
|
||||
|
||||
if (bytes.Length == hashCode.Length)
|
||||
{
|
||||
bool valid = true;
|
||||
|
||||
for (int i = 0; i < bytes.Length; ++i)
|
||||
if (bytes[i] != hashCode[i])
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (valid)
|
||||
{
|
||||
assembly = Assembly.LoadFrom("Scripts/Output/Scripts.VB.dll");
|
||||
|
||||
if (!m_AdditionalReferences.Contains(assembly.Location))
|
||||
m_AdditionalReferences.Add(assembly.Location);
|
||||
|
||||
Console.WriteLine("done (cached)");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
DeleteFiles("Scripts.VB*.dll");
|
||||
|
||||
using (CodeDomProvider provider = CodeDomProvider.CreateProvider("VisualBasic"))
|
||||
{
|
||||
string path = GetUnusedPath("Scripts.VB");
|
||||
|
||||
CompilerParameters parms = new CompilerParameters(GetReferenceAssemblies(), path, debug);
|
||||
|
||||
string options = GetCompilerOptions(debug);
|
||||
|
||||
if (options != null)
|
||||
parms.CompilerOptions = options;
|
||||
|
||||
if (Core.HaltOnWarning)
|
||||
parms.WarningLevel = 4;
|
||||
|
||||
if (Core.Unix)
|
||||
{
|
||||
parms.CompilerOptions = $"{parms.CompilerOptions} /nowarn:169,219,414 /recurse:Scripts/*.vb";
|
||||
files = new string[0];
|
||||
}
|
||||
|
||||
CompilerResults results = provider.CompileAssemblyFromFile(parms, files);
|
||||
m_AdditionalReferences.Add(path);
|
||||
|
||||
Display(results);
|
||||
|
||||
if (results.Errors.Count > 0)
|
||||
{
|
||||
if (!Core.Unix)
|
||||
{
|
||||
assembly = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (CompilerError err in results.Errors)
|
||||
if (!err.IsWarning)
|
||||
{
|
||||
assembly = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (cache && Path.GetFileName(path) == "Scripts.VB.dll")
|
||||
try
|
||||
{
|
||||
byte[] hashCode = GetHashCode(path, files, debug);
|
||||
|
||||
using (FileStream fs = new FileStream("Scripts/Output/Scripts.VB.hash", FileMode.Create,
|
||||
FileAccess.Write, FileShare.None))
|
||||
{
|
||||
using (BinaryWriter bin = new BinaryWriter(fs))
|
||||
{
|
||||
bin.Write(hashCode, 0, hashCode.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
assembly = results.CompiledAssembly;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Display(CompilerResults results)
|
||||
{
|
||||
if (results.Errors.Count > 0)
|
||||
{
|
||||
Dictionary<string, List<CompilerError>> errors =
|
||||
new Dictionary<string, List<CompilerError>>(results.Errors.Count, StringComparer.OrdinalIgnoreCase);
|
||||
Dictionary<string, List<CompilerError>> warnings =
|
||||
new Dictionary<string, List<CompilerError>>(results.Errors.Count, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (CompilerError e in results.Errors)
|
||||
{
|
||||
string file = e.FileName;
|
||||
|
||||
// Ridiculous. FileName is null if the warning/error is internally generated in csc.
|
||||
if (string.IsNullOrEmpty(file))
|
||||
{
|
||||
Console.WriteLine("ScriptCompiler: {0}: {1}", e.ErrorNumber, e.ErrorText);
|
||||
continue;
|
||||
}
|
||||
|
||||
Dictionary<string, List<CompilerError>> table = e.IsWarning ? warnings : errors;
|
||||
|
||||
if (!table.TryGetValue(file, out List<CompilerError> list))
|
||||
table[file] = list = new List<CompilerError>();
|
||||
|
||||
list.Add(e);
|
||||
}
|
||||
|
||||
Console.WriteLine(errors.Count > 0 ? "failed ({0} errors, {1} warnings)" : "done ({0} errors, {1} warnings)",
|
||||
errors.Count, warnings.Count);
|
||||
|
||||
string scriptRoot =
|
||||
Path.GetFullPath(Path.Combine(Core.BaseDirectory, "Scripts" + Path.DirectorySeparatorChar));
|
||||
Uri scriptRootUri = new Uri(scriptRoot);
|
||||
|
||||
Utility.PushColor(ConsoleColor.Yellow);
|
||||
|
||||
if (warnings.Count > 0)
|
||||
Console.WriteLine("Warnings:");
|
||||
|
||||
foreach (KeyValuePair<string, List<CompilerError>> kvp in warnings)
|
||||
{
|
||||
string fileName = kvp.Key;
|
||||
List<CompilerError> list = kvp.Value;
|
||||
|
||||
string fullPath = Path.GetFullPath(fileName);
|
||||
string usedPath =
|
||||
Uri.UnescapeDataString(scriptRootUri.MakeRelativeUri(new Uri(fullPath)).OriginalString);
|
||||
|
||||
Console.WriteLine(" + {0}:", usedPath);
|
||||
|
||||
Utility.PushColor(ConsoleColor.DarkYellow);
|
||||
|
||||
foreach (CompilerError e in list)
|
||||
Console.WriteLine(" {0}: Line {1}: {2}", e.ErrorNumber, e.Line, e.ErrorText);
|
||||
|
||||
Utility.PopColor();
|
||||
}
|
||||
|
||||
Utility.PopColor();
|
||||
|
||||
Utility.PushColor(ConsoleColor.Red);
|
||||
|
||||
if (errors.Count > 0)
|
||||
Console.WriteLine("Errors:");
|
||||
|
||||
foreach (KeyValuePair<string, List<CompilerError>> kvp in errors)
|
||||
{
|
||||
string fileName = kvp.Key;
|
||||
List<CompilerError> list = kvp.Value;
|
||||
|
||||
string fullPath = Path.GetFullPath(fileName);
|
||||
string usedPath =
|
||||
Uri.UnescapeDataString(scriptRootUri.MakeRelativeUri(new Uri(fullPath)).OriginalString);
|
||||
|
||||
Console.WriteLine(" + {0}:", usedPath);
|
||||
|
||||
Utility.PushColor(ConsoleColor.DarkRed);
|
||||
|
||||
foreach (CompilerError e in list)
|
||||
Console.WriteLine(" {0}: Line {1}: {2}", e.ErrorNumber, e.Line, e.ErrorText);
|
||||
|
||||
Utility.PopColor();
|
||||
}
|
||||
|
||||
Utility.PopColor();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("done (0 errors, 0 warnings)");
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetUnusedPath(string name)
|
||||
{
|
||||
string path = Path.Combine(Core.BaseDirectory, $"Scripts/Output/{name}.dll");
|
||||
|
||||
for (int i = 2; File.Exists(path) && i <= 1000; ++i)
|
||||
path = Path.Combine(Core.BaseDirectory, $"Scripts/Output/{name}.{i}.dll");
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static void DeleteFiles(string mask)
|
||||
{
|
||||
try
|
||||
{
|
||||
string[] files = Directory.GetFiles(Path.Combine(Core.BaseDirectory, "Scripts/Output"), mask);
|
||||
|
||||
foreach (string file in files)
|
||||
try
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Compile(bool debug, bool cache = true)
|
||||
{
|
||||
EnsureDirectory("Scripts/");
|
||||
EnsureDirectory("Scripts/Output/");
|
||||
|
||||
if (m_AdditionalReferences.Count > 0)
|
||||
m_AdditionalReferences.Clear();
|
||||
|
||||
List<Assembly> assemblies = new List<Assembly>();
|
||||
|
||||
if (CompileCSScripts(out Assembly assembly, cache, debug))
|
||||
{
|
||||
if (assembly != null)
|
||||
assemblies.Add(assembly);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Core.VBdotNet)
|
||||
{
|
||||
if (CompileVBScripts(out Assembly vbAssembly, cache, debug))
|
||||
{
|
||||
if (vbAssembly != null)
|
||||
assemblies.Add(vbAssembly);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Scripts: Skipping VB.NET Scripts...done (use -vb to enable)");
|
||||
}
|
||||
|
||||
if (assemblies.Count == 0)
|
||||
return false;
|
||||
|
||||
Assemblies = assemblies.ToArray();
|
||||
|
||||
Console.Write("Scripts: Verifying...");
|
||||
|
||||
Stopwatch watch = Stopwatch.StartNew();
|
||||
|
||||
Core.VerifySerialization();
|
||||
|
||||
watch.Stop();
|
||||
|
||||
Console.WriteLine("done ({0} items, {1} mobiles) ({2:F2} seconds)", Core.ScriptItems, Core.ScriptMobiles,
|
||||
watch.Elapsed.TotalSeconds);
|
||||
|
||||
return true;
|
||||
return files.Length > 0;
|
||||
}
|
||||
|
||||
public static void Invoke(string method)
|
||||
|
|
@ -618,32 +117,15 @@ namespace Server
|
|||
return type ?? GetTypeCache(Core.Assembly).GetTypeByName(name, ignoreCase);
|
||||
}
|
||||
|
||||
public static void EnsureDirectory(string dir)
|
||||
public static string EnsureDirectory(string dir)
|
||||
{
|
||||
string path = Path.Combine(Core.BaseDirectory, dir);
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static string[] GetScripts(string filter)
|
||||
{
|
||||
List<string> list = new List<string>();
|
||||
|
||||
GetScripts(list, Path.Combine(Core.BaseDirectory, "Scripts"), filter);
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public static void GetScripts(List<string> list, string path, string filter)
|
||||
{
|
||||
foreach (string dir in Directory.GetDirectories(path))
|
||||
GetScripts(list, dir, filter);
|
||||
|
||||
list.AddRange(Directory.GetFiles(path, filter));
|
||||
}
|
||||
|
||||
private delegate CompilerResults Compiler(bool debug);
|
||||
}
|
||||
|
||||
public class TypeCache
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue