Player (42)
Joined: 12/27/2008
Posts: 873
Location: Germany
First of all, apologies if I'm posting this in the wrong forum, I did read most of the things that were written about this subject in the IBM PC threads, but from skimming at it, I found nothing similar to what I'm suggesting, so I do think this idea is new. If not, we can use this topic to summarize what has been done on this. Anyway, as everyone knows, TASing PC games is a nightmare. The reasons for this are that, unlike Console games, PC games take input from lots of sources, and might interact with the OS in completely different ways, which are usually affected by several hidden configurations, which makes it very hard to run processes in a deterministic way. The most immediate solution, virtualization, doesn't work too well, either, because virtualization usually makes things too slow, and it works by virtualizing the entire operating system, which means that it's pretty hard to separate what syscalls and library calls are regular OS processes, and what is actually the application you are trying to virtualize. Given this, an idea I had was to use Docker and Wine to run the application in a containerized environment, and after some trouble I managed to get the latest version of Starcraft running on it. For those who don't know, containers are a more lightweight alternative to running a Virtual Machine. Containers, at least in Linux, make use of a feature called kernel namespaces. Essentially, you can think of the OS as an entity that provides lots of "services" to an application, like creating processes, using networking, file systems, etc. Now, when you spawn something in a kernel namespace, whenever the application requests something from the Linux kernel, Linux can see the namespace it requested from, and based on that, it can "hide" lots of things, to give the application the illusion that it's running in an isolated environment. Of course, this isolation is never complete, because you need to communicate somehow with the application, if not why start it? In any case, containers give you the ability to declare precisely the dependencies that the application has and only interact with the main OS resources in the way that you specified it. When you do this, you get the predictability of a virtual machine without the performance hit, because the app is still running in an ordinary process. To be fair, there's still some performance hit, because in order to run any application you need to include a lot of OS things in a container image, and this does take some time to startup. My containerized starcraft usually takes 2 minutes to start, but after startup the performance is just the same. Let me explain what I did and why I think it has potential to improve TASing for PC games. In order to run things in docker, you need to create an image, which you specify with a Dockerfile. I installed wine in it with the following commands:
Language: Dockerfile

FROM ubuntu:focal ## for apt to be noninteractive ENV DEBIAN_FRONTEND noninteractive ENV DEBCONF_NONINTERACTIVE_SEEN true ## preseed tzdata, update package index, upgrade packages and install needed software RUN truncate -s0 /tmp/preseed.cfg; \ echo "tzdata tzdata/Areas select Europe" >> /tmp/preseed.cfg; \ echo "tzdata tzdata/Zones/Europe select Berlin" >> /tmp/preseed.cfg; \ debconf-set-selections /tmp/preseed.cfg && \ rm -f /etc/timezone /etc/localtime && \ apt-get update && \ apt-get install -y tzdata ## cleanup of files from setup RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* RUN apt update RUN apt install -y wget RUN dpkg --add-architecture i386 RUN mkdir -pm755 /etc/apt/keyrings RUN wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key RUN wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/focal/winehq-focal.sources RUN apt update RUN apt install -y winehq-staging=8.4~focal-1 wine-staging=8.4~focal-1 wine-staging-amd64=8.4~focal-1 wine-staging-i386=8.4~focal-1 RUN wget -O /home/winetricks https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks RUN chmod +x /home/winetricks RUN /home/winetricks sound=pulse COPY StarCraft-Setup.exe /home/
Several comments here. I used an old version of ubuntu, 20.04 or "focal" because it seemed that Blizzard's battle.net client was refusing to start in the newest one because of "digital certificate verification". I suppose Blizzard is actively trying to stop people running Starcraft on wine, because of this I used this old Ubuntu version with the exact same wine version reported with platinum status, 8.4. In any case, the magic of Docker containers is that we only need to get a working image once, and then after that we can simply boot it up in any linux distro that it will work. I'm not distributing the image I made here, because even though StarCraft is a free game, that would probably violate Blizzard's EULA. The setup works like this: first is some automation to setup a locale in wine without the terminal (everything needs to be noninteractive for the image to build). Then I install the specific version of wine I want, and then setup winetricks and configure it to use pulseaudio. I will go into more detail later. It's the easiest way to get the audio working. Finally, I copy the battle-net client I downloaded from the Blizzard website into the image and do the rest of the setup manually. The way I got starcraft to work was: I logged into the container forwarding graphical output to X (more on that later), and then I ran the setup client as root, logged into my blizzard account, accepted everything and it installed starcraft. It installed all files on /root/.wine/drive_c/Program\ Files\ \(x86\)/ Then I created a user with uid 1000 and gid 1000, which matches the one from my user in the main host machine (1000 is the default id for the initial ubuntu user, unless you did something crazy when you installed it). Then I changed the ownership of everything in the container /root folder to this new user. The purpose of this is to setup pulseaudio as mentioned here. Essentially we setup a shared socket for the containerized process to communicate with pulseaudio, so that we can hear its sound, but for some hacky reason you need to run it with a user that has the same ID as the user in the host machine where the pulseaudio server is actually running. With this done, I setup a script that runs starcraft without Blizzard's launcher as this new user:
Language: bash

