Handmade Hero » Forums » Code » Save/Playback input with SDL
CodyCoderson
2 posts
#20890 Save/Playback input with SDL
1 week, 4 days ago Edited by CodyCoderson on April 11, 2019, 3:31 p.m. Reason: Initial post

First let me say that I'm new to programming, so if I'm asking stupid questions, sorry.

I've been working on a game on linux, in C with SDL, and trying to implement some of the tricks/ideas that Casey shares throughout the series. I'm still very, very early in the series.

I just got up to episode 23, Looped Live Code Editing, where he saves the players input to disk and plays it back. I'm not sure how to do this with SDL and I very much would like to figure out how to do it since not only will it help speed up testing things, but I could potentially use it in game since I'm planning on having time travel mechanics .

Casey just writes his memory block to disk and it seems to handle recording the inputs itself. I'm not handling memory in exactly the same way as Casey though. I keep everything in a large GameState struct and malloc that at the start, so I don't think I can save/load the way he does? Please correct if I'm wrong, because his way does look much easier.

I have saving/loading working by going through each member of GameState, and write/read it to disk. I'm not sure how to do this with SDL events though.

How do I write an SDL event to file? And when reading it back in, how will the game know the timing on each event?

I'm either missing something or over thinking things! Thanks for any help.
Delix
Ryan Fleury
157 posts / 1 project

Handmade Network lead and developer of The Melodist

#20891 Save/Playback input with SDL
1 week, 4 days ago

Hmm, I think there are probably many ways you can approach this. My first approach would be to have some per-frame information about input, perhaps in a form like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
typedef struct Input
{
    b32 key_down[KEY_MAX];
    b32 key_pressed[KEY_MAX];
    b32 left_mouse_down;
    f32 mouse_x;
    f32 mouse_y;
    // etc.
    // If you want event-driven input, have that stuff in here as well?
}
Input;


Then, recording input is as easy as saving this memory for each frame, and then when playing it back, instead of filling out this struct with SDL's events, you could just fill it out with the saved input history that you are playing back.

I don't use SDL so I don't know, but alternatively you might be able to pipe events back through SDL if you save them to disk and reload them?

On your point about your GameState struct not being able to be saved to disk, from what it sounds like, it very much sounds like you could just write that entire block of memory to disk, load it back up during a different instance of the program, and run with it, unless you are keeping pointers to further allocations in that storage (these pointers would no longer be valid if you loaded this memory at a different runtime).

From my understanding, malloc will just call either VirtualAlloc (what Casey uses to allocate his memory block at startup time) or HeapAlloc (which calls VirtualAlloc as necessary, as it will manage a heap within pages allocated by VirtualAlloc) under the hood. There isn't really much difference between the memory you allocate for your GameState struct and the memory Casey allocates. What you need to be careful about is when your memory contains pointers that reference memory that is allocated separately.

Handmade Network lead and developer of The Melodist
CodyCoderson
2 posts
#20892 Save/Playback input with SDL
1 week, 4 days ago

Thanks, that was very helpful. I'm not sure why I didn't think it was possible to write the state to disk like Casey. I mean, I'm able to compile and see the changes take effect in the live running program, so I should have architected things properly for this kind of thing?

I wrote these two functions and it sort of works. Makes me feel silly comparing it to all the lines of codes I had to write doing it the other way.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void save_state(char *filename, GameState *gameState)
{
    FILE *file = fopen(filename, "wb");

    fwrite(gameState, sizeof(GameState), 1, file);

    fclose(file);
    file = NULL;
}

void load_state(char *filename, GameState *gameState)
{
    FILE *file = fopen(filename, "rb");

    fread(gameState, sizeof(GameState), 1, file);

    fclose(file);
    file = NULL;
}


It isn't loading in 100% correctly (entity x, y positions and their velocities are all screwed up) but I'll keep messing with it. When I get this fixed (or give up) I'll move on to recording input with your advice. Thanks!