@@ -39,6 +39,9 @@ static void G_InitGame( int levelTime, int randomSeed, int restart );
3939static void G_RunFrame ( int levelTime );
4040static void G_ShutdownGame ( int restart );
4141static void CheckExitRules ( void );
42+ #ifndef OLD_CHECK_EXIT_RULES
43+ static void CheckExitRulesLater ( void );
44+ #endif
4245static void SendScoreboardMessageToAllClients ( void );
4346
4447// extension interface
@@ -59,40 +62,92 @@ This must be the very first function compiled into the .q3vm file
5962================
6063*/
6164DLLEXPORT intptr_t vmMain ( int command , int arg0 , int arg1 , int arg2 ) {
65+ int ret ;
66+
6267 switch ( command ) {
6368 case GAME_INIT :
6469 G_InitGame ( arg0 , arg1 , arg2 );
65- return 0 ;
70+ ret = 0 ;
71+ break ;
6672 case GAME_SHUTDOWN :
6773 G_ShutdownGame ( arg0 );
68- return 0 ;
74+ ret = 0 ;
75+ break ;
6976 case GAME_CLIENT_CONNECT :
70- return (intptr_t )ClientConnect ( arg0 , arg1 , arg2 );
77+ ret = (intptr_t )ClientConnect ( arg0 , arg1 , arg2 );
78+ break ;
7179 case GAME_CLIENT_THINK :
7280 ClientThink ( arg0 );
73- return 0 ;
81+ ret = 0 ;
82+ break ;
7483 case GAME_CLIENT_USERINFO_CHANGED :
7584 ClientUserinfoChanged ( arg0 );
76- return 0 ;
85+ ret = 0 ;
86+ break ;
7787 case GAME_CLIENT_DISCONNECT :
7888 ClientDisconnect ( arg0 );
79- return 0 ;
89+ ret = 0 ;
90+ break ;
8091 case GAME_CLIENT_BEGIN :
8192 ClientBegin ( arg0 );
82- return 0 ;
93+ ret = 0 ;
94+ break ;
8395 case GAME_CLIENT_COMMAND :
8496 ClientCommand ( arg0 );
85- return 0 ;
97+ ret = 0 ;
98+ break ;
8699 case GAME_RUN_FRAME :
87100 G_RunFrame ( arg0 );
88- return 0 ;
101+ ret = 0 ;
102+ break ;
89103 case GAME_CONSOLE_COMMAND :
90- return ConsoleCommand ();
104+ ret = ConsoleCommand ();
105+ break ;
91106 case BOTAI_START_FRAME :
92- return BotAIStartFrame ( arg0 );
107+ ret = BotAIStartFrame ( arg0 );
108+ break ;
109+ default :
110+ ret = -1 ;
111+ }
112+
113+ #ifndef OLD_CHECK_EXIT_RULES
114+ // In vanilla we did `CheckExitRules()` after each frag,
115+ // which there can be multiple of when dealing with e.g. splash damage.
116+ // And the order in which players get killed is not well-defined:
117+ // e.g. for `G_RadiusDamage` and telefrag, kills get performed
118+ // in the order returned from `trap_EntitiesInBox`;
119+ // for the shotgun each pellet deals damage by itself,
120+ // and the order is random;
121+ // for the railgun the closest enemy gets damaged first.
122+ // That resulted in inconsistent rules when there is one frag left
123+ // and someone frags both a enemy and themself (or a teammate):
124+ // if the enemy is processed first then the attacker wins.
125+ // If a teammate is processed first then the game continues.
126+ // Let's fix this by only doing `CheckExitRules()`
127+ // when we're done processing the command.
128+ //
129+ // Note that this new behavior can result in e.g. two players
130+ // hitting the frag limit at the same time.
131+ // This can happen with missiles, which we run in `G_RunFrame()`.
132+ // In such a case `ScoreIsTied()` will kick in, and the game will continue
133+ // until the score gets untied, so this new behavior doesn't introduce
134+ // a "multiple winners" situation.
135+ //
136+ // One might argue that in case of two missiles exploding on the same frame,
137+ // the old behavoir is more fair, because `G_RunMissile()` is run
138+ // in the order in which the missiles were created,
139+ // so whoever shot first should win.
140+ // However, this isn't entirely obviously fair,
141+ // because two missiles can explode on the same frame
142+ // even if they were shot a long time apart.
143+ // For example, grenade launcher vs rocket launcher.
144+ // In this case let's just tie the score.
145+ if ( level .needToCheckExitRules ) {
146+ CheckExitRules ();
93147 }
148+ #endif
94149
95- return -1 ;
150+ return ret ;
96151}
97152
98153
@@ -856,7 +911,11 @@ void CalculateRanks( void ) {
856911 }
857912
858913 // see if it is time to end the level
914+ #ifndef OLD_CHECK_EXIT_RULES
915+ CheckExitRulesLater ();
916+ #else
859917 CheckExitRules ();
918+ #endif
860919
861920 // if we are at the intermission, send the new info to everyone
862921 if ( level .intermissiontime ) {
@@ -1323,6 +1382,10 @@ static void CheckExitRules( void ) {
13231382 int i ;
13241383 gclient_t * cl ;
13251384
1385+ #ifndef OLD_CHECK_EXIT_RULES
1386+ level .needToCheckExitRules = qfalse ;
1387+ #endif
1388+
13261389 // if at the intermission, wait for all non-bots to
13271390 // signal ready, then go to next level
13281391 if ( level .intermissiontime ) {
@@ -1410,6 +1473,11 @@ static void CheckExitRules( void ) {
14101473 }
14111474 }
14121475}
1476+ #ifndef OLD_CHECK_EXIT_RULES
1477+ static void CheckExitRulesLater ( void ) {
1478+ level .needToCheckExitRules = qtrue ;
1479+ }
1480+ #endif
14131481
14141482
14151483static void ClearBodyQue ( void ) {
0 commit comments