Search Unity

Question Can't get Moonsharp Remote Debugger working

Discussion in 'Scripting' started by seona13, Oct 6, 2020.

  1. seona13

    seona13

    Joined:
    Nov 28, 2019
    Posts:
    24
    Hi folks,

    Can someone please help me with getting the remote debugger working? The docs say "you have to reference MoonSharp.RemoteDebugger.dll either manually or through NuGet". I'm not using NuGet, and I have no idea what it means by referencing the dll manually. Or even where the dll is.

    Any help or pointers would be greatly appreciated. I've tried searching for answers, but I haven't been able to find anything that helps.
     
  2. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
  3. seona13

    seona13

    Joined:
    Nov 28, 2019
    Posts:
    24
    Hi Madgvox, thanks for the reply.

    The release unitypackage is what I already have (at least, I have the version from the Asset Store, which appears to have the same set of files in it). While this includes a folder of debugger-related scripts, it doesn't have any DLLs. When I type using Moonsharp. the only option it gives me is "VsCodeDebugger" rather than the remote debugger.

    Ideally I'd prefer the remote debugger, but I've just spent the morning trying to get the VSCode one working instead. No luck there either - I've followed all of the documentation on the Moonsharp site, I've looked up other documentation via Google, I've followed the (minimal) documentation that comes with the Moonsharp Debug extension for VSCode. None of it seems to help. When I try to run the debugger in VSCode, after putting in a breakpoint in my Lua file, I get the following error:

    Couldn't find a debug adapter descriptor for debug type 'moonsharp-debug' (extension might have failed to activate).​

    There is a button on the error popup that invites me to open my launch.json file. These are the contents of that file:

    Code (CSharp):
    1. {
    2.     // Use IntelliSense to learn about possible attributes.
    3.     // Hover to view descriptions of existing attributes.
    4.     // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    5.     "version": "0.2.0",
    6.     "debugServer": 41912,
    7.     "configurations": [
    8.         {
    9.             "name": "MoonSharp Attach",
    10.             "type": "moonsharp-debug",
    11.             "request": "attach",
    12.             "HELP": "Please set 'debugServer':41912 (or whatever port you ar connecting to) right after the 'version' field in this json."
    13.         }
    14.     ]
    15. }
    I really don't know what to do or try next, and it's stopping me from moving forward with my development as I have an error of some sort in my Lua code that I'm having a devil of a time tracking down.
     
  4. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    There's always good old fashioned console logging. Bind a lua function to Debug.Log and have at it. Probably easier than setting up the debugger anyway.
     
  5. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    The releases page also has folders with DLLs in it. If the unitypackage doesn't have what you need, you can try importing those directly.
     
  6. seona13

    seona13

    Joined:
    Nov 28, 2019
    Posts:
    24
    But where do I put them? I can't find where the unitypackage put the base Interpreter dlls, or I'd put them in the same place. Do I just put them in my plugins folder along with all of the Moonsharp scripts?
     
  7. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    Yep! The DLLs can be basically anywhere in your Assets folder.
     
  8. seona13

    seona13

    Joined:
    Nov 28, 2019
    Posts:
    24
    So I'm feeling kinda dumb right now. I can't figure out what's going wrong.

    The release page gives me a zip file with the following contents:
    upload_2020-10-14_8-50-33.png

    Inside remotedebugger/net40 is the following:
    upload_2020-10-14_8-51-40.png

    (remotedebugger/net35 is the same but without the Resources directory)

    If I just copy the two RemoteDebugger files into my project's Assets, I get the following error in Unity:

    upload_2020-10-14_8-53-20.png

    If I include the three Interpreter files as well, I get the following 20 errors:
    upload_2020-10-14_8-55-38.png

    It doesn't seem to matter whether I use net35 or net40, and if I use net40 it doesn't seem to matter whether or not I include the Resources directory.

    I'm just super confused right now. It doesn't seem like it's supposed to be this hard, but nothing I try seems to work.
     
  9. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    You need both the
    MoonSharp.Interpreter.dll
    and the
    MoonSharp.RemoteDebugger.dll
    . It seems as though you have the Interpeter files (non-dll) under the Plugins/MoonSharp/Interpreter folder. Including the Interpreter.dll causes there to be duplicate definitions. Try deleting the loose files.

    I haven't tried installing it myself yet. If you continue to have issues, I'll attempt to reproduce.
     
    seona13 and Bunny83 like this.
  10. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,999
    Right, the MoonSharp.RemoteDebugger.dll depends on the MoonSharp.Interpreter.dll. If you use the source files in your Unity project of course this dependency can't be resolved. You essentially have two options:

    Either get rid of all the source files of MoonSharp and use the interpreter and the romotedebugger dll files inside Unity, or alternatively use the source files of both, the interpreter as well as the remote debugger. The source files for the remote debugger are here.

    Currently I'm playing around with MoonSharp as well (maybe I end up creating yet another computercraft emulator ^^). Though I don't really use the remote debugger. I've successfully implemented a way to limit the execution of all my lua code (even in nested coroutines) so I'm protected against infinite loops in lua code. This is important since my main goal is to target WebGL, so single threaded is the main issue here.

    If someone is interested, here's my kinda hacky solution which works just fine:
    This is essentially the bootstrap code. It creates a single coroutine that should handle everything.
    Code (CSharp):
    1.         script = new Script();
    2.         script.Options.DebugPrint = m => Debug.Log(m);
    3.         var newResume = script.DoString(@"
    4.        local oldResume = coroutine.resume
    5.        local function newResume(co, ...)
    6.           local t,args = nil, {...}
    7.           if #args == 0 then
    8.               t = {oldResume(co)}
    9.           else
    10.               t = {oldResume(co, ...)}
    11.           end
    12.           while t[1] and tostring(t[2]) == ""(YieldRequest -- INTERNAL!)"" do
    13.              coroutine.yield(t[2])
    14.              t = {coroutine.resume(co)}
    15.           end
    16.           return unpack(t)
    17.        end
    18.        return newResume");
    19.  
    20.         if (script.Globals["coroutine"] is Table coTab)
    21.         {
    22.             coTab["create"] = new CallbackFunction(createCoroutine);
    23.             coTab["resume"] = newResume.Function;
    24.         }
    25.         var method = script.DoString("return function() return pcall(function() " + m_BootStrapCode + " end) end");
    26.         coroutine = script.CreateCoroutine(method);
    27.         coroutine.Coroutine.AutoYieldCounter = AutoYieldInstructionCount;
    28.  
    This is the createCoroutine wrapper which ensures that all coroutines created by the lua runtime also have the AutoYieldCounter set to our limit.
    Code (CSharp):
    1.     public static DynValue createCoroutine(ScriptExecutionContext executionContext, CallbackArguments args)
    2.     {
    3.         var result = MoonSharp.Interpreter.CoreLib.CoroutineModule.create(executionContext, args);
    4.         result.Coroutine.AutoYieldCounter = AutoYieldInstructionCount;
    5.         return result;
    6.     }
    7.  
    This will ensure the interpreter will automatically issue a force yieldrequest when the processor has reached the specified instruction count. The new resume coroutine method does most of the magic. It essentially intercepts the forced yieldrequest and does not return it to the caller, but instead just yields it again so we bubble out of all nested coroutines this way. When we resume the forced yield, the execution will just continue exactly where we have left off.

    My Tick method currently looks like this:

    Code (CSharp):
    1. public void Tick()
    2.     {
    3.         if (coroutine == null || State == RuntimeState.Stopped || State == RuntimeState.Error)
    4.             return;
    5.         var co = coroutine.Coroutine;
    6.         if (co != null && co.State == CoroutineState.Dead)
    7.         {
    8.             State = RuntimeState.Stopped;
    9.             return;
    10.         }
    11.         if (m_Running || eventQueue.Count > 0)
    12.         {
    13.             DynValue res;
    14.             if (m_Running)
    15.             {
    16.                 // resume forced yield
    17.                 m_Running = false;
    18.                 res = co.Resume();
    19.                 m_Counter++;
    20.             }
    21.             else
    22.             {
    23.                 // resume normally, process the next event from the queue.
    24.                 m_Counter = 0;
    25.                 var evt = eventQueue.Dequeue();
    26.                 res = co.Resume(evt.Tuple);
    27.             }
    28.             if (res.Type == DataType.YieldRequest && res.YieldRequest.Forced)
    29.             {
    30.                 // we reached the instruction limit. Remember that we are still "running".
    31.                 m_Running = true;
    32.                 State = RuntimeState.Running;
    33.             }
    34.             else if (res.Type == DataType.Tuple && res.Tuple is DynValue[] t && t.Length > 1 && !t[0].Boolean)
    35.             {
    36.                 // coroutine did not execute successfully, display error from the pcall / coroutine
    37.                 Debug.Log("LUA ERROR: " + t[1].String);
    38.                 State = RuntimeState.Error;
    39.             }
    40.             if (co.State == CoroutineState.Dead)
    41.             {
    42.                 // coroutine has finished or terminated so this runtime is essentially dead
    43.                 State = RuntimeState.Stopped;
    44.             }
    45.         }
    46.         else
    47.             m_Counter = 0;
    48.     }
    If you aren't familiar how ComputerCraft uses the lua corouine feature to process events I give a quick overview. One computer essentially consists of a single coroutine. So everything runs inside this coroutine. We can use coroutine.yield() anywhere inside the lua code to essentially wait for the next event from the game. (could be a keypress, an expired timer, a competed http request or some other asynchronous event). We pass the event through the resume method into the lua runtime.

    That means when we hit a "natural" yield inside the lua code we will return to C# anyways and continue execution when a new event shows up in the queue. However in case we receive a forced yield request due to the max instruction count reached, we simply continue execution the next tick. For this we remember the running state and just resume the coroutine without any parameters. I still have an additional counter that counts how many times in a row we got a forced yield. If we never reach an actual yield after some time we may want to terminate the runtime and consider it crashed. CC itself runs coroutines in a seperate Java thread and uses some sort of watchdog timer that simply kills the execution / thread.

    Note that this is currently just in the state of "proof of concept". Feel free to use it.
     
  11. seona13

    seona13

    Joined:
    Nov 28, 2019
    Posts:
    24
    *cheers* Progress! I'd assumed that I needed the Unity-specific files no matter what. Now that I've deleted them I'm not getting any more errors.

    So I followed the code sample on the Moonsharp docs, and now when I run the game in Unity I get a debug interface opening in my browser. *more cheers*

    Sadly, this seems to crash Unity. Or at least, prevent it from actually loading the game. I left it for a while in case it was a matter of "first time takes longer", but if after close on half an hour it still hasn't worked I feel fairly safe in saying it's not gonna. (-_-)

    Anyone got any ideas on that problem?
     
  12. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    If you're in an empty project (or if you create a repro project), you can zip it up and send it over and I'll take a look. Otherwise, I will try to repro myself when I have the time & energy.
     
  13. seona13

    seona13

    Joined:
    Nov 28, 2019
    Posts:
    24
    Thank you so much for being willing to have a look at this for me, Madgvox. I'm at my wit's end!

    https://www.dropbox.com/s/ae6vy75ika8kpy6/LuaTesting.7z?dl=0

    It's not quite an empty project, but it's a substantially cut down version to just the bare bones needed to run one scene - if I turn off the debugger everything works fine. Turn the debugger on, and it hangs Unity every time.. :(

    Hopefully you can figure out where I'm going wrong with this.
     
  14. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    Just posting to say that I've set up the project and it does indeed hang when the debugger opens. I'll see what I can see!

    EDIT: Switching the
    freeRunAfterAttach
    parameter to true stopped the editor from freezing. Not sure if it also makes the debugger work, but I assume that remoteDebugger.Attach is normally a blocking action in that it waits for the debugger to detach (or rather the Lua script to execute and exit) before resuming code execution.

    Hopefully this will help you get a bit further!

    EDIT 2: The unity editor isn't frozen, just hanging. The debugger is working correctly, it's just breaking before the script executes (and therefore isn't letting execution continue). If you open the debugger and click "Run", it executes the script normally and returns execution to Unity.

    TL;DR -- There's nothing wrong, just a debugger debugging as intended!

    EDIT 3: I'm not seeing the "Buildables" code in the debugger though. Probably a separate issue.
     
    Last edited: Oct 15, 2020
    seona13 and Bunny83 like this.
  15. seona13

    seona13

    Joined:
    Nov 28, 2019
    Posts:
    24
    Aha! You, Madgvox, are an absolute champ! Thank you so much for looking into this for me and figuring it out. Now I can hopefully get back on track with my development and figure out why my stockpiles are breaking. :D

    The issue with the "Buildables" code showing up I actually know the answer to - in the Code panel of the debugger is a drop-down and if you set it to "chunk1" rather than whatever it defaults to you get your own code. I have no idea what that default view is for.
     
    Madgvox likes this.
  16. seona13

    seona13

    Joined:
    Nov 28, 2019
    Posts:
    24
    One last question, now that we both have a working version of the debugger...

    Can you figure out how to add a breakpoint in the Lua script? The Set Breakpoint / Toggle Breakpoint commands in the debugger don't seem to do anything, and breakpoints added in Visual Studio aren't recognised by the remote debugger (not that I thought they would be).
     
  17. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    It appears to work for me. Pressing the toggle breakpoint button or pressing (B) turns the highlighted line red, and when pressing run it highlights the line & stops execution.

    Looks like this:
    upload_2020-10-15_17-16-55.png
     
  18. seona13

    seona13

    Joined:
    Nov 28, 2019
    Posts:
    24
    Ah, got it! For me it doesn't show anything in the window until the breakpoint gets triggered! I wonder if it's a browser difference? Although I wouldn't have thought so, being Flash...

    Anyway, thanks once again for all your help. You've been an absolute star! <3 You also got a shout-out in my latest devlog. ;)
     
    Madgvox likes this.