From 30ef6a0c2da336d34a3ca3680e21e64cbbf48302 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 21 Oct 2008 02:27:21 +0000 Subject: [PATCH] - Game time is now frozen during screen wipes. This obsoletes the DEM_WIPEON and DEM_WIPEOFF commands. Fixes multimap demos desyncing when played back or recorded with wipes enabled, and prevents multiplayer games from starting until all players' wipes have finished. SVN r1272 (trunk) --- docs/rh-log.txt | 6 ++++++ src/d_main.cpp | 26 ++++++++++++++------------ src/d_net.cpp | 9 --------- src/d_player.h | 1 - src/d_protocol.h | 4 ++-- src/g_game.cpp | 2 -- src/p_tick.cpp | 4 ++-- src/sdl/i_system.cpp | 26 ++++++++++++++++++++++++++ src/sdl/i_system.h | 7 +++++-- src/win32/i_system.cpp | 38 +++++++++++++++++++++++++++++++++++++- src/win32/i_system.h | 10 ++++++---- 11 files changed, 98 insertions(+), 35 deletions(-) diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 4f7e4da67..bcd58bde0 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,9 @@ +October 20, 2008 +- Game time is now frozen during screen wipes. This obsoletes the DEM_WIPEON + and DEM_WIPEOFF commands. Fixes multimap demos desyncing when played back + or recorded with wipes enabled, and prevents multiplayer games from + starting until all players' wipes have finished. + October 19, 2008 (Changes by Graf Zahl) - Added native variables to expression evaluator and replaced the previous handling of actor variables in expressions with it. diff --git a/src/d_main.cpp b/src/d_main.cpp index 7ee5f6e60..a2d1a97ae 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -719,37 +719,39 @@ void D_Display () C_DrawConsole (hw2d); // draw console M_Drawer (); // menu is drawn even on top of everything FStat::PrintStat (); - screen->Update (); // page flip or blit buffer + screen->Update (); // page flip or blit buffer } else { // wipe update - int wipestart, nowtime, tics; + unsigned int wipestart, nowtime, diff; bool done; GSnd->SetSfxPaused(true, 1); + I_FreezeTime(true); screen->WipeEndScreen (); - wipestart = I_GetTime (false); - - Net_WriteByte (DEM_WIPEON); - NetUpdate (); // send out any new accumulation + wipestart = I_MSTime(); + NetUpdate(); // send out any new accumulation do { - nowtime = I_WaitForTic (wipestart); - tics = nowtime - wipestart; + do + { + I_WaitVBL(2); + nowtime = I_MSTime(); + diff = (nowtime - wipestart) * 40 / 1000; // Using 35 here feels too slow. + } while (diff < 1); wipestart = nowtime; - done = screen->WipeDo (tics); + done = screen->WipeDo (1); C_DrawConsole (hw2d); // console and M_Drawer (); // menu are drawn even on top of wipes screen->Update (); // page flip or blit buffer - NetUpdate (); + NetUpdate (); // [RH] not sure this is needed anymore } while (!done); screen->WipeCleanup(); + I_FreezeTime(false); GSnd->SetSfxPaused(false, 1); - - Net_WriteByte (DEM_WIPEOFF); } cycles.Unclock(); diff --git a/src/d_net.cpp b/src/d_net.cpp index 40916b527..9dac2ff37 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -607,7 +607,6 @@ void PlayerIsGone (int netnode, int netconsole) nodeingame[netnode] = false; playeringame[netconsole] = false; nodejustleft[netnode] = false; - playerswiping &= ~(1 << netconsole); if (deathmatch) { @@ -2316,14 +2315,6 @@ void Net_DoCommand (int type, BYTE **stream, int player) } break; - case DEM_WIPEON: - playerswiping |= 1 << player; - break; - - case DEM_WIPEOFF: - playerswiping &= ~(1 << player); - break; - case DEM_ADDCONTROLLER: { BYTE playernum = ReadByte (stream); diff --git a/src/d_player.h b/src/d_player.h index 4b271a7ff..50518a543 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -372,7 +372,6 @@ public: // Bookkeeping on players - state. extern player_t players[MAXPLAYERS]; -extern DWORD playerswiping; inline FArchive &operator<< (FArchive &arc, player_t *&p) { diff --git a/src/d_protocol.h b/src/d_protocol.h index 73be4583a..6edfdaad1 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -141,8 +141,8 @@ enum EDemoCommand DEM_DOAUTOSAVE, // 42 An autosave should be made DEM_MORPHEX, // 43 String: The class to morph to. DEM_SUMMONFOE, // 44 String: Thing to fabricate - DEM_WIPEON, // 45 Player started a screen wipe - DEM_WIPEOFF, // 46 Player finished a screen wipe + DEM_UNDONE9, // 45 + DEM_UNDONE10, // 46 DEM_TAKECHEAT, // 47 String: item to take, Word: quantity DEM_ADDCONTROLLER, // 48 Player to add to the controller list. DEM_DELCONTROLLER, // 49 Player to remove from the controller list. diff --git a/src/g_game.cpp b/src/g_game.cpp index 18e2b7560..12a3492c4 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -124,7 +124,6 @@ bool netgame; // only true if packets are broadcast bool multiplayer; player_t players[MAXPLAYERS]; bool playeringame[MAXPLAYERS]; -DWORD playerswiping; int consoleplayer; // player taking events int gametic; @@ -2148,7 +2147,6 @@ void G_ReadDemoTiccmd (ticcmd_t *cmd, int player) if (i < MAXPLAYERS) { playeringame[i] = false; - playerswiping &= ~(1 << i); } } break; diff --git a/src/p_tick.cpp b/src/p_tick.cpp index 56506bbb7..e07237562 100644 --- a/src/p_tick.cpp +++ b/src/p_tick.cpp @@ -77,11 +77,11 @@ void P_Ticker (void) { // This is a separate slot from the wipe in D_Display(), because this // is delayed slightly due to latency. (Even on a singleplayer game!) - GSnd->SetSfxPaused(!!playerswiping, 2); +// GSnd->SetSfxPaused(!!playerswiping, 2); } // run the tic - if (paused || (playerswiping && !demoplayback) || P_CheckTickerPaused()) + if (paused || P_CheckTickerPaused()) return; // [RH] Frozen mode is only changed every 4 tics, to make it work with A_Tracer(). diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index 058664cbd..fc2d50c8d 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -87,6 +87,7 @@ DWORD LanguageIDs[4] = int (*I_GetTime) (bool saveMS); int (*I_WaitForTic) (int); +void (*I_FreezTime) (bool frozen); void I_Tactile (int on, int off, int total) { @@ -116,6 +117,7 @@ unsigned int I_MSTime (void) static DWORD TicStart; static DWORD TicNext; +static int TicFrozen; // // I_GetTime @@ -123,6 +125,11 @@ static DWORD TicNext; // int I_GetTimePolled (bool saveMS) { + if (TicFrozen != 0) + { + return TicFrozen; + } + DWORD tm = SDL_GetTicks (); if (saveMS) @@ -137,12 +144,30 @@ int I_WaitForTicPolled (int prevtic) { int time; + assert (TicFrozen == 0); while ((time = I_GetTimePolled(false)) <= prevtic) ; return time; } +void I_FreezeTimePolled (bool frozen) +{ + if (frozen) + { + assert(TicFrozen == 0); + TicFrozen = I_GetTimePolled(false); + } + else + { + assert(TicFrozen != 0); + int froze = TicFrozen; + TicFrozen = 0; + int now = I_GetTimePolled(false); + basetime += (now - froze) * 1000 / TICRATE; + } +} + // Returns the fractional amount of a tic passed since the most recent tic fixed_t I_GetTimeFrac (uint32 *ms) { @@ -184,6 +209,7 @@ void I_Init (void) I_GetTime = I_GetTimePolled; I_WaitForTic = I_WaitForTicPolled; + I_FreezeTime = I_FreezeTimePolled; atterm (I_ShutdownSound); I_InitSound (); } diff --git a/src/sdl/i_system.h b/src/sdl/i_system.h index b6c9a35ce..9036aac02 100644 --- a/src/sdl/i_system.h +++ b/src/sdl/i_system.h @@ -56,8 +56,11 @@ extern int (*I_GetTime) (bool saveMS); // like I_GetTime, except it waits for a new tic before returning extern int (*I_WaitForTic) (int); -int I_GetTimePolled (bool saveMS); -int I_GetTimeFake (void); +// Freezes tic counting temporarily. While frozen, calls to I_GetTime() +// will always return the same value. This does not affect I_MSTime(). +// You must also not call I_WaitForTic() while freezing time, since the +// tic will never arrive (unless it's the current one). +extern void (*I_FreezeTime) (bool frozen); fixed_t I_GetTimeFrac (uint32 *ms); diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index 85cc2361e..4318d95dd 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -89,6 +89,7 @@ const IWADInfo *DoomStartupInfo; int (*I_GetTime) (bool saveMS); int (*I_WaitForTic) (int); +void (*I_FreezeTime) (bool frozen); os_t OSPlatform; @@ -120,6 +121,7 @@ unsigned int I_MSTime (void) static DWORD TicStart; static DWORD TicNext; +static int TicFrozen; // // I_GetTime @@ -129,6 +131,11 @@ int I_GetTimePolled (bool saveMS) { DWORD tm; + if (TicFrozen != 0) + { + return TicFrozen; + } + tm = timeGetTime(); if (!basetime) basetime = tm; @@ -146,12 +153,30 @@ int I_WaitForTicPolled (int prevtic) { int time; + assert(TicFrozen == 0); while ((time = I_GetTimePolled(false)) <= prevtic) ; return time; } +void I_FreezeTimePolled (bool frozen) +{ + if (frozen) + { + assert(TicFrozen == 0); + TicFrozen = I_GetTimePolled(false); + } + else + { + assert(TicFrozen != 0); + int froze = TicFrozen; + TicFrozen = 0; + int now = I_GetTimePolled(false); + basetime += (now - froze) * 1000 / TICRATE; + } +} + static int tics; static DWORD ted_start, ted_next; @@ -168,6 +193,7 @@ int I_GetTimeEventDriven (bool saveMS) int I_WaitForTicEvent (int prevtic) { + assert(!TicFrozen); while (prevtic >= tics) { WaitForSingleObject(NewTicArrived, 1000/TICRATE); @@ -178,12 +204,20 @@ int I_WaitForTicEvent (int prevtic) void CALLBACK TimerTicked (UINT id, UINT msg, DWORD_PTR user, DWORD_PTR dw1, DWORD_PTR dw2) { - tics++; + if (!TicFrozen) + { + tics++; + } ted_start = timeGetTime (); ted_next = ted_start + MillisecondsPerTic; SetEvent (NewTicArrived); } +void I_FreezeTimeEventDriven(bool frozen) +{ + TicFrozen = frozen; +} + // Returns the fractional amount of a tic passed since the most recent tic fixed_t I_GetTimeFrac (uint32 *ms) { @@ -384,11 +418,13 @@ void I_Init (void) { I_GetTime = I_GetTimeEventDriven; I_WaitForTic = I_WaitForTicEvent; + I_FreezeTime = I_FreezeTimeEventDriven; } else { // If no timer event, busy-loop with timeGetTime I_GetTime = I_GetTimePolled; I_WaitForTic = I_WaitForTicPolled; + I_FreezeTime = I_FreezeTimePolled; } atterm (I_ShutdownSound); diff --git a/src/win32/i_system.h b/src/win32/i_system.h index 0c53b3981..090d2307c 100644 --- a/src/win32/i_system.h +++ b/src/win32/i_system.h @@ -54,15 +54,17 @@ extern os_t OSPlatform; // Called by DoomMain. void I_Init (void); -// Called by D_DoomLoop, -// returns current time in tics. +// Called by D_DoomLoop, returns current time in tics. extern int (*I_GetTime) (bool saveMS); // like I_GetTime, except it waits for a new tic before returning extern int (*I_WaitForTic) (int); -int I_GetTimePolled (bool saveMS); -int I_GetTimeFake (void); +// Freezes tic counting temporarily. While frozen, calls to I_GetTime() +// will always return the same value. This does not affect I_MSTime(). +// You must also not call I_WaitForTic() while freezing time, since the +// tic will never arrive (unless it's the current one). +extern void (*I_FreezeTime) (bool frozen); fixed_t I_GetTimeFrac (uint32 *ms);