/* GAME_TIME.C symbols in this file: 000A4D00 0020: _game_time_initialized (0000) 000A4D20 0030: _game_time_initialize (0000) 000A4D50 0050: _game_time_initialize_for_new_map (0000) 000A4DA0 0020: _game_time_dispose_from_old_map (0000) 000A4DC0 0010: _game_time_dispose (0000) 000A4DD0 0040: _game_time_end (0000) 000A4E10 0040: _game_time_get (0000) 000A4E50 0040: _game_time_get_elapsed (0000) 000A4E90 0040: _local_time_get (0000) 000A4ED0 0040: _local_time_get_elapsed (0000) 000A4F10 0040: _game_predicting (0000) 000A4F50 0050: _game_in_progress (0000) 000A4FA0 0040: _game_time_get_paused (0000) 000A4FE0 0050: _game_time_set_paused (0000) 000A5030 0040: _game_time_get_speed (0000) 000A5070 0050: _game_time_set_speed (0000) 000A50C0 0010: _code_000a50c0 (0000) 000A50D0 01e0: _code_000a50d0 (0000) 000A52B0 00e0: _game_time_start (0000) 000A5390 0360: _game_time_update (0000) 0025C480 0012: ??_C@_0BC@ENHAGKAA@game?5time?5globals?$AA@ (0000) 0025C494 0035: ??_C@_0DF@KKMNPDPK@game_time_globals?5?$CG?$CG?5?$CBgame_time_@ (0000) 0025C4CC 0020: ??_C@_0CA@INLHHDGN@c?3?2halo?2SOURCE?2game?2game_time?4c?$AA@ (0000) 0025C4EC 0012: ??_C@_0BC@NOJBGBLH@game_time_globals?$AA@ (0000) 0025C500 0034: ??_C@_0DE@PPGKJIJK@game_time_globals?5?$CG?$CG?5game_time_g@ (0000) 0025C534 001b: ??_C@_0BL@DECMFGMD@?$CBgame_time_globals?9?$DOactive?$AA@ (0000) 0025C550 0021: ??_C@_0CB@OIFBECDL@ticks_elapsed?5?$DM?$DN?5maximum_actions@ (0000) 0025C578 004c: ??_C@_0EM@FOLKNDLG@game_time_globals?9?$DOleftover_dt?$DO?$DN@ (0000) 0025C5C8 004c: ??_C@_0EM@FBBKPKLD@update?5server?5is?5too?5far?5ahead?5o@ (0000) 0043EE04 002c: _bss_0043ee04 (0000) */ /* ---------- headers */ #include "cseries.h" #include "cseries_windows.h" #include "real_math.h" #include "game.h" /* ---------- constants */ enum { SOME_LARGE_NUMBER_OF_TICKS = 1000, // 0x03E8 }; /* ---------- macros */ /* ---------- structures */ struct game_time_statistics { FILE *file; // 0x0 boolean active; // 0x4 boolean first_line; // 0x5 unsigned long last_milliseconds; // 0x8 short frame_count; // 0xC short total_milliseconds_elapsed; // 0xE short minimum_milliseconds_per_frame; // 0x10 short maximum_milliseconds_per_frame; // 0x12 short total_latency; // 0x14 short minimum_latency; // 0x16 short maximum_latency; // 0x18 short server_updates; // 0x1A short minimum_server_updates; // 0x1C short maximum_server_updates; // 0x1E short predicted_updates; // 0x20 short minimum_predicted_updates; // 0x22 short maximum_predicted_updates; // 0x24 }; struct game_time_globals_struct { boolean initialized; boolean active; boolean paused; short monitor_state; short monitor_counter; short monitor_latency; long local_time; short last_local_time_elapsed; long server_time; real speed; real leftover_dt; }; /* ---------- prototypes */ extern void update_client_start(void); extern void update_server_start(void); extern void *game_state_malloc(char const *, char const *, long); struct network_game_server; extern struct network_game_server *global_network_game_server_get(void); extern long network_game_server_get_oldest_client_update_received(struct network_game_server *server); extern void network_game_server_stalled_on_client(struct network_game_server *server, boolean stalled); extern void network_game_server_update_ticks(struct network_game_server *server, long ticks); extern long update_client_get_maximum_actions(void); extern long update_client_get_maximum_possible_server_time(void); extern void update_client_local_ticks(long ticks); /* ---------- globals */ static struct game_time_statistics game_time_statistics; static struct game_time_globals_struct *game_time_globals; /* ---------- public code */ boolean game_time_initialized( void) { return (game_time_globals && game_time_globals->initialized); } void game_time_initialize( void) { game_time_globals = (struct game_time_globals_struct *)game_state_malloc("game time globals", NULL, sizeof(*game_time_globals)); memset(game_time_globals, 0, sizeof(*game_time_globals)); return; } void game_time_initialize_for_new_map( void) { match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 131, game_time_globals && !game_time_globals->initialized); memset(game_time_globals, 0, sizeof(*game_time_globals)); game_time_globals->initialized = TRUE; return; } void game_time_dispose_from_old_map( void) { if (game_time_globals) { game_time_globals->initialized = FALSE; game_time_globals->active = FALSE; } return; } void game_time_dispose( void) { return; } void game_time_end( void) { match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 196, game_time_globals); game_time_globals->active = FALSE; return; } long game_time_get( void) { match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 463, game_time_globals && game_time_globals->initialized); return game_time_globals->local_time; } short game_time_get_elapsed( void) { match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 471, game_time_globals && game_time_globals->initialized); return game_time_globals->last_local_time_elapsed; } long local_time_get( void) { match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 479, game_time_globals && game_time_globals->initialized); return game_time_globals->local_time; } short local_time_get_elapsed( void) { match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 487, game_time_globals && game_time_globals->initialized); return game_time_globals->last_local_time_elapsed; } boolean game_predicting( void) { match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 495, game_time_globals && game_time_globals->initialized); return FALSE; } boolean game_in_progress( void) { match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 505, game_time_globals); if (game_time_globals->initialized) { if (game_time_globals->active) { return TRUE; } /* bug? */ if (game_time_globals->paused) { return TRUE; } else { return FALSE; } } return FALSE; } boolean game_time_get_paused( void) { match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 533, game_time_globals); return game_time_globals->paused; } void game_time_set_paused( boolean paused) { match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 541, game_time_globals); if (game_time_globals->initialized) { game_time_globals->active = !paused; } game_time_globals->paused = paused; return; } real game_time_get_speed( void) { match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 555, game_time_globals); return game_time_globals->speed; } void game_time_set_speed( real speed) { match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 562, game_time_globals); game_time_globals->speed = speed; return; } void code_000a50c0( void) { game_time_statistics.first_line = TRUE; game_time_statistics.active = FALSE; return; } static void code_000a50d0( short latency, short server_updates, short predicted_updates, boolean first_line) { short milliseconds_elapsed; unsigned long milliseconds; if (!game_time_statistics.active) { game_time_statistics.frame_count = 0; game_time_statistics.total_milliseconds_elapsed = 0; game_time_statistics.minimum_milliseconds_per_frame = SHORT_MAX; game_time_statistics.maximum_milliseconds_per_frame = SHORT_MIN; game_time_statistics.total_latency = 0; game_time_statistics.minimum_latency = SHORT_MAX; game_time_statistics.maximum_latency = SHORT_MIN; game_time_statistics.server_updates = 0; game_time_statistics.minimum_server_updates = SHORT_MAX; game_time_statistics.maximum_server_updates = SHORT_MIN; game_time_statistics.predicted_updates = 0; game_time_statistics.minimum_predicted_updates = SHORT_MAX; game_time_statistics.maximum_predicted_updates = SHORT_MIN; game_time_statistics.last_milliseconds = system_milliseconds(); game_time_statistics.active = TRUE; return; } milliseconds = system_milliseconds(); milliseconds_elapsed = (short)(milliseconds - game_time_statistics.last_milliseconds); game_time_statistics.frame_count++; game_time_statistics.total_milliseconds_elapsed += milliseconds_elapsed; game_time_statistics.last_milliseconds = milliseconds; if (milliseconds_elapsed > game_time_statistics.maximum_milliseconds_per_frame) game_time_statistics.maximum_milliseconds_per_frame = milliseconds_elapsed; if (milliseconds_elapsed < game_time_statistics.minimum_milliseconds_per_frame) game_time_statistics.minimum_milliseconds_per_frame = milliseconds_elapsed; game_time_statistics.total_latency += latency; if (latency > game_time_statistics.maximum_latency) game_time_statistics.maximum_latency = latency; if (latency < game_time_statistics.minimum_latency) game_time_statistics.minimum_latency = latency; game_time_statistics.server_updates += server_updates; if (server_updates > game_time_statistics.maximum_server_updates) game_time_statistics.maximum_server_updates = server_updates; if (server_updates < game_time_statistics.minimum_server_updates) game_time_statistics.minimum_server_updates = server_updates; game_time_statistics.predicted_updates += predicted_updates; if (predicted_updates > game_time_statistics.maximum_predicted_updates) game_time_statistics.maximum_predicted_updates = predicted_updates; if (predicted_updates < game_time_statistics.minimum_predicted_updates) game_time_statistics.minimum_predicted_updates = predicted_updates; if (game_time_statistics.total_milliseconds_elapsed >= SOME_LARGE_NUMBER_OF_TICKS && game_time_statistics.frame_count > 0) { if (game_time_statistics.minimum_latency) { short monitor_state = game_time_statistics.minimum_latency >= 0; if (monitor_state != game_time_globals->monitor_state) { game_time_globals->monitor_state = monitor_state; game_time_globals->monitor_counter = 0; game_time_globals->monitor_latency = monitor_state ? SHORT_MAX : SHORT_MIN; } switch (game_time_globals->monitor_state) { case 0: if (game_time_statistics.minimum_latency > game_time_globals->monitor_latency) game_time_globals->monitor_latency = game_time_statistics.minimum_latency; break; case 1: if (game_time_statistics.minimum_latency < game_time_globals->monitor_latency) game_time_globals->monitor_latency = game_time_statistics.minimum_latency; break; } game_time_globals->monitor_counter++; if (game_time_globals->monitor_counter == 5) { game_time_globals->monitor_state = NONE; game_time_statistics.active = FALSE; return; } } else { game_time_globals->monitor_state = NONE; } game_time_statistics.active = FALSE; } return; } void game_time_start( void) { short connection; match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 162, game_time_globals && game_time_globals->initialized); match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 163, !game_time_globals->active); match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 562, game_time_globals); game_time_globals->speed = 1.f; game_time_globals->leftover_dt = 0; game_time_globals->active = TRUE; code_000a50c0(); connection = game_connection(); switch (connection) { case _game_connection_local: case _game_connection_network_server: { update_server_start(); break; } case _game_connection_network_client: case _game_connection_film_playback: { update_client_start(); break; } } return; } void game_time_update( real time_delta_sec) { match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 205, game_time_globals); if (game_time_globals->active) { long maximum_ticks_elapsed; long ticks_elapsed; real ticks_per_second = game_time_globals->speed*TICKS_PER_SECOND; if (ticks_per_second > 0.f) { boolean discard_leftover_time = TRUE; real game_time; switch (game_connection()) { case _game_connection_local: maximum_ticks_elapsed = 7; break; case _game_connection_network_client: maximum_ticks_elapsed = TICKS_PER_SECOND; break; case _game_connection_network_server: { struct network_game_server *server = global_network_game_server_get(); long oldest_client_update = network_game_server_get_oldest_client_update_received(server); long game_time = game_time_get(); match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 243, (unsigned long)(game_time - oldest_client_update) <= 128); if (game_time > 0) { maximum_ticks_elapsed = oldest_client_update - game_time + 128; if (maximum_ticks_elapsed < TICKS_PER_SECOND) { if (maximum_ticks_elapsed <= 0) { network_game_server_stalled_on_client(server, TRUE); break; } } else { maximum_ticks_elapsed = TICKS_PER_SECOND; } network_game_server_stalled_on_client(server, FALSE); } else { maximum_ticks_elapsed = 1; } } break; case _game_connection_film_playback: maximum_ticks_elapsed = TICKS_PER_SECOND; discard_leftover_time = FALSE; break; } game_time = time_delta_sec + game_time_globals->leftover_dt; ticks_elapsed = (long)MIN(floor(game_time*ticks_per_second), (real)SOME_LARGE_NUMBER_OF_TICKS); if (ticks_elapsed > maximum_ticks_elapsed) { ticks_elapsed = maximum_ticks_elapsed; if (discard_leftover_time) game_time = ticks_elapsed/ticks_per_second; } game_time_globals->leftover_dt = game_time - ticks_elapsed/ticks_per_second; if (game_time_globals->leftover_dt <= 0.f) game_time_globals->leftover_dt = 0.f; match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 306, game_time_globals->leftover_dt >= 0.f && game_time_globals->leftover_dt <= 100.f); if (game_connection() == _game_connection_network_client) { long maximum_actions = update_client_get_maximum_actions(); if (ticks_elapsed > maximum_actions) ticks_elapsed = FLOOR(maximum_actions - 1, 0); else if (ticks_elapsed + 7 < maximum_actions) ticks_elapsed = FLOOR(maximum_actions - 1, 0); else if (ticks_elapsed + 1 < maximum_actions) ticks_elapsed++; match_assert("c:\\halo\\SOURCE\\game\\game_time.c", 339, ticks_elapsed <= maximum_actions); if (ticks_elapsed > maximum_actions) ticks_elapsed = maximum_actions; } if (ticks_elapsed > 0) { long ticks_remaining = ticks_elapsed; long final_local_time; long maximum_possible_server_time; while (game_time_globals->local_time < game_time_globals->server_time && ticks_remaining > 0) { game_time_globals->local_time++; ticks_remaining--; } final_local_time = game_time_globals->local_time + ticks_remaining; switch (game_connection()) { case _game_connection_local: update_client_local_ticks(ticks_remaining); break; case _game_connection_network_server: network_game_server_update_ticks(global_network_game_server_get(), ticks_remaining); break; } maximum_possible_server_time = update_client_get_maximum_possible_server_time(); if (maximum_possible_server_time > game_time_globals->server_time) { long final_server_time = MIN(maximum_possible_server_time, final_local_time); long server_updates = final_server_time - game_time_globals->server_time; long update_index; for (update_index = 0; update_index < server_updates; update_index++) { game_tick(); game_time_globals->server_time++; game_time_globals->local_time++; } code_000a50d0((short)(maximum_possible_server_time - game_time_globals->local_time), (short)server_updates, 0, FALSE); } else { code_000a50d0((short)(maximum_possible_server_time - game_time_globals->local_time), 0, 0, FALSE); } game_time_globals->last_local_time_elapsed = (short)ticks_elapsed; } } game_frame(game_time_globals->speed*time_delta_sec); } else { game_time_globals->last_local_time_elapsed = 0; } return; } /* ---------- private code */