#!/bin/bash wine /root/.wine/drive_c/Program\ Files\ \(x86\)/StarCraft/x86_64/StarCraft.exe -launch
And then I saved the image with docker commit to persist my changes and it works, as long as you remember to allow the container to access the X server so that it can create a window and receive input, and the pulseaudio socket and configuration to receive the sound. My full command was:
docker run -it --rm --env PULSE_SERVER=unix:/tmp/pulseaudio.socket --env PULSE_COOKIE=/tmp/pulseaudio.cookie -v /tmp/pulseaudio.socket:/tmp/pulseaudio.socket  \ 
-v /tmp/pulseaudio.client.conf:/etc/pulse/client.conf --user $(id -u):$(id -g) -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix:ro \
--network none --entrypoint /home/wineuser/sc starcraft
So, why do I think this idea has potential? Well, with this I managed to isolate everything and the only performance hit is on startup. We know exactly the image used to run the game, we can completely disable the network and possibly other os stuff that might cause desyncs, and it's even more fantastic once you understand how the communication with X and pulseaudio actually happens. For those who don't know, communication with them happens through Unix domain sockets, which are just like network sockets, but Unix specific because they handle stuff which makes no sense to pass through the network. In my setup, I simply opened all the sockets in /tmp/.X11-unix to make it simple, but in theory it should also be possible to mess around with X to create a specific socket to trade UI and input data with the game, and use Docker to pretend it's the main one in the containerized OS, so that the game accesses it. The end result is: with some combination of Docker and wine magic, we can redirect all the stuff that matters to TASing, which is audio, video and input, to some unix domain sockets, and we could potentially code a library around them to intercept these calls. This not only is much simpler than what libTAS does, which is wrap around several different types of library calls, but also works much better! That's because not only the game is running in an isolated environment, but also because wine redirects EVERYTHING a multimedia app needs to the X server and pulseaudio, unlike standard Linux apps which can depend on lots of different stuff. So this setup should in theory work no matter what library the app is using to run the game, as long as wine is able to run it. Let me know what you guys think of this idea, and if you have more suggestions!
Masterjun
He/Him
Site Developer, Expert player (2047)
Joined: 10/12/2010
Posts: 1185
Location: Germany
How would savestates be implemented?
Warning: Might glitch to credits I will finish this ACE soon as possible (or will I?)
Player (42)
Joined: 12/27/2008
Posts: 873
Location: Germany
That's a pretty good question that I did not address. It requires some Linux and Docker knowledge. Basically, Linux has an API for you to debug processes. The easiest way to use it is through gdb. When you attach gdb to a process, it sends signals to them like PTRACE and others I don't remember, which tell Linux how much to execute in each of them. Although in the main OS you have lots of processes, in Docker containers you don't have a lot of them. This is the output of ps aux in the image I built:
root@969ea0cd49f0:/# ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  1.0  0.0   4252  3472 pts/0    Ss   19:20   0:00 /bin/bash
root           9  0.0  0.0   5900  2996 pts/0    R+   19:20   0:00 ps aux
See how there are only two of them, the /bin/bash command with the shell, and ps aux itself. If you know Linux you can already see that this is not a "real" OS, because PID 1 is not systemd or whatever init system you're using. So, implementing savestates is as simple as: send signals to all process to stop them (there are no permission issues, this PID 1 process for example is just an ordinary process in the host machine, but the containers sees it as 1 because of the namespace). Look at the kernel info to see what memory it's accessing and dump it. That's how you save. To load, you spawn all processes by hand, put the memory there and send a signal for them to resume. That's the whole point for using containers, you can literally stop all their processes, while in your host OS you can't do that, for obvious reasons.
Player (42)
Joined: 12/27/2008
Posts: 873
Location: Germany
Just a small update. I kept hacking around today and I can confirm that this idea totally works for stopping and resuming execution of starcraft. First of all, I got tired of fighting Blizzard and decided to run the old version 1.16.1. Even with containerization, it starts almost instantaneously. Just to show how much gaming companies have gone out of the way to put stuff you don't need... Also, I configured wine to run a virtual desktop of resolution 640x480 so that Starcraft runs on a small window in my machine. I researched how applications communicate with the X server. It's all done through the X11 protocol, which is a protocol that behaves like a TCP one (actually if you run X over a network it will use TCP to implement it). In any case, to intercept the communication, we can setup a proxy. We start a process that fakes an X server in a Unix socket, but in reality what it does is simply forward them to a real X server. To the X server it looks like the proxy is the client, so it answers it. With this, we have full control over the messages and can play with them. Then we could either code an X server proxy, but like most things in programming this has already been done. After a bit of digging, I found this page, and from all applications there it looks like xscope is the most powerful. It's an obscure app, though. I did not find a package for it and had to compile from source. After this was done, I started xscope with the following command:
xscope -i2
What this does is: it starts xscope listening on X unix socket number 2, and with the DISPLAY environment variable set to :0, xscope will forward everything from that socket to the real X server at socket 0. Then, we start the containerized starcraft with DISPLAY set to :2 so that wine sends the data to xscope:
docker run --rm --env PULSE_SERVER=unix:/tmp/pulseaudio.socket --env PULSE_COOKIE=/tmp/pulseaudio.cookie -v /tmp/pulseaudio.socket:/tmp/pulseaudio.socket  -v /tmp/pulseaudio.client.conf:/etc/pulse/client.conf --user 1000:1000 -e DISPLAY=:2 -v /tmp/.X11-unix:/tmp/.X11-unix:ro  --network none --entrypoint /home/wineuser/sc starcraft
And it works! xscope can sniff the traffic. Here's a typical output:
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 31768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
	 ............REQUEST: PutImage
	              format: ZPixmap
	            drawable: DWB 02400001
	                  gc: GXC 02000016
	               width: 0280
	              height: 0066
	               dst-x: 0
	               dst-y: 204
	            left-pad: 00
	               depth: 18
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 27992 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
	 ............REQUEST: PutImage
	              format: ZPixmap
	            drawable: DWB 02400001
	                  gc: GXC 02000016
	               width: 0280
	              height: 0066
	               dst-x: 0
	               dst-y: 306
	            left-pad: 00
	               depth: 18
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) --> 32768 bytes
31.62: Client 7 (pid 975254 StarCraft.exe) -->  624 bytes
	 ............REQUEST: PutImage
	              format: ZPixmap
	            drawable: DWB 02400001
	                  gc: GXC 02000016
	               width: 0280
	              height: 0048
	               dst-x: 0
	               dst-y: 408
	            left-pad: 00
	               depth: 18
