fix gamestate lifecycle management when game logic frame takes longer than 1/60s and implement SwitchCurrentGamestate

This commit is contained in:
Sebastian Krzyszkowiak 2016-08-16 18:41:50 +02:00
parent 686514fba5
commit 2dfc565178
5 changed files with 43 additions and 18 deletions

View file

@ -73,6 +73,7 @@ SYMBOL_EXPORT void LoadGamestate(struct Game *game, const char* name) {
gs->showLoading = true; gs->showLoading = true;
} }
PrintConsole(game, "Gamestate \"%s\" marked to be LOADED.", name); PrintConsole(game, "Gamestate \"%s\" marked to be LOADED.", name);
game->_priv.gamestate_scheduled = true;
} }
SYMBOL_EXPORT void UnloadGamestate(struct Game *game, const char* name) { SYMBOL_EXPORT void UnloadGamestate(struct Game *game, const char* name) {
@ -93,6 +94,7 @@ SYMBOL_EXPORT void UnloadGamestate(struct Game *game, const char* name) {
} else { } else {
PrintConsole(game, "Tried to unload nonexisitent gamestate \"%s\"", name); PrintConsole(game, "Tried to unload nonexisitent gamestate \"%s\"", name);
} }
game->_priv.gamestate_scheduled = true;
} }
SYMBOL_EXPORT void StartGamestate(struct Game *game, const char* name) { SYMBOL_EXPORT void StartGamestate(struct Game *game, const char* name) {
@ -107,6 +109,7 @@ SYMBOL_EXPORT void StartGamestate(struct Game *game, const char* name) {
} else { } else {
PrintConsole(game, "Tried to start nonexisitent gamestate \"%s\"", name); PrintConsole(game, "Tried to start nonexisitent gamestate \"%s\"", name);
} }
game->_priv.gamestate_scheduled = true;
} }
SYMBOL_EXPORT void StopGamestate(struct Game *game, const char* name) { SYMBOL_EXPORT void StopGamestate(struct Game *game, const char* name) {
@ -126,6 +129,7 @@ SYMBOL_EXPORT void StopGamestate(struct Game *game, const char* name) {
} else { } else {
PrintConsole(game, "Tried to stop nonexisitent gamestate \"%s\"", name); PrintConsole(game, "Tried to stop nonexisitent gamestate \"%s\"", name);
} }
game->_priv.gamestate_scheduled = true;
} }
SYMBOL_EXPORT void PauseGamestate(struct Game *game, const char* name) { SYMBOL_EXPORT void PauseGamestate(struct Game *game, const char* name) {
@ -178,3 +182,7 @@ SYMBOL_EXPORT void SwitchGamestate(struct Game *game, const char* current, const
LoadGamestate(game, n); LoadGamestate(game, n);
StartGamestate(game, n); StartGamestate(game, n);
} }
SYMBOL_EXPORT void SwitchCurrentGamestate(struct Game *game, const char* n) {
SwitchGamestate(game, game->_priv.current_gamestate->name, n);
}

View file

@ -60,5 +60,6 @@ void PauseGamestate(struct Game *game, const char* name);
void ResumeGamestate(struct Game *game, const char* name); void ResumeGamestate(struct Game *game, const char* name);
void UnloadAllGamestates(struct Game *game); void UnloadAllGamestates(struct Game *game);
void SwitchGamestate(struct Game *game, const char* current, const char* n); void SwitchGamestate(struct Game *game, const char* current, const char* n);
void SwitchCurrentGamestate(struct Game *game, const char* n);
#endif #endif

View file

@ -29,6 +29,7 @@ SYMBOL_INTERNAL void DrawGamestates(struct Game *game) {
struct Gamestate *tmp = game->_priv.gamestates; struct Gamestate *tmp = game->_priv.gamestates;
while (tmp) { while (tmp) {
if ((tmp->loaded) && (tmp->started)) { if ((tmp->loaded) && (tmp->started)) {
game->_priv.current_gamestate = tmp;
(*tmp->api.Gamestate_Draw)(game, tmp->data); (*tmp->api.Gamestate_Draw)(game, tmp->data);
} }
tmp = tmp->next; tmp = tmp->next;
@ -39,6 +40,7 @@ SYMBOL_INTERNAL void LogicGamestates(struct Game *game) {
struct Gamestate *tmp = game->_priv.gamestates; struct Gamestate *tmp = game->_priv.gamestates;
while (tmp) { while (tmp) {
if ((tmp->loaded) && (tmp->started) && (!tmp->paused)) { if ((tmp->loaded) && (tmp->started) && (!tmp->paused)) {
game->_priv.current_gamestate = tmp;
(*tmp->api.Gamestate_Logic)(game, tmp->data); (*tmp->api.Gamestate_Logic)(game, tmp->data);
} }
tmp = tmp->next; tmp = tmp->next;
@ -49,6 +51,7 @@ SYMBOL_INTERNAL void EventGamestates(struct Game *game, ALLEGRO_EVENT *ev) {
struct Gamestate *tmp = game->_priv.gamestates; struct Gamestate *tmp = game->_priv.gamestates;
while (tmp) { while (tmp) {
if ((tmp->loaded) && (tmp->started) && (!tmp->paused)) { if ((tmp->loaded) && (tmp->started) && (!tmp->paused)) {
game->_priv.current_gamestate = tmp;
(*tmp->api.Gamestate_ProcessEvent)(game, tmp->data, ev); (*tmp->api.Gamestate_ProcessEvent)(game, tmp->data, ev);
} }
tmp = tmp->next; tmp = tmp->next;
@ -59,6 +62,7 @@ SYMBOL_INTERNAL void PauseGamestates(struct Game *game) {
struct Gamestate *tmp = game->_priv.gamestates; struct Gamestate *tmp = game->_priv.gamestates;
while (tmp) { while (tmp) {
if ((tmp->loaded) && (tmp->started)) { if ((tmp->loaded) && (tmp->started)) {
game->_priv.current_gamestate = tmp;
(*tmp->api.Gamestate_Pause)(game, tmp->data); (*tmp->api.Gamestate_Pause)(game, tmp->data);
} }
tmp = tmp->next; tmp = tmp->next;
@ -70,6 +74,7 @@ SYMBOL_INTERNAL void ResumeGamestates(struct Game *game) {
struct Gamestate *tmp = game->_priv.gamestates; struct Gamestate *tmp = game->_priv.gamestates;
while (tmp) { while (tmp) {
if ((tmp->loaded) && (tmp->started)) { if ((tmp->loaded) && (tmp->started)) {
game->_priv.current_gamestate = tmp;
(*tmp->api.Gamestate_Resume)(game, tmp->data); (*tmp->api.Gamestate_Resume)(game, tmp->data);
} }
tmp = tmp->next; tmp = tmp->next;
@ -123,17 +128,17 @@ SYMBOL_INTERNAL void Console_Unload(struct Game *game) {
} }
SYMBOL_INTERNAL void GamestateProgress(struct Game *game) { SYMBOL_INTERNAL void GamestateProgress(struct Game *game) {
struct Gamestate *tmp = game->_priv.cur_gamestate.tmp; struct Gamestate *tmp = game->_priv.tmp_gamestate.tmp;
game->_priv.cur_gamestate.p++; game->_priv.tmp_gamestate.p++;
DrawGamestates(game); DrawGamestates(game);
float progressCount = *(tmp->api.Gamestate_ProgressCount) ? (float)*(tmp->api.Gamestate_ProgressCount) : 1; float progressCount = *(tmp->api.Gamestate_ProgressCount) ? (float)*(tmp->api.Gamestate_ProgressCount) : 1;
float progress = ((game->_priv.cur_gamestate.p / progressCount) / (float)game->_priv.cur_gamestate.toLoad) + (game->_priv.cur_gamestate.loaded/(float)game->_priv.cur_gamestate.toLoad); float progress = ((game->_priv.tmp_gamestate.p / progressCount) / (float)game->_priv.tmp_gamestate.toLoad) + (game->_priv.tmp_gamestate.loaded/(float)game->_priv.tmp_gamestate.toLoad);
if (game->config.debug) PrintConsole(game, "[%s] Progress: %d% (%d/%d)", tmp->name, (int)(progress*100), game->_priv.cur_gamestate.p, *(tmp->api.Gamestate_ProgressCount)); if (game->config.debug) PrintConsole(game, "[%s] Progress: %d% (%d/%d)", tmp->name, (int)(progress*100), game->_priv.tmp_gamestate.p, *(tmp->api.Gamestate_ProgressCount));
if (tmp->showLoading) (*game->_priv.loading.Draw)(game, game->_priv.loading.data, progress); if (tmp->showLoading) (*game->_priv.loading.Draw)(game, game->_priv.loading.data, progress);
DrawConsole(game); DrawConsole(game);
if (al_get_time() - game->_priv.cur_gamestate.t >= 1/60.0) { if (al_get_time() - game->_priv.tmp_gamestate.t >= 1/60.0) {
al_flip_display(); al_flip_display();
game->_priv.cur_gamestate.t = al_get_time(); game->_priv.tmp_gamestate.t = al_get_time();
} }
} }

View file

@ -148,6 +148,7 @@ SYMBOL_EXPORT struct Game* libsuperderpy_init(int argc, char** argv, const char*
al_set_new_bitmap_flags(ALLEGRO_MIN_LINEAR); al_set_new_bitmap_flags(ALLEGRO_MIN_LINEAR);
game->_priv.gamestates = NULL; game->_priv.gamestates = NULL;
game->_priv.gamestate_scheduled = false;
game->_priv.event_queue = al_create_event_queue(); game->_priv.event_queue = al_create_event_queue();
if(!game->_priv.event_queue) { if(!game->_priv.event_queue) {
@ -230,24 +231,26 @@ SYMBOL_EXPORT int libsuperderpy_run(struct Game *game) {
while(1) { while(1) {
ALLEGRO_EVENT ev; ALLEGRO_EVENT ev;
if (redraw && al_is_event_queue_empty(game->_priv.event_queue)) { if ((redraw && al_is_event_queue_empty(game->_priv.event_queue)) || (game->_priv.gamestate_scheduled)) {
game->_priv.gamestate_scheduled = false;
struct Gamestate *tmp = game->_priv.gamestates; struct Gamestate *tmp = game->_priv.gamestates;
game->_priv.cur_gamestate.toLoad = 0; game->_priv.tmp_gamestate.toLoad = 0;
game->_priv.cur_gamestate.loaded = 0; game->_priv.tmp_gamestate.loaded = 0;
// FIXME: move to function // FIXME: move to function
// TODO: support dependences // TODO: support dependences
while (tmp) { while (tmp) {
if (tmp->pending_stop) { if (tmp->pending_stop) {
PrintConsole(game, "Stopping gamestate \"%s\"...", tmp->name); PrintConsole(game, "Stopping gamestate \"%s\"...", tmp->name);
game->_priv.current_gamestate = tmp;
(*tmp->api.Gamestate_Stop)(game, tmp->data); (*tmp->api.Gamestate_Stop)(game, tmp->data);
tmp->started = false; tmp->started = false;
tmp->pending_stop = false; tmp->pending_stop = false;
} }
if (tmp->pending_load) game->_priv.cur_gamestate.toLoad++; if (tmp->pending_load) game->_priv.tmp_gamestate.toLoad++;
tmp=tmp->next; tmp=tmp->next;
} }
@ -255,7 +258,7 @@ SYMBOL_EXPORT int libsuperderpy_run(struct Game *game) {
// FIXME: move to function // FIXME: move to function
// TODO: support dependences // TODO: support dependences
game->_priv.cur_gamestate.t = -1; game->_priv.tmp_gamestate.t = -1;
while (tmp) { while (tmp) {
if (tmp->pending_unload) { if (tmp->pending_unload) {
@ -263,6 +266,7 @@ SYMBOL_EXPORT int libsuperderpy_run(struct Game *game) {
al_stop_timer(game->_priv.timer); al_stop_timer(game->_priv.timer);
tmp->loaded = false; tmp->loaded = false;
tmp->pending_unload = false; tmp->pending_unload = false;
game->_priv.current_gamestate = tmp;
(*tmp->api.Gamestate_Unload)(game, tmp->data); (*tmp->api.Gamestate_Unload)(game, tmp->data);
dlclose(tmp->handle); dlclose(tmp->handle);
tmp->handle = NULL; tmp->handle = NULL;
@ -300,20 +304,21 @@ SYMBOL_EXPORT int libsuperderpy_run(struct Game *game) {
if (!(tmp->api.Gamestate_ProgressCount = dlsym(tmp->handle, "Gamestate_ProgressCount"))) { GS_ERROR; } if (!(tmp->api.Gamestate_ProgressCount = dlsym(tmp->handle, "Gamestate_ProgressCount"))) { GS_ERROR; }
game->_priv.cur_gamestate.p = 0; game->_priv.tmp_gamestate.p = 0;
DrawGamestates(game); DrawGamestates(game);
if (tmp->showLoading) { if (tmp->showLoading) {
(*game->_priv.loading.Draw)(game, game->_priv.loading.data, game->_priv.cur_gamestate.loaded/(float)game->_priv.cur_gamestate.toLoad); (*game->_priv.loading.Draw)(game, game->_priv.loading.data, game->_priv.tmp_gamestate.loaded/(float)game->_priv.tmp_gamestate.toLoad);
} }
DrawConsole(game); DrawConsole(game);
if (al_get_time() - game->_priv.cur_gamestate.t >= 1/60.0) { if (al_get_time() - game->_priv.tmp_gamestate.t >= 1/60.0) {
al_flip_display(); al_flip_display();
game->_priv.cur_gamestate.t = al_get_time(); game->_priv.tmp_gamestate.t = al_get_time();
} }
game->_priv.cur_gamestate.tmp = tmp; game->_priv.tmp_gamestate.tmp = tmp;
game->_priv.current_gamestate = tmp;
tmp->data = (*tmp->api.Gamestate_Load)(game, &GamestateProgress); tmp->data = (*tmp->api.Gamestate_Load)(game, &GamestateProgress);
game->_priv.cur_gamestate.loaded++; game->_priv.tmp_gamestate.loaded++;
tmp->loaded = true; tmp->loaded = true;
tmp->pending_load = false; tmp->pending_load = false;
@ -331,6 +336,7 @@ SYMBOL_EXPORT int libsuperderpy_run(struct Game *game) {
if ((tmp->pending_start) && (tmp->loaded)) { if ((tmp->pending_start) && (tmp->loaded)) {
PrintConsole(game, "Starting gamestate \"%s\"...", tmp->name); PrintConsole(game, "Starting gamestate \"%s\"...", tmp->name);
al_stop_timer(game->_priv.timer); al_stop_timer(game->_priv.timer);
game->_priv.current_gamestate = tmp;
(*tmp->api.Gamestate_Start)(game, tmp->data); (*tmp->api.Gamestate_Start)(game, tmp->data);
al_start_timer(game->_priv.timer); al_start_timer(game->_priv.timer);
tmp->started = true; tmp->started = true;
@ -421,11 +427,13 @@ SYMBOL_EXPORT void libsuperderpy_destroy(struct Game *game) {
while (tmp) { while (tmp) {
if (tmp->started) { if (tmp->started) {
PrintConsole(game, "Stopping gamestate \"%s\"...", tmp->name); PrintConsole(game, "Stopping gamestate \"%s\"...", tmp->name);
game->_priv.current_gamestate = tmp;
(*tmp->api.Gamestate_Stop)(game, tmp->data); (*tmp->api.Gamestate_Stop)(game, tmp->data);
tmp->started = false; tmp->started = false;
} }
if (tmp->loaded) { if (tmp->loaded) {
PrintConsole(game, "Unloading gamestate \"%s\"...", tmp->name); PrintConsole(game, "Unloading gamestate \"%s\"...", tmp->name);
game->_priv.current_gamestate = tmp;
(*tmp->api.Gamestate_Unload)(game, tmp->data); (*tmp->api.Gamestate_Unload)(game, tmp->data);
dlclose(tmp->handle); dlclose(tmp->handle);
tmp->loaded = false; tmp->loaded = false;

View file

@ -75,6 +75,7 @@ struct Game {
struct { struct {
struct Gamestate *gamestates; /*!< List of known gamestates. */ struct Gamestate *gamestates; /*!< List of known gamestates. */
bool gamestate_scheduled; /*!< Whether there's some gamestate lifecycle management work to do. */
ALLEGRO_FONT *font_console; /*!< Font used in game console. */ ALLEGRO_FONT *font_console; /*!< Font used in game console. */
ALLEGRO_FONT *font_bsod; /*!< Font used in Blue Screens of Derp. */ ALLEGRO_FONT *font_bsod; /*!< Font used in Blue Screens of Derp. */
ALLEGRO_BITMAP *console; /*!< Bitmap with game console. */ ALLEGRO_BITMAP *console; /*!< Bitmap with game console. */
@ -107,7 +108,9 @@ struct Game {
struct Gamestate *tmp; struct Gamestate *tmp;
double t; double t;
int loaded, toLoad; int loaded, toLoad;
} cur_gamestate; } tmp_gamestate;
struct Gamestate *current_gamestate;
struct libsuperderpy_list *garbage; struct libsuperderpy_list *garbage;