Thanks!
I implemented gl_overbright since i was asked about it.
Without overbright: With overbright:
Created 6 years ago2018-05-06 19:15:02 UTC by Solokiller
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.<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).<?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).private static void ConfigureAPI(APIConfigurationBuilder builder)
{
builder.AddAssembly(typeof(App).Assembly);
builder.AddNamespace(typeof(App).Namespace);
{
var type = builder.AddType<App>();
type.AddMethod(a => a.Test(default, default));
}
{
var type = builder.AddType<string>();
type.AddMember(() => string.Empty);
type.AddMethod(s => s.Split(default));
}
}
First you add assemblies that you want scripts to be able to access. Then you can add namespaces and types to grant or deny access to.default
and default(type)
is pretty common.APIConfiguration
instance denies access to everything except built-in types (int, float, bool, string, etc).private static Type FindEntityClass(CSharpScriptInfo info, Assembly assembly)
{
var scriptClasses = assembly.GetTypes().Where(t => t.IsClass && typeof(TScript).IsAssignableFrom(t)).ToList();
if (scriptClasses.Count == 0)
{
//No classes found that extend from the required type
throw some exception;
}
if (scriptClasses.Count > 1)
{
//More than one candidate class found
throw some exception;
}
return scriptClasses[0];
}
Where TScript
is the type of the entity that it needs to inherit from.//The script system should cache the loaded script so repeated calls return the same script instance
var script = Context.ScriptSystem.Load("my_script_file.csx");
//The script itself doesn't contain any object instances, these are created on demand only
var entity = script?.CreateEntity(Context.EntityList, typeof(Item));
It may be easier to rework the scripting system to only provide an abstraction for the provider and letting the user get the type information using the resulting Assembly
, but this would restrict the supported languages to whatever can produce an Assembly
instance.#load
, which CSharpScript already supports but which requires me to provide a SourceReferenceResolver
. This should let you split scripts up just like you can with Angelscript. That in turn means that this resolver will need to understand how the SteamPipe filesystem works. That shouldn't be a problem since the resolver class API is pretty easy to use, and should just pass through to the filesystem class i've already created for SharpLife.<gamedir>/logs/SharpLifeWrapper-Native.log
.DebugLoggingEnabled=true
in cfg/SharpLife-Wrapper-Native.ini
), you'll now get this in the log:
[09/01/2019 18:56:14 +0100]: Managed host initialized with game directory sharplife_full (client)
[09/01/2019 18:56:14 +0100]: Configuration loaded
[09/01/2019 18:56:14 +0100]: CoreCLR loaded from C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\2.1.2
[09/01/2019 18:56:14 +0100]: Runtime started
[09/01/2019 18:56:14 +0100]: AppDomain 1 created
[09/01/2019 18:56:14 +0100]: Managed host started
[09/01/2019 18:56:14 +0100]: Attempting to load assembly and acquire entry point
[09/01/2019 18:56:14 +0100]: Created delegate to entry point SharpLife.Engine.Host.NativeLauncher.Start
[09/01/2019 18:56:14 +0100]: Attempting to execute entry point
[09/01/2019 18:56:16 +0100]: Entry point executed with exit code 0
[09/01/2019 18:56:16 +0100]: Shutting down managed host
[09/01/2019 18:56:16 +0100]: Exiting with code Success (0)
The engine now returns an exit code indicating if anything went wrong. Since the engine can't log anything until the game directory is known, a missing game directory is logged as UnhandledException
since it throws an exception. If there are no command line arguments at all the error is NoCommandLineArguments
.you told me 3d skybox and HD sky is posible.Both are possible.
are bigger maps and bigger textures posible too?
And wad files are pain in the ass too xD could you make it so we dont need wadded textures.Due to how maps reference textures it's not currently possible to ditch wad files since you can only have 15 characters in a texture name, not nearly enough for directory names.
A image file in the mod folder works very well, at least for developing a mod.
One question, what will be the structure of SL?, I mean, will it load a mod directly or it´ll depend of a Half-Life folder with the valve folder and all the rest of junk?, I like the way (don´t flame me please) Xash works. With it you must only create a folder with whatever name you want, you put the Xash exe, three dlls and the very folder of the mod in it and that´s all. Less than 10 Mb in size. And you can launch a full mod as if it was a standalone game (at least if you have all the needed assets in the MOD folder, of course).SharpLife is an all-in-one package, so you'll make a mod that're the game libraries in SharpLife, and deploy the entire engine as a Half-Life mod. This allows each mod to change the engine as needed. Currently the assemblies directory is 11.2 Mb, but that includes debug builds and debug info, so it probably adds up to a similar size when it's all release builds.
and i have coded projectile bullets before. based on ar grenadeBetter use hornetgun... But the entity limit is a problem (BIG PROBLEM) as you say. How much entities will support SL at a time before struggling?, also, and remember that I am quite an ignorant in coding, will it depend of the PC specs or this number will adapt itself independently of the system on which SL will run on?