|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- using Discord.Overrides;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Linq;
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Net.Http;
- using System.Reflection;
- using System.Runtime.Loader;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace Discord
- {
-
-
-
- public sealed class Override
- {
-
-
-
- public Guid Id { get; internal set; }
-
-
-
-
- public string Name { get; internal set; }
-
-
-
-
- public string Description { get; internal set; }
-
-
-
-
- public DateTimeOffset CreatedAt { get; internal set; }
-
-
-
-
- public DateTimeOffset LastUpdated { get; internal set; }
-
- internal static Override FromJson(string json)
- {
- var result = new Override();
-
- using(var textReader = new StringReader(json))
- using(var reader = new JsonTextReader(textReader))
- {
- var obj = JObject.ReadFrom(reader);
- result.Id = obj["id"].ToObject<Guid>();
- result.Name = obj["name"].ToObject<string>();
- result.Description = obj["description"].ToObject<string>();
- result.CreatedAt = obj["created_at"].ToObject<DateTimeOffset>();
- result.LastUpdated = obj["last_updated"].ToObject<DateTimeOffset>();
- }
-
- return result;
- }
- }
-
-
-
-
- public sealed class LoadedOverride
- {
-
-
-
- public Assembly Assembly { get; internal set; }
-
-
-
-
- public IOverride Instance { get; internal set; }
-
-
-
-
- public Type Type { get; internal set; }
- }
-
- public sealed class BuildOverrides
- {
-
-
-
- public static event Func<Override, string, Task> Log
- {
- add => _logEvents.Add(value);
- remove => _logEvents.Remove(value);
-
- }
-
-
-
-
- public IReadOnlyDictionary<Override, IReadOnlyCollection<LoadedOverride>> LoadedOverrides
- => _loadedOverrides.Select(x => new KeyValuePair<Override, IReadOnlyCollection<LoadedOverride>> (x.Key, x.Value)).ToDictionary(x => x.Key, x => x.Value);
-
- private static AssemblyLoadContext _overrideDomain;
- private static List<Func<Override, string, Task>> _logEvents = new();
- private static ConcurrentDictionary<Override, List<LoadedOverride>> _loadedOverrides = new ConcurrentDictionary<Override, List<LoadedOverride>>();
-
- private const string ApiUrl = "https://overrides.discordnet.dev";
-
- static BuildOverrides()
- {
- _overrideDomain = new AssemblyLoadContext("Discord.Net.Overrides.Runtime");
-
- _overrideDomain.Resolving += _overrideDomain_Resolving;
- }
-
-
-
-
-
-
-
-
-
-
-
-
- public static async Task<Override> GetOverrideAsync(string name)
- {
- using (var client = new HttpClient())
- {
- var result = await client.GetAsync($"{ApiUrl}/overrides/{name}");
-
- if (result.IsSuccessStatusCode)
- {
- var content = await result.Content.ReadAsStringAsync();
-
- return Override.FromJson(content);
- }
- else
- return null;
- }
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
- public static async Task<bool> AddOverrideAsync(string name)
- {
- var ovrride = await GetOverrideAsync(name);
-
- if (ovrride == null)
- return false;
-
- return await AddOverrideAsync(ovrride);
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
- public static async Task<bool> AddOverrideAsync(Override ovrride)
- {
-
- var ms = new MemoryStream();
-
- using (var client = new HttpClient())
- {
- var result = await client.GetAsync($"{ApiUrl}/overrides/download/{ovrride.Id}");
-
- if (!result.IsSuccessStatusCode)
- return false;
-
- await (await result.Content.ReadAsStreamAsync()).CopyToAsync(ms);
- }
-
- ms.Position = 0;
-
-
-
- var asm = _overrideDomain.LoadFromStream(ms);
-
-
- var overrides = asm.GetTypes().Where(x => x.GetInterfaces().Any(x => x == typeof(IOverride)));
-
- List<LoadedOverride> loaded = new();
-
- var context = new OverrideContext((m) => HandleLog(ovrride, m), ovrride);
-
- foreach (var ovr in overrides)
- {
- var inst = (IOverride)Activator.CreateInstance(ovr);
-
- inst.RegisterPackageLookupHandler((s) =>
- {
- return GetDependencyAsync(ovrride.Id, s);
- });
-
- _ = Task.Run(async () =>
- {
- try
- {
- await inst.InitializeAsync(context);
- }
- catch (Exception x)
- {
- HandleLog(ovrride, $"Failed to initialize build override: {x}");
- }
- });
-
- loaded.Add(new LoadedOverride()
- {
- Assembly = asm,
- Instance = inst,
- Type = ovr
- });
- }
-
- return _loadedOverrides.AddOrUpdate(ovrride, loaded, (_, __) => loaded) != null;
- }
-
- internal static void HandleLog(Override ovr, string msg)
- {
- _ = Task.Run(async () =>
- {
- foreach (var item in _logEvents)
- {
- await item.Invoke(ovr, msg).ConfigureAwait(false);
- }
- });
- }
-
- private static Assembly _overrideDomain_Resolving(AssemblyLoadContext arg1, AssemblyName arg2)
- {
-
- var v = _loadedOverrides.FirstOrDefault(x => x.Value.Any(x => x.Assembly.FullName == arg1.Assemblies.FirstOrDefault().FullName));
-
- return GetDependencyAsync(v.Key.Id, $"{arg2}").GetAwaiter().GetResult();
- }
-
- private static async Task<Assembly> GetDependencyAsync(Guid id, string name)
- {
- using(var client = new HttpClient())
- {
- var result = await client.PostAsync($"{ApiUrl}/overrides/{id}/dependency", new StringContent($"{{ \"info\": \"{name}\"}}", Encoding.UTF8, "application/json"));
-
- if (!result.IsSuccessStatusCode)
- throw new Exception("Failed to get dependency");
-
- using(var ms = new MemoryStream())
- {
- var innerStream = await result.Content.ReadAsStreamAsync();
- await innerStream.CopyToAsync(ms);
- ms.Position = 0;
- return _overrideDomain.LoadFromStream(ms);
- }
- }
- }
- }
- }
|