Skip to main content

Converters

Ghost Commands comes with built-in support for primitive types as well as popular Unity types such as Vector3, Quaternion, Color and more. However, there may arise instances where you'd want to extend support for your own custom types.

Supporting custom types as parameters

To demonstrate how a custom type can be supported, we are going to create a converter for an arbitrary class.

public class Person
{
public readonly string name;
public readonly int age;

public Person(string name, int age)
{
this.name = name;
this.age = age;
}
}

Converter methods

The way converters work, is by creating a method that takes in a string and return an actual value. Converter methods must return a non-primitive type, and they must only take 1 parameter of type ArgumentReader.

To mark a method as a converter method, simply place a [Converter] attribute on it.

[Converter]
public static Person PersonTypeConverter(ArgumentReader reader)
{

}
note

The method doesn't have to be located within the type we're attempting to convert. These methods can be stored anywhere you prefer.

Using the ArgumentReader

The ArgumentReader abstracts the argument, and makes it easier to retrieve information from it. The information can be retrieved and interpreted from within our method to create the actual Person instance we want to return.
Because we know the constructor takes a string and an int, we can expect the provided argument to be constructed in a way that allows us to retrieve those values.

[Converter]
public static Person PersonTypeConverter(ArgumentReader reader)
{
string name = reader.Read<string>();
int age = reader.Read<int>();

return new Person(name, age);
}

Each component of the argument is being read in order. This means that once a component has been read, it can not be read again. It simply functions as a queue. This makes it so you do not have to specify any indices, but it also means you have to store the values in order.
With the simple method, We can now support parameters of type Person. The input string could look like this: hello (Bob, 52), which would result in a new instance of Person, with name set to Bob, and age set to 52.

note

When utilizing the Read() method, the ArgumentReader will recursively attempt to convert the given argument into the specified type. If the type is primitive, it already knows how to convert it, as it is supported by default. If a custom type is specified however, it'll convert it using a custom converter method instead.

Multiple ways to interpret an argument

By getting the amount of values with reader.Length, we can support multiple ways to interpret an argument. An example would be how a UnityEngine.Color can be interpreted both from a hex value and an RGB value.

[Converter]
public static Color ColorConverter(ArgumentReader reader)
{
float r, g, b, a;

switch (reader.Length)
{
// In case the amount of components was 1
case 1:
if (reader.IsNumeric())
{
// If the component is a number, return an RGB color with this value
float value = reader.Read<float>() / 255;
return new(value, value, value);
}
else
{
// If the component can be read as a string, try to return a color based on a hex value
string hex = reader.Read<string>();
if (ColorUtility.TryParseHtmlString(hex, out Color hexColor))
return hexColor;
else
throw new Exception($"Invalid hex value: '{hex}'.");
}

// In case the amount of components was 3
case 3:
// Return an RGB value using the 3 numbers
r = reader.Read<float>();
g = reader.Read<float>();
b = reader.Read<float>();
return new(r, g, b);

// In case the amount of components was 4
case 4:
// Return an RGBA value using the 4 numbers
r = reader.Read<float>();
g = reader.Read<float>();
b = reader.Read<float>();
a = reader.Read<float>();
return new(r, g, b, a);
}

// If more or less components was provided, we notify of the error
throw new Exception($"Could not retrieve color from input: {reader.Source}.");
}
note

This converter is built-in, and is purely used as an example. You wouldn't need to create this in order to support parameters of type UnityEngine.Color.