/// <summary> /// Scans the registered plugins for one that handles the named API string /// </summary> /// <param name="APIname">The API string to handle, excluding any parameters</param> /// <returns>An API-processing delegate, or null if none were found</returns> public APIDelegate GetAPIDelegate(string APIname) { lock (_dataLock) { // Check our handler cache first. if (handlers.ContainsKey(APIname)) { return(handlers[APIname]); } foreach (var plugin in registeredPlugins) { // Does this match the api at all? if (plugin.commands.Contains(APIname) || plugin.regexCommands.Any(x => x.IsMatch(APIname))) { try { var del = plugin.apiHandler(APIname); if (del != null) { // Add to the handling cache and the plugin response list handlers[APIname] = del; plugin.known_handlers.Add(APIname); return(del); } } catch (Exception ex) { PluginLogger.print("Got Exception processing plugin " + plugin.instance.ToString() + ", " + ex.ToString()); } } } } return(null); }
static private void readConfiguration() { config.load(); // Read the port out of the config file int port = config.GetValue <int>("PORT"); if (port != 0 && port.IsPortNumber()) { serverConfig.port = port; } else if (!port.IsPortNumber()) { PluginLogger.print("Port specified in configuration file '" + serverConfig.port + "' must be a value between 1 and 65535 inclusive"); } else { PluginLogger.print("No port in configuration file - using default of " + serverConfig.port.ToString()); } // Read a specific IP address to bind to string ip = config.GetValue <String>("IPADDRESS"); if (ip != null) { IPAddress ipAddress = null; if (IPAddress.TryParse(ip, out ipAddress)) { serverConfig.ipAddress = ipAddress; } else { PluginLogger.print("Invalid IP address in configuration file, falling back to default"); } } else { PluginLogger.print("No IP address in configuration file."); } // Fill the serverconfig list of addresses.... if IPAddress.Any, then enumerate them if (serverConfig.ipAddress == IPAddress.Any) { // Build a list of addresses we will be able to recieve at serverConfig.ValidIpAddresses.Add(IPAddress.Loopback); serverConfig.ValidIpAddresses.AddRange(Dns.GetHostAddresses(Dns.GetHostName())); } else { serverConfig.ValidIpAddresses.Add(serverConfig.ipAddress); } serverConfig.version = Assembly.GetExecutingAssembly().GetName().Version.ToString(); serverConfig.name = "Telemachus"; isPartless = config.GetValue <int>("PARTLESS") == 0 ? false : true; PluginLogger.print("Partless:" + isPartless); }
static private void stopDataLink() { if (webServer != null) { PluginLogger.print("Telemachus data link shutting down."); webServer.Stop(); webServer = null; } }
private void updateHasTelemachusPart(Vessel vessel) { try { hasTelemachusPart = vessel.parts.FindAll(p => p.Modules.Contains("TelemachusDataLink")).Count > 0; } catch (Exception e) { PluginLogger.debug(e.Message + " " + e.StackTrace); } }
static private void stopDataLink() { if (server != null) { PluginLogger.print("Telemachus data link shutting down."); server.stopServing(); server = null; webSocketServer.stopServing(); webSocketServer = null; } }
static private void readConfiguration() { config.load(); int port = config.GetValue <int>("PORT"); if (port != 0) { serverConfig.port = port; } else { PluginLogger.print("No port in configuration file."); } String ip = config.GetValue <String>("IPADDRESS"); if (ip != null) { try { serverConfig.addIPAddressAsString(ip); } catch { PluginLogger.print("Invalid IP address in configuration file, falling back to find."); } } else { PluginLogger.print("No IP address in configuration file."); } serverConfig.maxRequestLength = config.GetValue <int>("MAXREQUESTLENGTH"); if (serverConfig.maxRequestLength < 8000) { PluginLogger.print("No max request length specified, setting to 8000."); serverConfig.maxRequestLength = 10000; } else { PluginLogger.print("Max request length set to:" + serverConfig.maxRequestLength); } serverConfig.version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); serverConfig.name = "Telemachus"; serverConfig.backLog = 1000; isPartless = config.GetValue <int>("PARTLESS") == 0 ? false : true; PluginLogger.print("Partless:" + isPartless); }
public void Update() { delayedAPIRunner.execute(); if (FlightGlobals.fetch != null) { vesselChangeDetector.update(FlightGlobals.ActiveVessel); } else { PluginLogger.debug("Flight globals was null during start up; skipping update of vessel change."); } }
/// <summary> /// Deregisters an API plugin handler instance via handler reference /// </summary> /// <param name="handler">The plugin handler for the plugin</param> private void Deregister(PluginHandler handler) { PluginLogger.print("Removing registered plugin " + handler.instance.ToString()); // Remove the handler caches lock (_dataLock) { foreach (var api in handler.known_handlers) { handlers.Remove(api); } registeredPlugins.Remove(handler); } }
public void openBrowser() { try { Application.OpenURL("http://" + TelemachusBehaviour.getServerPrimaryIPAddress() + ":" + TelemachusBehaviour.getServerPort() + "/telemachus/information.html"); } catch { PluginLogger.print( "Unable to open the data link. Please try to edit the Telemachus configuration file manually"); } }
static private void startDataLink() { if (webServer == null) { try { PluginLogger.print("Telemachus data link starting"); readConfiguration(); // Data access tools vesselChangeDetector = new VesselChangeDetector(isPartless); apiInstance = new KSPAPI(JSONFormatterProvider.Instance, vesselChangeDetector, serverConfig, pluginManager); // Create the dispatcher and handlers. Handlers added in reverse priority order so that new ones are not ignored. webDispatcher = new KSPWebServerDispatcher(); webDispatcher.AddResponder(new ElseResponsibility()); webDispatcher.AddResponder(new IOPageResponsibility()); var cameraLink = new CameraResponsibility(apiInstance, rateTracker); webDispatcher.AddResponder(cameraLink); var dataLink = new DataLinkResponsibility(apiInstance, rateTracker); webDispatcher.AddResponder(dataLink); // Create the server and associate the dispatcher webServer = new HttpServer(serverConfig.ipAddress, serverConfig.port); webServer.OnGet += webDispatcher.DispatchRequest; webServer.OnPost += webDispatcher.DispatchRequest; // Create the websocket server and attach to the web server webServer.AddWebSocketService("/datalink", () => new KSPWebSocketService(apiInstance, rateTracker)); // Finally, start serving requests! try { webServer.Start(); } catch (Exception ex) { PluginLogger.print("Error starting web server: " + ex.ToString()); throw; } PluginLogger.print("Telemachus data link listening for requests on the following addresses: (" + string.Join(", ", serverConfig.ValidIpAddresses.Select(x => x.ToString() + ":" + serverConfig.port.ToString()).ToArray()) + "). Try putting them into your web browser, some of them might not work."); } catch (Exception e) { PluginLogger.print(e.Message); PluginLogger.print(e.StackTrace); } } }
public void Update() { delayedAPIRunner.execute(); if (FlightGlobals.fetch != null) { vesselChangeDetector.update(FlightGlobals.ActiveVessel); kspWebSocketDataStreamer.update(new UpdateTimerEventArgs(Time.time * MICRO_SECONDS)); } else { PluginLogger.debug("Flight globals was null during start up; skipping update of vessel change."); } }
public bool process(Servers.AsynchronousServer.ClientConnection cc, HTTPRequest request) { DataSources dataSources = new DataSources(); if (request.path.StartsWith(PAGE_PREFIX)) { if (request.requestType == HTTPRequest.GET) { dataRates.addUpLinkPoint(System.DateTime.Now, request.path.Length * UpLinkDownLinkRate.BITS_PER_BYTE); } else if (request.requestType == HTTPRequest.POST) { dataRates.addUpLinkPoint(System.DateTime.Now, request.content.Length * UpLinkDownLinkRate.BITS_PER_BYTE); } try { dataSources.vessel = kspAPI.getVessel(); } catch (Exception e) { PluginLogger.debug(e.Message + " " + e.StackTrace); } if (request.requestType == HTTPRequest.GET) { dataRates.addDownLinkPoint( System.DateTime.Now, ((Servers.MinimalHTTPServer.ClientConnection)cc).Send(new OKResponsePage( argumentsParse(request.path.Remove(0, request.path.IndexOf(ARGUMENTS_START) + 1), dataSources) )) * UpLinkDownLinkRate.BITS_PER_BYTE); } else if (request.requestType == HTTPRequest.POST) { dataRates.addDownLinkPoint( System.DateTime.Now, ((Servers.MinimalHTTPServer.ClientConnection)cc).Send(new OKResponsePage( argumentsParse(request.content, dataSources) )) * UpLinkDownLinkRate.BITS_PER_BYTE); } return(true); } return(false); }
/// Iterate over all responders to find one that works public void DispatchRequest(object sender, HttpRequestEventArgs request) { foreach (var responder in responderChain.Reverse <IHTTPRequestResponder>()) { try { if (responder.process(request.Request, request.Response)) { return; } } catch (Exception ex) { PluginLogger.print("Caught exception in web handlers: " + ex.ToString()); } } // If here, we had no responder. request.Response.StatusCode = (int)HttpStatusCode.NotFound; }
/// <summary> /// Registers a plugin API with Telemachus /// </summary> /// <param name="toRegister">An instance of a Plugin object, that conforms to the TelemachusPlugin interface. /// NOTE: Does NOT have to be a physical instance of the interface.</param> /// <returns>An Action, calling of which Deregisters the plugin. This is disposeable.</returns> public void Register(object toRegister) { var pluginType = toRegister.GetType(); // Must conform at least to the minimal interface if (!typeof(IMinimalTelemachusPlugin).IsAssignableFrom(pluginType) && !pluginType.DoesMatchInterfaceOf(typeof(IMinimalTelemachusPlugin))) { throw new ArgumentException("Object " + toRegister.GetType().ToString() + " does not conform to the minimal interface"); } var handler = new PluginHandler() { instance = toRegister }; // Get a list of commands that this instance handles var commands = ReadCommandList(toRegister); // Get the plugin handler function var apiMethod = toRegister.GetType().GetMethod("GetAPIHandler", new Type[] { typeof(string) }); handler.apiHandler = (APIHandler)Delegate.CreateDelegate(typeof(APIHandler), toRegister, apiMethod); // Does it match the Deregistration? If so, pass it the deregistration method if (toRegister is IDeregisterableTelemachusPlugin || pluginType.DoesMatchInterfaceOf(typeof(IDeregisterableTelemachusPlugin))) { Action deregistration = () => Deregister(handler); pluginType.GetProperty("Deregister").SetValue(toRegister, deregistration, null); handler.is_deregisterable = true; } var optional_interfaces = new List <string>(); if (handler.is_deregisterable) { optional_interfaces.Add("Deregister"); } PluginLogger.print("Got plugin registration call for " + toRegister.GetType() + ".\n Optional interfaces enabled: " + (optional_interfaces.Count == 0 ? "None" : string.Join(", ", optional_interfaces.ToArray()))); // Make a simple hashset of basic commands handler.commands = new HashSet <string>(commands.Where(x => !x.Contains("*"))); // Now, deal with any wildcard plugin strings by building a regex handler.regexCommands = commands .Where(x => x.Contains("*")) .Select(x => new Regex("^" + x.Replace(".", "\\.").Replace("*", ".*") + "$")).ToList(); lock (_dataLock) registeredPlugins.Add(handler); }
public bool process(HttpListenerRequest request, HttpListenerResponse response) { PluginLogger.print("Falling back on default handler"); // For now, copy the behaviour until we understand it more if (!KSP.IO.FileInfo.CreateForType <TelemachusDataLink>(INDEX_PAGE).Exists) { throw new FileNotFoundException("Unable to find the Telemachus index page. Is it installed in the PluginData folder?"); } else if (request.RawUrl == "/" || request.RawUrl.ToLowerInvariant().StartsWith("/index")) { // Just redirect them var index = new Uri(request.Url, "/" + INDEX_PAGE); response.Redirect(index.ToString()); return(true); } return(false); }
private void rate(string p) { int proposedRate = 0; try { proposedRate = int.Parse(p); if (proposedRate >= MAX_STREAM_RATE) { streamRate = proposedRate; } } catch (Exception) { PluginLogger.debug("Swallowing integer parse failure when setting stream rate."); } }
static private void startDataLink() { if (server == null) { try { PluginLogger.print("Telemachus data link starting"); readConfiguration(); server = new Server(serverConfig); server.ServerNotify += HTTPServerNotify; server.addHTTPResponsibility(new ElseResponsibility()); ioPageResponsibility = new IOPageResponsibility(); server.addHTTPResponsibility(ioPageResponsibility); vesselChangeDetector = new VesselChangeDetector(isPartless); dataLinkResponsibility = new DataLinkResponsibility(serverConfig, new KSPAPI(JSONFormatterProvider.Instance, vesselChangeDetector, serverConfig)); server.addHTTPResponsibility(dataLinkResponsibility); Servers.MinimalWebSocketServer.ServerConfiguration webSocketconfig = new Servers.MinimalWebSocketServer.ServerConfiguration(); webSocketconfig.bufferSize = 300; webSocketServer = new Servers.MinimalWebSocketServer.Server(webSocketconfig); webSocketServer.ServerNotify += WebSocketServerNotify; kspWebSocketService = new KSPWebSocketService(new KSPAPI(JSONFormatterProvider.Instance, vesselChangeDetector, serverConfig), kspWebSocketDataStreamer); webSocketServer.addWebSocketService("/datalink", kspWebSocketService); webSocketServer.subscribeToHTTPForStealing(server); server.startServing(); PluginLogger.print("Telemachus data link listening for requests on the following addresses: (" + server.getIPsAsString() + "). Try putting them into your web browser, some of them might not work."); } catch (Exception e) { PluginLogger.print(e.Message); PluginLogger.print(e.StackTrace); } } }
public void Update() { delayedAPIRunner.execute(); if (FlightGlobals.fetch != null) { vesselChangeDetector.update(FlightGlobals.ActiveVessel); foreach (var client in webServer.WebSocketServices["/datalink"].Sessions.Sessions.OfType <KSPWebSocketService>()) { if (client.UpdateRequired(Time.time)) { client.SendDataUpdate(); } } } else { PluginLogger.debug("Flight globals was null during start up; skipping update of vessel change."); } }
static private void readConfiguration() { config.load(); int port = config.GetValue <int>("PORT"); if (port != 0) { serverConfig.port = port; } else { PluginLogger.print("No port in configuration file."); } String ip = config.GetValue <String>("IPADDRESS"); if (ip != null) { try { serverConfig.addIPAddressAsString(ip); } catch { PluginLogger.print("Invalid IP address in configuration file, falling back to find."); } } else { PluginLogger.print("No IP address in configuration file."); } serverConfig.maxRequestLength = 8000; serverConfig.version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); serverConfig.name = "Telemachus"; serverConfig.backLog = 1000; }
public void parseParams(ref String arg, ref DataSources dataSources) { dataSources.args.Clear(); try { if (arg.Contains("[")) { String[] argsSplit = arg.Split('['); argsSplit[1] = argsSplit[1].Substring(0, argsSplit[1].Length - 1); arg = argsSplit[0]; String[] paramSplit = argsSplit[1].Split(','); for (int i = 0; i < paramSplit.Length; i++) { dataSources.args.Add(paramSplit[i]); } } } catch (Exception e) { PluginLogger.debug(e.Message + " " + e.StackTrace); } }
private String argumentsParse(String args, DataSources dataSources) { APIEntry currentEntry = null; var APIResults = new Dictionary <string, object>(); String[] argsSplit = args.Split(ARGUMENTS_DELIMETER); foreach (String arg in argsSplit) { string refArg = arg; PluginLogger.fine(refArg); kspAPI.parseParams(ref refArg, ref dataSources); currentEntry = argumentParse(refArg, dataSources); APIResults[dataSources.getVarName()] = currentEntry.formatter.prepareForSerialization(currentEntry.function(dataSources)); //Only parse the paused argument if the active vessel is null if (dataSources.vessel == null) { break; } } return(SimpleJson.SimpleJson.SerializeObject(APIResults)); }
private String argumentsParse(String args, DataSources dataSources) { APIEntry currentEntry = null; List <string> APIResults = new List <string>(); String[] argsSplit = args.Split(ARGUMENTS_DELIMETER); foreach (String arg in argsSplit) { string refArg = arg; PluginLogger.fine(refArg); kspAPI.parseParams(ref refArg, ref dataSources); currentEntry = argumentParse(refArg, dataSources); APIResults.Add(currentEntry.formatter.format(currentEntry.function(dataSources))); //Only parse the paused argument if the active vessel is null if (dataSources.vessel == null) { break; } } return(currentEntry.formatter.pack(APIResults)); }
private void streamData(object sender, ElapsedEventArgs e) { streamTimer.Interval = streamRate; DataSources dataSources = new DataSources(); if (toRun.Count + subscriptions.Count > 0) { try { List <string> entries = new List <string>(); APIEntry entry = null; lock (subscriptionLock) { dataSources.vessel = kspAPI.getVessel(); //Only parse the paused argument if the active vessel is null if (dataSources.vessel != null) { toRun.UnionWith(subscriptions); foreach (string s in toRun) { DataSources dataSourcesClone = dataSources.Clone(); string trimedQuotes = s.Trim(); string refArg = trimedQuotes; kspAPI.parseParams(ref refArg, ref dataSourcesClone); kspAPI.process(refArg, out entry); if (entry != null) { dataSourcesClone.setVarName(trimedQuotes); entries.Add(entry.formatter.format(entry.function(dataSourcesClone), dataSourcesClone.getVarName())); } } toRun.Clear(); if (entry != null) { WebSocketFrame frame = new WebSocketFrame(ASCIIEncoding.UTF8.GetBytes(entry.formatter.pack(entries))); byte[] bFrame = frame.AsBytes(); dataRates.addDownLinkPoint(System.DateTime.Now, bFrame.Length * UpLinkDownLinkRate.BITS_PER_BYTE); clientConnection.Send(bFrame); } } else { sendNullMessage(); } } } catch (NullReferenceException) { PluginLogger.debug("Swallowing null reference exception, potentially due to async game state change."); sendNullMessage(); } catch (Exception ex) { PluginLogger.debug("Closing socket due to potential client disconnect:" + ex.GetType().ToString()); close(); } } else { sendNullMessage(); } }
} // OnMessage /// Read all variables and send back the responses for just this client public void SendDataUpdate() { // Don't do anything if we are e.g. still awaiting data to be fully set if (!readyToSend) { return; } lastUpdate = UnityEngine.Time.time; // Grab all of the variables at once string[] allVariables; lock (dataLock) { allVariables = subscriptions.Union(oneShotRuns).Union(binarySubscriptions).ToArray(); oneShotRuns.Clear(); } var vessel = api.getVessel(); // Now, process them all into a data dictionary var apiResults = new Dictionary <string, object>(); var unknowns = new List <string>(); var errors = new Dictionary <string, string>(); foreach (var apiString in allVariables) { try { apiResults[apiString] = api.ProcessAPIString(apiString); } catch (IKSPAPI.UnknownAPIException) { // IF we get this message, we know it was because no variable was found unknowns.Add(apiString); } catch (IKSPAPI.VariableNotEvaluable) { // We can't evaluate this at the moment. Just ignore until we can. } catch (Exception ex) { errors[apiString] = ex.ToString(); } } if (unknowns.Count > 0) { apiResults["unknown"] = unknowns; } if (errors.Count > 0) { apiResults["errors"] = errors; } // Handle sending a binary packet if requested if (binarySubscriptions.Length > 0) { var variableValues = new List <float>(); // Read every binary value foreach (var name in binarySubscriptions) { try { variableValues.Add(Convert.ToSingle(apiResults[name])); } catch (Exception ex) { variableValues.Add(0); if (apiResults.ContainsKey(name)) { errors[name] = "Error streaming to binary " + name + "='" + apiResults[name] + "'; " + ex.ToString(); } else { errors[name] = "Error streaming to binary: value for " + name + " not found; " + ex.ToString(); } } } // Which byte translation? Func <float, IEnumerable <byte> > reverser = x => BitConverter.GetBytes(x).Reverse(); var byteTranslation = BitConverter.IsLittleEndian ? reverser : BitConverter.GetBytes; // Now translate these to binary bytes var byteData = new List <byte>(); byteData.Add(1); byteData.AddRange(variableValues.SelectMany(x => byteTranslation(x))); SendAsync(byteData.ToArray(), x => { }); } //if (allVariables.Contains("binaryNavigation")) //{ // allVariables = allVariables.Where(x => x != "binaryNavigation").ToArray(); // // Build and dispatch the binary information, here and quickly.... // var pitch = Convert.ToSingle(api.ProcessAPIString("n.pitch")); // var roll = Convert.ToSingle(api.ProcessAPIString("n.roll")); // var heading = Convert.ToSingle(api.ProcessAPIString("n.heading")); // var deltaV = Convert.ToSingle(api.ProcessAPIString("v.verticalSpeed")); // var parts = new List<byte[]>(); // parts.Add(new byte[] { 1 }); // parts.Add(BitConverter.GetBytes(heading)); // parts.Add(BitConverter.GetBytes(pitch)); // parts.Add(BitConverter.GetBytes(roll)); // parts.Add(BitConverter.GetBytes(deltaV)); // if (BitConverter.IsLittleEndian) parts = parts.Select(x => x.Reverse().ToArray()).ToList(); // var byteData = parts.SelectMany(x => x).ToArray(); // SendAsync(byteData, x => { }); // PluginLogger.print(string.Format("Send byte data for {0}, {1}, {2}, {3}", heading, pitch, roll, deltaV)); //} var data = SimpleJson.SimpleJson.SerializeObject(apiResults); // Now, if we have data send a message, otherwise send a null message readyToSend = false; try { SendAsync(data, (b) => readyToSend = true); dataRates.SendDataToClient(data.Length); } catch (Exception ex) { PluginLogger.print("Caught " + ex.ToString()); } finally { readyToSend = true; } }
/// Process a message recieved from a client protected override void OnMessage(MessageEventArgs e) { // We only care about text messages, for now. if (e.Type != Opcode.Text) { return; } dataRates.RecieveDataFromClient(e.RawData.Length); // deserialize the message as JSON var json = SimpleJson.SimpleJson.DeserializeObject(e.Data) as SimpleJson.JsonObject; lock (dataLock) { // Do any tasks requested here foreach (var entry in json) { // Try converting the item to a list - this is the most common expected. // If we got a string, then add it to the list to allow "one-shot" submission string[] listContents = new string[] { }; if (entry.Value is SimpleJson.JsonArray) { listContents = (entry.Value as SimpleJson.JsonArray).OfType <string>().Select(x => x.Trim()).ToArray(); } else if (entry.Value is string) { listContents = new[] { entry.Value as string }; } // Process the possible API entries if (entry.Key == "+") { PluginLogger.print(string.Format("Client {0} added {1}", ID, string.Join(",", listContents))); subscriptions.UnionWith(listContents); } else if (entry.Key == "-") { PluginLogger.print(string.Format("Client {0} removed {1}", ID, string.Join(",", listContents))); subscriptions.ExceptWith(listContents); } else if (entry.Key == "run") { PluginLogger.print(string.Format("Client {0} running {1}", ID, string.Join(",", listContents))); oneShotRuns.UnionWith(listContents); } else if (entry.Key == "rate") { streamRate = Convert.ToInt32(entry.Value); PluginLogger.print(string.Format("Client {0} setting rate {1}", ID, streamRate)); } else if (entry.Key == "binary") { binarySubscriptions = listContents; PluginLogger.print(string.Format("Client {0} requests binary packets {1}", ID, string.Join(", ", listContents))); } else { PluginLogger.print(String.Format("Client {0} send unrecognised key {1}", ID, entry.Key)); } } } // Lock } // OnMessage
void LookForModsToInject() { string foundMods = "Loading; Looking for compatible mods to inject registration....\nTelemachus compatible modules Found:\n"; int found = 0; foreach (var asm in AssemblyLoader.loadedAssemblies) { foreach (var type in asm.assembly.GetTypes()) { if (type.IsSubclassOf(typeof(MonoBehaviour))) { // Does this have a static property named "Func<string> TelemachusPluginRegister { get; set; }? var prop = type.GetProperty("TelemachusPluginRegister", BindingFlags.Static | BindingFlags.Public); if (prop == null) { continue; } found += 1; foundMods += " - " + type.ToString() + " "; if (prop.PropertyType != typeof(Action <object>)) { foundMods += "(Fail - Invalid property type)\n"; continue; } if (!prop.CanWrite) { foundMods += "(Fail - Property not writeable)\n"; continue; } // Can we read it - if so, only write if it is not null. if (prop.CanRead) { if (prop.GetValue(null, null) != null) { foundMods += "(Fail - Property not null)\n"; continue; } } // Write the value here Action <object> pluginRegister = PluginRegistration.Register; prop.SetValue(null, pluginRegister, null); foundMods += "(Success)\n"; } } } if (found == 0) { foundMods += " None\n"; } foundMods += "Internal plugins loaded:\n"; found = 0; // Look for any mods in THIS assembly that inherit ITelemachusMinimalPlugin... foreach (var typ in Assembly.GetExecutingAssembly().GetTypes()) { try { if (!typeof(IMinimalTelemachusPlugin).IsAssignableFrom(typ)) { continue; } // Make sure we have a default constructor if (typ.GetConstructor(Type.EmptyTypes) == null) { continue; } // We have found a plugin internally. Instantiate it PluginRegistration.Register(Activator.CreateInstance(typ)); foundMods += " - " + typ.ToString() + "\n"; found += 1; } catch (Exception ex) { PluginLogger.print("Exception caught whilst loading internal plugin " + typ.ToString() + "; " + ex.ToString()); } } if (found == 0) { foundMods += " None"; } PluginLogger.print(foundMods); }
private static void WebSocketServerNotify(object sender, Servers.NotifyEventArgs e) { PluginLogger.debug(e.message); }