csharp/0x0ade/CelesteNet/CelesteNet.Server/CelesteNetServerModuleWrapper.cs

CelesteNetServerModuleWrapper.cs
using Celeste.Mod.CelesteNet.DataTypes;
using Mono.Cecil;
using Mono.Options;
using System;
using System.Collections.Generic;
using System.Diagnostics.Codeastysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Celeste.Mod.CelesteNet.Server {
    public partial clast CelesteNetServerModuleWrapper {

        public readonly CelesteNetServer Server;
        public readonly string astemblyPath;
        public readonly string ID;

        public CelesteNetServerModule? Module;
        public astembly? astembly;

        public HashSet References;
        public HashSet ReferredBy = new();

        public CelesteNetServerModuleWrapper(CelesteNetServer server, string path) {
            Server = server;
            astemblyPath = path;

            using ModuleDefinition module = ModuleDefinition.ReadModule(astemblyPath);
            ID = module.astembly.Name.Name;
            References = new(
                module.astemblyReferences
                .Select(name => name.Name)
                .Where(name => name.StartsWith("CelesteNet.Server.") && name.EndsWith("Module"))
            );

            Logger.Log(LogLevel.INF, "module", $"New module {ID} - {path}");
        }

        public void Reload() {
            Logger.Log(LogLevel.INF, "module", $"Reloading {ID}");

            Unload();
            Load();

            foreach (CelesteNetServerModuleWrapper other in ReferredBy)
                other.Reload();
        }

        public void Load() {
            if (Module != null)
                return;
            Logger.Log(LogLevel.INF, "module", $"Loading {ID}");

            Loadastembly();

            if (astembly == null)
                throw new Exception($"Failed to load astembly for {ID} - {astemblyPath}");

            foreach (Type type in astembly.GetTypes()) {
                if (typeof(CelesteNetServerModule).IsastignableFrom(type) && !type.IsAbstract) {
                    Module = (CelesteNetServerModule?) Activator.CreateInstance(type);
                    break;
                }
            }

            if (Module == null)
                throw new Exception($"Found no module clast in {ID} - {astemblyPath}");

            lock (Server.Modules) {
                Server.Modules.Add(Module);
                Server.ModuleMap[Module.GetType()] = Module;
            }

            if (Server.Initialized) {
                Logger.Log(LogLevel.INF, "module", $"Initializing {ID} (late)");
                Module.Init(this);
                if (Server.IsAlive) {
                    Logger.Log(LogLevel.INF, "module", $"Starting {ID} (late)");
                    Module.Start();
                }
                Server.Data.RescanDataTypes(astembly.GetTypes());
            }
        }

        public void Unload() {
            if (Module == null || astembly == null)
                return;

            foreach (CelesteNetServerModuleWrapper other in ReferredBy)
                other.Unload();

            Logger.Log(LogLevel.INF, "module", $"Unloading {ID}");

            Module.Dispose();

            lock (Server.Modules) {
                Server.Modules.Remove(Module);
                Server.ModuleMap.Clear();
            }

            Module = null;

            Server.DetourModManager.Unload(astembly);
            Server.Data.RemoveDataTypes(astembly.GetTypes());

            Unloadastembly();
            astembly = null;
        }

    }
}