The fun thing is: if you hit Ctrl-C in xscope it goes into interactive mode, and will only let the data through after some commands. If I do this, the starcraft window freezes completely, but the sound keeps on playing in a loop. Another test I did was wait until some audio was played and hit Ctrl-C. The audio finished playing, and the sound loop kept going, but the audio that would play afterwards did not start. After I hit continue in xscope the window unfroze and the new audio immediately started. That leads me to believe that sound in starcraft runs in a separate thread, and the main thread, which gets blocked on UI calls, sends messages to it so that it updates the sound currently on. With this setup, I managed to pause and resume the app without breaking it. I see that xscope can set breakpoints on certain protocol requests. I'm studying the protocol to see if I can find something that signals if a frame was rendered, and if keyboard or mouse input was sent to the client. In any case, it looks like the "emulator" we need to code should be a generalization of xscope, which records frame renders, records input events, or filters them, replacing with fake ones to playback the movie.
Post subject: Midnight club 2 demo test
Joined: 9/12/2014
Posts: 543
Location: Waterford, MI
I believe midnight club 2 pc is single threaded given how old it is. I would assume single threaded games would work better with this? This sounds too good to be true if this works out.
Player (42)
Joined: 12/27/2008
Posts: 873
Location: Germany
So, I studied the X11 protocol and realized I can virtualize it even more. X11 works like this: there's really no definition of a frame. What X11 does is: it keeps the state of the image of your desktop system, and any client with the proper permissions can view it with a GetImage call. So, when something is 60 fps in X11, what's actually happening is that your device is a client of the X server, and it's dumping its content 60 times per second. X can also call its clients by sending events, and it's precisely how it sends input. The client doesn't poll X, it simply sends an event that the client registered for, and in the client-side there's usually Xlib with a queue so that it can process them in order. Therefore, in my previous setup, when I wired the starcraft app to the main X server in my machine I did not completely virtualize it. That's because when it registered with X it started receiving a plethora of events from the host which I had no control. Fortunately there's a way around this. There's a tool called Xvfb. It's just a dumb X server with no display and no input devices, but works as a backend for GUI apps. I started starcraft using xvfb as a backend and it does work. How do I know this? Well, even though xvfb is dumb, it does keep the image, and you can run a VNC server, like x11vnc, on top of it, just like with a regular X server. With this, the events from your machine never reach it, unless you explicitly configure mouse and keyboard events to go through VNC. Here's a screenshot: Running it through VNC there's some horrible lag, though. But anyway, from this setup we have (1) the xvfb process with the state of the virtual X server (2) the container wine processes with the state of the virtualization layer (3) setting up xscope between the container and xvfb we know exactly when input events are sent, and can pause it anytime After some googling, I found the criu utility which can checkpoint processes and containers, maybe it's already implemented in docker itself, I don't know. It definitely looks like we can control input, video, audio and savestates. It looks like the best way to see if there will be any desyncs is to actually implement this...
CoolHandMike
He/Him
Editor, Judge, Experienced player (897)
Joined: 3/9/2019
Posts: 728
I am a complete novice at almost every tool you mentioned, would probably need to see a video of tasing using this in action to really understand. A starcraft tas would be incredible though!
discord: CoolHandMike#0352
Player (42)
Joined: 12/27/2008
Posts: 873
Location: Germany
Alright, here it is. Please don't mock me for my subpar Youtuber skills. Link to video
Editor, Player (44)
Joined: 7/11/2010
Posts: 1029
I was working on something like this several years ago (primarily for native Linux programs, although running Wine in it was an obvious "further work"). There are three main problems:
  1. Sandboxing the environment so that it doesn't interact with processes and files outside the sandbox. I was using containers + xvfb for this, just like you were (although I was using namespaces directly, rather than going via Docker). There are plenty of alternatives nowadays, such as bubblewrap and AppArmor, although I haven't experimented with these myself.
  2. Savestate / reload. There are programs like CRIU and rr that are good starting points for this. I didn't personally get anything like that working; IIRC dwangoAC has some experience with getting CRIU to work.
  3. Determinism / sync-stability. This is the really hard part, which I invested most of my time into, and eventually gave up. There are a huge number of things that can potentially affect the behaviour of a running program and cause it to act differently given the same input, and to make a stable TASing platform, you need to ensure that all of them work deterministically – otherwise, attempts to replay an input file from the start will fail to sync and there will be no way to verify runs.
