LibTAS Crashes: The Danger Of Lua Input Functions In OnFrame()

by Alex Johnson 63 views

Hey there, fellow TAS enthusiasts and Lua scripting adventurers! If you've ever dipped your toes into libTAS scripting, you know the power it gives you to craft incredibly precise and awe-inspiring Tool-Assisted Speedruns. But, like any powerful tool, it comes with its quirks. One particular head-scratcher that many of us, including seasoned scripters, have stumbled upon is the mysterious case of lua input.* functions causing total havoc when called from the onFrame() callback. It's like putting the wrong fuel in your car—it might seem like it should work, but trust me, you're in for a rough ride, often ending in a libTAS crash and your CPU screaming at 100% until you forcefully shut it down. Let's unpack this common pitfall, understand why it happens, and learn how to avoid it so your TAS projects can run smoothly and flawlessly.

The Heart of libTAS: onInput() vs. onFrame() – A Crucial Distinction

To really get a handle on why those lua input.* functions go rogue, we first need to understand the fundamental difference between onInput() and onFrame() in libTAS. Think of libTAS as a meticulous stage manager for your game. It has specific cues for specific actions. The onInput() callback is the moment the stage manager focuses solely on the actors' direct movements—pressing buttons, moving joysticks, and all those immediate input commands. This is where you, the scripter, get to tell libTAS precisely what button inputs should be fed to the game for that specific frame. Functions like input.set_buttons(), input.get(), or input.set_stick() are designed to operate exclusively within this narrow window. They are like direct communication lines to the game's input buffer, and they expect to be called when libTAS is actively gathering inputs for the current frame. If you're trying to achieve frame-perfect movement, hold a specific button down, or rapidly tap an input, onInput() is your absolute go-to. It's the dedicated space for anything that directly manipulates the game's controls. Ignoring this designated role is the first step towards an unexpected libTAS crash. Moreover, understanding that onInput() runs before the game state updates for the current frame is key; it ensures your inputs are processed in time for the game to react to them on that very frame. This strict timing and execution order are vital for the integrity and determinism of the TAS. Developers built libTAS with this specific lifecycle in mind, optimizing how inputs are handled to guarantee reproducibility. So, when you're crafting your intricate button presses and movements, always remember: onInput() is the dedicated control panel for all your input wizardry, ensuring your commands are registered at the precise moment they are needed by the game engine.

On the flip side, the onFrame() callback is a broader, more relaxed environment. This is where the stage manager observes everything happening on stage after the actors have moved and the scene has played out. Here, you're primarily concerned with the game's state—checking player coordinates, monitoring scores, observing enemy positions, or performing calculations that don't directly manipulate inputs for the current frame. Functions within the runtime.* family, for instance, are perfect for onFrame(). You might use runtime.get_value() to read a memory address or runtime.log() to output information to the console. onFrame() happens after the game has processed the inputs given in onInput() for that frame and updated its internal state. It's your window into the game's current reality. You can think of onFrame() as the analytical part of your script; it's designed for observation, complex logic that doesn't involve pressing buttons right now, or setting up conditions for future inputs. Trying to inject input commands into this