Search Unity

Limiting the execution time of a Lua script?

Discussion in 'Scripting' started by shoffing, Mar 29, 2015.

  1. shoffing

    shoffing

    Joined:
    Oct 31, 2013
    Posts:
    9
    Hey, I'm working on a competitive programming game where players write their code in Lua and do battle against each other. I'm attempting to limit the execution time of Lua scripts in my game so that players can't just do:

    while true do end​

    and end up crashing the game with an infinite loop. I've tried doing it with Coroutines, but that didn't work because the Lua script doesn't yield execution and the game ends up freezing. I've tried doing it with Threads, but that doesn't work because my Lua functions need access to Unity API stuff like transform, and it throws errors like "get_transform can only be called from the main thread."

    Is this simply not possible in Unity, because Unity is not thread-safe? This issue is a project killer for me, any help would be appreciated. Here's my attempt at doing it with threads, if you want to see what I've already tried:

    Code (CSharp):
    1. // Lua functionality
    2. public System.Object[] Call(string function, params System.Object[] args) {
    3.     if(lua == null) return null;
    4.     LuaFunction lf = lua.GetFunction(function);
    5.     if(lf == null) return null;
    6.  
    7.     System.Object[] result = new System.Object[0];
    8.     CallTimeLimit(lf, args, value => result = value);
    9.    
    10.     return result;
    11. }
    12. public System.Object[] Call(string function) {
    13.     return Call(function, null);
    14. }
    15.  
    16. private void CallTimeLimit(LuaFunction lf, System.Object[] args, Action<System.Object[]> result) {
    17.     // Start two threads, timer and lua execution.
    18.     // Whichever thread finishes first will abort the other one.
    19.     Thread timerThread = new Thread(CallTimer);
    20.     Thread execThread = new Thread(CallExecution);
    21.     CallTimerParams ctParams = new CallTimerParams(execThread);
    22.     CallExecutionParams ceParams = new CallExecutionParams(lf, args, result, timerThread);
    23.     timerThread.Start(ctParams);
    24.     execThread.Start(ceParams);
    25. }
    26.  
    27. private struct CallTimerParams {
    28.     public Thread execThread;
    29.     public CallTimerParams(Thread execThread) {
    30.         this.execThread = execThread;
    31.     }
    32. }
    33. private void CallTimer(object parameters) {
    34.     CallTimerParams timerParams = (CallTimerParams) parameters;
    35.  
    36.     Thread.Sleep(500);
    37.     timerParams.execThread.Abort();
    38.  
    39.     Debug.Log("TIMER FINISHED FIRST");
    40.     luaValid = false;
    41. }
    42.  
    43. private struct CallExecutionParams {
    44.     public LuaFunction lf;
    45.     public System.Object[] args;
    46.     public Action<System.Object[]> result;
    47.     public Thread timerThread;
    48.     public CallExecutionParams(LuaFunction lf, System.Object[] args, Action<System.Object[]> result, Thread timerThread) {
    49.         this.lf = lf;
    50.         this.args = args;
    51.         this.result = result;
    52.         this.timerThread = timerThread;
    53.     }
    54. }
    55. private void CallExecution(object parameters) {
    56.     CallExecutionParams execParams = (CallExecutionParams) parameters;
    57.  
    58.     System.Object[] funcResult = null;
    59.  
    60.     try {
    61.         if(execParams.args != null) {
    62.             funcResult = execParams.lf.Call(execParams.args);
    63.         } else {
    64.             funcResult = execParams.lf.Call();
    65.         }
    66.     } catch(NLua.Exceptions.LuaException e) {
    67.         LuaError(e.Message);
    68.     }
    69.  
    70.     execParams.timerThread.Abort();
    71.  
    72.     Debug.Log("EXECUTION FINISHED FIRST");
    73.     execParams.result(funcResult);
    74. }
     
  2. Detrus

    Detrus

    Joined:
    Apr 22, 2014
    Posts:
    8
    I might be dealing with similar things, so was looking up sandboxes and these

    https://github.com/gtimworks/lua-process
    https://github.com/Meae/luajit-sandbox
    https://github.com/APItools/sandbox.lua
    http://webserver2.tecgraf.puc-rio.br/~lhf/ftp/lua/ - live demo, source of http://www.lua.org/demo.html

    Try to solve the problems. All work from within Lua VM

    What's in your Lua code?

    If you're using the whole Unity API from Lua in user scripts, it's not much of a sandbox, since they can transform the whole world away. I don't think you can let user scripts use most of the NLua/uLua binding functionality. That's for the trusted game developer to use in place of C# so he doesn't have to recompile from Unity each time.

    For users you have to provide a safe API. So you wouldn't transform the Unity object from Lua directly, you would call something like a self.moveTo() function that would send the coordinate to C# and in C# it would transform the object. Or you'd have to whitelist which objects can be transformed from user scripts. But I'm guessing to be constrained by in game rules, you'd still have to have a self.moveTo(), which would be checked against the game world bounds to make sure they don't break the rules of physics in your game.

    I've never done this, that's just my understanding from the research. Many games use Lua scripting http://en.wikipedia.org/wiki/Category:Lua-scripted_video_games and some are open source https://github.com/AquariaOSE/Aquaria so I'll be looking through their code to see if they dealt with this.

    And even if you user scripts can't be fully secured, or the sandbox makes unacceptable tradeoffs in performance, there's always the option of the developer checking each submitted user script.