Here is a summary of the main changes in v1.4.6. You can see the full changelog file here.

ImGui interface

The libTAS game HUD was rewritten to use ImGui. It has some constraints to be used, so it will only appear for games using OpenGL, Vulkan or SDL2 renderer (> 2.0.18). For now, the previous elements were implemented (framecount, inputs, crosshair, lua, messages, ram watches). It is very easy to write for ImGui, so more will come.

A last feature is game window detach. When used, the game will not take the full window, but will appear inside an ImGui window.

Savestates and threading

A limitation from using savestates has existed for a very long time: the impossibility to load a savestate if threads have been created or destroyed. It was very annoying to users, and made several games impossible to TAS. Some features were implemented to mitigate this issue, mainly the backtrack savestate that is automatically saved after a thread was created or destroyed, to be able to go back as early as possible. Still it was not good enough.

So I went ahead and started working on savestate code being able to create or destroy threads. My main help was coming from DMTCP which has a similar feature, although this program is created the process and all the threads, so the destroy part was not handled.

This feature is still largely untested, but has shown good results on games that use threading a lot (like .NET games), with still crashes from time to time. Thanks to this feature, backtrack savestates and savestate invalidation are not needed anymore, so they were removed.

Terminate threads

Terminating a thread must be performed when threads are being suspended, before the actual state loading. Two methods are used to terminate a thread: the first one is by calling pthread_cancel(), which is a relatively safe way to stop a thread, but it relies on the thread hitting a cancellation point. If this method times out, we signal the thread, and the signal handler calls pthread_exit() (highly unsafe).

Creating threads

Creating a thread must be performed after the loading state has been performed because the memory layout and content must be present when the thread is created (especially the thread stack). We use the low-level clone() function so that it does not mess with pthread library, and the thread function then calls setcontext() to resume execution to where it was saved.

Markers

Input editor markers have been extended a bit with its own panel to handle markers. We can now edit a marker’s text and seek to its frame

Lua

Add several new lua functions:

  • memory.baseAddress() to get the base address of a file loaded in the game process memory
  • runtime.saveState() and runtime.loadState() to save and load savestates
  • runtime.isFastForward() and runtime.setFastForward() to control fast-forward
  • memory.readcstring() to read a C-style string from memory into a lua string
  • gui.quad() to draw quads
  • gui.text() was extended to include horizontal/vertical alignment, font size and optional monospace font
  • gui.window() to draw an ImGui window that may be movable.

Additionally, a few modifications were made on the callbacks:

  • onPaint() callback is not called when the screen is not rendering (due to either non-draw frame or skipping with fast-forward).
  • Move around onFrame() callback so it is executed before onPaint(), which feels more natural.
  • onInput() can now modify inputs even in playback mode, and the modifications are stored inside the movie.

More automated detections

  • Autodetect missing libraries and download them: libTAS looks at libraries missing to launch the game, and for some of them, it can download them from an Ubuntu package mirror and extract the library into a libTAS library folder. This is especially important for old libraries that are not present in newer Linux distributions. You are supposed to use the Steam runtime to launch the game, but having a way to get the libraries directly is better.
  • Autodetect Unity engine: When a Unity game is detected, libTAS automatically adds -force-gfx-direct to the commandline options.
  • Autodetect Godot engine: When a Godot game is detected, libTAS automatically adds --audio-driver ALSA to the commandline options.
  • Autodetect GameMaker Studio engine: When a GM:S game is detected, libTAS automatically sets time-tracking clock_gettime() monotonic option.
  • Autodetect newer Unity coroutines: The Unity hack used in v1.4.4 was only working for old engines (before 2019/2020). Now the hack is also working for newer engines, only if the file containing symbols (UnityPlayer_s.debug) is present in the game. This is to locate the function that threads use to wait for new jobs to execute: UnityClassic::Baselib_SystemFutex_Wait(). In the new engines, locating loading threads is even easier because the engine does assign a name to each thread. So we just have to look for Loading.Preload, Loading.AsyncRe, Background Job. and Job.Worker [N].