Skip to content

Integrating With C#

mystborn edited this page Jun 22, 2018 · 3 revisions

One of the major focuses during the development was the ability to integrate with other .NET languages. You can see how that's possible from within TaffyScript (TS) by creating imports. But even more importantly is being able to use TaffyScript assemblies from within C# and other .NET languages.

Loading a Library

Setting Up

The first thing you must do is add the TaffyScript.Bcl nuget package into your project. This will add the two most important libraries: TaffyScript and TaffyScrtip.Bcl. The Bcl is only there for the TaffyScript projects, while the TaffyScript library contains all of the code needed to interact with TaffyScript objects.

Referencing a TaffyScript Library

The first way to interact with TaffyScript is to reference the library directly. However, just referencing it isn't enough. You actually have to initialize it. In every TS library there is a special object that contains the proper initialization code. This object will be in a namespace that shares a name with the assembly and it's type will be [AssmblyName]_Initializer. It will only have one method, Initialize. Call that, and now you'll be ready to interact with TS. For example, here is how to load the BCL:

TaffyScript.BCL.TaffyScript_BCL_Initializer.Initialize();

Loading a TaffyScript Library at Runtime

The second wat to interact with TaffyScript is to load a TS assembly at runtime. This way is recommended for things like mods and plugins (some of the major targets of the language). Inside of the TaffyScript assembly, there is a type called TaffyScriptLoader that will load an assembly in for you. Use it like so:

using TaffyScript;

//Inside a method
TaffyScriptLoader.Load(pathToAssembly);

As you can see, both methods are extremely simple.

Using TaffyScript

Type Casts

Before we really get started, there is one important thing to note. All variables, arguments, and return values in TS are of the type TsObject. This type has an implicit conversion FROM any of the basic .net types. However, in order to get a basic type from it, you must use an explicit cast, or one of the many methods builtin to that class to retrieve a variable.

On the other hand, there are a number of hoops to jump through in order to actually use the TS code. Here are some short examples showing you how to use various language constructs:

Wrapping a TsInstance

Suppose we have the following object:

object obj_action_figure {
    script create(name, catchphrase) {
        self.name = name;
        self.catchphrase = catchphrase;
    }

    script speak() {
        print(catchphrase);
    }
}

To create an easy to use version of that in C#, we can write a simple wrapper:

using TaffyScript;

namespace TaffyScript.Example 
{
    public class ActionFigure
    {
        private obj_action_figure _source;

        public string Name
        {
            get => (string)_source["name"];
            set => _source["name"] = value;
        }

        public string Catchphrase 
        {
            get => (string)_source["catchphrase"];
            set => _source["catchphrase"] = value;
        }

        public ActionFigure(string name, string catchphrase) 
        {
            _source = new obj_action_figure(new TsObject[] { name, catchphrase });
        }

        public void Speak()
        {
            // Note how all methods are static.
            obj_action_figure.speak(_source, null);
        }
    }
}

As you can see, while it is still relatively easy, there is a decent amount of boilerplate code. Of course that's only to keep tidy api's with objects stored in C#. This won't be necessary for every object. But for objects that are used often, it's recommended.

Calling a Script

Calling a global script is a lot easier. These are wrapped in the TsDelegate class which handles some of the internals related to calling the script properly. If you want to simulate an instance calling the script, you can pass an ITsInstance as the first value to the invoke method. Otherwise, you can pass any number of arguments to the script.

TaffyScript:

script trace {
    if(argument_count == 0) {
        print("");
        exit;
    }
    var output = string(argument0);
    for(var i = 1; i < argument_count; i++) {
        output += ", " + string(argument[i]);
    }
    print(output);
}

C#:

public void Trace(params TsObject[] args) 
{
    TsReflection.GlobalScripts["trace"].Invoke(args);
}

//OR

public void Trace(string arg1, int arg2, bool arg3) 
{
    TsReflection.GlobalScripts["trace"].Invoke(arg1, arg2, arg3);
}

Calling an Instance Script

Because an event requires an object, much of the boilerplate is the same. Here I cut out most of it, since it's already been shown.

TaffyScript:

object obj_greeting {
    script create {
        phrase = "Hello";
    }

    script speak {
        print(phrase);
    }
}

C#:

public void Greet() 
{
    var inst = new obj_greeting(null);
    inst.GetDelegate("speak").Invoke();
}

If you're not sure if the object has the desired event, you can use the TryGetDelegate function as well.

Alternatively, if you don't have an instance, or you want to get a script from another type, you can get the method via reflection. However, it is expected that you supply an instance when calling the method:

C#:

public void Greet(ITsInstance inst) {
    TsReflection.InstanceScripts.TryGetValue("obj_greeting", "speak").Invoke(inst);
}
Clone this wiki locally