1
0
Fork 0
forked from fte/fteqw

improved q1qvm support - now runs on 64bit servers with 32bit qvm

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@2635 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2007-09-03 22:37:13 +00:00
parent eaf6335e74
commit 06da06f9b3
10 changed files with 351 additions and 159 deletions

View file

@ -155,8 +155,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#endif
#ifdef __amd64__
//nah... not gonna work too well
#undef VM_Q1
//I'm slowly getting these working in 64bit
#undef Q3CLIENT
#undef Q3SERVER
#undef PLUGINS

View file

@ -43,6 +43,7 @@ Also, can efficiency be improved much?
#define RETURNOFFSETMARKER NULL
typedef enum vm_type_e
{
@ -149,6 +150,7 @@ void *Sys_LoadDLL(const char *name, void **vmMain, int (EXPORT_FN *syscall)(int
#endif
#if defined(__amd64__)
return 0; //give up early, don't even try going there
sprintf(dllname, "%samd.so", name);
#elif defined(_M_IX86) || defined(__i386__)
sprintf(dllname, "%sx86.so", name);
@ -254,25 +256,25 @@ typedef struct vmHeader_s
typedef struct qvm_s
{
// segments
unsigned long *cs; // code segment, each instruction is 2 longs
unsigned int *cs; // code segment, each instruction is 2 ints
qbyte *ds; // data segment, partially filled on load (data, lit, bss)
qbyte *ss; // stack segment, (follows immediatly after ds, corrupting data before vm)
// pointer registers
unsigned long *pc; // program counter, points to cs, goes up
unsigned long *sp; // stack pointer, initially points to end of ss, goes down
unsigned long bp; // base pointer, initially len_ds+len_ss/2
unsigned int *pc; // program counter, points to cs, goes up
unsigned int *sp; // stack pointer, initially points to end of ss, goes down
unsigned int bp; // base pointer, initially len_ds+len_ss/2
unsigned long *min_sp;
unsigned long *max_sp;
unsigned long min_bp;
unsigned long max_bp;
unsigned int *min_sp;
unsigned int *max_sp;
unsigned int min_bp;
unsigned int max_bp;
// status
unsigned int len_cs; // size of cs
unsigned int len_ds; // size of ds
unsigned int len_ss; // size of ss
unsigned long ds_mask; // ds mask (ds+ss)
unsigned int ds_mask; // ds mask (ds+ss)
// memory
unsigned int mem_size;
@ -427,9 +429,9 @@ qvm_t *QVM_Load(const char *name, sys_callqvm_t syscall)
// memory
qvm->ds_mask = qvm->len_ds*sizeof(qbyte)+(qvm->len_ss+16*4)*sizeof(qbyte);//+4 for a stack check decrease
for (i = 0; i < 31; i++)
for (i = 0; i < sizeof(qvm->ds_mask)*8-1; i++)
{
if ((1<<i) >= qvm->ds_mask) //is this bit greater than our minimum?
if ((1<<(unsigned int)i) >= qvm->ds_mask) //is this bit greater than our minimum?
break;
}
qvm->len_ss = (1<<i) - qvm->len_ds*sizeof(qbyte) - 4; //expand the stack space to fill it.
@ -437,25 +439,25 @@ qvm_t *QVM_Load(const char *name, sys_callqvm_t syscall)
qvm->len_ss -= qvm->len_ss&7;
qvm->mem_size=qvm->len_cs*sizeof(long)*2 + qvm->ds_mask;
qvm->mem_size=qvm->len_cs*sizeof(int)*2 + qvm->ds_mask;
qvm->mem_ptr=Z_Malloc(qvm->mem_size);
// set pointers
qvm->cs=(long*)qvm->mem_ptr;
qvm->ds=(qbyte*)(qvm->mem_ptr+qvm->len_cs*sizeof(long)*2);
qvm->cs=(unsigned int*)qvm->mem_ptr;
qvm->ds=(qbyte*)(qvm->mem_ptr+qvm->len_cs*sizeof(int)*2);
qvm->ss=(qbyte*)((qbyte*)qvm->ds+qvm->len_ds*sizeof(qbyte));
//waste 32 bits here.
//As the opcodes often check stack 0 and 1, with a backwards stack, 1 can leave the stack area. This is where we compensate for it.
// setup registers
qvm->pc=qvm->cs;
qvm->sp=(long*)(qvm->ss+qvm->len_ss);
qvm->sp=(unsigned int*)(qvm->ss+qvm->len_ss);
qvm->bp=qvm->len_ds+qvm->len_ss/2;
// qvm->cycles=0;
qvm->syscall=syscall;
qvm->ds_mask--;
qvm->min_sp = (long*)(qvm->ds+qvm->len_ds+qvm->len_ss/2);
qvm->max_sp = (long*)(qvm->ds+qvm->len_ds+qvm->len_ss);
qvm->min_sp = (unsigned int*)(qvm->ds+qvm->len_ds+qvm->len_ss/2);
qvm->max_sp = (unsigned int*)(qvm->ds+qvm->len_ds+qvm->len_ss);
qvm->min_bp = qvm->len_ds;
qvm->max_bp = qvm->len_ds+qvm->len_ss/2;
@ -464,14 +466,14 @@ qvm_t *QVM_Load(const char *name, sys_callqvm_t syscall)
// load instructions
{
qbyte *src=raw+header->codeOffset;
long *dst=qvm->cs;
int *dst=(int*)qvm->cs;
int total=header->instructionCount;
qvm_op_t op;
for(n=0; n<total; n++)
{
op=*src++;
*dst++=(long)op;
*dst++=(int)op;
switch(op)
{
case OP_ENTER:
@ -495,11 +497,11 @@ qvm_t *QVM_Load(const char *name, sys_callqvm_t syscall)
case OP_GTF:
case OP_GEF:
case OP_BLOCK_COPY:
*dst++=LittleLong(*(long*)src);
*dst++=LittleLong(*(int*)src);
src+=4;
break;
case OP_ARG:
*dst++=(long)*src++;
*dst++=(int)*src++;
break;
default:
*dst++=0;
@ -512,8 +514,8 @@ qvm_t *QVM_Load(const char *name, sys_callqvm_t syscall)
// load data segment
{
long *src=(long*)(raw+header->dataOffset);
long *dst=(long*)qvm->ds;
int *src=(int*)(raw+header->dataOffset);
int *dst=(int*)qvm->ds;
int total=header->dataLength/4;
for(n=0; n<total; n++)
@ -562,9 +564,9 @@ static void inline QVM_Call(qvm_t *vm, int addr)
{
// system trap function
{
long *fp;
int *fp;
fp=(long*)(vm->ds+vm->bp)+2;
fp=(int*)(vm->ds+vm->bp)+2;
vm->sp[0] = vm->syscall(vm->ds, vm->ds_mask, -addr-1, fp);
return;
}
@ -573,7 +575,7 @@ static void inline QVM_Call(qvm_t *vm, int addr)
if(addr>=vm->len_cs)
Sys_Error("VM run time error: program jumped off to hyperspace\n");
vm->sp[0]=(long)(vm->pc-vm->cs); // push pc /return address/
vm->sp[0]=(vm->pc-vm->cs); // push pc /return address/
vm->pc=vm->cs+addr*2;
if (!vm->pc)
Sys_Error("VM run time error: program called the void\n");
@ -585,15 +587,15 @@ static void inline QVM_Call(qvm_t *vm, int addr)
** [oPC][0][.......]| <- oldBP
** ^BP
*/
static void inline QVM_Enter(qvm_t *vm, long size)
static void inline QVM_Enter(qvm_t *vm, int size)
{
long *fp;
int *fp;
vm->bp-=size;
if(vm->bp<vm->min_bp)
Sys_Error("VM run time error: out of stack\n");
fp=(long*)(vm->ds+vm->bp);
fp=(int*)(vm->ds+vm->bp);
fp[0]=vm->sp-vm->max_sp; // unknown /maybe size/
fp[1]=*vm->sp++; // saved PC
@ -603,21 +605,21 @@ static void inline QVM_Enter(qvm_t *vm, long size)
/*
** QVM_Return
*/
static void inline QVM_Return(qvm_t *vm, long size)
static void inline QVM_Return(qvm_t *vm, int size)
{
long *fp;
int *fp;
fp=(long*)(vm->ds+vm->bp);
fp=(int*)(vm->ds+vm->bp);
vm->bp+=size;
if(vm->bp>vm->max_bp)
Sys_Error("VM run time error: freed too much stack\n");
if(fp[1]>=vm->len_cs*2)
if (vm->cs+fp[1]) //this being false causes the program to quit.
Sys_Error("VM run time error: program returned to hyperspace\n");
if ((int)(vm->cs+fp[1]) != (int)RETURNOFFSETMARKER) //this being false causes the program to quit.
Sys_Error("VM run time error: program returned to hyperspace (%p, %p)\n", (char*)vm->cs, (char*)fp[1]);
if(fp[1]<0)
if (vm->cs+fp[1])
if ((int)(vm->cs+fp[1]) != (int)RETURNOFFSETMARKER)
Sys_Error("VM run time error: program returned to negative hyperspace\n");
if (vm->sp-vm->max_sp != fp[0])
@ -641,19 +643,19 @@ int QVM_Exec(register qvm_t *qvm, int command, int arg0, int arg1, int arg2, int
#define POP(t) qvm->sp+=t;if (qvm->sp > qvm->max_sp) Sys_Error("QVM Stack underflow");
#define PUSH(v) qvm->sp--;if (qvm->sp < qvm->min_sp) Sys_Error("QVM Stack overflow");*qvm->sp=v
qvm_op_t op=-1;
unsigned long param;
unsigned int param;
long *fp;
unsigned long *oldpc;
int *fp;
unsigned int *oldpc;
oldpc = qvm->pc;
// setup execution environment
qvm->pc=NULL;
qvm->pc=RETURNOFFSETMARKER;
// qvm->cycles=0;
// prepare local stack
qvm->bp -= 15*4; //we have to do this each call for the sake of (reliable) recursion.
fp=(long*)(qvm->ds+qvm->bp);
fp=(int*)(qvm->ds+qvm->bp);
// push all params
fp[0]=0;
fp[1]=0;
@ -698,7 +700,7 @@ int QVM_Exec(register qvm_t *qvm, int command, int arg0, int arg1, int arg2, int
case OP_LEAVE:
QVM_Return(qvm, param);
if (!qvm->pc)
if ((int)qvm->pc == (int)RETURNOFFSETMARKER)
{
// pick return value from stack
qvm->pc = oldpc;
@ -746,35 +748,35 @@ int QVM_Exec(register qvm_t *qvm, int command, int arg0, int arg1, int arg2, int
POP(2);
break;
case OP_LTI:
if(*(signed long*)&qvm->sp[1]<*(signed long*)&qvm->sp[0]) QVM_Goto(qvm, param);
if(*(signed int*)&qvm->sp[1]<*(signed int*)&qvm->sp[0]) QVM_Goto(qvm, param);
POP(2);
break;
case OP_LEI:
if(*(signed long*)&qvm->sp[1]<=*(signed long*)&qvm->sp[0]) QVM_Goto(qvm, param);
if(*(signed int*)&qvm->sp[1]<=*(signed int*)&qvm->sp[0]) QVM_Goto(qvm, param);
POP(2);
break;
case OP_GTI:
if(*(signed long*)&qvm->sp[1]>*(signed long*)&qvm->sp[0]) QVM_Goto(qvm, param);
if(*(signed int*)&qvm->sp[1]>*(signed int*)&qvm->sp[0]) QVM_Goto(qvm, param);
POP(2);
break;
case OP_GEI:
if(*(signed long*)&qvm->sp[1]>=*(signed long*)&qvm->sp[0]) QVM_Goto(qvm, param);
if(*(signed int*)&qvm->sp[1]>=*(signed int*)&qvm->sp[0]) QVM_Goto(qvm, param);
POP(2);
break;
case OP_LTU:
if(*(unsigned long*)&qvm->sp[1]<*(unsigned long*)&qvm->sp[0]) QVM_Goto(qvm, param);
if(*(unsigned int*)&qvm->sp[1]<*(unsigned int*)&qvm->sp[0]) QVM_Goto(qvm, param);
POP(2);
break;
case OP_LEU:
if(*(unsigned long*)&qvm->sp[1]<=*(unsigned long*)&qvm->sp[0]) QVM_Goto(qvm, param);
if(*(unsigned int*)&qvm->sp[1]<=*(unsigned int*)&qvm->sp[0]) QVM_Goto(qvm, param);
POP(2);
break;
case OP_GTU:
if(*(unsigned long*)&qvm->sp[1]>*(unsigned long*)&qvm->sp[0]) QVM_Goto(qvm, param);
if(*(unsigned int*)&qvm->sp[1]>*(unsigned int*)&qvm->sp[0]) QVM_Goto(qvm, param);
POP(2);
break;
case OP_GEU:
if(*(unsigned long*)&qvm->sp[1]>=*(unsigned long*)&qvm->sp[0]) QVM_Goto(qvm, param);
if(*(unsigned int*)&qvm->sp[1]>=*(unsigned int*)&qvm->sp[0]) QVM_Goto(qvm, param);
POP(2);
break;
case OP_EQF:
@ -804,13 +806,13 @@ int QVM_Exec(register qvm_t *qvm, int command, int arg0, int arg1, int arg2, int
// memory I/O: masks protect main memory
case OP_LOAD1:
*(unsigned long*)&qvm->sp[0]=*(unsigned char*)&qvm->ds[qvm->sp[0]&qvm->ds_mask];
*(unsigned int*)&qvm->sp[0]=*(unsigned char*)&qvm->ds[qvm->sp[0]&qvm->ds_mask];
break;
case OP_LOAD2:
*(unsigned long*)&qvm->sp[0]=*(unsigned short*)&qvm->ds[qvm->sp[0]&qvm->ds_mask];
*(unsigned int*)&qvm->sp[0]=*(unsigned short*)&qvm->ds[qvm->sp[0]&qvm->ds_mask];
break;
case OP_LOAD4:
*(unsigned long*)&qvm->sp[0]=*(unsigned long*)&qvm->ds[qvm->sp[0]&qvm->ds_mask];
*(unsigned int*)&qvm->sp[0]=*(unsigned int*)&qvm->ds[qvm->sp[0]&qvm->ds_mask];
break;
case OP_STORE1:
*(qbyte*)&qvm->ds[qvm->sp[1]&qvm->ds_mask]=(qbyte)(qvm->sp[0]&0xFF);
@ -821,11 +823,11 @@ int QVM_Exec(register qvm_t *qvm, int command, int arg0, int arg1, int arg2, int
POP(2);
break;
case OP_STORE4:
*(unsigned long*)&qvm->ds[qvm->sp[1]&qvm->ds_mask]=*(unsigned long*)&qvm->sp[0];
*(unsigned int*)&qvm->ds[qvm->sp[1]&qvm->ds_mask]=*(unsigned int*)&qvm->sp[0];
POP(2);
break;
case OP_ARG:
*(unsigned long*)&qvm->ds[(param+qvm->bp)&qvm->ds_mask]=*(unsigned long*)&qvm->sp[0];
*(unsigned int*)&qvm->ds[(param+qvm->bp)&qvm->ds_mask]=*(unsigned int*)&qvm->sp[0];
POP(1);
break;
case OP_BLOCK_COPY:
@ -838,73 +840,73 @@ int QVM_Exec(register qvm_t *qvm, int command, int arg0, int arg1, int arg2, int
// integer arithmetic
case OP_SEX8:
if(*(signed long*)&qvm->sp[0]&0x80) *(signed long*)&qvm->sp[0]|=0xFFFFFF00;
if(*(signed int*)&qvm->sp[0]&0x80) *(signed int*)&qvm->sp[0]|=0xFFFFFF00;
break;
case OP_SEX16:
if(*(signed long*)&qvm->sp[0]&0x8000) *(signed long*)&qvm->sp[0]|=0xFFFF0000;
if(*(signed int*)&qvm->sp[0]&0x8000) *(signed int*)&qvm->sp[0]|=0xFFFF0000;
break;
case OP_NEGI:
*(signed long*)&qvm->sp[0]=-*(signed long*)&qvm->sp[0];
*(signed int*)&qvm->sp[0]=-*(signed int*)&qvm->sp[0];
break;
case OP_ADD:
*(signed long*)&qvm->sp[1]+=*(signed long*)&qvm->sp[0];
*(signed int*)&qvm->sp[1]+=*(signed int*)&qvm->sp[0];
POP(1);
break;
case OP_SUB:
*(signed long*)&qvm->sp[1]-=*(signed long*)&qvm->sp[0];
*(signed int*)&qvm->sp[1]-=*(signed int*)&qvm->sp[0];
POP(1);
break;
case OP_DIVI:
*(signed long*)&qvm->sp[1]/=*(signed long*)&qvm->sp[0];
*(signed int*)&qvm->sp[1]/=*(signed int*)&qvm->sp[0];
POP(1);
break;
case OP_DIVU:
*(unsigned long*)&qvm->sp[1]/=(*(unsigned long*)&qvm->sp[0]);
*(unsigned int*)&qvm->sp[1]/=(*(unsigned int*)&qvm->sp[0]);
POP(1);
break;
case OP_MODI:
*(signed long*)&qvm->sp[1]%=*(signed long*)&qvm->sp[0];
*(signed int*)&qvm->sp[1]%=*(signed int*)&qvm->sp[0];
POP(1);
break;
case OP_MODU:
*(unsigned long*)&qvm->sp[1]%=(*(unsigned long*)&qvm->sp[0]);
*(unsigned int*)&qvm->sp[1]%=(*(unsigned int*)&qvm->sp[0]);
POP(1);
break;
case OP_MULI:
*(signed long*)&qvm->sp[1]*=*(signed long*)&qvm->sp[0];
*(signed int*)&qvm->sp[1]*=*(signed int*)&qvm->sp[0];
POP(1);
break;
case OP_MULU:
*(unsigned long*)&qvm->sp[1]*=(*(unsigned long*)&qvm->sp[0]);
*(unsigned int*)&qvm->sp[1]*=(*(unsigned int*)&qvm->sp[0]);
POP(1);
break;
// logic
case OP_BAND:
*(unsigned long*)&qvm->sp[1]&=*(unsigned long*)&qvm->sp[0];
*(unsigned int*)&qvm->sp[1]&=*(unsigned int*)&qvm->sp[0];
POP(1);
break;
case OP_BOR:
*(unsigned long*)&qvm->sp[1]|=*(unsigned long*)&qvm->sp[0];
*(unsigned int*)&qvm->sp[1]|=*(unsigned int*)&qvm->sp[0];
POP(1);
break;
case OP_BXOR:
*(unsigned long*)&qvm->sp[1]^=*(unsigned long*)&qvm->sp[0];
*(unsigned int*)&qvm->sp[1]^=*(unsigned int*)&qvm->sp[0];
POP(1);
break;
case OP_BCOM:
*(unsigned long*)&qvm->sp[0]=~*(unsigned long*)&qvm->sp[0];
*(unsigned int*)&qvm->sp[0]=~*(unsigned int*)&qvm->sp[0];
break;
case OP_LSH:
*(unsigned long*)&qvm->sp[1]<<=*(unsigned long*)&qvm->sp[0];
*(unsigned int*)&qvm->sp[1]<<=*(unsigned int*)&qvm->sp[0];
POP(1);
break;
case OP_RSHI:
*(signed long*)&qvm->sp[1]>>=*(signed long*)&qvm->sp[0];
*(signed int*)&qvm->sp[1]>>=*(signed int*)&qvm->sp[0];
POP(1);
break;
case OP_RSHU:
*(unsigned long*)&qvm->sp[1]>>=*(unsigned long*)&qvm->sp[0];
*(unsigned int*)&qvm->sp[1]>>=*(unsigned int*)&qvm->sp[0];
POP(1);
break;
@ -931,10 +933,10 @@ int QVM_Exec(register qvm_t *qvm, int command, int arg0, int arg1, int arg2, int
// format conversion
case OP_CVIF:
*(float*)&qvm->sp[0]=(float)(signed long)qvm->sp[0];
*(float*)&qvm->sp[0]=(float)(signed int)qvm->sp[0];
break;
case OP_CVFI:
*(signed long*)&qvm->sp[0]=(signed long)(*(float*)&qvm->sp[0]);
*(signed int*)&qvm->sp[0]=(signed int)(*(float*)&qvm->sp[0]);
break;
}
}
@ -959,8 +961,9 @@ void VM_PrintInfo(vm_t *vm)
{
qvm_t *qvm;
if(!vm->name[0]) return;
Con_Printf("%s (%p): ", vm->name, (int)vm->hInst);
if(!vm->name[0])
return;
Con_Printf("%s (%p): ", vm->name, vm->hInst);
switch(vm->type)
{

View file

@ -11,12 +11,12 @@
typedef int (EXPORT_FN *sys_calldll_t) (int arg, ...);
typedef long (*sys_callqvm_t) (void *offset, unsigned int mask, int fn, const long *arg);
typedef int (*sys_callqvm_t) (void *offset, unsigned int mask, int fn, const int *arg);
typedef struct vm_s vm_t;
// for syscall users
#define VM_LONG(x) (*(long*)&(x))
#define VM_LONG(x) (*(int*)&(x))
#define VM_FLOAT(x) (*(float*)&(x))
#define VM_POINTER(x) ((x)?(void*)((char *)offset+((x)%mask)):NULL)
#define VM_OOB(p,l) (p + l >= mask || VM_POINTER(p) < offset)

View file

@ -1959,6 +1959,9 @@ void PF_setmodel_Internal (progfuncs_t *prinst, edict_t *e, char *m)
{
if (!strcmp(sv.strings.model_precache[i], m))
{
#ifdef VM_Q1
if (svs.gametype != GT_Q1QVM)
#endif
m = sv.strings.model_precache[i];
break;
}
@ -1967,9 +1970,14 @@ void PF_setmodel_Internal (progfuncs_t *prinst, edict_t *e, char *m)
{
if (i!=MAX_MODELS)
{
sv.strings.model_precache[i] = PR_AddString(prinst, m, 0);
#ifdef VM_Q1
if (svs.gametype == GT_Q1QVM)
sv.strings.model_precache[i] = m; //in a qvm, we expect the caller to have used a static location.
else
#endif
m = sv.strings.model_precache[i] = PR_AddString(prinst, m, 0);
if (!strcmp(m + strlen(m) - 4, ".bsp"))
sv.models[i] = Mod_FindName(sv.strings.model_precache[i]);
sv.models[i] = Mod_FindName(m);
Con_Printf("WARNING: SV_ModelIndex: model %s not precached\n", m);
if (sv.state != ss_loading)
@ -1992,7 +2000,7 @@ void PF_setmodel_Internal (progfuncs_t *prinst, edict_t *e, char *m)
}
}
e->v->model = PR_SetString(prinst, sv.strings.model_precache[i]);
e->v->model = PR_SetString(prinst, m);
e->v->modelindex = i;
// if it is an inline model, get the size information for it
@ -3095,7 +3103,7 @@ name checkclient ()
*/
#define MAX_CHECK 16
int c_invis, c_notvis;
void PF_checkclient (progfuncs_t *prinst, struct globalvars_s *pr_globals)
int PF_checkclient_Internal (progfuncs_t *prinst)
{
edict_t *ent, *self;
int l;
@ -3112,8 +3120,7 @@ void PF_checkclient (progfuncs_t *prinst, struct globalvars_s *pr_globals)
ent = EDICT_NUM(prinst, sv.lastcheck);
if (ent->isfree || ent->v->health <= 0)
{
RETURN_EDICT(prinst, sv.edicts);
return;
return 0;
}
// if current entity can't possibly see the check entity, return 0
@ -3123,13 +3130,17 @@ void PF_checkclient (progfuncs_t *prinst, struct globalvars_s *pr_globals)
if ( (l<0) || !(checkpvs[l>>3] & (1<<(l&7)) ) )
{
c_notvis++;
RETURN_EDICT(prinst, sv.edicts);
return;
return 0;
}
// might be able to see it
c_invis++;
RETURN_EDICT(prinst, ent);
return sv.lastcheck;
}
void PF_checkclient (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
RETURN_EDICT(prinst, EDICT_NUM(prinst, PF_checkclient_Internal(prinst)));
}
//============================================================================
@ -3760,9 +3771,15 @@ void PF_precache_model_Internal (progfuncs_t *prinst, char *s)
PR_BIError (prinst, "Precache name too long");
return;
}
#ifdef VM_Q1
if (svs.gametype == GT_Q1QVM)
sv.strings.model_precache[i] = s;
else
#endif
sv.strings.model_precache[i] = PR_AddString(prinst, s, 0);
s = sv.strings.model_precache[i];
if (!strcmp(s + strlen(s) - 4, ".bsp"))
sv.models[i] = Mod_FindName(sv.strings.model_precache[i]);
sv.models[i] = Mod_FindName(s);
if (sv.state != ss_loading)
{

View file

@ -155,11 +155,7 @@ typedef enum
GAME_SHUTDOWN, // (void);
GAME_CLIENT_CONNECT, // ( int clientNum ,int isSpectator);
// ( int clientNum, qboolean firstTime, qboolean isBot );
// return NULL if the client is allowed to connect, otherwise return
// a text string with the reason for denial
GAME_PUT_CLIENT_IN_SERVER,
//GAME_CLIENT_BEGIN, // ( int clientNum ,int isSpectator);
GAME_CLIENT_USERINFO_CHANGED, // ( int clientNum,int isSpectator );
@ -178,11 +174,7 @@ typedef enum
GAME_EDICT_TOUCH, //(self,other)
GAME_EDICT_THINK, //(self,other=world,time)
GAME_EDICT_BLOCKED, //(self,other)
// ConsoleCommand will be called when a command has been issued
// that is not recognized as a builtin function.
// The game can issue trap_argc() / trap_argv() commands to get the command
// and parameters. Return qfalse if the game doesn't recognize it as a command.
GAME_CLIENT_SAY, //(int isteam)
} gameExport_t;
@ -267,6 +259,7 @@ typedef struct {
} q1qvmglobalvars_t;
//this is not usable in 64bit to refer to a 32bit qvm (hence why we have two versions).
typedef struct
{
edict_t *ents;
@ -274,7 +267,16 @@ typedef struct
q1qvmglobalvars_t *global;
field_t *fields;
int APIversion;
} gameData_t;
} gameDataN_t;
typedef struct
{
unsigned int ents;
int sizeofent;
unsigned int global;
unsigned int fields;
int APIversion;
} gameData32_t;
typedef int fileHandle_t;
@ -311,19 +313,22 @@ static edict_t *q1qvmedicts[MAX_Q1QVM_EDICTS];
static void *evars; //pointer to the gamecodes idea of an edict_t
static long vevars; //offset into the vm base of evars
static int vevars; //offset into the vm base of evars
char *Q1QVMPF_AddString(progfuncs_t *pf, char *base, int minlength)
/*
static char *Q1QVMPF_AddString(progfuncs_t *pf, char *base, int minlength)
{
char *n;
int l = strlen(base);
Con_Printf("warning: string %s will not be readable from the qvm\n", base);
l = l<minlength?minlength:l;
n = Z_TagMalloc(l+1, VMFSID_Q1QVM);
strcpy(n, base);
return n;
}
*/
edict_t *Q1QVMPF_EdictNum(progfuncs_t *pf, unsigned int num)
static edict_t *Q1QVMPF_EdictNum(progfuncs_t *pf, unsigned int num)
{
edict_t *e;
@ -341,16 +346,16 @@ edict_t *Q1QVMPF_EdictNum(progfuncs_t *pf, unsigned int num)
return e;
}
int Q1QVMPF_NumForEdict(progfuncs_t *pf, edict_t *e)
static unsigned int Q1QVMPF_NumForEdict(progfuncs_t *pf, edict_t *e)
{
return e->entnum;
}
int Q1QVMPF_EdictToProgs(progfuncs_t *pf, edict_t *e)
static int Q1QVMPF_EdictToProgs(progfuncs_t *pf, edict_t *e)
{
return e->entnum*pr_edict_size;
}
edict_t *Q1QVMPF_ProgsToEdict(progfuncs_t *pf, int num)
static edict_t *Q1QVMPF_ProgsToEdict(progfuncs_t *pf, int num)
{
if (num % pr_edict_size)
Con_Printf("Edict To Progs with remainder\n");
@ -368,7 +373,7 @@ void Q1QVMED_ClearEdict (edict_t *e, qboolean wipe)
e->entnum = num;
}
void Q1QVMPF_EntRemove(progfuncs_t *pf, edict_t *e)
static void Q1QVMPF_EntRemove(progfuncs_t *pf, edict_t *e)
{
if (!ED_CanFree(e))
return;
@ -376,7 +381,7 @@ void Q1QVMPF_EntRemove(progfuncs_t *pf, edict_t *e)
e->freetime = sv.time;
}
edict_t *Q1QVMPF_EntAlloc(progfuncs_t *pf)
static edict_t *Q1QVMPF_EntAlloc(progfuncs_t *pf)
{
int i;
edict_t *e;
@ -427,7 +432,7 @@ edict_t *Q1QVMPF_EntAlloc(progfuncs_t *pf)
return (struct edict_s *)e;
}
int Q1QVMPF_LoadEnts(progfuncs_t *pf, char *mapstring, float spawnflags)
static int Q1QVMPF_LoadEnts(progfuncs_t *pf, char *mapstring, float spawnflags)
{
q1qvmentstring = mapstring;
VM_Call(q1qvm, GAME_LOADENTS);
@ -435,7 +440,7 @@ int Q1QVMPF_LoadEnts(progfuncs_t *pf, char *mapstring, float spawnflags)
return pr_edict_size;
}
eval_t *Q1QVMPF_GetEdictFieldValue(progfuncs_t *pf, edict_t *e, char *fieldname, evalc_t *cache)
static eval_t *Q1QVMPF_GetEdictFieldValue(progfuncs_t *pf, edict_t *e, char *fieldname, evalc_t *cache)
{
if (!strcmp(fieldname, "message"))
{
@ -444,24 +449,24 @@ eval_t *Q1QVMPF_GetEdictFieldValue(progfuncs_t *pf, edict_t *e, char *fieldname,
return NULL;
}
eval_t *Q1QVMPF_FindGlobal (progfuncs_t *prinst, char *name, progsnum_t num)
static eval_t *Q1QVMPF_FindGlobal (progfuncs_t *prinst, char *name, progsnum_t num)
{
return NULL;
}
globalvars_t *Q1QVMPF_Globals(progfuncs_t *prinst, int prnum)
static globalvars_t *Q1QVMPF_Globals(progfuncs_t *prinst, int prnum)
{
return NULL;
}
string_t Q1QVMPF_StringToProgs(progfuncs_t *prinst, char *str)
static string_t Q1QVMPF_StringToProgs(progfuncs_t *prinst, char *str)
{
return (string_t)str;
return (string_t)(str - (char*)VM_MemoryBase(q1qvm));
}
char *Q1QVMPF_StringToNative(progfuncs_t *prinst, string_t str)
static char *Q1QVMPF_StringToNative(progfuncs_t *prinst, string_t str)
{
return (char*)str;
return (char*)VM_MemoryBase(q1qvm) + str;
}
void PF_WriteByte (progfuncs_t *prinst, struct globalvars_s *pr_globals);
@ -493,12 +498,13 @@ void PF_setspawnparms (progfuncs_t *prinst, struct globalvars_s *pr_globals);
void PF_walkmove (progfuncs_t *prinst, struct globalvars_s *pr_globals);
int PF_checkclient_Internal (progfuncs_t *prinst);
void PF_precache_sound_Internal (progfuncs_t *prinst, char *s);
void PF_precache_model_Internal (progfuncs_t *prinst, char *s);
void PF_setmodel_Internal (progfuncs_t *prinst, edict_t *e, char *m);
char *PF_infokey_Internal (int entnum, char *value);
int WrapQCBuiltin(builtin_t func, void *offset, unsigned int mask, const long *arg, char *argtypes)
static int WrapQCBuiltin(builtin_t func, void *offset, unsigned int mask, const int *arg, char *argtypes)
{
globalvars_t gv;
int argnum=0;
@ -534,7 +540,7 @@ int WrapQCBuiltin(builtin_t func, void *offset, unsigned int mask, const long *a
}
#define VALIDATEPOINTER(o,l) if ((int)o + l >= mask || VM_POINTER(o) < offset) SV_Error("Call to game trap %i passes invalid pointer\n", fn); //out of bounds.
long syscallqvm (void *offset, unsigned int mask, int fn, const long *arg)
static int syscallqvm (void *offset, unsigned int mask, int fn, const int *arg)
{
switch (fn)
{
@ -657,8 +663,7 @@ long syscallqvm (void *offset, unsigned int mask, int fn, const long *arg)
break;
case G_CHECKCLIENT:
// return PF_checkclientinternal(VM_LONG(arg[0]));
break;
return PF_checkclient_Internal(svprogfuncs);
case G_STUFFCMD:
{
@ -695,7 +700,7 @@ long syscallqvm (void *offset, unsigned int mask, int fn, const long *arg)
case G_FINDRADIUS:
{
int start = ((char*)VM_POINTER(arg[0]) - (char*)vevars) / pr_edict_size;
int start = ((char*)VM_POINTER(arg[0]) - (char*)evars) / pr_edict_size;
edict_t *ed;
vec3_t diff;
float *org = VM_POINTER(arg[1]);
@ -708,7 +713,7 @@ long syscallqvm (void *offset, unsigned int mask, int fn, const long *arg)
continue;
VectorSubtract(ed->v->origin, org, diff);
if (rad > DotProduct(diff, diff))
return (long)((char*)vevars + start*pr_edict_size);
return (int)(vevars + start*pr_edict_size);
}
return 0;
}
@ -851,14 +856,20 @@ long syscallqvm (void *offset, unsigned int mask, int fn, const long *arg)
break;
case g_memset:
{
void *dst = VM_POINTER(arg[0]);
VALIDATEPOINTER(arg[0], arg[2]);
memset(VM_POINTER(arg[0]), arg[1], arg[2]);
memset(dst, arg[1], arg[2]);
return arg[0];
break;
}
case g_memcpy:
{
void *dst = VM_POINTER(arg[0]);
void *src = VM_POINTER(arg[1]);
VALIDATEPOINTER(arg[0], arg[2]);
memmove(VM_POINTER(arg[0]), VM_POINTER(arg[1]), arg[2]);
memmove(dst, src, arg[2]);
return arg[0];
}
break;
case g_strncpy:
VALIDATEPOINTER(arg[0], arg[2]);
@ -1006,12 +1017,12 @@ long syscallqvm (void *offset, unsigned int mask, int fn, const long *arg)
if (field == NULL)
{
if (*match == '\0')
return VM_LONG(e->v)-WASTED_EDICT_T_SIZE - (int)offset;
return ((char*)e->v - (char*)offset)-WASTED_EDICT_T_SIZE;
}
else
{
if (!strcmp(field, match))
return VM_LONG(e->v)-WASTED_EDICT_T_SIZE - (int)offset;
return ((char*)e->v - (char*)offset)-WASTED_EDICT_T_SIZE;
}
}
}
@ -1089,7 +1100,7 @@ Con_DPrintf("PF_readcmd: %s\n%s", s, output);
if (VM_OOB(arg[0], arg[1]) || !out)
return -1; //please don't corrupt me
time(&curtime);
curtime + VM_LONG(arg[3]);
curtime += VM_LONG(arg[3]);
local = localtime(&curtime);
strftime(out, VM_LONG(arg[1]), fmt, local);
}
@ -1137,11 +1148,11 @@ Con_DPrintf("PF_readcmd: %s\n%s", s, output);
case G_NEXTCLIENT:
{
unsigned int start = ((char*)VM_POINTER(arg[0]) - (char*)vevars) / pr_edict_size;
unsigned int start = ((char*)VM_POINTER(arg[0]) - (char*)evars) / pr_edict_size;
while (start < sv.allocated_client_slots)
{
if (svs.clients[start].state == cs_spawned)
return (long)((char*)vevars + (start+1) * pr_edict_size);
return (int)(vevars + (start+1) * pr_edict_size);
start++;
}
return 0;
@ -1155,9 +1166,9 @@ Con_DPrintf("PF_readcmd: %s\n%s", s, output);
return 0;
}
long EXPORT_FN syscallnative (int arg, ...)
static int EXPORT_FN syscallnative (int arg, ...)
{
long args[13];
int args[13];
va_list argptr;
va_start(argptr, arg);
@ -1201,7 +1212,8 @@ qboolean PR_LoadQ1QVM(void)
{
static float writable;
int i;
gameData_t *gd;
gameDataN_t *gd, gdm;
gameData32_t *gd32;
int ret;
if (q1qvm)
@ -1218,7 +1230,7 @@ qboolean PR_LoadQ1QVM(void)
svprogfuncs = &q1qvmprogfuncs;
q1qvmprogfuncs.AddString = Q1QVMPF_AddString;
// q1qvmprogfuncs.AddString = Q1QVMPF_AddString; //using this breaks 64bit support, and is a 'bad plan' elsewhere too,
q1qvmprogfuncs.EDICT_NUM = Q1QVMPF_EdictNum;
q1qvmprogfuncs.NUM_FOR_EDICT = Q1QVMPF_NumForEdict;
q1qvmprogfuncs.EdictToProgs = Q1QVMPF_EdictToProgs;
@ -1246,21 +1258,33 @@ qboolean PR_LoadQ1QVM(void)
Q1QVM_Shutdown();
return false;
}
gd = (gameData_t*)((char*)VM_MemoryBase(q1qvm) + ret);
gd32 = (gameData32_t*)((char*)VM_MemoryBase(q1qvm) + ret); //qvm is 32bit
//when running native64, we need to convert these to real types, so we can use em below
gd = &gdm;
gd->ents = gd32->ents;
gd->sizeofent = gd32->sizeofent;
gd->global = gd32->global;
gd->fields = gd32->fields;
gd->APIversion = gd32->APIversion;
pr_edict_size = gd->sizeofent;
vevars = (long)gd->ents;
evars = ((char*)VM_MemoryBase(q1qvm) + vevars);
//FIXME: range check this pointer
//FIXME: range check the globals pointer
sv.num_edicts = 1;
sv.max_edicts = sizeof(q1qvmedicts)/sizeof(q1qvmedicts[0]);
#define globalint(required, name) pr_nqglobal_struct->name = (int*)((char*)VM_MemoryBase(q1qvm)+(int)&gd->global->name)
#define globalfloat(required, name) pr_nqglobal_struct->name = (float*)((char*)VM_MemoryBase(q1qvm)+(int)&gd->global->name)
#define globalstring(required, name) pr_nqglobal_struct->name = (string_t*)((char*)VM_MemoryBase(q1qvm)+(int)&gd->global->name)
#define globalvec(required, name) pr_nqglobal_struct->V_##name = (vec3_t*)((char*)VM_MemoryBase(q1qvm)+(int)&gd->global->name)
#define globalfunc(required, name) pr_nqglobal_struct->name = (int*)((char*)VM_MemoryBase(q1qvm)+(int)&gd->global->name)
//WARNING: global is not remapped yet...
//This code is written evilly, but works well enough
#define globalint(required, name) pr_nqglobal_struct->name = (int*)((char*)VM_MemoryBase(q1qvm)+(long)&gd->global->name) //the logic of this is somewhat crazy
#define globalfloat(required, name) pr_nqglobal_struct->name = (float*)((char*)VM_MemoryBase(q1qvm)+(long)&gd->global->name)
#define globalstring(required, name) pr_nqglobal_struct->name = (string_t*)((char*)VM_MemoryBase(q1qvm)+(long)&gd->global->name)
#define globalvec(required, name) pr_nqglobal_struct->V_##name = (vec3_t*)((char*)VM_MemoryBase(q1qvm)+(long)&gd->global->name)
#define globalfunc(required, name) pr_nqglobal_struct->name = (int*)((char*)VM_MemoryBase(q1qvm)+(long)&gd->global->name)
globalint (true, self); //we need the qw ones, but any in standard quake and not quakeworld, we don't really care about.
globalint (true, other);
globalint (true, world);
@ -1308,8 +1332,7 @@ qboolean PR_LoadQ1QVM(void)
pr_nqglobal_struct->trace_endcontents = &writable;
for (i = 0; i < 16; i++)
spawnparamglobals[i] = (float*)((char*)VM_MemoryBase(q1qvm)+(int)(&gd->global->parm1 + i));
//spawnparamglobals[i] = (float *)&gd->global->parm1 + i;
spawnparamglobals[i] = (float*)((char*)VM_MemoryBase(q1qvm)+(long)(&gd->global->parm1 + i));
for (; i < NUM_SPAWN_PARMS; i++)
spawnparamglobals[i] = NULL;
@ -1327,7 +1350,8 @@ void Q1QVM_ClientConnect(client_t *cl)
if (cl->edict->v->netname)
{
strcpy(cl->namebuf, cl->name);
cl->name = cl->edict->v->netname + (char*)VM_MemoryBase(q1qvm);
cl->name = Q1QVMPF_StringToNative(svprogfuncs, cl->edict->v->netname);
//FIXME: check this pointer
strcpy(cl->name, cl->namebuf);
}
// call the spawn function
@ -1341,6 +1365,56 @@ void Q1QVM_ClientConnect(client_t *cl)
VM_Call(q1qvm, GAME_PUT_CLIENT_IN_SERVER, cl->spectator);
}
qboolean Q1QVM_GameConsoleCommand(void)
{
int oldself, oldother;
if (!q1qvm)
return false;
//FIXME: if an rcon command from someone on the server, mvdsv sets self to match the ip of that player
//this is not required (broken by proxies anyway) but is a nice handy feature
pr_global_struct->time = sv.time;
oldself = pr_global_struct->self; //these are usually useless
oldother = pr_global_struct->other; //but its possible that someone makes a mod that depends on the 'mod' command working via redirectcmd+co
//this at least matches mvdsv
pr_global_struct->self = 0;
pr_global_struct->other = 0;
VM_Call(q1qvm, GAME_CONSOLE_COMMAND); //mod uses Cmd_Argv+co to get args
pr_global_struct->self = oldself;
pr_global_struct->other = oldother;
return true;
}
qboolean Q1QVM_ClientSay(edict_t *player, qboolean team)
{
qboolean washandled;
if (!q1qvm)
return false;
SV_EndRedirect();
pr_global_struct->time = sv.time;
pr_global_struct->self = Q1QVMPF_EdictToProgs(svprogfuncs, player);
washandled = VM_Call(q1qvm, GAME_CLIENT_SAY, team);
SV_BeginRedirect(RD_CLIENT, host_client->language); //put it back to how we expect it was. *shudder*
return washandled;
}
qboolean Q1QVM_UserInfoChanged(edict_t *player)
{
if (!q1qvm)
return false;
pr_global_struct->time = sv.time;
pr_global_struct->self = Q1QVMPF_EdictToProgs(svprogfuncs, player);
return VM_Call(q1qvm, GAME_CLIENT_USERINFO_CHANGED);
}
void Q1QVM_PlayerPreThink(void)
{
VM_Call(q1qvm, GAME_CLIENT_PRETHINK, host_client->spectator);

View file

@ -126,3 +126,27 @@ void PR_ClientUserInfoChanged(char *name, char *oldivalue, char *newvalue);
void PR_LocalInfoChanged(char *name, char *oldivalue, char *newvalue);
void PF_InitTempStrings(progfuncs_t *prinst);
#ifdef VM_Q1
struct client_s;
void Q1QVM_Shutdown(void);
qboolean PR_LoadQ1QVM(void);
void Q1QVM_ClientConnect(struct client_s *cl);
qboolean Q1QVM_GameConsoleCommand(void);
qboolean Q1QVM_ClientSay(edict_t *player, qboolean team);
qboolean Q1QVM_UserInfoChanged(edict_t *player);
void Q1QVM_PlayerPreThink(void);
void Q1QVM_RunPlayerThink(void);
void Q1QVM_PostThink(void);
void Q1QVM_StartFrame(void);
void Q1QVM_Touch(void);
void Q1QVM_Think(void);
void Q1QVM_Blocked(void);
void Q1QVM_SetNewParms(void);
void Q1QVM_SetChangeParms(void);
void Q1QVM_ClientCommand(void);
void Q1QVM_DropClient(struct client_s *cl);
void Q1QVM_ChainMoved(void);
void Q1QVM_EndFrame(void);
void Q1QVMED_ClearEdict (edict_t *e, qboolean wipe);
#endif

View file

@ -1886,6 +1886,11 @@ void SV_SendGameCommand_f(void)
return;
#endif
#ifdef VM_Q1
if (Q1QVM_GameConsoleCommand())
return;
#endif
#ifdef Q2SERVER
if (ge)
{
@ -1980,6 +1985,7 @@ void SV_InitOperatorCommands (void)
// Cmd_AddCommand ("writeip", SV_WriteIP_f);
Cmd_AddCommand ("sv", SV_SendGameCommand_f);
Cmd_AddCommand ("mod", SV_SendGameCommand_f);
Cmd_AddCommand ("killserver", SV_KillServer_f);
Cmd_AddCommand ("map", SV_Map_f);

View file

@ -908,7 +908,26 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us
sv.models[1] = sv.worldmodel;
if (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM)
#ifdef VM_Q1
if (svs.gametype == GT_Q1QVM)
{
strcpy(sv.strings.sound_precache[0], "");
sv.strings.model_precache[0] = "";
sv.strings.model_precache[1] = sv.modelname; //the qvm doesn't have access to this array
for (i=1 ; i<sv.worldmodel->numsubmodels ; i++)
{
sv.strings.model_precache[1+i] = localmodels[i];
sv.models[i+1] = Mod_ForName (localmodels[i], false);
}
//check player/eyes models for hacks
sv.model_player_checksum = SV_CheckModel("progs/player.mdl");
sv.eyes_player_checksum = SV_CheckModel("progs/eyes.mdl");
}
else
#endif
if (svs.gametype == GT_PROGS)
{
strcpy(sv.strings.sound_precache[0], "");
sv.strings.model_precache[0] = "";
@ -992,8 +1011,18 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us
if (!svs.clients[i].state && svs.clients[i].name[0]) //this is a bot.
svs.clients[i].name[0] = '\0'; //make it go away
#ifdef VM_Q1
if (svs.gametype == GT_Q1QVM)
{ //we'll fix it up later anyway
svs.clients[i].name = svs.clients[i].namebuf;
svs.clients[i].team = svs.clients[i].teambuf;
}
else
#endif
{
svs.clients[i].name = PR_AddString(svprogfuncs, svs.clients[i].namebuf, sizeof(svs.clients[i].namebuf));
svs.clients[i].team = PR_AddString(svprogfuncs, svs.clients[i].teambuf, sizeof(svs.clients[i].teambuf));
}
#ifdef PEXT_CSQC
memset(svs.clients[i].csqcentsequence, 0, sizeof(svs.clients[i].csqcentsequence));
@ -1053,6 +1082,9 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us
eval_t *eval;
ent = EDICT_NUM(svprogfuncs, 0);
ent->isfree = false;
#ifdef VM_Q1
if (svs.gametype != GT_Q1QVM) //we cannot do this with qvm
#endif
ent->v->model = PR_NewString(svprogfuncs, sv.worldmodel->name, 0);
ent->v->modelindex = 1; // world model
ent->v->solid = SOLID_BSP;
@ -1062,14 +1094,22 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us
if (progstype == PROG_QW && pr_imitatemvdsv.value>0)
{
#ifdef VM_Q1
if (svs.gametype != GT_Q1QVM) //we cannot do this with qvm
#endif
{
ent->v->targetname = PR_NewString(svprogfuncs, "mvdsv", 0);
ent->v->netname = PR_NewString(svprogfuncs, va("%s %f %s, build %d\nBuild date: " __DATE__ ", " __TIME__ "", DISTRIBUTIONLONG, 2.4, PLATFORM, build_number()), 0);
}
ent->v->impulse = 0;//QWE_VERNUM;
ent->v->items = 103;
}
#ifdef VM_Q1
if (svs.gametype != GT_Q1QVM) //we cannot do this with qvm
pr_global_struct->mapname = PR_NewString(svprogfuncs, sv.name, 0);
#endif
// serverflags are for cross level information (sigils)
pr_global_struct->serverflags = svs.serverflags;
pr_global_struct->time = 0.1; //HACK!!!! A few QuakeC mods expect time to be non-zero in spawn funcs - like prydon gate...

View file

@ -2871,6 +2871,12 @@ Spawns a client, uses an impulse, uses that clients think then removes the clien
void SV_Impulse_f (void)
{
int i;
if (svs.gametype != GT_PROGS)
{
Con_Printf("Not supported in this game type\n");
return;
}
for (i = 0; i < sv.allocated_client_slots; i++)
{
if (svs.clients[i].state == cs_free)

View file

@ -2278,6 +2278,11 @@ void SV_Say (qboolean team)
return;
}
#ifdef VM_Q1
if (Q1QVM_ClientSay(sv_player, team))
return;
#endif
if ((floodtime=SV_CheckFloodProt(host_client)))
{
SV_ClientTPrintf(host_client, PRINT_CHAT,
@ -2740,7 +2745,12 @@ void SV_SetInfo_f (void)
if (strstr(Cmd_Argv(1), "\\") || strstr(Cmd_Argv(2), "\\"))
return; // illegal char
strcpy(oldval, Info_ValueForKey(host_client->userinfo, Cmd_Argv(1)));
Q_strncpyz(oldval, Info_ValueForKey(host_client->userinfo, Cmd_Argv(1)), MAX_INFO_STRING);
#ifdef VM_Q1
if (Q1QVM_UserInfoChanged(sv_player))
return;
#endif
Info_SetValueForKey (host_client->userinfo, Cmd_Argv(1), Cmd_Argv(2), sizeof(host_client->userinfo));
// name is extracted below in ExtractFromUserInfo
@ -2756,7 +2766,7 @@ void SV_SetInfo_f (void)
SV_ExtractFromUserinfo (host_client);
if (progstype != PROG_QW && !strcmp(Cmd_Argv(1), "bottomcolor"))
{
{ //team fortress has a nasty habit of booting people without this
sv_player->v->team = atoi(Cmd_Argv(2))+1;
}
@ -3187,6 +3197,7 @@ void SV_SetUpClientEdict (client_t *cl, edict_t *ent)
string_t preserve;
preserve = ent->v->netname;
Q1QVMED_ClearEdict(ent, true);
Con_Printf("client netname: %x\n", preserve);
ent->v->netname = preserve;
}
else
@ -3194,12 +3205,12 @@ void SV_SetUpClientEdict (client_t *cl, edict_t *ent)
{
if (progstype != PROG_NQ) //allow frikbots to work in NQ mods (but not qw!)
ED_ClearEdict(svprogfuncs, ent);
ent->v->netname = PR_SetString(svprogfuncs, cl->name);
}
ED_Spawned(ent);
ent->isfree = false;
ent->v->colormap = NUM_FOR_EDICT(svprogfuncs, ent);
ent->v->netname = PR_SetString(svprogfuncs, cl->name);
if (pr_teamfield)
((string_t *)ent->v)[pr_teamfield] = (string_t)(cl->team-svprogfuncs->stringtable);
@ -3238,6 +3249,12 @@ void Cmd_Join_f (void)
if (!host_client->spectator)
return; // already a player
if (svs.gametype != GT_PROGS)
{
Con_Printf ("Sorry, not implemented in this gamecode type. Try moaning at the dev team\n");
return;
}
if (!(host_client->zquake_extensions & Z_EXT_JOIN_OBSERVE))
{
Con_Printf ("Your QW client doesn't support this command\n");
@ -3325,6 +3342,12 @@ void Cmd_Observe_f (void)
if (host_client->spectator)
return; // already a spectator
if (svs.gametype != GT_PROGS)
{
Con_Printf ("Sorry, not implemented in this gamecode type. Try moaning at the dev team\n");
return;
}
if (!(host_client->zquake_extensions & Z_EXT_JOIN_OBSERVE))
{
Con_Printf ("Your QW client doesn't support this command\n");