/* ../sdk-2.60/src/slavebot.c for linux
*
* License: GPL (uses code from ubersoldat mod)
* Based on the shim idea from Ubot http://ubot.org/
* gcc -Wall -fPIC -DGAMEDLL -shared slavebot.c -o qagame.mp.i386.so
*
* using with etpro:
* cd <enemy territory installation> (/usr/games/enemy-territor)
* mkdir slavebot
* cd slavebot
* unzip ~/games/et/etpro-3_2_0.zip
* cd etpro
* mv * ../
* cd ..
* rmdir etpro
* unzip -o etpro-3_2_0.pk3
* mv qagame.mp.i386.so libetpro.so
* change REAL_MOD_FILENAME below to point to it
* cp <compiled slavebot.c> qagame.mp.i386.so
*
* et +set cl_punkbuster 0 +set sv_punkbuster 0 +set sv_pure 0
* +set g_gametype 2 +set fs_game slavebot +set b_anticheat 0
* +set b_antiwarp 0 +devmap radar
*
* addslavebot [same options as 'team' command]
* botcommand or botc +sprint
* botcommand +moveright
* etc..
*/
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
#include "game/q_shared.h"
#include "game/g_local.h"
#define BOT_CLIENT_NUMBER 13
#define REAL_MOD_FILENAME "/home/user/own_src/work/slavebot/src/libetpro.so"
#define MAX_ARGC 5
static char *sva (char *format, ... );
static void Botthink (void);
static int (*modVmMain) (int, int, int, int, int, int, int, int);
static void (*modDllEntry) (int (QDECL *)(int arg, ...));
static int (QDECL *syscall)( int arg, ... ) = (int (QDECL *)(int, ...)) - 1;
static void *qagameLib = NULL;
static int frameTime;
/* data snooped from syscall:G_LOCATE_GAME_DATA */
static gentity_t *gEnts = NULL;
static int numGEntities;
static int sizeofGEntity_t = 0;
static playerState_t *clients = NULL;
static int sizeofGClient = 0;
static char fakeArgv[MAX_ARGC][MAX_TOKEN_CHARS];
static int fakeArgc = -1;
static qboolean handleArgv = qfalse;
static usercmd_t prevCmd, currCmd;
static int botFaceEnemy = -1;
static int botTargetingError = 0;
static gentity_t *get_gentity_pointer (int entNumber)
{
return (gentity_t *)((void *)&gEnts[0] + (entNumber * sizeofGEntity_t));
}
static playerState_t *get_playerState_pointer (int entNumber)
{
return (playerState_t *)((void *)&clients[0] + (entNumber * sizeofGClient));
}
static int our_trap_Argv (int n, char *buffer, int bufferLength)
{
if (n >= MAX_ARGC) {
buffer[0] = '\0';
return qtrue;
}
strncpy (buffer, fakeArgv[n], bufferLength - 1);
return qtrue; //FIXME
}
static int our_syscall (int arg, ...)
{
va_list va;
#define syscall_jmp asm volatile ( \
"movl -4(%%ebp), %%ebx
" \
"movl %%ebp, %%esp
" \
"popl %%ebp
" \
"jmp *%0" \
:: "r" (syscall));
switch (arg) {
case G_LOCATE_GAME_DATA: {
va_start (va, arg);
gEnts = va_arg (va, gentity_t *);
numGEntities = va_arg (va, int);
sizeofGEntity_t = va_arg (va, int);
clients = va_arg (va, playerState_t *);
sizeofGClient = va_arg (va, int);
va_end (va);
break;
}
case G_ARGC: {
if (!handleArgv)
break;
return fakeArgc;
}
case G_ARGV: {
int n;
char *buffer;
int bufferLength;
va_start (va, arg);
n = va_arg (va, int);
buffer = va_arg (va, char *);
bufferLength = va_arg (va, int);
if (!handleArgv)
break;
return our_trap_Argv(n, buffer, bufferLength);
}
default:
syscall_jmp;
}
syscall_jmp;
return -1; /* never gets here */
#undef syscall_jmp
}
using Ubot idea - a little "slavebot"
brugal
(brugal)
#1
brugal
(brugal)
#2
extern void dllEntry( int (QDECL *syscallptr)(int arg, ...) ) {
syscall = syscallptr;
modDllEntry ((int (*)(int, ...))our_syscall);
}
static void addslavebot_f (void)
{
char buf[MAX_TOKEN_CHARS];
char *userinfo = "\\cg_uinfo\\13 0 30\\snaps\\20\\rate\\25000\
ame\\^bSlave^0Bot^7\0";
syscall (G_SET_USERINFO, BOT_CLIENT_NUMBER, userinfo);
if (modVmMain(GAME_CLIENT_CONNECT, BOT_CLIENT_NUMBER, qtrue, qfalse, 0, 0, 0, 0)) {
syscall (G_PRINT, "error: bot couldn't connect
");
return;
}
memset (&prevCmd, 0, sizeof(usercmd_t));
botFaceEnemy = -1;
modVmMain (GAME_CLIENT_BEGIN, BOT_CLIENT_NUMBER, 0, 0, 0, 0, 0, 0);
// set team
strncpy (fakeArgv[0], "team", sizeof("team") + 1);
syscall (G_ARGV, 1, buf, MAX_TOKEN_CHARS);
strncpy (fakeArgv[1], buf, MAX_TOKEN_CHARS - 1); // "blue", "red"
syscall (G_ARGV, 2, buf, MAX_TOKEN_CHARS);
strncpy (fakeArgv[2], buf, MAX_TOKEN_CHARS - 1); // 0:sold 1:medic 2:eng 3:field 4:cov
syscall (G_ARGV, 3, buf, MAX_TOKEN_CHARS);
strncpy (fakeArgv[3], buf, MAX_TOKEN_CHARS - 1);
fakeArgc = 4;
#if 0
// set team
strncpy (fakeArgv[0], "team", sizeof("team") + 1);
strncpy (fakeArgv[1], "blue", sizeof("blue") + 1);
strncpy (fakeArgv[2], "2", 2); // 0:sold 1:medic 2:eng 3:field 4:cov
strncpy (fakeArgv[3], "8", 2);
fakeArgc = 4;
#endif
handleArgv = qtrue;
modVmMain (GAME_CLIENT_COMMAND, BOT_CLIENT_NUMBER, 0, 0, 0, 0, 0, 0);
handleArgv = qfalse;
}
static void botcommand_f (void)
{
int argc, i, numCommands;
char buf[MAX_TOKEN_CHARS];
argc = syscall (G_ARGC);
numCommands = 0;
for (i = 1; i < argc; i++) {
syscall (G_ARGV, i, buf, sizeof(buf));
if (strncmp(buf, "+moveleft", sizeof("+moveleft")) == 0) {
currCmd.rightmove = -127;
} else if (strncmp(buf, "+moveright", sizeof("+moveright")) == 0) {
currCmd.rightmove = 127;
} else if (strncmp(buf, "+moveup", sizeof("+moveup")) == 0) {
currCmd.upmove = 127;
} else if (strncmp(buf, "-moveup", sizeof("-moveup")) == 0) {
currCmd.upmove = 0;
} else if (strncmp(buf, "+movedown", sizeof("+movedown")) == 0) {
currCmd.upmove = -127;
} else if (strncmp(buf, "-movedown", sizeof("-movedown")) == 0) {
currCmd.upmove = 0;
} else if (strncmp(buf, "+forward", sizeof("+forward")) == 0) {
currCmd.forwardmove = 127;
} else if (strncmp(buf, "+back", sizeof("+back")) == 0) {
currCmd.forwardmove = -127;
} else if (strncmp(buf, "+prone", sizeof("+prone")) == 0) {
currCmd.wbuttons |= WBUTTON_PRONE;
} else if (strncmp(buf, "+sprint", sizeof("+sprint")) == 0) {
currCmd.buttons |= BUTTON_SPRINT;
} else if (strncmp(buf, "-sprint", sizeof("-sprint")) == 0) {
currCmd.buttons &= ~BUTTON_SPRINT;
} else if (strncmp(buf, "+speed", sizeof("+speed")) == 0) {
currCmd.buttons |= BUTTON_WALKING;
} else if (strncmp(buf, "-speed", sizeof("-speed")) == 0) {
currCmd.buttons &= ~BUTTON_WALKING;
} else if (strncmp(buf, "+reload", sizeof("+reload")) == 0) {
currCmd.wbuttons |= WBUTTON_RELOAD;
} else if (strncmp(buf, "+attack", sizeof("+attack")) == 0) {
currCmd.buttons |= BUTTON_ATTACK;
} else if (strncmp(buf, "-attack", sizeof("-attack")) == 0) {
currCmd.buttons &= ~BUTTON_ATTACK;
} else if (strncmp(buf, "faceenemy", sizeof("faceenemy")) == 0) {
i++;
if (i >= argc) {
syscall (G_PRINT, "botcommand faceenemy: needs entity/client to face
");
return;
}
syscall (G_ARGV, i, buf, sizeof(buf));
//FIXME check for int
botFaceEnemy = atoi (buf);
i++;
if (i >= argc) {
syscall (G_PRINT, "FIXME need targeting error
");
return;
}
syscall (G_ARGV, i, buf, sizeof(buf));
botTargetingError = atoi (buf);
} else if (strncmp(buf, "angles", sizeof("angles")) == 0) {
int pitch, yaw, roll;
i++;
syscall (G_ARGV, i, buf, sizeof(buf));
pitch = atoi (buf);
i++;
syscall (G_ARGV, i, buf, sizeof(buf));
yaw = atoi (buf);
i++;
syscall (G_ARGV, i, buf, sizeof(buf));
roll = atoi (buf);
currCmd.angles[PITCH] = pitch;
currCmd.angles[YAW] = yaw;
currCmd.angles[ROLL] = roll;
} else if (strncmp(buf, "weapon", sizeof("weapon")) == 0) {
i++;
syscall (G_ARGV, i, buf, sizeof(buf));
currCmd.weapon = atoi (buf);
} else if (strncmp(buf, "+activate", sizeof("+activate")) == 0) {
currCmd.buttons |= BUTTON_ACTIVATE;
} else if (strncmp(buf, "-activate", sizeof("-activate")) == 0) {
currCmd.buttons &= ~BUTTON_ACTIVATE;
} else if (strncmp(buf, "stop", sizeof("stop")) == 0) {
currCmd.forwardmove = 0;
currCmd.rightmove = 0;
currCmd.upmove = 0;
currCmd.wbuttons = 0;
currCmd.buttons = 0;
botFaceEnemy = -1;
botTargetingError = 0;
} else {
syscall (G_PRINT, sva("unknown botcommand: '%s'
", buf));
}
}
}
static void cinfo_f (void)
{
int argc;
char buf[MAX_TOKEN_CHARS];
gentity_t *ent;
argc = syscall (G_ARGC);
if (argc < 2) {
syscall (G_PRINT, "cinfo: needs client number
");
return;
}
syscall (G_ARGV, 1, buf, sizeof(buf));
ent = get_gentity_pointer (atoi(buf));
syscall (G_PRINT, "ent->s (entityState_t)
");
syscall (G_PRINT, sva(" origin: %f, %f, %f
", ent->s.origin[0], ent->s.origin[1], ent->s.origin[2]));
syscall (G_PRINT, sva(" origin2: %f, %f, %f
", ent->s.origin2[0], ent->s.origin2[1], ent->s.origin2[2]));
syscall (G_PRINT, sva(" angles: %f, %f, %f
", ent->s.angles[0], ent->s.angles[1], ent->s.angles[2]));
syscall (G_PRINT, sva(" angles2: %f, %f, %f
", ent->s.angles2[0], ent->s.angles2[1], ent->s.angles2[2]));
}
static void testit_f (void)
{
strncpy (fakeArgv[0], "vsay_team", 66);
strncpy (fakeArgv[1], "FireInTheHole", 66);
fakeArgc = 2;
handleArgv = qtrue;
modVmMain (GAME_CLIENT_COMMAND, BOT_CLIENT_NUMBER, 0, 0, 0, 0, 0, 0);
handleArgv = qfalse;
}
static qboolean SConsoleCommand (void)
{
char cmd[MAX_TOKEN_CHARS];
syscall (G_ARGV, 0, cmd, sizeof(cmd));
if (strncmp(cmd, "addslavebot", sizeof("addslavebot")) == 0) {
addslavebot_f ();
return qtrue;
} else if (strncmp(cmd, "botcommand", sizeof("botcommand")) == 0 ||
strncmp(cmd, "botc", sizeof("botc")) == 0) {
botcommand_f ();
return qtrue;
} else if (strncmp(cmd, "testit", sizeof("testit")) == 0) {
testit_f ();
return qtrue;
} else if (strncmp(cmd, "cinfo", sizeof("cinfo")) == 0) {
cinfo_f ();
return qtrue;
}
return qfalse;
}
brugal
(brugal)
#3
static void BotParseServerCommand(gentity_t *bot, int *state)
{
char buf[1024];
while (syscall(BOTLIB_GET_CONSOLE_MESSAGE, BOT_CLIENT_NUMBER, buf, sizeof(buf)))
{
}
}
static char *sva (char *format, ... ) {
va_list argptr;
#define MAX_VA_STRING 32000
static char temp_buffer[MAX_VA_STRING];
static char string[MAX_VA_STRING]; // in case va is called by nested functions
static int index = 0;
char *buf;
int len;
va_start (argptr, format);
vsprintf (temp_buffer, format,argptr);
va_end (argptr);
if ((len = strlen(temp_buffer)) >= MAX_VA_STRING) {
//Com_Error( ERR_DROP, "Attempted to overrun string in call to va()
");
syscall (G_PRINT, "^1error: ^7Attempted to overrun string in call to sva()
");
}
if (len + index >= MAX_VA_STRING-1) {
index = 0;
}
buf = &string[index];
memcpy( buf, temp_buffer, len+1 );
index += len + 1;
return buf;
#undef MAX_VA_STRING
}
static void svectoangles( const vec3_t value1, vec3_t angles ) {
float forward;
float yaw, pitch;
if ( value1[1] == 0 && value1[0] == 0 ) {
yaw = 0;
if ( value1[2] > 0 ) {
pitch = 90;
}
else {
pitch = 270;
}
}
else {
if ( value1[0] ) {
yaw = ( atan2 ( value1[1], value1[0] ) * 180 / M_PI );
}
else if ( value1[1] > 0 ) {
yaw = 90;
}
else {
yaw = 270;
}
if ( yaw < 0 ) {
yaw += 360;
}
forward = sqrt ( value1[0]*value1[0] + value1[1]*value1[1] );
pitch = ( atan2(value1[2], forward) * 180 / M_PI );
if ( pitch < 0 ) {
pitch += 360;
}
}
angles[PITCH] = -pitch;
angles[YAW] = yaw;
angles[ROLL] = 0;
}
static void BotTargetEnemy (void)
{
gentity_t *bot, *enemy;
playerState_t *botState, *enemyState;
vec3_t vec, enemyvec, enemyangles;
bot = get_gentity_pointer (BOT_CLIENT_NUMBER);
botState = get_playerState_pointer (BOT_CLIENT_NUMBER);
enemy = get_gentity_pointer (botFaceEnemy);
enemyState = get_playerState_pointer (botFaceEnemy);
if (enemy->inuse) {
VectorCopy (enemyState->origin, enemyvec);
if (enemyState->eFlags & EF_PRONE) {
enemyvec[2] = PRONE_VIEWHEIGHT; //FIXME not sure, crouch and prone diff for etpro anyway
}
else if (enemyState->pm_flags & PMF_DUCKED)
enemyvec[2] -= enemyState->crouchViewHeight;
enemyvec[PITCH] += (crandom() * botTargetingError);
enemyvec[YAW] += (crandom() * botTargetingError);
//enemyvec[ROLL] += (crandom() * (botTargetingError / 2));
// ROLL ??
VectorSubtract (enemyvec, bot->r.currentOrigin, vec);
svectoangles (vec, enemyangles);
currCmd.angles[PITCH] = ANGLE2SHORT(enemyangles[PITCH]);
currCmd.angles[YAW] = ANGLE2SHORT(enemyangles[YAW]);
currCmd.angles[ROLL] = ANGLE2SHORT(enemyangles[ROLL]);
svectoangles (vec, bot->s.angles);
}
}
static void Botthink (void)
{
gentity_t *ent;
playerState_t *botState;
qboolean tappingOut = qfalse;
int tmpUpmove = 0;
if (!gEnts)
return;
ent = get_gentity_pointer (BOT_CLIENT_NUMBER);
botState = get_playerState_pointer (BOT_CLIENT_NUMBER);
if (!ent->inuse)
return;
BotParseServerCommand (NULL, NULL);
VectorSet (botState->delta_angles, 0, 0, 0);
//currCmd.serverTime = frameTime - 20;
currCmd.serverTime = frameTime;
// if (ent->client->ps.eFlags & EF_DEAD && !(ent->client->ps.pm_flags & PMF_LIMBO)) {
if (botState->eFlags & EF_DEAD && !(botState->pm_flags & PMF_LIMBO)) {
// tap out
tmpUpmove = currCmd.upmove;
currCmd.upmove = 127;
tappingOut = qtrue;
}
if (botFaceEnemy > -1 && botFaceEnemy < MAX_CLIENTS) {
if (!(botState->eFlags & EF_DEAD))
BotTargetEnemy ();
}
#if 0
if (ent->client) {
client = ent->client;
// remove spawn invulnerability for bots
client->ps.powerups[PW_INVULNERABLE] = 0;
}
#endif
botState->powerups[PW_INVULNERABLE] = 0; // no spawn invuln for bots
syscall (BOTLIB_USER_COMMAND, BOT_CLIENT_NUMBER, &currCmd);
if (tappingOut)
currCmd.upmove = tmpUpmove;
memcpy (&prevCmd, &currCmd, sizeof(usercmd_t));
/* clear some flags */
currCmd.wbuttons &= ~WBUTTON_PRONE;
currCmd.wbuttons &= ~WBUTTON_RELOAD;
modVmMain (GAME_CLIENT_THINK, BOT_CLIENT_NUMBER, 0, 0, 0, 0, 0, 0);
}
extern int vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6 )
{
int returnVal;
int val;
switch ( command ) {
case GAME_RUN_FRAME:
frameTime = syscall (G_MILLISECONDS);
Botthink ();
break;
case GAME_CONSOLE_COMMAND:
val = SConsoleCommand ();
if (val)
return val;
break;
default:
break;
}
returnVal = modVmMain(command, arg0, arg1, arg2, arg3, arg4, arg5, arg6);
return returnVal;
}
void __attribute__ ((constructor)) my_init (void)
{
const char *errorMsg;
qagameLib = dlopen (REAL_MOD_FILENAME, RTLD_LAZY);
if (qagameLib == NULL) {
printf ("shim: error opening real .so
");
return;
}
modVmMain = (int (*)(int,int,int,int,int,int,int,int)) dlsym (qagameLib, "vmMain");
errorMsg = dlerror ();
if (errorMsg) {
printf ("shim: error getting vmMain() from mod
");
return;
}
modDllEntry = dlsym (qagameLib, "dllEntry");
errorMsg = dlerror ();
if (errorMsg) {
printf ("shim: error getting dllEntry() from mod
");
return;
}
}
