I’m working on a game project where players will build and code their own spaceships to do battle with other players’ spaceships. Think CodeCombat / FightCode, but in space with cool 2D physics and stuff. Here’s a demo video of what I have working so far.

I’ve chosen to implement the ship scripting with Lua, as it’s a good scripting language that is relatively easy to implement. I’m using NLua (specifically Unity3D-NLua) as an interface between my C# code and the Lua scripts, and everything is going swimmingly. It was super easy to link my MonoBehaviour scripts to a Lua instance - it goes something like this:

using NLua;

Lua lua = new Lua();
lua["thrusterLeft"] = thrusterObject.GetComponent<Part>();

Where thrusterObject is a GameObject containing a Component that is a child class of Part (e.g. PartThruster, PartTurret, PartRanger, etc). Then, in Lua, the player’s script can simply do:

thrusterLeft:SetThrust(0.8)

And so it will be. Easy, right? But there’s a problem! Because there are no restrictions on what the player can do with my GameObject in the Lua script, the player is able to use functions that should be out of bounds. For example, they can manually set properties like thrusterLeft.transform.position and thrusterLeft.rigidbody2D.velocity. This is obviously not intended behaviour. So I’m faced with this problem - how can I prevent the Lua scripts from having too much power?

I can think of a few potential solutions, but they all will require quite a bit of work to implement. If anybody’s done something like this before, I would be interesting to know how you approached it.

If you need more details about how my code is set up, you can check out the project repository on GitHub. Some files you might want to check out are:

You could try making a “wrapper” class that holds the component you’re passing to Lua, then only expose the methods you want users to have access to:

public class ThrusterWrapper{
  private Part thruster;
  
  public ThrusterWrapper(Part thruster){
    this.thruster = thruster;
  }
  
  public void setThrust(float power){
    thruster.SetThrust(power);
  }
}

Then create an instance of ThrusterWrapper and pass that to Lua:

 Lua lua = new Lua();
 lua["thrusterLeft"] = new ThrusterWrapper (thrusterObject.GetComponent<Part>());