servuo/Server/ScriptCompiler.cs

621 lines
13 KiB
C#
Raw Permalink Normal View History

#region References
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
#endregion
namespace Server
{
public static class ScriptCompiler
2020-08-21 02:19:04 +01:00
{
public static Assembly[] Assemblies { get; set; }
2020-08-21 02:19:04 +01:00
public static bool Compile(bool debug, bool cache)
{
var assemblies = new List<Assembly>
{
Assembly.LoadFrom("Scripts.dll"),
typeof(ScriptCompiler).Assembly
};
2020-08-21 02:19:04 +01:00
Assemblies = assemblies.ToArray();
Utility.PushColor(ConsoleColor.Yellow);
Console.WriteLine("Scripts: Verifying...");
Utility.PopColor();
var watch = Stopwatch.StartNew();
2020-08-21 02:19:04 +01:00
Core.VerifySerialization();
watch.Stop();
Utility.PushColor(ConsoleColor.Green);
Console.WriteLine(
"Finished ({0} items, {1} mobiles, {2} customs) ({3:F2} seconds)",
Core.ScriptItems,
Core.ScriptMobiles,
Core.ScriptCustoms,
watch.Elapsed.TotalSeconds);
Utility.PopColor();
return true;
}
public static void Invoke(string method)
{
var invoke = new List<MethodInfo>();
2020-08-21 02:19:04 +01:00
foreach (var a in Assemblies)
2020-08-21 02:19:04 +01:00
{
var types = a.GetTypes();
2020-08-21 02:19:04 +01:00
foreach (var t in types)
2020-08-21 02:19:04 +01:00
{
var m = t.GetMethod(method, BindingFlags.Static | BindingFlags.Public);
2020-08-21 02:19:04 +01:00
if (m != null)
{
invoke.Add(m);
}
}
}
invoke.Sort(new CallPriorityComparer());
foreach (var m in invoke)
2020-08-21 02:19:04 +01:00
{
m.Invoke(null, null);
}
}
private static readonly Dictionary<Assembly, TypeCache> m_TypeCaches = new Dictionary<Assembly, TypeCache>();
private static TypeCache m_NullCache;
public static TypeCache GetTypeCache(Assembly asm)
{
if (asm == null)
{
return m_NullCache ?? (m_NullCache = new TypeCache(null));
}
m_TypeCaches.TryGetValue(asm, out var c);
2020-08-21 02:19:04 +01:00
if (c == null)
{
m_TypeCaches[asm] = c = new TypeCache(asm);
}
return c;
}
public static int FindHashByName(string name)
{
if (String.IsNullOrWhiteSpace(name))
{
return 0;
}
var hash = 0;
for (var i = 0; hash == 0 && i < Assemblies.Length; ++i)
{
hash = GetTypeCache(Assemblies[i]).GetTypeHashByName(name);
}
return hash != 0 ? hash : GetTypeCache(Core.Assembly).GetTypeHashByName(name);
}
public static int FindHashByFullName(string fullName)
{
if (String.IsNullOrWhiteSpace(fullName))
{
return 0;
}
var hash = 0;
for (var i = 0; hash == 0 && i < Assemblies.Length; ++i)
{
hash = GetTypeCache(Assemblies[i]).GetTypeHashByFullName(fullName);
}
return hash != 0 ? hash : GetTypeCache(Core.Assembly).GetTypeHashByFullName(fullName);
}
public static Type FindTypeByFullName(string fullName)
2020-08-21 02:19:04 +01:00
{
return FindTypeByFullName(fullName, true);
}
public static Type FindTypeByFullName(string fullName, bool ignoreCase)
{
if (String.IsNullOrWhiteSpace(fullName))
2020-08-21 02:19:04 +01:00
{
return null;
}
Type type = null;
for (var i = 0; type == null && i < Assemblies.Length; ++i)
2020-08-21 02:19:04 +01:00
{
type = GetTypeCache(Assemblies[i]).GetTypeByFullName(fullName, ignoreCase);
}
return type ?? GetTypeCache(Core.Assembly).GetTypeByFullName(fullName, ignoreCase);
}
public static IEnumerable<Type> FindTypesByFullName(string name)
{
return FindTypesByFullName(name, true);
}
public static IEnumerable<Type> FindTypesByFullName(string name, bool ignoreCase)
{
if (String.IsNullOrWhiteSpace(name))
2020-08-21 02:19:04 +01:00
{
yield break;
}
for (var i = 0; i < Assemblies.Length; ++i)
2020-08-21 02:19:04 +01:00
{
foreach (var t in GetTypeCache(Assemblies[i]).GetTypesByFullName(name, ignoreCase))
2020-08-21 02:19:04 +01:00
{
yield return t;
}
}
foreach (var t in GetTypeCache(Core.Assembly).GetTypesByFullName(name, ignoreCase))
2020-08-21 02:19:04 +01:00
{
yield return t;
}
}
public static Type FindTypeByName(string name)
{
return FindTypeByName(name, true);
}
public static Type FindTypeByName(string name, bool ignoreCase)
{
if (String.IsNullOrWhiteSpace(name))
2020-08-21 02:19:04 +01:00
{
return null;
}
Type type = null;
for (var i = 0; type == null && i < Assemblies.Length; ++i)
2020-08-21 02:19:04 +01:00
{
type = GetTypeCache(Assemblies[i]).GetTypeByName(name, ignoreCase);
}
return type ?? GetTypeCache(Core.Assembly).GetTypeByName(name, ignoreCase);
}
public static IEnumerable<Type> FindTypesByName(string name)
{
return FindTypesByName(name, true);
}
public static IEnumerable<Type> FindTypesByName(string name, bool ignoreCase)
{
if (String.IsNullOrWhiteSpace(name))
2020-08-21 02:19:04 +01:00
{
yield break;
}
for (var i = 0; i < Assemblies.Length; ++i)
2020-08-21 02:19:04 +01:00
{
foreach (var t in GetTypeCache(Assemblies[i]).GetTypesByName(name, ignoreCase))
2020-08-21 02:19:04 +01:00
{
yield return t;
}
}
foreach (var t in GetTypeCache(Core.Assembly).GetTypesByName(name, ignoreCase))
2020-08-21 02:19:04 +01:00
{
yield return t;
}
}
public static Type FindTypeByNameHash(int hash)
{
Type type = null;
for (var i = 0; type == null && i < Assemblies.Length; ++i)
{
type = GetTypeCache(Assemblies[i]).GetTypeByNameHash(hash);
}
return type ?? GetTypeCache(Core.Assembly).GetTypeByNameHash(hash);
}
public static IEnumerable<Type> FindTypesByNameHash(int hash)
{
for (var i = 0; i < Assemblies.Length; ++i)
{
foreach (var t in GetTypeCache(Assemblies[i]).GetTypesByNameHash(hash))
{
yield return t;
}
}
foreach (var t in GetTypeCache(Core.Assembly).GetTypesByNameHash(hash))
{
yield return t;
}
}
public static Type FindTypeByFullNameHash(int hash)
{
Type type = null;
for (var i = 0; type == null && i < Assemblies.Length; ++i)
{
type = GetTypeCache(Assemblies[i]).GetTypeByFullNameHash(hash);
}
return type ?? GetTypeCache(Core.Assembly).GetTypeByFullNameHash(hash);
}
public static IEnumerable<Type> FindTypesByFullNameHash(int hash)
{
for (var i = 0; i < Assemblies.Length; ++i)
{
foreach (var t in GetTypeCache(Assemblies[i]).GetTypesByFullNameHash(hash))
{
yield return t;
}
}
foreach (var t in GetTypeCache(Core.Assembly).GetTypesByFullNameHash(hash))
{
yield return t;
}
}
}
2020-08-21 02:19:04 +01:00
public class TypeCache
{
private readonly Type[] m_Types;
private readonly TypeTable m_Names;
2020-08-21 02:19:04 +01:00
private readonly TypeTable m_FullNames;
public Type[] Types => m_Types;
public TypeTable Names => m_Names;
2020-08-21 02:19:04 +01:00
public TypeTable FullNames => m_FullNames;
public Type GetTypeByNameHash(int hash)
{
return GetTypesByNameHash(hash).FirstOrDefault(t => t != null);
}
public IEnumerable<Type> GetTypesByNameHash(int hash)
{
return m_Names.Get(hash);
}
public Type GetTypeByFullNameHash(int hash)
{
return GetTypesByFullNameHash(hash).FirstOrDefault(t => t != null);
}
public IEnumerable<Type> GetTypesByFullNameHash(int hash)
{
return m_FullNames.Get(hash);
}
public Type GetTypeByName(string name, bool ignoreCase)
2020-08-21 02:19:04 +01:00
{
return GetTypesByName(name, ignoreCase).FirstOrDefault(t => t != null);
}
public IEnumerable<Type> GetTypesByName(string name, bool ignoreCase)
{
return m_Names.Get(name, ignoreCase);
}
public Type GetTypeByFullName(string fullName, bool ignoreCase)
{
return GetTypesByFullName(fullName, ignoreCase).FirstOrDefault(t => t != null);
}
public IEnumerable<Type> GetTypesByFullName(string fullName, bool ignoreCase)
{
return m_FullNames.Get(fullName, ignoreCase);
}
public int GetTypeHashByName(string name)
{
return m_Names.GetHash(name);
}
public int GetTypeHashByFullName(string fullName)
{
return m_FullNames.GetHash(fullName);
}
2020-08-21 02:19:04 +01:00
public TypeCache(Assembly asm)
2020-08-21 02:19:04 +01:00
{
if (asm == null)
{
m_Types = Type.EmptyTypes;
}
else
{
m_Types = asm.GetTypes();
}
m_Names = new TypeTable(m_Types.Length);
m_FullNames = new TypeTable(m_Types.Length);
foreach (var g in m_Types.ToLookup(t => t.Name))
2020-08-21 02:19:04 +01:00
{
m_Names.Add(g.Key, g);
foreach (var type in g)
2020-08-21 02:19:04 +01:00
{
m_FullNames.Add(type.FullName, type);
var attr = type.GetCustomAttribute<TypeAliasAttribute>(false);
2020-08-21 02:19:04 +01:00
if (attr != null)
{
foreach (var a in attr.Aliases)
2020-08-21 02:19:04 +01:00
{
m_FullNames.Add(a, type);
}
}
}
}
m_Names.Sort();
m_FullNames.Sort();
}
}
public class TypeTable
{
private readonly Dictionary<string, int> m_Hashes;
private readonly Dictionary<int, HashSet<Type>> m_Hashed;
private readonly Dictionary<string, HashSet<Type>> m_Sensitive;
private readonly Dictionary<string, HashSet<Type>> m_Insensitive;
2020-08-21 02:19:04 +01:00
public void Sort()
2020-08-21 02:19:04 +01:00
{
Sort(m_Hashed);
Sort(m_Sensitive);
Sort(m_Insensitive);
2020-08-21 02:19:04 +01:00
}
private static void Sort<T>(Dictionary<T, HashSet<Type>> types)
2020-08-21 02:19:04 +01:00
{
var sorter = new List<Type>();
2020-08-21 02:19:04 +01:00
foreach (var list in types.Values)
2020-08-21 02:19:04 +01:00
{
sorter.AddRange(list);
sorter.Sort(InternalSort);
2020-08-21 02:19:04 +01:00
list.Clear();
list.UnionWith(sorter);
2020-08-21 02:19:04 +01:00
sorter.Clear();
}
2020-08-21 02:19:04 +01:00
sorter.TrimExcess();
2020-08-21 02:19:04 +01:00
}
private static int InternalSort(Type l, Type r)
{
if (l == r)
{
return 0;
}
if (l != null && r == null)
{
return -1;
}
if (l == null && r != null)
{
return 1;
}
var a = IsEntity(l);
var b = IsEntity(r);
2020-08-21 02:19:04 +01:00
if (a && b)
{
a = IsConstructable(l, out var x);
b = IsConstructable(r, out var y);
2020-08-21 02:19:04 +01:00
if (a && !b)
{
return -1;
}
if (!a && b)
{
return 1;
}
return x > y ? -1 : x < y ? 1 : 0;
}
return a ? -1 : b ? 1 : 0;
}
private static bool IsEntity(Type type)
{
return type.GetInterface("IEntity") != null;
}
private static bool IsConstructable(Type type, out AccessLevel access)
{
foreach (var ctor in type.GetConstructors().OrderBy(o => o.GetParameters().Length))
2020-08-21 02:19:04 +01:00
{
var attr = ctor.GetCustomAttribute<ConstructableAttribute>(false);
2020-08-21 02:19:04 +01:00
if (attr != null)
{
access = attr.AccessLevel;
return true;
}
}
access = 0;
return false;
}
2020-08-21 02:19:04 +01:00
public void Add(string key, IEnumerable<Type> types)
2020-08-21 02:19:04 +01:00
{
if (!String.IsNullOrWhiteSpace(key) && types != null)
2020-08-21 02:19:04 +01:00
{
Add(key, types.ToArray());
}
}
public void Add(string key, params Type[] types)
{
if (String.IsNullOrWhiteSpace(key) || types == null || types.Length == 0)
2020-08-21 02:19:04 +01:00
{
return;
}
if (!m_Sensitive.TryGetValue(key, out var sensitive) || sensitive == null)
2020-08-21 02:19:04 +01:00
{
m_Sensitive[key] = new HashSet<Type>(types);
2020-08-21 02:19:04 +01:00
}
else if (types.Length == 1)
{
sensitive.Add(types[0]);
}
else
{
sensitive.UnionWith(types);
2020-08-21 02:19:04 +01:00
}
if (!m_Insensitive.TryGetValue(key, out var insensitive) || insensitive == null)
2020-08-21 02:19:04 +01:00
{
m_Insensitive[key] = new HashSet<Type>(types);
2020-08-21 02:19:04 +01:00
}
else if (types.Length == 1)
{
insensitive.Add(types[0]);
}
else
{
insensitive.UnionWith(types);
}
var hash = GenerateHash(key);
if (!m_Hashed.TryGetValue(hash, out var hashed) || hashed == null)
{
m_Hashed[hash] = new HashSet<Type>(types);
}
else if (types.Length == 1)
{
hashed.Add(types[0]);
}
else
{
hashed.UnionWith(types);
}
}
2020-08-21 02:19:04 +01:00
public IEnumerable<Type> Get(string key, bool ignoreCase)
{
if (String.IsNullOrWhiteSpace(key))
2020-08-21 02:19:04 +01:00
{
return Type.EmptyTypes;
}
HashSet<Type> t;
2020-08-21 02:19:04 +01:00
if (ignoreCase)
{
m_Insensitive.TryGetValue(key, out t);
}
else
{
m_Sensitive.TryGetValue(key, out t);
}
if (t == null)
{
return Type.EmptyTypes;
}
return t.AsEnumerable();
}
public IEnumerable<Type> Get(int hash)
{
m_Hashed.TryGetValue(hash, out var t);
if (t == null)
{
return Type.EmptyTypes;
}
return t.AsEnumerable();
}
public int GetHash(string key)
{
if (String.IsNullOrWhiteSpace(key))
{
return 0;
}
m_Hashes.TryGetValue(key, out var hash);
return hash;
}
private int GenerateHash(string key)
{
if (String.IsNullOrWhiteSpace(key))
{
return 0;
}
var hash = GetHash(key);
if (hash != 0)
{
return hash;
}
hash = key.Length;
unchecked
{
for (var i = 0; i < key.Length; i++)
{
hash = (hash * 397) ^ Convert.ToInt32(key[i]);
}
}
m_Hashes[key] = hash;
return hash;
}
2020-08-21 02:19:04 +01:00
public TypeTable(int capacity)
2020-08-21 02:19:04 +01:00
{
m_Hashes = new Dictionary<string, int>();
m_Hashed = new Dictionary<int, HashSet<Type>>(capacity);
m_Sensitive = new Dictionary<string, HashSet<Type>>(capacity);
m_Insensitive = new Dictionary<string, HashSet<Type>>(capacity, StringComparer.OrdinalIgnoreCase);
2020-08-21 02:19:04 +01:00
}
}
}