Forcing idle players to team spectator problem

I’m trying to force idle players (g_inactivity matches) to team spectator.

However, I’m getting an error when compiling:

LD build/release-linux-x86_64/ioq3ded.x86_64
build/release-linux-x86_64/ded/sv_game.o: In function SV_GameSystemCalls': sv_game.c:(.text+0x2474): undefined reference to SV_forcespec’
build/release-linux-x86_64/ded/sv_game.o: In function SV_GameDropClient': sv_game.c:(.text+0x268b): undefined reference to SV_forcespec’
collect2: error: ld returned 1 exit status

/code/game/g_svcmds.c:

void SV_forcespec( int client ) {
        Com_Printf(“Forcing player to spectate\n”);
        trap_SendConsoleCommand( EXEC_NOW, va( “forceteam %d spectator\n”, client ) );
}

/code/game/g_public.h:

extern void SV_forcespec( int client );

/code/server/sv_game.c:

void SV_GameDropClient( int clientNum, const char *reason ) {
        if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) {
                return;
        }
        // clientNum confirmed to return correct clientid
        char *inactivity, *team = “”;
        sprintf( team, “%d spectator”, clientNum );
        inactivity = “Dropped due to inactivity”;
        if( strcmp(inactivity,reason) == 0 ) {
                // force player to team spectator and notify them
                SV_forcespec( clientNum );
                SV_SendServerCommand( svs.clients + clientNum, “cp "^1You’ve been moved to the spectators\n^1for idling too long!\n"” );
        } else {
                SV_DropClient( svs.clients + clientNum, reason );
        }
}

I can’t figure out what I’m doing wrong. I’m certain it’s some simple, amateur mistake. Thanks!

You’re trying to call a function that is in the Game VM from inside the engine. You cannot do without using VM_Call function and GAME_* gameExport_t, which requires using custom game VM. However, from your example you do not need to have the function in the Game VM at all.

I’d recommend removing SV_forcespec from g_public.h and g_svcmds.c, instead put the code in SV_GameDropClient directly;

void SV_GameDropClient( int clientNum, const char *reason ) {
        char *inactivity, team[32]; // NOTE: team must be an array, not a pointer.

        if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) {
                return;
        }

        // clientNum confirmed to return correct clientid
        Com_sprintf( team, sizeof (team), "%d spectator", clientNum ); // NOTE: sprint is not safe, might go past end of array. Use Com_sprint instead.
        inactivity = "Dropped due to inactivity";
        if( strcmp(inactivity,reason) == 0 ) {
                // force player to team spectator and notify them
//START SV_forcespec
                Com_Printf("Forcing player to spectate\n");
                Cbuf_ExecuteText( EXEC_NOW, va( "forceteam %d spectator\n", clientNum ) );
//END
                SV_SendServerCommand( svs.clients + clientNum, "cp 
\"^1You've been moved to the spectators\n^1for idling too long!\n\"" );
        } else {
                SV_DropClient( svs.clients + clientNum, reason );
        }
}
2 Likes

Zturtleman, you’re a life-saver. Thank you for taking the time to explain your answer, too. I wasn’t aware of the Cbuf_ExecuteText() function.

Everything works great now. Thanks again.

You’re welcome. I’m glad it worked for you.