My third engine mod with code

2020/01/08 - games

This is my third Quake 3 mod. It is pretty simple, and probably unsafe to actually implement. Basically, when you touch a button on a map, it opens a browser window to a specific URL.

Working sample

This commit demonstrate the ease of adding a command to the server, that can then be sent and executed by clients, either a specific client or all clients.

Github - trigger_open branch

Basically, to make a full round trip there are 2 completely seperate systems. The first system runs on the server, and it loads the game data from a level:

spawn_t        spawns[] = {
...,
    {"target_open", SP_target_open},
    {"target_push", SP_target_push},
...
};

The next step is to send the event from inside the game to the server, which then turns right around and sends it to the respective clients.

char target_execs[10][16384]; // max of 10 commands per level?
int num_target_execs=0;

void target_use_open( gentity_t *self, gentity_t *other, gentity_t *activator ) {
        char *nx= target_execs[self->health];
        //G_Printf("Opening: %s\n", nx);
        trap_SendServerCommand(activator-g_entities, va("open \"%s\"", nx));
}

void SP_target_open( gentity_t *self ) {
        char *buf;
        char *nx=target_execs[num_target_execs];

        self->health=num_target_execs; // TODO set to message instead?
        G_SpawnString( "message", "print no command", &buf);
        Com_sprintf(nx, sizeof(target_execs[0]), "%s", buf);
        self->use = target_use_open;
        num_target_execs++;
}

trap_SendServerCommand is really the key command here, this allows the game code to essentially jump out of the virtual machine in to the server code. The rest of the server code we leave alone, it will send arbitrary commands to clients, and without the next step our client would report something like "Unrecognized command". Progress!

static void CG_ServerCommand( void ) {
...
        if ( !strcmp( cmd, "open" ) ) {
                char command[16384];
                Q_strncpyz(command, CG_Argv(1), sizeof(command));
                CG_Printf( "Opening: %s\n", command );
                trap_Open( command );
                return;
        }
...
}

The final step is to add the trap_Open command to the VM and then add the real system() open command to the client.

qboolean Sys_Open(char *command) {
#ifndef DEDICATED
        IN_DeactivateMouse(qfalse);
#endif
        system(va("open \"%s\"", command));
        return qtrue;
}

We added IN_DeactivateMouse to the game to release the mouse when the window focus switches to the browser window. We've now added a game command that opens a web browser when you click a link. This may seem a little silly to support on a native build, but if we combine this concept with the emscripted Javascript build, we quickly realize that our game could open or change web pages with the triggering of an in game button such as a lobby to redirect users to the forums.