How to use the game_memory & game_state

Hiya I don't have the source (and am a complete beginner at c) so it's a little hard to figure out, but I am trying to understand how the game_memory is used. I've followed episode 14 and built the GameMemory, but in that same episode there is no real usage of that thing ;)

I am wondering how and when the game_state is initialized, also wondering how structs are put in there, I have seen a function in *some* stream called push_struct I think that's involved somehow. I think I understand how the data is retrieved from the game_memory it's by casting the whole game_memory to a game_state and then using the variables in there... right?

so it's a few broad questions, I would also be very happy if one can point me to another episode that goes in these details.

I have this code that's not yest using a game_state but yeah.

typedef struct foo
{
uint8 age;
char* name;
} foo;

void push_foo_struct(foo* s, game_memory* mem){
*(foo*) (mem->PermanentStorage + mem->PermanentOffset) = *s;
mem->PermanentOffset += sizeof(*s);
}

int main (){
game_memory GameMemory = {};
void *BaseAddress = (void *) Gigabytes(1);
GameMemory.PermanentOffset = 0;
GameMemory.PermanentStorageSize = Megabytes(64);
GameMemory.TransientStorageSize = Gigabytes(1);

uint64 TotalStorageSize = GameMemory.PermanentStorageSize + GameMemory.TransientStorageSize;
GameMemory.PermanentStorage = mmap(BaseAddress, TotalStorageSize,
PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE,
-1, 0);
GameMemory.TransientStorage = (uint8*)(GameMemory.PermanentStorage) + GameMemory.PermanentStorageSize;

foo thing1;
thing1.age = 23;
thing1.name = "John";
foo thing2;
thing2.age = 30;
thing2.name = "Alfred";
foo thing3;
thing3.age = 232;
thing3.name = "JobbieWock the Ent";

push_foo_struct(&thing1, &GameMemory);
push_foo_struct(&thing2, &GameMemory);
push_foo_struct(&thing3, &GameMemory);

foo* stored_first = ((foo*)(GameMemory.PermanentStorage+sizeof(foo)*0));
foo* stored_second = ((foo*)(GameMemory.PermanentStorage+sizeof(foo)*1));
foo* stored_third = ((foo*)(GameMemory.PermanentStorage+sizeof(foo)*2));

printf("age of %s: %d\n", stored_first->name, stored_first->age);
printf("age of %s: %d\n", stored_second->name, stored_second->age);
printf("age of %s: %d\n", stored_third->name, stored_third->age);
}



edit: before I forget, what I am also wondering about is assuming there are many structs in my game_state, how the initting these structs look like, do I init them inside the game_state and somehow move it in the game_memory ?
or are thee only pointers in the game_state?

edit2: I found the episode where PushStruct is introduced (ep 34 around the 45 min mark), so I think I can manage from here.

Edited by nikki on Reason: answering part of my own questions
Hi!

first I want to point out that there's an earlier use of GameMemory/GameState at the beginning of week 5. Thanks to the forum guides (bless the authors!), I can point you to the exact moment where these memory abstractions are put to good use.

Since you've already studied everything up to day 14, I believe you don't need to watch week 4 to understand the use of the platform-independent memory structs (although, of course, you'll need to watch them eventually in order to understand some stuff later on). But anyways, I'll just give you a barebones explanation that you should in no way mistake for Casey's in-depth explanation.

The platform layer defines a GameMemory struct and points it to a block of memory that will be handed to the game_update_and_render function each time it is called. The platform layer only knows the size of that chunk of memory, but remains completely ignorant of how the GUAR function uses it.

The GUAR function takes that block of memory and casts it as a GameState struct and uses that struct to keep all the game-related information it needs to keep between calls.

So, for instance, if you wanted your character to start with 5 health points and to lose one point per frame, your GameState and GUAR would look something like this (I'm avoiding Casey's GUAR macros to make the signature of the function more accessible to beginners):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
struct GameState{
    int health_points;
};

void game_update_and_render(GameMemory *game_memory, GameInput *game_input){
    GameState *game_state = (GameState) game_memory;

    if(!game_memory->is_initialized){
        game_state->health_points = 5;
    }

    --game_state->health_points;
    if(game_state->health_points <= 0){
        // YOU'RE DEAD !!!
    }
}

The management of the memory in the platform layer might seem strange from the low vantage point of day 14 and the importance of allocating that block of memory at a known address (2TB, IIRC) may not be apparent yet (I seem to remember Casey justifying it by mumbling something about being able to see if a variable belonged to the GameState by looking at its address in the debugger to see if it was higher that 2TB; I guess he wanted to avoid spoiling the big surprise). Week 5, which is a thing of beauty, discusses how this memory management scheme makes possible hot code reloading and loop code editing, among other things. I hope you enjoy it as much as I did!!

The push_struct is related to managing dynamic chunks of memory. I would advise against jumping straight into it if it means skipping week 6. You don't want to skip week 6. Oh, I love week 6! If only I could forget week 6 to be able to experience it again for the first time!

Edited by Miguel Lechón on