I'm using the seed that you posted with the mods you posted yesterday. I haven't changed it at all, and everything else is generating properly.
Also see this: https://twitter.com/voxcpw/status/1058816540659867648
_Solokiller_
.<?xml version="1.0"?>
<PluginListFile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Plugins>
<PluginConfiguration>
<AssemblyName>TestPlugin.dll</AssemblyName>
<PluginClassName>TestPlugin.TestPlugin</PluginClassName>
<Arguments>
<TestPluginArguments>
<Name>Test Plugin</Name>
</TestPluginArguments>
<cvar name="developer" value="2"/>
<cvar name="sv_cheats" value="1"/>
</Arguments>
</PluginConfiguration>
</Plugins>
</PluginListFile>
A plugin is loaded based on assembly name, the plugin is instantiated based on the given class name.PluginConfigurationArguments
.using PluginHost;
using System.Xml;
using System.Xml.Serialization;
namespace TestPlugin
{
public sealed class TestPlugin : IPlugin
{
public string Name => "TestPlugin";
public TestPlugin(PluginConfigurationArguments arguments)
{
var serializer = new XmlSerializer(typeof(TestPluginArguments));
var myArguments = serializer.Deserialize(new XmlNodeReader(arguments.Elements[0]));
}
public void Initialize()
{
}
public void Shutdown()
{
}
}
}
This plugin takes only the config arguments, and then converts it into an object of type TestPluginArguments
, which looks like this:
namespace TestPlugin
{
public class TestPluginArguments
{
public string Name { get; set; }
}
}
This allows you to specify complex arguments and read it with ease. Note that the example does not do error handling, but that's not hard.pluginManager.ForEach(plugin => plugin.DoSomething());
foreach (var plugin in pluginManager.GetPlugins())
{
plugin.DoSomething();
}
The first option is the simplest and won't break easily. It does however create delegates which can add overhead in critical per-frame game logic like RunFrame
. The latter option avoids that cost.Event
object around that has properties to support preempting further processing of an event. This is similar to features that exist on most GUI frameworks such as JavaScript's preventDefault.SayText
pretty easy to implement and use, and avoids the direct reference between the code that emits the event and the plugin system.public sealed class TestPlugin : IPlugin
{
private readonly IEventSystem _eventSystem;
public string Name => "TestPlugin";
public TestPlugin(IEventSystem eventSystem)
{
_eventSystem = eventSystem ?? throw new ArgumentNullException(nameof(eventSystem));
}
public void Initialize()
{
//The event system can deduce the event name from the event object type
_eventSystem.AddListener(OnSayText);
}
public void Shutdown()
{
//Remove all listeners registered by this plugin object (but not other objects created by the plugin!)
_eventSystem.RemoveListener(this);
}
private void OnSayText(SayTextEvent @event)
{
//Chat text starting with this command is hidden and suppressed from further event handling
if (@event.CommandLine.FirstOrDefault() == "/hide")
{
@event.Visible = false;
@event.Cancel = true;
}
}
}
This way plugins can plug into the engine and game without ever needing to expose the concept of plugins anywhere.Initialize
and removing them in Shutdown
plugins and the game itself can be rebooted, which can flush all past state.ICommandContext
. The same goes for registering entities; taking the entity factory and registering itself as providing a set of entities that override the normal entities so it can override built-in entities when needed (that isn't implemented yet).I think you can do that already. What other integration mods do you suggest?Looks like it isn't necessary anymore these days, it was the case in older versions.
config\enderio\recipes\examples\legacy_recipes.xml
. The file explains how to use it, it reverts everything to the old redstone and iron based recipes. Since grains can only be acquired by setting bedrock on fire or using Ex Nihilo/Compressum to grind for them, it can really slow down EnderIO progression.<Property name="Position.X" value="10" condition="AspectRatio == 16/9"/>
The condition could be evaluated using CSharpScript, though in this case a constant value for 16/9 may be preferable. This would make it more flexible, for instance allowing you to support multiple ratios or different conditions (e.g. Game == "Half-Life", but that's pretty hacky).A sound VMT style thing would be perfect as a replacementI could do something that's similar to Source's soundscripts: https://developer.valvesoftware.com/wiki/Soundscripts
[Output("OnPass")]
public OutputEvent<float> OnPass { get; } = new OutputEvent<float>();
[Output("OnFail")]
public OutputEvent<float> OnFail { get; } = new OutputEvent<float>();
[Output("OnPrint")]
public OutputEvent OnPrint { get; } = new OutputEvent();
[Input("TestValue")]
public void InputTestValue(in InputData inputData, float value)
{
Console.WriteLine($"Firing {value}");
if (value != 0)
{
OnPass.Fire(EventQueue, inputData.Activator, this, value);
}
else
{
OnFail.Fire(EventQueue, inputData.Activator, this, value);
}
}
[Input("Print")]
public void InputPrint(in InputData inputData)
{
Console.WriteLine("Print");
OnPrint.Fire(EventQueue, inputData.Activator, this);
}
Inputs are public methods marked with an Input
attribute, which takes the name of the input as used in the map editor or console commands. They can optionally take a single additional parameter that is the value that outputs can pass to it (or the parameter override if specified). Inputs can also access the value as an object
instance through the InputData
instance passed to the method, which allows for variant type inputs.Output
attribute, which takes the name of the output as used in the map editor or console commands.Convert
method.public void Use(BaseEntity activator, BaseEntity caller, UseType useType)
{
//Value not passed to InputData to avoid cost of boxing
Use(new InputData(activator, caller, null), useType);
}
//Usage:
entity.Use(this, this, UseType.Toggle);
GoldSource actually uses USE_SET when +using entities, so i'll need to figure that out.