From 06da06f9b3befde31e3371fc8b83cbd11055a244 Mon Sep 17 00:00:00 2001 From: Spoike Date: Mon, 3 Sep 2007 22:37:13 +0000 Subject: [PATCH] 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 --- engine/common/bothdefs.h | 3 +- engine/common/qvm.c | 155 ++++++++++++++++---------------- engine/common/vm.h | 4 +- engine/server/pr_cmds.c | 41 ++++++--- engine/server/pr_q1qvm.c | 188 +++++++++++++++++++++++++++------------ engine/server/progs.h | 24 +++++ engine/server/sv_ccmds.c | 6 ++ engine/server/sv_init.c | 54 +++++++++-- engine/server/sv_main.c | 6 ++ engine/server/sv_user.c | 29 +++++- 10 files changed, 351 insertions(+), 159 deletions(-) diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 65e95686d..00713d520 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -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 diff --git a/engine/common/qvm.c b/engine/common/qvm.c index f9ef90adc..3c8ff3147 100644 --- a/engine/common/qvm.c +++ b/engine/common/qvm.c @@ -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<= 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<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; ndataOffset); - long *dst=(long*)qvm->ds; + int *src=(int*)(raw+header->dataOffset); + int *dst=(int*)qvm->ds; int total=header->dataLength/4; for(n=0; nds+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->bpmin_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) { diff --git a/engine/common/vm.h b/engine/common/vm.h index 2eb7abf1b..bb14b5f5c 100644 --- a/engine/common/vm.h +++ b/engine/common/vm.h @@ -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) diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index d0ef85d36..2922a096d 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -1959,7 +1959,10 @@ void PF_setmodel_Internal (progfuncs_t *prinst, edict_t *e, char *m) { if (!strcmp(sv.strings.model_precache[i], m)) { - m = sv.strings.model_precache[i]; +#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; } - sv.strings.model_precache[i] = PR_AddString(prinst, s, 0); +#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) { diff --git a/engine/server/pr_q1qvm.c b/engine/server/pr_q1qvm.c index 3aed4f767..05d5f4a03 100755 --- a/engine/server/pr_q1qvm.c +++ b/engine/server/pr_q1qvm.c @@ -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 = lentnum; } -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: - VALIDATEPOINTER(arg[0], arg[2]); - memset(VM_POINTER(arg[0]), arg[1], arg[2]); - return arg[0]; - break; + { + void *dst = VM_POINTER(arg[0]); + VALIDATEPOINTER(arg[0], arg[2]); + memset(dst, arg[1], arg[2]); + return arg[0]; + } case g_memcpy: - VALIDATEPOINTER(arg[0], arg[2]); - memmove(VM_POINTER(arg[0]), VM_POINTER(arg[1]), arg[2]); - return arg[0]; + { + void *dst = VM_POINTER(arg[0]); + void *src = VM_POINTER(arg[1]); + VALIDATEPOINTER(arg[0], 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); diff --git a/engine/server/progs.h b/engine/server/progs.h index c5cb6f753..99c6d0b5b 100644 --- a/engine/server/progs.h +++ b/engine/server/progs.h @@ -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 + diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 9bfa17924..1d8338351 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -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); diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index b88f02abc..bf19289c1 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -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 ; inumsubmodels ; 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 - 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 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,7 +1082,10 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us eval_t *eval; ent = EDICT_NUM(svprogfuncs, 0); ent->isfree = false; - ent->v->model = PR_NewString(svprogfuncs, sv.worldmodel->name, 0); +#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; ent->v->movetype = MOVETYPE_PUSH; @@ -1062,14 +1094,22 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us if (progstype == PROG_QW && pr_imitatemvdsv.value>0) { - 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); +#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; } - pr_global_struct->mapname = PR_NewString(svprogfuncs, sv.name, 0); +#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... diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index a84211323..91b11a58a 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -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) diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index ee9dc31e9..8bcbe18f2 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -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"); @@ -3324,6 +3341,12 @@ void Cmd_Observe_f (void) return; 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)) {