Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Console System Improvements #46

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2d4a377
Move Run from consolesystem.h to cvarmanager
MuffinTastic Jan 30, 2023
e28afa7
Reorganize cvarmanager
MuffinTastic Jan 30, 2023
3354c9a
Add optional callbacks to cvars
MuffinTastic Jan 30, 2023
4b22b02
Implement concommands
MuffinTastic Jan 30, 2023
438870f
Use references directly to get rid of some unnecessary looping
MuffinTastic Jan 30, 2023
be8f9cc
Move list to a CCmd for proof of concept
MuffinTastic Jan 30, 2023
bebca44
Parse text for quotes, comments, break statements up with ';'. Pass a…
MuffinTastic Jan 30, 2023
d0d0b37
Shuffle the CVarEntry& variants to the struct, made no sense the othe…
MuffinTastic Jan 30, 2023
1454a1c
Make CVarSystem API a little more user-friendly, add a few bindings
MuffinTastic Jan 30, 2023
fc6303a
Change Run, GetStatements, GetStatementArguments' docs/args a tad
MuffinTastic Jan 31, 2023
9753b5d
Managed ConCmds
MuffinTastic Jan 31, 2023
2a94989
Compile fix
MuffinTastic Jan 31, 2023
96a966e
GetStatementArguments/GetStatements uniformity
MuffinTastic Jan 31, 2023
3d30764
Add TryConvert() and several ToX() extensions to StringExtensions
MuffinTastic Jan 31, 2023
1b7a01e
Revamp dispatch, convert strings to callback parameter types on the fly
MuffinTastic Jan 31, 2023
5271736
Merge fixes
MuffinTastic Jan 31, 2023
7d71493
Remove debug logfrom ConCmd dispatch
MuffinTastic Jan 31, 2023
75d7540
Hook up ConVar dispatches (can't test right now)
MuffinTastic Jan 31, 2023
5e5ed0e
Make CVarSystem case insensitive by lowering strings when getting hashes
MuffinTastic Jan 31, 2023
3b36366
Rely more heavily on ConCmds variants rather than setting flags directly
MuffinTastic Jan 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions Source/Common/Console/CVarFlags.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Mocha.Common;

public enum CVarFlags
{
// Mirrors native

None = 0,

// If this isn't present, it's inherently assumed to be a variable
Command = 1 << 0,

// If this is present, it lives in managed space
Managed = 1 << 1,

// This cvar was created by the game, it should be wiped on hotload
Game = 1 << 2,

// Save this convar to cvars.json
Archive = 1 << 3,

Cheat = 1 << 4,

Temp = 1 << 5,

Replicated = 1 << 6,
}
61 changes: 61 additions & 0 deletions Source/Common/Console/ConCmd.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
namespace Mocha.Common;

public static class ConCmd
{
public abstract class BaseAttribute : Attribute
{
internal string? Name { get; init; }
internal CVarFlags Flags { get; init; }
internal string? Description { get; init; }

public BaseAttribute( string? name, CVarFlags flags, string? description )
{
Name = name;
Flags = flags;
Description = description;
}
}

// public class ServerAttribute
// public class ClientAttribute

public sealed class TestAttribute : BaseAttribute
{
public TestAttribute()
: base( null, CVarFlags.None, null )
{

}
public TestAttribute( string name )
: base( name, CVarFlags.None, null )
{

}

public TestAttribute( string name, string description )
: base( name, CVarFlags.None, description )
{

}
}

public sealed class CheatAttribute : BaseAttribute
{
public CheatAttribute()
: base( null, CVarFlags.Cheat, null )
{

}
public CheatAttribute( string name )
: base( name, CVarFlags.Cheat, null )
{

}

public CheatAttribute( string name, string description )
: base( name, CVarFlags.Cheat, description )
{

}
}
}
16 changes: 16 additions & 0 deletions Source/Common/Console/ConVar.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Mocha.Common;

public static class ConVar
{
public abstract class BaseAttribute : Attribute
{
public required string Name { get; init; }
public required CVarFlags Flags { get; init; }
public required string Description { get; init; }
}

public class TestAttribute : Attribute
{

}
}
41 changes: 41 additions & 0 deletions Source/Common/Console/ConsoleDispatchInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace Mocha.Common.Console;


[StructLayout( LayoutKind.Sequential )]
public struct ConCmdDispatchInfo
{
public IntPtr name;
public IntPtr data;
public int size;
}

[StructLayout( LayoutKind.Sequential )]
public struct StringCVarDispatchInfo
{
public IntPtr name;
public IntPtr oldValue;
public IntPtr newValue;
}

[StructLayout( LayoutKind.Sequential )]
public struct FloatCVarDispatchInfo
{
public IntPtr name;
public float oldValue;
public float newValue;
}

[StructLayout( LayoutKind.Sequential )]
public struct BoolCVarDispatchInfo
{
public IntPtr name;
public bool oldValue;
public bool newValue;
}
176 changes: 176 additions & 0 deletions Source/Common/Console/ConsoleSystem.Internal.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Mocha.Common;

public static partial class ConsoleSystem
{
public static class Internal
{
internal struct ConCmdCallbackInfo
{
public required MethodInfo method;
public required ParameterInfo[] parameters;
}

internal static class ConVarCallbackStore<T>
{
public static Dictionary<string, Action<T, T>> Callbacks = new();
}

internal static List<string> s_items = new();

internal static Dictionary<string, ConCmdCallbackInfo> s_commandCallbacks = new();

internal static void RegisterCommand( string name, CVarFlags flags, string description, ConCmdCallbackInfo callbackInfo )
{
Glue.ConsoleSystem.RegisterCommand( name, flags, description );
s_commandCallbacks[name] = callbackInfo;
s_items.Add( name );
}

internal static void RegisterStringConVar( string name, string value, CVarFlags flags, string description, Action<string, string> callback )
{
Glue.ConsoleSystem.RegisterString( name, value, flags, description );
ConVarCallbackStore<string>.Callbacks[name] = callback;
s_items.Add( name );
}

internal static void RegisterFloatConVar( string name, float value, CVarFlags flags, string description, Action<float, float> callback )
{
Glue.ConsoleSystem.RegisterFloat( name, value, flags, description );
ConVarCallbackStore<float>.Callbacks[name] = callback;
s_items.Add( name );
}

internal static void RegisterBoolConVar( string name, bool value, CVarFlags flags, string description, Action<bool, bool> callback )
{
Glue.ConsoleSystem.RegisterBool( name, value, flags, description );
ConVarCallbackStore<bool>.Callbacks[name] = callback;
s_items.Add( name );
}

/// <summary>
/// Register all of the CVars in an assembly
/// </summary>
/// <param name="assembly">The assembly to grab from</param>
/// <param name="extraFlags">Extra flags to force each CVar to have. Used mainly for hotloaded assemblies</param>
public static void RegisterAssembly( Assembly assembly, CVarFlags extraFlags = CVarFlags.None )
{
if ( assembly is null )
return;

foreach ( Type type in assembly.GetTypes() )
{
foreach ( MethodInfo method in type.GetMethods( BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic ) )
{
ConCmd.BaseAttribute? customAttribute = method.GetCustomAttribute<ConCmd.BaseAttribute>();
if ( customAttribute is not null )
{
var parameters = method.GetParameters();

var callbackInfo = new ConCmdCallbackInfo
{
method = method,
parameters = parameters
};

RegisterCommand( customAttribute.Name ?? method.Name, customAttribute.Flags | extraFlags, customAttribute.Description ?? "", callbackInfo );
}
}
}
}

public static void ClearGameCVars()
{
foreach ( var name in s_items.ToList() )
{
bool exists = Glue.ConsoleSystem.Exists( name );
CVarFlags flags = Glue.ConsoleSystem.GetFlags( name );

if ( exists && (flags & CVarFlags.Game) != 0 )
{
s_items.Remove( name );
s_commandCallbacks.Remove( name );
ConVarCallbackStore<string>.Callbacks.Remove( name );
ConVarCallbackStore<float>.Callbacks.Remove( name );
ConVarCallbackStore<bool>.Callbacks.Remove( name );

Glue.ConsoleSystem.Remove( name );
}
}
}

public static void DispatchCommand( string name, List<string> dispatchArguments )
{
if ( !s_commandCallbacks.TryGetValue( name, out ConCmdCallbackInfo callbackInfo ) )
return;

ParameterInfo[] callbackParameters = callbackInfo.parameters;

if ( callbackParameters.Length == 1 &&
callbackParameters[0].ParameterType == dispatchArguments.GetType() )
{
// All it takes is a List<string> so we can probably assume they want direct access to the arguments
callbackInfo.method.Invoke( null, new object[] { dispatchArguments } );
return;
}

object?[]? arguments = null;

if ( callbackParameters.Length > 0 )
{
//
// Softly convert arguments from strings to whatever the callback expects
//

arguments = new object[callbackParameters.Length];

for ( int i = 0; i < callbackParameters.Length; i++ )
{
object? value;

ParameterInfo parameter = callbackParameters[i];
Type parameterType = parameter.ParameterType;

if ( i < dispatchArguments.Count )
{
// Try to convert
if ( !dispatchArguments[i].TryConvert( parameterType, out value ) )
{
Log.Error( $"Error dispatching ConCmd '{name}': Couldn't convert '{dispatchArguments[i]}' to type {parameterType}" );
return;
}
}
else if ( parameter.HasDefaultValue )
{
// There weren't enough arguments passed in,
// but we have a default value, so use that instead
value = parameter.DefaultValue;
}
else
{
Log.Error( $"Error dispatching ConCmd '{name}': Not enough arguments - expected {callbackParameters.Length}, got {dispatchArguments.Count}" );
return;
}

arguments[i] = value;
}
}

callbackInfo.method.Invoke( null, arguments );
}

public static void DispatchConVarCallback<T>( string name, T oldValue, T newValue )
{
if ( !ConVarCallbackStore<T>.Callbacks.TryGetValue( name, out var callback ) )
return;

callback( oldValue, newValue );
}
}
}
43 changes: 43 additions & 0 deletions Source/Common/Console/ConsoleSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Reflection;
using Mocha.Common.Console;

namespace Mocha.Common;

public static partial class ConsoleSystem
{
public static void Run( string command )
{
Glue.ConsoleSystem.Run( command );
}

public static float GetFloat( string name )
{
return Glue.ConsoleSystem.GetFloat( name );
}

public static bool GetBool( string name )
{
return Glue.ConsoleSystem.GetBool( name );
}

public static void Set( string name, string value )
{
Glue.ConsoleSystem.SetString( name, value );
}

public static void Set( string name, float value )
{
Glue.ConsoleSystem.SetFloat( name, value );
}

public static void Set( string name, bool value )
{
Glue.ConsoleSystem.SetBool( name, value );
}

public static void SetFromString( string name, string value )
{
Glue.ConsoleSystem.FromString( name, value );
}
}
Loading