Forcing intermission countdown via server-side

This isn’t tested, but I think roughly you would:

  • increment svs.clients[clientNum].lastUsercmd.serverTime (adding 1ms should be enough to get the command parsed)
  • set svs.clients[clientNum].lastUsercmd.buttons |= 1 (BUTTON_ATTACK)
  • run VM_Call( gvm, GAME_CLIENT_THINK, clientNum );

The goal is to get client->readyToExit = 1 via ClientThink_real->ClientIntermissionThink.

It’s also likely possible to just set readyToExit directly from the engine. You have to figure out the address relative to the playerstate. It’s a bit more mod-dependent but might be more robust otherwise.

Perfect. Thank you.

I wrote up an /rcon attack function to test it out:

static void SV_Attack_f(void) {

int             i;
client_t        *cl;

if (!com_sv_running->integer) {
    Com_Printf("Server is not running\n");
    return;
}

if (Cmd_Argc() != 2) {
    Com_Printf("Usage: attack <client>\n");
    return;
}
    
cl = SV_GetPlayerByHandle();
if (!cl) {
	return;
}

i = cl - svs.clients;

cl->lastUsercmd.serverTime += 1;
cl->lastUsercmd.buttons = 1;
VM_Call(gvm, GAME_CLIENT_THINK, i);

}

The only problem is that it only works every other time. If I type it 4 times in a row: /rcon attack 1 — it’ll work the first and third time. But, not 2nd or 4th. Is there a reason why it’s not being processed every single time?

Thank you again!

I’m not sure. A bigger increment on the serverTime (like 100ms) might be something to try.

That’s an impressive approach. I’m curious how it’s implemented?

100ms worked. Thank you.

None of the intermission functions in g_main.c like CheckIntermissionExit() are being called with OSP it appears.

OSP must have its own intermission functionality and it bypasses what’s in g_main.c.

Must be something new in cgame, the OSP pak files have a cgame.qvm, several as a matter of fact…

Interesting approach you took though.

Here’s how I’ve managed to get this working:

At the end of the map, I set a timestamp to now + 4 seconds and if that much time has elapsed, it will call the intermissionReady() function which forces all clients to click attack and become ready. With OSP, I don’t think there’s really any other way to do it.

Inside of sv_game.c:

Add the function:

void intermissionReady(void){
client_t *cl;
int i;
for ( i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) {
if ( !cl->state ) { continue; }
cl->lastUsercmd.serverTime += 100;
cl->lastUsercmd.buttons = 1;
VM_Call(gvm, GAME_CLIENT_THINK, i);
}
}

Then inside of void SV_GameSendServerCommand( int clientNum, const char *text ) {

I parse the line and if the first word is ‘print’ and second word is ‘Next’, I do the following:

sv.intermissionReadyEnable = 1;
sv.intermissionReadyTime = (unsigned)time(NULL) + 4;

And then inside of intptr_t SV_GameSystemCalls( intptr_t *args ) {

Add the following to the top of it:

if( sv.intermissionReadyEnable == 1 && (unsigned)time(NULL) >= sv.intermissionReadyTime ){
sv.intermissionReadyEnable = 0;
intermissionReady();
}

In server.h, add:

int intermissionReadyEnable;
int intermissionReadyTime;

And once intermission has started, 4 seconds later all clients will be ready!

1 Like