I believe that all three problems are solvable, but the third will require the most effort. There are non-deterministic system calls that are very commonly used, like gettimeofday, and your execution environment needs to ensure that they give consistent values in every run of the program; that means that it needs to keep track of "emulated time" in a way that's consistent and that doesn't cause the program to malfunction. There are blocking system calls for which the effect of the program will change if they are run out of order (including thread synchronization calls like futex, and pretty much everything that does I/O); you need to create rules to make sure they will run in a consistent sequence. That in turn generally means that you need to ensure that only one thread is running at any given time (across all processes, e.g. you don't want the game and xvfb to run simultaneously because otherwise you might have non-determinism based on which of them gets to a system call first). There's no reason in theory that that shouldn't be possible, but it is hard to get that sort of code right, and very easy to accidentally introduce deadlocks. There are processor instructions that have nondeterministic behaviour, like RDTSC and RDRAND; there are ways to get around this but it requires extra levels of emulation. (And of course, you need to replace system calls that allow a process to ask for non-deterministic behaviour explicitly, such as attempts to read /dev/random.)
Warepire
He/Him
Editor
Joined: 3/2/2010
Posts: 2178
Location: A little to the left of nowhere (Sweden)
ais523 wrote:
There are plenty of alternatives nowadays, such as bubblewrap and AppArmor, although I haven't experimented with these myself.
At least AppArmor is a Mandatory Access Control framework and not a namespacing approach to sandboxing, it's more of a security tool than something useful for these kinds of things. ------------------------------------------------ I'm a bit short on time today so I will just dump some stuff I wrote on IRC when feos pinged me about your thread.
20:27 < Warepire> Yeah, time-keeping and memory management were the 2 major things that really killed off Hourglass. I wonder if a custom KVM driven
                  solution would work rather than docker containers.
20:29 < Warepire> The KVM kernel interface is surprisingly "easy" (in regards to how a full-fledged VM API can be easy), and as long as the
                  licensing in terms of software is managed properly, we can probably leech of QEMU KVM backend for a lot of the code that doesn't
                  need custom handling.
20:33 < Warepire> Either way, VM route or Docker route, there would need to be some support from TASVideos to provide the "base OS", so that the
                  environment is preserved.
20:34 < Warepire> For Docker it would be rules for Dockerfiles, and hosting a so called registry. For VMs it would probably be hosting custom
                  install medias or disk images.
20:34 < Warepire> (not including the games, obviously)
Judge, Moderator, Player (200)
Joined: 7/15/2021
Posts: 112
Location: United States
How much of this work has already been done by Hermit, Meta (Facebook)'s deterministic Linux replay software?
Site Admin, Skilled player (1255)
Joined: 4/17/2010
Posts: 11495
Location: Lake Char­gogg­a­gogg­man­chaugg­a­gogg­chau­bun­a­gung­a­maugg
Warepire wrote:
20:33 < Warepire> Either way, VM route or Docker route, there would need to be some support from TASVideos to provide the "base OS", so that the
                  environment is preserved.
20:34 < Warepire> For Docker it would be rules for Dockerfiles, and hosting a so called registry. For VMs it would probably be hosting custom
                  install medias or disk images.
We can definitely host text files in Wiki: UserFiles by adding support for more file extensions, and image files we can host on the server directly as long as they are free.
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Editor, Emulator Coder, Site Developer
Joined: 5/11/2011
Posts: 1108
Location: Murka
I think the threading determinism part is probably the hardest to solve. As already discussed, you can't ever have multiple actual threads running at once, as this introduces nondeterminism that's built in all the way down to the silicon level -- your multi-core cpu does not guarantee anything about the relative timing of multiple threads of execution running on it at the same time. I don't even think you can trust a scheduler to preempt deterministically. Many applications are more or less single-threaded and call timing sensitive APIs in predictable manners that can have them run deterministically even if the underlying system is not, but there will be many other titles that you just can't get to any reasonable amount of determinism.
Player (42)
Joined: 12/27/2008
Posts: 873
Location: Germany
Lots of interesting replies in this topic. Well, I think the problems introduced by multithreading are being overestimated. Definitely the end result of a program can depend on the order everything is processed, but it's in general extremely bad programming practice to do this. In fact, true concurrent code should never produce different results when you change execution order. While it's simple to come up with cases where this matters a lot, the fact is that code written by humans for humans to use does not look like that. For example, using xscope I can clearly see that there's a burst of requests to X in a short amount of time, then it stops, and after around 30 ms another burst happens. This is probably because it runs at 30 fps, and even if X doesn't know what a frame is, the UI library in the application is enforcing it. This is pretty typical when you have much more computational power than you actually need. Most of the time, the application sits around doing nothing, waits for user input, then it does a lot at the same time. In general, if the output of your program depends on the order, there's a bug. In theory you could use this as a source of randomness, but it's a very bad one. It's very biased and the bias can drastically change with some small changes to the code or even changes in the platform running it. If you're using Linux processes, you really have to go out of your way to mess things up, because most interprocess communication in Linux makes everything order independent. You'd need to either use shared memory between processes and program terrible synchronization code to make this mistake. It's unlikely that an app would do this unless they explicitly want to detect virtualization. The only way I can see this affecting real apps is when a game uses time and random devices to choose an action. From my experience with emulators, though, it's pretty easy to get determinism by "discretizing" the world. You essentially choose a time window and fake all of these devices to return the same value during it. Then when the time window is over, you stop everything and update the device values. That usually works pretty well. Regarding image building, I did not do everything through Dockerfiles, because the installer usually has a GUI. It's definitely possible to do, though. You can run xvfb in the image to run GUI apps, and use xdotool to automate the input. It's annoying, but possible. It should be OK as long as you don't include the game binaries yourself, and tell the user that by running the tool they accept some license which they should read in another environment, but it's a good idea to check with a lawyer.
Judge, Moderator, Player (200)
Joined: 7/15/2021
Posts: 112
Location: United States
p4wn3r wrote:
Well, I think the problems introduced by multithreading are being overestimated. Definitely the end result of a program can depend on the order everything is processed, but it's in general extremely bad programming practice to do this. In fact, true concurrent code should never produce different results when you change execution order. While it's simple to come up with cases where this matters a lot, the fact is that code written by humans for humans to use does not look like that. [...] In general, if the output of your program depends on the order, there's a bug. In theory you could use this as a source of randomness, but it's a very bad one. It's very biased and the bias can drastically change with some small changes to the code or even changes in the platform running it. If you're using Linux processes, you really have to go out of your way to mess things up, because most interprocess communication in Linux makes everything order independent. You'd need to either use shared memory between processes and program terrible synchronization code to make this mistake. It's unlikely that an app would do this unless they explicitly want to detect virtualization.
This does not reassure me. Game development is full of buggy code. If your argument is that "programs wouldn't do this because it would result in bugs", well that's already a very tenuous argument in the best of circumstances, but these are games we are talking about. Games are full of bugs and extremely bad programming practices because developers care more about finishing their game than being correct and clean. For a counterexample to your argument, consider any game made with Unity, a notorious engine known for inexplicable desyncs due to the many threads it spawns. Just look at how hard #7822: rythin's MacOS Yo! Noid 2: Enter the Void in 07:59.83 was to sync for publishers. After I accepted it, I needed to dump the game myself because no publisher was able to sync it.
Player (42)
Joined: 12/27/2008
Posts: 873
Location: Germany
Well, I've been in this site for a long while. In the old days there was even a page explaining how emulators could never be accurate, and it made no sense to compare TAS to real runs because of that, and how cosmic rays, temperature changes, or whatever, made it impossible to simulate real hardware. That was until people actually tried to reproduce some runs on real hardware and saw the situation was not so dire. Even something as simple as the Game Boy has some sort of multithreading, because it has processors other than the main CPU to do memory transfers and so on, and it still works. The reason is that, for programming that makes sense, it doesn't matter. And if the programming doesn't make sense, don't even try to get it working, because it's likely that in original hardware it won't work either. Take Pokemon RBY for example, it uses an RNG that calls a timing register several times per frame, and yet, real time runners can and do manipulate it with some tricks to get the input at the right time in lots of runs. I cannot really comment about the run you linked, because I'm not familiar, but I've seen several desyncs, even in emulators, which control the entire execution. What's the evidence that you guys have that it was caused by multithreading? Asking sincerely, because I could take a look and learn from it. My first impression is that if you run Unity, which is a massive engine with lots of dependencies, many small configuration changes in the OS can affect the replay, which is the reason I'm running the game sandboxed.
Judge, Moderator, Player (200)
Joined: 7/15/2021
Posts: 112
Location: United States
p4wn3r wrote:
Well, I've been in this site for a long while. In the old days there was even a page explaining how emulators could never be accurate, and it made no sense to compare TAS to real runs because of that, and how cosmic rays, temperature changes, or whatever, made it impossible to simulate real hardware.
Link? And this is not the sort of specious, pedantic argument I'm making. Cosmic rays and temperature changes don't make for deterministic playback even on real hardware.
p4wn3r wrote:
The reason is that, for programming that makes sense, it doesn't matter. And if the programming doesn't make sense, don't even try to get it working, because it's likely that in original hardware it won't work either.
In Yo! Noid 2, on "original hardware" (i.e. outside of libTAS), the game works completely fine. The desyncs affect movement and desync the TAS by making the player character move in slightly different directions. To an RTA runner, it wouldn't matter, because they would adjust accordingly, but a TAS cannot do that.
p4wn3r wrote:
Take Pokemon RBY for example, it uses an RNG that calls a timing register several times per frame, and yet, real time runners can and do manipulate it with some tricks to get the input at the right time in lots of runs.
I would assume this is because it's deterministic. Which isn't much of a significant point; of course it would work, even for RTA.
p4wn3r wrote:
I cannot really comment about the run you linked, because I'm not familiar, but I've seen several desyncs, even in emulators, which control the entire execution. What's the evidence that you guys have that it was caused by multithreading?
If I remember correctly, it's because the desyncs happen whenever the game loads and transitions between levels.
p4wn3r wrote:
Asking sincerely, because I could take a look and learn from it. My first impression is that if you run Unity, which is a massive engine with lots of dependencies, many small configuration changes in the OS can affect the replay, which is the reason I'm running the game sandboxed.
Please do take a look at Unity and see if you can figure something out. In theory, you're right that small configuration changes in the OS can affect the replay (which could be why it never synced for EZGames69 or fsvgm777), but in practice, I had to keep playing the TAS over and over before I got a run that would sync. And I'm pretty sure I didn't make any configuration changes to my OS between runs.
Joined: 7/29/2023
Posts: 1
since VNC and CRIU were mentioned, a while ago I saw this dude who was able to savestate a whole ass web browser running a (likely GPU-accelerated) YouTube video in 2019 https://www.youtube.com/watch?v=kjhuzSl6JYc... am I seeing this right? would this approach work on a game? also last time I checked AMD's GPU-related work on CRIU checkpoints got merged upstream (https://github.com/checkpoint-restore/criu/pull/1709)... so can we savestate GPU-accelerated programs now or nah?