This allows libretro save states to actually behave the same every time
you load them with respect to when randomly generated events occur and
the random movements that NPCs make.
The stage 1 build in .gitlab-ci.yml originally used Clang because I
couldn't get the base Ruby to build with GCC for some reason, even
though it works in GitHub Actions. Since the base Ruby build was removed
in aad16c4e74, we can now use either GCC
or Clang in .gitlab-ci.yml.
Stage 1 of the libretro build process originally built a base Ruby
targeting the build machine that was then used to build the version of
Ruby actually used in the core for better determinism, but it turns out
that the base Ruby matters very little. We can just use the system
version of Ruby as the base Ruby.
I'm planning on allowing the user to put an empty file with the
extension .mkxp in the game directory and load that as the game so that
they can control the file name RetroArch uses for save states, since
RetroArch uses the name of the game file as the name of the save states.
The file extension .mkxpz is still recognized as an archive.
In big-endian libretro builds, the WebAssembly memory is reversed, so no
byte-swapping is required to read from/write to WebAssembly memory
(which is little-endian).
However, that means the ways to get and set values in WebAssembly memory
are endianness-dependent, so I've added the correct such ways for
big-endian platforms.
The binding coroutines in libretro builds are constructed on the VM
stack, so reallocating the VM memory would corrupt the memory of any
currently existing coroutines.
I've changed it so that the coroutines are no longer constructed on the
VM stack so that they're unaffected by VM memory reallocations, and
added a "slot" mechanism for storing variables on the VM stack. (Any
Ruby `VALUE`s used by a coroutine have to be stored on the VM stack so
that the Ruby garbage collector doesn't free them while they're being
used, which is why the slot mechanism is necessary.)
Any relative paths that the game tries to access in libretro builds will
now be relative to whatever is the current working directory in the Ruby
sandbox, which will also now be initialized to the game directory during
initialization. Before, all of the bindings that took paths were
hardcoded to prepend the path with the game directory.
The copy constructors are causing problems when the `fiber.stack` vector
gets reallocated when its capacity is full, since when vectors are
reallocated, the elements are moved (or copied if there's no usable move
constructor) to the reallocated memory and then the original elements
are destroyed.
This premature calling of destructors leads to double-free and
use-after-free errors.
I fixed it by deleting the copy constructors and explicitly defining
move constructors.
Not sure why, but this fixes crashes when calling variadic functions in
the Ruby API in libretro builds when Ruby is built without `-DNDEBUG`.
Maybe the previous way of calling varargs functions was undefined
behaviour somehow.
There's currently a memory leak in libretro builds where memory usage
increases by several kilobytes every frame you hold down an arrow key
while the player character is visible on the map in KNight-Blade.
This fixes a bug where parts of the WebAssembly stack are leaked when an
exception is caught in Ruby. There seems to be another source of memory
leaking, though, because memory usage still increases by several
kilobytes every frame like before.
I'm pretty sure this bug I fixed is an internal Ruby bug that I should
be creating a pull request to fix. I'll create one once I fully fix the
memory leak, since there might be more Ruby bugs that I have yet to
find.
Okay, the coroutine implementation of `sandbox_malloc` is clearly
broken. It would be working if Asyncify instrumented the `memory.grow`
WebAssembly instruction, but it doesn't instrument it.
This commit reverts commit 42c4ff9497 and
also increases the default VM memory allocation from 64 MiB to 96 MiB to
account for the lack of ability to increase the memory allocation at run
time. I'll find some new way to implement increasing the memory
allocation later.
According to AddressSanitizer, when `sandbox_malloc` causes the
WebAssembly memory to grow in size, every single coroutine on the
sandbox stack gets corrupted. So if `sandbox_malloc` is going to cause
the memory to grow in size, we need to yield so that there are no
coroutines on the sandbox stack while the reallocation occurs.