...and you can have full file path in your Lua Runtime Errors too

Pazyryk

Deity
Joined
Jun 13, 2008
Messages
3,584
Not only can you have reliable Lua error reporting (which is broken in base Civ5; see here), but when you do get an error traceback, you can have function name and full file path that is not truncated into uselessness.

Here's an example error report:
Code:
 EaMain: [string "C:\Users\Pazyryk\Documents\My Gam..."]:32: attempt to index global 'nonexistentObject' (a nil value)
 EaMain: 2: BuggeredFunction C:\Users\Pazyryk\Documents\My Games\Sid Meier's Civilization 5\MODS\Ea II The Ageless and the Divine (v 5)\Utilities\EaDebugUtils.lua: 32
 EaMain: 3: nil C:\Users\Pazyryk\Documents\My Games\Sid Meier's Civilization 5\MODS\Ea II The Ageless and the Divine (v 5)\EaMain\EaPrereqs.lua: 107
 EaMain: 4:  =(tail call): -1
 EaMain: 5: C function
 EaMain: 6: nil C:\Users\Pazyryk\Documents\My Games\Sid Meier's Civilization 5\MODS\Ea II The Ageless and the Divine (v 5)\EaMain/EaMain.lua: 335
 EaMain: 7:  =(tail call): -1
The first line is truncated because it is from the base error reporting system. But the rest is generated by my own Lua traceback function, so it can give full path (or any other info from the debug library if you care to edit it).

Basically, C++ calls Lua (via GameEvents or Events) and everything downstream of that is a Lua chunk (until that last Lua function reaches it end). If there is an error anywhere in that chunk then all the Lua stops and it goes back to C++, which can either give you an error message (which it does if it was from Events) or tell you nothing at all (which it does if it was called from GameEvents). Even for Events, the error printing is out of your control and you get the truncated (uninformative) path name.

To get around both issues, I've implemented my own Lua Error Handler. It uses xpcall to "contain" any downstream error and trigger my own Traceback.lua function.

See details here.
 
I really have to try that ASAP, thanks :)
 
I just read where you developed this, not only did I bookmark it, but downloaded it to study in peace.
 
Please excuse me if this is a stupid question as I have not read all the posts in detail yet :blush:

Are you planning on 'modulizing' this so that it can be plugged into mods or is the only option for each modder to fit these findings into their code.
 
Can't be done. I mean, I could put the code in a mod, but it won't do anything on its own. You have to decide which of your functions you want to pass through the Error Handler (e.g., all of your Lua "entry points" from GameEvents ... that's what I did), and then do a little re-coding to do that. It's not too hard, but not totally trivial either. (And if you're going to do that, you can cut and paste a little code too.)

Edit: If someone found and fixed the problem in the dll, then this would all be a moot point anyway. Not sure if the Lua error handling is in the dll we have though.

Edit2: Where is Lua "embedded", anyway? Does anyone know? I think the truncation happens in the default library debug.traceback method. Can we replace that?
 
So much for a silver bullet approach! I have finally read your main thread and it does not look difficult to implement.

Would be nice if the DLL could be fixed if the error handling code is in it.
 
The "script system" code is not in the DLL we have the codebase for. We have the wrapper class (CvLuaSupport) but that just delegates to the game engine script system interface, so we have no details of what is actually happening, and even if we did, we have no way to fix it.

All the bits in red are "magic"

Code:
[COLOR="Red"]ICvEngineScriptSystem1* pkScriptSystem = gDLL->GetScriptSystem();[/COLOR]
if(pkScriptSystem) {
  CvLuaArgsHandle args;
  args->Push(getOwner());
  args->Push(GetID());
  args->Push(getX());
  args->Push(getY());

  bool bResult;
  LuaSupport::CallHook(pkScriptSystem, "UnitSetXY", args.get(), bResult);
}

Code:
bool LuaSupport::CallHook(ICvEngineScriptSystem1* pkScriptSystem, const char* szName, ICvEngineScriptSystemArgs1* args, bool& value) {
  // Must release our lock so that if the main thread has the Lua lock and is waiting for the Game Core lock, we don't freeze
  bool bHadLock = gDLL->HasGameCoreLock();
  if(bHadLock)
    gDLL->ReleaseGameCoreLock();
  bool bResult = [COLOR="red"]pkScriptSystem->CallHook[/COLOR](szName, args, value);
  if(bHadLock)
    gDLL->GetGameCoreLock();
  return bResult;
}
 
A programmer at Firaxis said truncated file paths are a problem with the basic Lua error handler itself, instead of the code written by the Civ team, which is why it's never been fixed directly. I got the impression fixing it would require them to change their implementation of Lua somehow.
 
Pazyryk, I'll never be as useful as you :(
 
A programmer at Firaxis said truncated file paths are a problem with the basic Lua error handler itself, instead of the code written by the Civ team, which is why it's never been fixed directly. I got the impression fixing it would require them to change their implementation of Lua somehow.

Yes, they would have had to replace debug.traceback with their own traceback method (this is what I did in the tutorial). The Lua library function debug.traceback prints info.short_src rather than info.source after running info = debug.getinfo(<stack level>):
  • source --- Where the function was defined. If the function was defined in a string (through loadstring), source is that string. If the function was defined in a file, source is the file name prefixed with a `@´.
  • short_src --- A short version of source (up to 60 characters), useful for error messages.
In fact, the Lua ref gives an example of a traceback function:
The following function illustrates the use of debug.getinfo. It prints a primitive traceback of the active stack:
Code:
    function traceback ()
      local level = 1
      while true do
        local info = debug.getinfo(level, "Sl")
        if not info then break end
        if info.what == "C" then   -- is a C function?
          print(level, "C function")
        else   -- a Lua function
          print(string.format("[%s]:%d",
                              info.short_src, info.currentline))
        end
        level = level + 1
      end
    end
It is not difficult to improve this function, including more data from getinfo. Actually, the debug library offers such an improved version, debug.traceback. Unlike our version, debug.traceback does not print its result; instead, it returns a string.
You can see that even the example given prints info.short_src which would give you the 60-char truncation. In the tutorial, I wrote my own traceback function starting with the above but printing info.source instead of info.short_src.
 
Or, given that they ship the Lua dll with the game, they could have edited one line to change 60 into something more useful (given that they know that file paths are very long), recompiled the Lua DLL and shipped the modified version with the game.

Mmmmm .... :mischief:
 
Back
Top Bottom