diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index fd1a7c4c7..67a861873 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -1381,6 +1381,9 @@ void CL_SendCmd (double frametime) cl.frames[i].senttime = realtime; cl.frames[i].receivedtime = 0; // nq doesn't allow us to find our own packetloss +#ifdef CSQC_DAT + CSQC_Input_Frame(cmd); +#endif CLNQ_SendCmd (); memset(&independantphysics[0], 0, sizeof(independantphysics[plnum])); return; @@ -1422,7 +1425,6 @@ void CL_SendCmd (double frametime) cls.resendinfo = false; } - MSG_WriteByte (&buf, clcq2_move); // save the position for a checksum qbyte @@ -1461,6 +1463,12 @@ void CL_SendCmd (double frametime) i = cls.netchan.outgoing_sequence & UPDATE_MASK; cmd = &cl.frames[i].cmd[plnum]; *cmd = independantphysics[plnum]; + + cmd->lightlevel = lightlev; +#ifdef CSQC_DAT + CSQC_Input_Frame(cmd); +#endif + cl.frames[i].senttime = realtime; cl.frames[i].receivedtime = -1; // we haven't gotten a reply yet memset(&independantphysics[plnum], 0, sizeof(independantphysics[plnum])); @@ -1475,7 +1483,6 @@ void CL_SendCmd (double frametime) i = (cls.netchan.outgoing_sequence-2) & UPDATE_MASK; cmd = &cl.frames[i].cmd[plnum]; - cmd->lightlevel = lightlev; if (cl_c2sImpulseBackup.value >= 2) dontdrop = dontdrop || cmd->impulse; MSG_WriteDeltaUsercmd (&buf, &nullcmd, cmd); @@ -1485,7 +1492,6 @@ void CL_SendCmd (double frametime) if (cl_c2sImpulseBackup.value >= 3) dontdrop = dontdrop || cmd->impulse; cmd = &cl.frames[i].cmd[plnum]; - cmd->lightlevel = lightlev; MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd); oldcmd = cmd; @@ -1493,7 +1499,6 @@ void CL_SendCmd (double frametime) if (cl_c2sImpulseBackup.value >= 1) dontdrop = dontdrop || cmd->impulse; cmd = &cl.frames[i].cmd[plnum]; - cmd->lightlevel = lightlev; MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd); if (!firstsize) @@ -1576,6 +1581,8 @@ void CL_SendCmd (double frametime) dropcount = 0; } + if (cl.sendprespawn) + buf.cursize = 0; //tastyspleen.net is alergic. // // deliver the message // diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 8cdfa1d80..711949466 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. void CL_GetNumberedEntityInfo (int num, float *org, float *ang); void CLNQ_ParseDarkPlaces5Entities(void); -void CL_SetStat (int pnum, int stat, int value); +void CL_SetStatInt (int pnum, int stat, int value); int nq_dp_protocol; int msgflags; @@ -725,6 +725,21 @@ int CL_LoadModels(int stage) } } + + + if (atstage()) + { + cl.worldmodel = cl.model_precache[1]; + if (!cl.worldmodel || cl.worldmodel->type == mod_dummy) + Host_EndGame("Worldmodel wasn't sent\n"); + + R_CheckSky(); + + CSQC_WorldLoaded(); + + endstage(); + } + for (i=1 ; itype == mod_dummy) - Host_EndGame("Worldmodel wasn't sent\n"); - - R_CheckSky(); - - endstage(); - } if (atstage()) { Wad_NextDownload(); @@ -2180,9 +2185,9 @@ void CLNQ_ParseClientdata (void) bits |= (MSG_ReadByte() << 24); if (bits & SU_VIEWHEIGHT) - CL_SetStat(0, STAT_VIEWHEIGHT, MSG_ReadChar ()); + CL_SetStatInt(0, STAT_VIEWHEIGHT, MSG_ReadChar ()); else if (nq_dp_protocol < 6) - CL_SetStat(0, STAT_VIEWHEIGHT, DEFAULT_VIEWHEIGHT); + CL_SetStatInt(0, STAT_VIEWHEIGHT, DEFAULT_VIEWHEIGHT); if (bits & SU_IDEALPITCH) /*cl.idealpitch =*/ MSG_ReadChar (); @@ -2216,7 +2221,7 @@ void CLNQ_ParseClientdata (void) } if (bits & SU_ITEMS) - CL_SetStat(0, STAT_ITEMS, MSG_ReadLong()); + CL_SetStatInt(0, STAT_ITEMS, MSG_ReadLong()); // cl.onground = (bits & SU_ONGROUND) != 0; // cl.inwater = (bits & SU_INWATER) != 0; @@ -2226,37 +2231,37 @@ void CLNQ_ParseClientdata (void) } else if (nq_dp_protocol == 5) { - CL_SetStat(0, STAT_WEAPONFRAME, (bits & SU_WEAPONFRAME)?(unsigned short)MSG_ReadShort():0); - CL_SetStat(0, STAT_ARMOR, (bits & SU_ARMOR)?MSG_ReadShort():0); - CL_SetStat(0, STAT_WEAPON, (bits & SU_WEAPON)?MSG_ReadShort():0); + CL_SetStatInt(0, STAT_WEAPONFRAME, (bits & SU_WEAPONFRAME)?(unsigned short)MSG_ReadShort():0); + CL_SetStatInt(0, STAT_ARMOR, (bits & SU_ARMOR)?MSG_ReadShort():0); + CL_SetStatInt(0, STAT_WEAPON, (bits & SU_WEAPON)?MSG_ReadShort():0); - CL_SetStat(0, STAT_HEALTH, MSG_ReadShort()); + CL_SetStatInt(0, STAT_HEALTH, MSG_ReadShort()); - CL_SetStat(0, STAT_AMMO, MSG_ReadShort()); + CL_SetStatInt(0, STAT_AMMO, MSG_ReadShort()); - CL_SetStat(0, STAT_SHELLS, MSG_ReadShort()); - CL_SetStat(0, STAT_NAILS, MSG_ReadShort()); - CL_SetStat(0, STAT_ROCKETS, MSG_ReadShort()); - CL_SetStat(0, STAT_CELLS, MSG_ReadShort()); + CL_SetStatInt(0, STAT_SHELLS, MSG_ReadShort()); + CL_SetStatInt(0, STAT_NAILS, MSG_ReadShort()); + CL_SetStatInt(0, STAT_ROCKETS, MSG_ReadShort()); + CL_SetStatInt(0, STAT_CELLS, MSG_ReadShort()); - CL_SetStat(0, STAT_ACTIVEWEAPON, (unsigned short)MSG_ReadShort()); + CL_SetStatInt(0, STAT_ACTIVEWEAPON, (unsigned short)MSG_ReadShort()); } else { - CL_SetStat(0, STAT_WEAPONFRAME, (bits & SU_WEAPONFRAME)?(unsigned char)MSG_ReadByte():0); - CL_SetStat(0, STAT_ARMOR, (bits & SU_ARMOR)?MSG_ReadByte():0); - CL_SetStat(0, STAT_WEAPON, (bits & SU_WEAPON)?MSG_ReadByte():0); + CL_SetStatInt(0, STAT_WEAPONFRAME, (bits & SU_WEAPONFRAME)?(unsigned char)MSG_ReadByte():0); + CL_SetStatInt(0, STAT_ARMOR, (bits & SU_ARMOR)?MSG_ReadByte():0); + CL_SetStatInt(0, STAT_WEAPON, (bits & SU_WEAPON)?MSG_ReadByte():0); - CL_SetStat(0, STAT_HEALTH, MSG_ReadShort()); + CL_SetStatInt(0, STAT_HEALTH, MSG_ReadShort()); - CL_SetStat(0, STAT_AMMO, MSG_ReadByte()); + CL_SetStatInt(0, STAT_AMMO, MSG_ReadByte()); - CL_SetStat(0, STAT_SHELLS, MSG_ReadByte()); - CL_SetStat(0, STAT_NAILS, MSG_ReadByte()); - CL_SetStat(0, STAT_ROCKETS, MSG_ReadByte()); - CL_SetStat(0, STAT_CELLS, MSG_ReadByte()); + CL_SetStatInt(0, STAT_SHELLS, MSG_ReadByte()); + CL_SetStatInt(0, STAT_NAILS, MSG_ReadByte()); + CL_SetStatInt(0, STAT_ROCKETS, MSG_ReadByte()); + CL_SetStatInt(0, STAT_CELLS, MSG_ReadByte()); - CL_SetStat(0, STAT_ACTIVEWEAPON, MSG_ReadByte()); + CL_SetStatInt(0, STAT_ACTIVEWEAPON, MSG_ReadByte()); } if (bits & DPSU_VIEWZOOM) @@ -2267,10 +2272,10 @@ void CLNQ_ParseClientdata (void) i = MSG_ReadByte(); if (i < 2) i = 2; - CL_SetStat(0, STAT_VIEWZOOM, i); + CL_SetStatInt(0, STAT_VIEWZOOM, i); } else if (nq_dp_protocol < 6) - CL_SetStat(0, STAT_VIEWZOOM, 255); + CL_SetStatInt(0, STAT_VIEWZOOM, 255); } #endif /* @@ -2613,11 +2618,10 @@ void CL_ParseBaseline2 (void) void CLQ2_Precache_f (void) { cl.sendprespawn = true; + Sound_NextDownload(); #ifdef VM_CG CG_Start(); #endif - - CL_RequestNextDownload(); } @@ -3186,7 +3190,7 @@ static void CL_SetStat_Internal (int pnum, int stat, int value) TP_StatChanged(stat, value); } -void CL_SetStat (int pnum, int stat, int value) +void CL_SetStatInt (int pnum, int stat, int value) { if (stat < 0 || stat >= MAX_CL_STATS) return; @@ -3213,7 +3217,46 @@ void CL_SetStat (int pnum, int stat, int value) else CL_SetStat_Internal(pnum, stat, value); } +void CL_SetStatFloat (int pnum, int stat, float value) +{ + if (stat < 0 || stat >= MAX_CL_STATS) + return; +// Host_EndGame ("CL_SetStat: %i is invalid", stat); + if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + { + extern int cls_lastto; + cl.players[cls_lastto].statsf[stat]=value; + + for (pnum = 0; pnum < cl.splitclients; pnum++) + if (spec_track[pnum] == cls_lastto) + cl.statsf[pnum][stat] = value; + } + else + cl.statsf[pnum][stat] = value; +} +void CL_SetStatString (int pnum, int stat, char *value) +{ + if (stat < 0 || stat >= MAX_CL_STATS) + return; +// Host_EndGame ("CL_SetStat: %i is invalid", stat); + + if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + { +/* extern int cls_lastto; + cl.players[cls_lastto].statsstr[stat]=value; + + for (pnum = 0; pnum < cl.splitclients; pnum++) + if (spec_track[pnum] == cls_lastto) + cl.statsstr[pnum][stat] = value;*/ + } + else + { + if (cl.statsstr[pnum][stat]) + Z_Free(cl.statsstr[pnum][stat]); + cl.statsstr[pnum][stat] = Z_Malloc(strlen(value)); + } +} /* ============== CL_MuzzleFlash @@ -4059,6 +4102,7 @@ void CL_ParseServerMessage (void) char *s; int i, j; int destsplit; + float f; received_framecount = host_framecount; cl.last_servermessage = realtime; @@ -4092,7 +4136,7 @@ void CL_ParseServerMessage (void) cmd = MSG_ReadByte (); - if (cmd == svc_choosesplitclient) + if (cmd == svcfte_choosesplitclient) { SHOWNET(svc_strings[cmd]); @@ -4234,7 +4278,7 @@ void CL_ParseServerMessage (void) cl_lightstyle[i].length = Q_strlen(cl_lightstyle[i].map); break; #ifdef PEXT_LIGHTSTYLECOL - case svc_lightstylecol: + case svcfte_lightstylecol: if (!(cls.fteprotocolextensions & PEXT_LIGHTSTYLECOL)) Host_EndGame("PEXT_LIGHTSTYLECOL is meant to be disabled\n"); i = MSG_ReadByte (); @@ -4291,7 +4335,7 @@ void CL_ParseServerMessage (void) Host_EndGame("CL_ParseServerMessage: svc_spawnbaseline failed with size %i", i); CL_ParseBaseline (cl_baselines + i); break; - case svc_spawnbaseline2: + case svcfte_spawnbaseline2: CL_ParseBaseline2 (); break; case svc_spawnstatic: @@ -4307,20 +4351,20 @@ void CL_ParseServerMessage (void) CL_ParseTEnt (); #endif break; - case svc_customtempent: + case svcfte_customtempent: CL_ParseCustomTEnt(); break; case svc_particle: CLNQ_ParseParticleEffect (); break; - case svc_particle2: + case svcfte_particle2: CL_ParseParticleEffect2 (); break; - case svc_particle3: + case svcfte_particle3: CL_ParseParticleEffect3 (); break; - case svc_particle4: + case svcfte_particle4: CL_ParseParticleEffect4 (); break; @@ -4335,12 +4379,26 @@ void CL_ParseServerMessage (void) case svc_updatestat: i = MSG_ReadByte (); j = MSG_ReadByte (); - CL_SetStat (destsplit, i, j); + CL_SetStatInt (destsplit, i, j); + CL_SetStatFloat (destsplit, i, j); break; case svc_updatestatlong: i = MSG_ReadByte (); j = MSG_ReadLong (); //make qbyte if nq compatability? - CL_SetStat (destsplit, i, j); + CL_SetStatInt (destsplit, i, j); + CL_SetStatFloat (destsplit, i, j); + break; + + case svcfte_updatestatstring: + i = MSG_ReadByte(); + s = MSG_ReadString(); + CL_SetStatString (destsplit, i, s); + break; + case svcfte_updatestatfloat: + i = MSG_ReadByte(); + f = MSG_ReadFloat(); + CL_SetStatInt (destsplit, i, f); + CL_SetStatFloat (destsplit, i, f); break; case svc_spawnstaticsound: @@ -4427,7 +4485,7 @@ void CL_ParseServerMessage (void) case svc_modellist: CL_ParseModellist (false); break; - case svc_modellistshort: + case svcfte_modellistshort: CL_ParseModellist (true); break; @@ -4462,7 +4520,7 @@ void CL_ParseServerMessage (void) break; #ifdef PEXT_BULLETENS - case svc_bulletentext: + case svcfte_bulletentext: if (!(cls.fteprotocolextensions & PEXT_BULLETENS)) Host_EndGame("PEXT_BULLETENS is meant to be disabled\n"); Bul_ParseMessage(); @@ -4489,32 +4547,32 @@ void CL_ParseServerMessage (void) break; #endif - case svc_showpic: + case svcfte_showpic: SCR_ShowPic_Create(); break; - case svc_hidepic: + case svcfte_hidepic: SCR_ShowPic_Hide(); break; - case svc_movepic: + case svcfte_movepic: SCR_ShowPic_Move(); break; - case svc_updatepic: + case svcfte_updatepic: SCR_ShowPic_Update(); break; - case svcqw_effect: + case svcfte_effect: CL_ParseEffect(false); break; - case svcqw_effect2: + case svcfte_effect2: CL_ParseEffect(true); break; #ifdef PEXT_CSQC - case svc_csqcentities: + case svcfte_csqcentities: CSQC_ParseEntities(); break; #endif - case svc_precache: + case svcfte_precache: CL_ParsePrecache(); break; } @@ -5062,12 +5120,14 @@ void CLNQ_ParseServerMessage (void) case svc_updatestat: i = MSG_ReadByte (); j = MSG_ReadLong (); - CL_SetStat (0, i, j); + CL_SetStatInt (0, i, j); + CL_SetStatFloat (0, i, j); break; case svcdp_updatestatbyte: i = MSG_ReadByte (); j = MSG_ReadByte (); - CL_SetStat (0, i, j); + CL_SetStatInt (0, i, j); + CL_SetStatFloat (0, i, j); break; case svc_setangle: for (i=0 ; i<3 ; i++) diff --git a/engine/client/client.h b/engine/client/client.h index 677e9cc33..dc5b35b29 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -164,6 +164,7 @@ typedef struct player_info_s int prevcount; int stats[MAX_CL_STATS]; + int statsf[MAX_CL_STATS]; } player_info_t; @@ -456,6 +457,8 @@ typedef struct // information for local display int stats[MAX_SPLITS][MAX_CL_STATS]; // health, etc + float statsf[MAX_SPLITS][MAX_CL_STATS]; // health, etc + char *statsstr[MAX_SPLITS][MAX_CL_STATS]; // health, etc float item_gettime[MAX_SPLITS][32]; // cl.time of aquiring item, for blinking float faceanimtime[MAX_SPLITS]; // use anim frame if cl.time < this diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index c560b19d3..1cca2e3cc 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -1,5 +1,19 @@ #include "quakedef.h" +/* + + EXT_CSQC is the 'root' extension + EXT_CSQC_1 are a collection of additional features to cover omissions in the original spec + + + note the CHEAT_PARANOID define disables certain EXT_CSQC_1 features, + in an attempt to prevent the player from finding out where he/she is, thus preventing aimbots. + This is specifically targetted towards deathmatch mods where each player is a single player. + In reality, this paranoia provides nothing which could not be done with a cheat proxy. + Seeing as the client ensures hashes match in the first place, this paranoia gives nothing in the long run. + Unfortunatly EXT_CSQC was designed around this paranoia. +*/ + #ifdef CSQC_DAT #ifdef RGLQUAKE @@ -7,8 +21,12 @@ #include "shader.h" #endif +//#define CHEAT_PARANOID + #include "pr_common.h" +#define ANGLE2SHORT(x) ((x/360.0)*65535) + static progfuncs_t *csqcprogs; typedef struct csqctreadstate_s { @@ -34,8 +52,8 @@ cvar_t cl_nocsqc = SCVAR("cl_nocsqc", "0"); cvar_t pr_csqc_coreonerror = SCVAR("pr_csqc_coreonerror", "1"); -#define MASK_ENGINE 1 -#define MASK_ENGINEVIEWMODEL 2 +#define MASK_DELTA 1 +#define MASK_STDVIEWMODEL 2 // standard effect cvars/sounds @@ -51,20 +69,25 @@ extern sfx_t *cl_sfx_r_exp3; //If I do it like this, I'll never forget to register something... #define csqcglobals \ globalfunction(init_function, "CSQC_Init"); \ + globalfunction(worldloaded, "CSQC_WorldLoaded"); \ globalfunction(shutdown_function, "CSQC_Shutdown"); \ globalfunction(draw_function, "CSQC_UpdateView"); \ globalfunction(parse_stuffcmd, "CSQC_Parse_StuffCmd"); \ globalfunction(parse_centerprint, "CSQC_Parse_CenterPrint"); \ globalfunction(input_event, "CSQC_InputEvent"); \ + globalfunction(input_frame, "CSQC_Input_Frame");/*EXT_CSQC_1*/ \ globalfunction(console_command, "CSQC_ConsoleCommand"); \ \ globalfunction(ent_update, "CSQC_Ent_Update"); \ globalfunction(ent_remove, "CSQC_Ent_Remove"); \ + globalfunction(delta_update, "CSQC_Delta_Update");/*EXT_CSQC_1*/ \ + globalfunction(delta_remove, "CSQC_Delta_Remove");/*EXT_CSQC_1*/ \ \ globalfunction(serversound, "CSQC_ServerSound"); \ \ /*These are pointers to the csqc's globals.*/ \ globalfloat(time, "time"); /*float Written before entering most qc functions*/ \ + globalfloat(simtime, "simtime"); /*float Written before entering most qc functions*/ \ globalentity(self, "self"); /*entity Written before entering most qc functions*/ \ globalentity(other, "other"); /*entity Written before entering most qc functions*/ \ \ @@ -84,14 +107,15 @@ extern sfx_t *cl_sfx_r_exp3; globalfloat(trace_plane_dist, "trace_plane_dist"); /*float written by traceline*/ \ globalentity(trace_ent, "trace_ent"); /*entity written by traceline*/ \ globalfloat(trace_surfaceflags, "trace_surfaceflags"); /*float written by traceline*/ \ - globalfloat(trace_endcontents, "trace_endcontents"); /*float written by traceline*/ \ + globalfloat(trace_endcontents, "trace_endcontents"); /*float written by traceline EXT_CSQC_1*/ \ \ globalfloat(clientcommandframe, "clientcommandframe"); /*float the next frame that will be sent*/ \ globalfloat(servercommandframe, "servercommandframe"); /*float the most recent frame received from the server*/ \ \ globalfloat(player_localentnum, "player_localentnum"); /*float the entity number of the local player*/ \ + globalfloat(player_localnum, "player_localnum"); /*float the entity number of the local player*/ \ globalfloat(intermission, "intermission"); /*float set when the client receives svc_intermission*/ \ - globalvector(view_angles, "view_angles"); /*float set to the view angles at the start of each new frame */ \ + globalvector(view_angles, "view_angles"); /*float set to the view angles at the start of each new frame (EXT_CSQC_1)*/ \ \ globalvector(pmove_org, "pmove_org"); /*read/written by runplayerphysics*/ \ globalvector(pmove_vel, "pmove_vel"); /*read/written by runplayerphysics*/ \ @@ -104,6 +128,10 @@ extern sfx_t *cl_sfx_r_exp3; globalvector(input_angles, "input_angles"); /*vector filled by getinputstate, read by runplayerphysics*/ \ globalvector(input_movevalues, "input_movevalues"); /*vector filled by getinputstate, read by runplayerphysics*/ \ globalfloat(input_buttons, "input_buttons"); /*float filled by getinputstate, read by runplayerphysics*/ \ + globalfloat(input_impulse, "input_impulse"); /*float filled by getinputstate, read by runplayerphysics*/ \ + globalfloat(input_lightlevel, "input_lightlevel"); /*float filled by getinputstate, read by runplayerphysics*/ \ + globalfloat(input_weapon, "input_weapon"); /*float filled by getinputstate, read by runplayerphysics*/ \ + globalfloat(input_servertime, "input_servertime"); /*float filled by getinputstate, read by runplayerphysics*/ \ \ globalfloat(movevar_gravity, "movevar_gravity"); /*float obtained from the server*/ \ globalfloat(movevar_stopspeed, "movevar_stopspeed"); /*float obtained from the server*/ \ @@ -156,9 +184,13 @@ static void CSQC_FindGlobals(void) if (csqcg.time) *csqcg.time = Sys_DoubleTime(); + if (csqcg.simtime) + *csqcg.simtime = cl.servertime; if (csqcg.player_localentnum) *csqcg.player_localentnum = cl.playernum[plnum]+1; + if (csqcg.player_localnum) + *csqcg.player_localnum = cl.playernum[plnum]; if (csqcg.maxclients) *csqcg.maxclients = MAX_CLIENTS; @@ -181,12 +213,12 @@ static void CSQC_FindGlobals(void) fieldfloat(colormap); \ fieldfloat(flags); \ fieldfloat(frame); \ - fieldfloat(frame2); \ - fieldfloat(frame1time); \ - fieldfloat(frame2time); \ - fieldfloat(lerpfrac); \ + fieldfloat(frame2); /*EXT_CSQC_1*/\ + fieldfloat(frame1time); /*EXT_CSQC_1*/\ + fieldfloat(frame2time); /*EXT_CSQC_1*/\ + fieldfloat(lerpfrac); /*EXT_CSQC_1*/\ fieldfloat(renderflags);\ - fieldfloat(forceshader);\ + fieldfloat(forceshader);/*FTE_CSQC_SHADERS*/\ fieldfloat(dimension_hit); \ fieldfloat(dimension_solid); \ \ @@ -264,6 +296,9 @@ static int maxcsqcentities; static int csqcentsize; +static char *csqcmapentitydata; +static qboolean csqcmapentitydataloaded; + static model_t *CSQC_GetModelForIndex(int index); static void CS_LinkEdict(csqcedict_t *ent, qboolean touchtriggers); @@ -844,14 +879,19 @@ static void PF_R_AddEntityMask(progfuncs_t *prinst, struct globalvars_s *pr_glob } } - if (mask & MASK_ENGINE && cl.worldmodel) + if (cl.worldmodel) { - if (mask & MASK_ENGINEVIEWMODEL) + if (mask & MASK_STDVIEWMODEL) + { CL_LinkViewModel (); - CL_LinkPlayers (); - CL_LinkPacketEntities (); - CL_LinkProjectiles (); - CL_UpdateTEnts (); + } + if (mask & MASK_DELTA) + { + CL_LinkPlayers (); + CL_LinkPacketEntities (); + CL_LinkProjectiles (); + CL_UpdateTEnts (); + } } } @@ -1107,6 +1147,108 @@ static void PF_R_SetViewFlag(progfuncs_t *prinst, struct globalvars_s *pr_global } } +static void PF_R_GetViewFlag(progfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + viewflags parametertype = G_FLOAT(OFS_PARM0); + float *r = G_VECTOR(OFS_RETURN); + + r[0] = 0; + r[1] = 0; + r[2] = 0; + + switch(parametertype) + { + case VF_FOV: + r[0] = r_refdef.fov_x; + r[1] = r_refdef.fov_y; + break; + + case VF_FOVX: + *r = r_refdef.fov_x; + break; + + case VF_FOVY: + *r = r_refdef.fov_y; + break; + + case VF_ORIGIN: +#ifdef CHEAT_PARANOID + VectorClear(r); +#else + VectorCopy(r_refdef.vieworg, r); +#endif + break; + + case VF_ORIGIN_Z: + case VF_ORIGIN_X: + case VF_ORIGIN_Y: +#ifdef CHEAT_PARANOID + *r = 0; +#else + *r = r_refdef.vieworg[parametertype-VF_ORIGIN_X]; +#endif + break; + + case VF_ANGLES: + VectorCopy(r_refdef.viewangles, r); + break; + case VF_ANGLES_X: + case VF_ANGLES_Y: + case VF_ANGLES_Z: + *r = r_refdef.viewangles[parametertype-VF_ANGLES_X]; + break; + + case VF_CARTESIAN_ANGLES: + Con_Printf(CON_WARNING "WARNING: CARTESIAN ANGLES ARE NOT YET SUPPORTED!\n"); + break; + + case VF_VIEWPORT: + r[0] = r_refdef.vrect.width; + r[1] = r_refdef.vrect.height; + break; + + case VF_SIZE_X: + *r = r_refdef.vrect.width; + break; + case VF_SIZE_Y: + *r = r_refdef.vrect.height; + break; + case VF_SIZE: + r[0] = r_refdef.vrect.width; + r[1] = r_refdef.vrect.height; + break; + + case VF_MIN_X: + *r = r_refdef.vrect.x; + break; + case VF_MIN_Y: + *r = r_refdef.vrect.y; + break; + case VF_MIN: + r[0] = r_refdef.vrect.x; + r[1] = r_refdef.vrect.y; + break; + + case VF_DRAWWORLD: + *r = !(r_refdef.flags&Q2RDF_NOWORLDMODEL);; + break; + case VF_ENGINESBAR: + *r = csqc_drawsbar; + break; + case VF_DRAWCROSSHAIR: + *r = csqc_addcrosshair; + break; + + case VF_PERSPECTIVE: + *r = r_refdef.useperspective; + break; + + default: + Con_DPrintf("GetViewFlag: %i not recognised\n", parametertype); + break; + } +} + static void PF_R_RenderScene(progfuncs_t *prinst, struct globalvars_s *pr_globals) { if (cl.worldmodel) @@ -1441,11 +1583,17 @@ static void csqc_setmodel(progfuncs_t *prinst, csqcedict_t *ent, int modelindex) ent->v->modelindex = modelindex; if (modelindex < 0) { + if (modelindex <= -MAX_MODELS) + return; ent->v->model = PR_SetString(prinst, cl.model_csqcname[-modelindex]); + if (!cl.model_csqcprecache[-modelindex]) + cl.model_csqcprecache[-modelindex] = Mod_ForName(cl.model_csqcname[-modelindex], false); model = cl.model_csqcprecache[-modelindex]; } else { + if (modelindex >= MAX_MODELS) + return; ent->v->model = PR_SetString(prinst, cl.model_name[modelindex]); model = cl.model_precache[modelindex]; } @@ -1476,7 +1624,7 @@ static void PF_cs_SetModel(progfuncs_t *prinst, struct globalvars_s *pr_globals) Q_strncpyz(cl.model_csqcname[-freei], modelname, sizeof(cl.model_csqcname[-freei])); //allocate a slot now modelindex = freei; - cl.model_csqcprecache[-freei] = Mod_ForName(cl.model_csqcname[-freei], false); + cl.model_csqcprecache[-freei] = NULL; } csqc_setmodel(prinst, ent, modelindex); @@ -1514,7 +1662,8 @@ static void PF_cs_PrecacheModel(progfuncs_t *prinst, struct globalvars_s *pr_glo Q_strncpyz(cl.model_csqcname[-freei], modelname, sizeof(cl.model_csqcname[-freei])); //allocate a slot now modelindex = freei; - cl.model_csqcprecache[-freei] = Mod_ForName(cl.model_csqcname[-freei], false); + CL_CheckOrEnqueDownloadFile(modelname, modelname); + cl.model_csqcprecache[-freei] = NULL; } G_FLOAT(OFS_RETURN) = modelindex; @@ -1550,6 +1699,20 @@ static void PF_ReadShort(progfuncs_t *prinst, struct globalvars_s *pr_globals) G_FLOAT(OFS_RETURN) = MSG_ReadShort(); } +static void PF_ReadEntityNum(progfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + unsigned short val; + val = MSG_ReadShort(); + if (val & 0x8000) + { //our protocol only supports 15bits of revelent entity number (16th bit is used as 'remove'). + //so warn with badly coded mods. + Con_Printf("ReadEntityNumber read bad entity number\n"); + G_FLOAT(OFS_RETURN) = 0; + } + else + G_FLOAT(OFS_RETURN) = val; +} + static void PF_ReadLong(progfuncs_t *prinst, struct globalvars_s *pr_globals) { G_FLOAT(OFS_RETURN) = MSG_ReadLong(); @@ -1645,6 +1808,64 @@ static void PF_cs_particlesloaded (progfuncs_t *prinst, struct globalvars_s *pr_ G_FLOAT(OFS_RETURN) = P_DescriptionIsLoaded(effectname); } +static void cs_set_input_state (usercmd_t *cmd) +{ + if (csqcg.input_timelength) + *csqcg.input_timelength = cmd->msec/1000.0f; + if (csqcg.input_angles) + { + csqcg.input_angles[0] = SHORT2ANGLE(cmd->angles[0]+0.5); + csqcg.input_angles[1] = SHORT2ANGLE(cmd->angles[1]+0.5); + csqcg.input_angles[2] = SHORT2ANGLE(cmd->angles[2]+0.5); + } + if (csqcg.input_movevalues) + { + csqcg.input_movevalues[0] = cmd->forwardmove; + csqcg.input_movevalues[1] = cmd->sidemove; + csqcg.input_movevalues[2] = cmd->upmove; + } + if (csqcg.input_buttons) + *csqcg.input_buttons = cmd->buttons; + + if (csqcg.input_impulse) + *csqcg.input_impulse = cmd->impulse; + if (csqcg.input_lightlevel) + *csqcg.input_lightlevel = cmd->lightlevel; + if (csqcg.input_weapon) + *csqcg.input_weapon = cmd->weapon; + if (csqcg.input_servertime) + *csqcg.input_servertime = cmd->servertime/1000.0f; +} + +static void cs_get_input_state (usercmd_t *cmd) +{ + if (csqcg.input_timelength) + cmd->msec = *csqcg.input_timelength*1000; + if (csqcg.input_angles) + { + cmd->angles[0] = ANGLE2SHORT(csqcg.input_angles[0]); + cmd->angles[1] = ANGLE2SHORT(csqcg.input_angles[1]); + cmd->angles[2] = ANGLE2SHORT(csqcg.input_angles[2]); + } + if (csqcg.input_movevalues) + { + cmd->forwardmove = csqcg.input_movevalues[0]; + cmd->sidemove = csqcg.input_movevalues[1]; + cmd->upmove = csqcg.input_movevalues[2]; + } + if (csqcg.input_buttons) + cmd->buttons = *csqcg.input_buttons; + + if (csqcg.input_impulse) + cmd->impulse = *csqcg.input_impulse; + if (csqcg.input_lightlevel) + cmd->lightlevel = *csqcg.input_lightlevel; + if (csqcg.input_weapon) + cmd->weapon = *csqcg.input_weapon; + if (csqcg.input_servertime) + cmd->servertime = *csqcg.input_servertime*1000; +} + //get the input commands, and stuff them into some globals. static void PF_cs_getinputstate (progfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -1666,26 +1887,11 @@ static void PF_cs_getinputstate (progfuncs_t *prinst, struct globalvars_s *pr_gl // save this command off for prediction cmd = &cl.frames[f&UPDATE_MASK].cmd[plnum]; - if (csqcg.input_timelength) - *csqcg.input_timelength = cmd->msec/1000.0f; - if (csqcg.input_angles) - { - csqcg.input_angles[0] = SHORT2ANGLE(cmd->angles[0]+0.5); - csqcg.input_angles[1] = SHORT2ANGLE(cmd->angles[1]+0.5); - csqcg.input_angles[2] = SHORT2ANGLE(cmd->angles[2]+0.5); - } - if (csqcg.input_movevalues) - { - csqcg.input_movevalues[0] = cmd->forwardmove; - csqcg.input_movevalues[1] = cmd->sidemove; - csqcg.input_movevalues[2] = cmd->upmove; - } - if (csqcg.input_buttons) - *csqcg.input_buttons = cmd->buttons; + cs_set_input_state(cmd); G_FLOAT(OFS_RETURN) = true; } -#define ANGLE2SHORT(x) ((x/360.0)*65535) + //read lots of globals, run the default player physics, write lots of globals. static void PF_cs_runplayerphysics (progfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -1796,6 +2002,20 @@ typedef struct { VectorCopy(pmove.velocity, csqcg.pmove_vel); } +static void PF_cs_getentitytoken (progfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + if (!csqcmapentitydata) + { + //nothing more to parse + G_INT(OFS_RETURN) = 0; + } + else + { + csqcmapentitydata = COM_ParseToken(csqcmapentitydata, "{}()\'\":,"); + RETURN_TSTRING(com_token); + } +} + static void CheckSendPings(void) { //quakeworld sends a 'pings' client command to retrieve the frequently updating stuff if (realtime - cl.last_ping_request > 2) @@ -2991,6 +3211,188 @@ static void PF_cs_setlistener (progfuncs_t *prinst, struct globalvars_s *pr_glob S_Update(origin, forward, right, up); } +typedef struct oldcsqcpack_s +{ + unsigned int numents; + unsigned int maxents; + unsigned short *entnum; + csqcedict_t **entptr; +} oldcsqcpack_t; +static oldcsqcpack_t loadedcsqcpack[2]; +static int loadedcsqcpacknum; + +#define RSES_NOLERP 1 +#define RSES_AUTOROTATE 2 +packet_entities_t *CL_ProcessPacketEntities(float *servertime, qboolean nolerp); +static void PF_ReadServerEntityState(progfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + //read the arguments the csqc gave us + unsigned int flags = G_FLOAT(OFS_PARM0); + float servertime = G_FLOAT(OFS_PARM1); + + //locals + packet_entities_t *pack; + csqcedict_t *ent; + entity_state_t *src; + unsigned int i; + lerpents_t *le; + csqcedict_t *oldent; + oldcsqcpack_t *oldlist, *newlist; + int oldidx = 0, newidx = 0; + model_t *model; + + //setup + servertime += cl.servertime; + pack = CL_ProcessPacketEntities(&servertime, (flags & RSES_NOLERP)); + if (!pack) + return; //can't do anything, just don't update + + oldlist = &loadedcsqcpack[loadedcsqcpacknum]; + loadedcsqcpacknum ^= 1; + newlist = &loadedcsqcpack[loadedcsqcpacknum]; + newlist->numents = 0; + + for (i = 0; i < pack->num_entities; i++) + { + src = &pack->entities[i]; +// CL_LinkPacketEntities + +#ifndef _MSC_VER +#warning what to do here? +#endif +// if (csqcent[src->number]) +// continue; //don't add the entity if we have one sent specially via csqc protocols. + + if (oldidx == oldlist->numents) + { //reached the end of the old frame's ents + oldent = NULL; + } + else + { + while (oldidx < oldlist->numents && oldlist->entnum[oldidx] < src->number) + { + //this entity is stale, remove it. + oldent = oldlist->entptr[oldidx]; + *csqcg.self = EDICT_TO_PROG(prinst, (void*)oldent); + PR_ExecuteProgram(prinst, csqcg.delta_remove); + oldidx++; + } + + if (src->number < oldlist->entnum[oldidx]) + oldent = NULL; + else + { + oldent = oldlist->entptr[oldidx]; + oldidx++; + } + } + + //note: we don't delta the state here. we just replace the old. + //its already lerped + + if (oldent) + ent = oldent; + else + ent = (csqcedict_t *)ED_Alloc(prinst); + + le = &cl.lerpents[src->number]; + + //frames needs special handling + ent->v->frame = src->frame; + ent->v->frame2 = le->frame; + if (le->framechange == le->oldframechange) + ent->v->lerpfrac = 0; + else + { + ent->v->lerpfrac = 1-(servertime - le->framechange) / (le->framechange - le->oldframechange); + if (ent->v->lerpfrac > 1) + ent->v->lerpfrac = 1; + else if (ent->v->lerpfrac < 0) + { + ent->v->lerpfrac = 0; + } + } + + + ent->v->entnum = src->number; + ent->v->modelindex = src->modelindex; + model = cl.model_precache[src->modelindex]; +// ent->v->bitmask = src->bitmask; + ent->v->flags = src->flags; +// ent->v->effects = src->effects; + ent->v->origin[0] = src->origin[0]; + ent->v->origin[1] = src->origin[1]; + ent->v->origin[2] = src->origin[2]; + ent->v->angles[0] = src->angles[0]; + ent->v->angles[1] = src->angles[1]; + ent->v->angles[2] = src->angles[2]; + + //we ignore the q2 state fields + + ent->v->colormap = src->colormap; + ent->v->skin = src->skinnum; +// ent->v->glowsize = src->glowsize; +// ent->v->glowcolor = src->glowcolour; + ent->v->scale = src->scale/16.0f; + ent->v->fatness = src->fatness/16.0f; +// ent->v->hexen2flags = src->hexen2flags; +// ent->v->abslight = src->abslight; +// ent->v->dpflags = src->dpflags; +// ent->v->colormod[0] = (src->colormod[0]/255.0f)*8; +// ent->v->colormod[1] = (src->colormod[1]/255.0f)*8; +// ent->v->colormod[2] = (src->colormod[2]/255.0f)*8; + ent->v->alpha = src->trans/255.0f; +// ent->v->lightstyle = src->lightstyle; +// ent->v->lightpflags = src->lightpflags; +// ent->v->solid = src->solid; +// ent->v->light[0] = src->light[0]; +// ent->v->light[1] = src->light[1]; +// ent->v->light[2] = src->light[2]; +// ent->v->light[3] = src->light[3]; +// ent->v->tagentity = src->tagentity; +// ent->v->tagindex = src->tagindex; + + if (model) + { + if (model->flags & EF_ROTATE) + { + ent->v->angles[0] = 0; + ent->v->angles[1] = 100*servertime; + ent->v->angles[2] = 0; + } + } + + if (csqcg.delta_update) + { + *csqcg.self = EDICT_TO_PROG(prinst, (void*)ent); + G_FLOAT(OFS_PARM0) = !oldent; + PR_ExecuteProgram(prinst, csqcg.delta_update); + } + + if (newlist->maxents <= newidx) + { + newlist->maxents = newidx + 64; + newlist->entptr = BZ_Realloc(newlist->entptr, sizeof(*newlist->entptr)*newlist->maxents); + newlist->entnum = BZ_Realloc(newlist->entnum, sizeof(*newlist->entnum)*newlist->maxents); + } + newlist->entptr[newidx] = ent; + newlist->entnum[newidx] = src->number; + newidx++; + + } + + //remove any unreferenced ents stuck on the end + while (oldidx < oldlist->numents) + { + oldent = oldlist->entptr[oldidx]; + *csqcg.self = EDICT_TO_PROG(prinst, (void*)oldent); + PR_ExecuteProgram(prinst, csqcg.delta_remove); + oldidx++; + } + + newlist->numents = newidx; +} + #define PF_FixTen PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme //prefixes: @@ -3271,7 +3673,7 @@ PF_R_PolygonBegin, // #306 void(string texturename) R_BeginPolygon (EXT_CSQC_ PF_R_PolygonVertex, // #307 void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex (EXT_CSQC_???) PF_R_PolygonEnd, // #308 void() R_EndPolygon (EXT_CSQC_???) -PF_Fixme, // #309 +PF_R_GetViewFlag, // #309 vector/float(float property) getproperty (EXT_CSQC_1) //310 //maths stuff that uses the current view settings. @@ -3339,7 +3741,7 @@ PF_cs_registercommand, // #352 void(string cmdname) registercommand (EXT_CSQC) PF_WasFreed, // #353 float(entity ent) wasfreed (EXT_CSQC) (should be availabe on server too) PF_cs_serverkey, // #354 string(string key) serverkey; -PF_Fixme, // #355 +PF_cs_getentitytoken, // #355 string() getentitytoken; PF_Fixme, // #356 PF_Fixme, // #357 PF_Fixme, // #358 @@ -3357,8 +3759,8 @@ PF_ReadAngle, // #365 float() readangle (EXT_CSQC) PF_ReadString, // #366 string() readstring (EXT_CSQC) PF_ReadFloat, // #367 string() readfloat (EXT_CSQC) -PF_Fixme, // #368 -PF_Fixme, // #369 +PF_ReadEntityNum, // #368 float() readentitynum (EXT_CSQC) +PF_ReadServerEntityState, // #369 void(float flags, float simtime) readserverentitystate (EXT_CSQC_1) //370 PF_FixTen, @@ -3622,6 +4024,8 @@ int CSQC_PRFileSize (char *path) double csqctime; qboolean CSQC_Init (unsigned int checksum) { + int i; + csqcedict_t *worldent; csqcchecksum = checksum; CSQC_Shutdown(); @@ -3677,6 +4081,7 @@ qboolean CSQC_Init (unsigned int checksum) if (!csqcprogs) { in_sensitivityscale = 1; + csqcmapentitydataloaded = true; csqcprogs = InitProgs(&csqcprogparms); PR_Configure(csqcprogs, -1, 16); @@ -3707,11 +4112,32 @@ qboolean CSQC_Init (unsigned int checksum) ED_Alloc(csqcprogs); //we need a word entity. //world edict becomes readonly - EDICT_NUM(csqcprogs, 0)->readonly = true; - EDICT_NUM(csqcprogs, 0)->isfree = false; + worldent = (csqcedict_t *)EDICT_NUM(csqcprogs, 0); + + worldent->readonly = true; + worldent->isfree = false; + worldent->v->model = PR_SetString(csqcprogs, cl.model_name[1]); + for (i = 0; i < 2; i++) + { + loadedcsqcpack[i].numents = 0; + loadedcsqcpack[i].maxents = 0; + Z_Free(loadedcsqcpack[i].entptr); + loadedcsqcpack[i].entptr = NULL; + Z_Free(loadedcsqcpack[i].entnum); + loadedcsqcpack[i].entnum = NULL; + } + + csqcmapentitydata = NULL; + csqcmapentitydataloaded = false; if (csqcg.init_function) + { + void *pr_globals = PR_globals(csqcprogs, PR_CURRENT); + G_FLOAT(OFS_PARM0) = 1.0; //api version + (((string_t *)pr_globals)[OFS_PARM1] = PR_TempString(csqcprogs, FULLENGINENAME)); + G_FLOAT(OFS_PARM2) = build_number(); PR_ExecuteProgram(csqcprogs, csqcg.init_function); + } Con_Printf("Loaded csqc\n"); } @@ -3719,6 +4145,19 @@ qboolean CSQC_Init (unsigned int checksum) return true; //success! } +void CSQC_WorldLoaded(void) +{ + if (!csqcprogs) + return; + if (csqcmapentitydataloaded) + return; + csqcmapentitydataloaded = true; + csqcmapentitydata = cl.worldmodel->entities; + if (csqcg.worldloaded) + PR_ExecuteProgram(csqcprogs, csqcg.worldloaded); + csqcmapentitydata = NULL; +} + void CSQC_CoreDump(void) { if (!csqcprogs) @@ -3781,6 +4220,13 @@ qboolean CSQC_DrawView(void) CSQC_RunThreads(); //wake up any qc threads + //EXT_CSQC_1 + { + void *pr_globals = PR_globals(csqcprogs, PR_CURRENT); + G_FLOAT(OFS_PARM0) = vid.width; + G_FLOAT(OFS_PARM1) = vid.height; + } + //end EXT_CSQC_1 PR_ExecuteProgram(csqcprogs, csqcg.draw_function); return true; @@ -3857,6 +4303,21 @@ qboolean CSQC_CenterPrint(char *cmd) return G_FLOAT(OFS_RETURN); } +void CSQC_Input_Frame(usercmd_t *cmd) +{ + if (!csqcprogs || !csqcg.input_frame) + return; + + if (csqcg.time) + *csqcg.time = Sys_DoubleTime(); + if (csqcg.clientcommandframe) + *csqcg.clientcommandframe = cls.netchan.outgoing_sequence; + + cs_set_input_state(cmd); + PR_ExecuteProgram (csqcprogs, csqcg.input_frame); + cs_get_input_state(cmd); +} + //this protocol allows up to 32767 edicts. #ifdef PEXT_CSQC void CSQC_EntityCheck(int entnum) @@ -3867,7 +4328,7 @@ void CSQC_EntityCheck(int entnum) { newmax = entnum+64; csqcent = BZ_Realloc(csqcent, sizeof(*csqcent)*newmax); - memset(csqcent + maxcsqcentities, 0, newmax - maxcsqcentities); + memset(csqcent + maxcsqcentities, 0, (newmax - maxcsqcentities)*sizeof(csqcent)); maxcsqcentities = newmax; } } diff --git a/engine/client/skin.c b/engine/client/skin.c index 583c943a3..679319d38 100644 --- a/engine/client/skin.c +++ b/engine/client/skin.c @@ -615,3 +615,4 @@ int i; } } + diff --git a/engine/client/zqtp.c b/engine/client/zqtp.c index 16417c7a9..81b1343c3 100644 --- a/engine/client/zqtp.c +++ b/engine/client/zqtp.c @@ -1526,7 +1526,7 @@ char *TP_LocationName (vec3_t location) } recursive = true; - Cmd_ExpandString (locdata[minnum].name, buf, sizeof(buf), Cmd_ExecLevel, false); + Cmd_ExpandString (locdata[minnum].name, buf, sizeof(buf), Cmd_ExecLevel, true, false); recursive = false; return buf; @@ -3217,7 +3217,7 @@ void CL_Say (qboolean team, char *extra) !strchr(s, '\x0d') /* explicit $\ in message overrides cl_fakename */) { char buf[1024]; - Cmd_ExpandString (cl_fakename.string, buf, sizeof(buf), Cmd_ExecLevel, true); + Cmd_ExpandString (cl_fakename.string, buf, sizeof(buf), Cmd_ExecLevel, true, true); strcpy (buf, TP_ParseMacroString (buf)); Q_snprintfz (sendtext, sizeof(sendtext), "\x0d%s: ", TP_ParseFunChars(buf, true)); } diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 58b9d1d83..2e3233178 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -1150,7 +1150,7 @@ If not SERVERONLY, also expands $macro expressions Note: dest must point to a 1024 byte buffer ================ */ -char *Cmd_ExpandString (char *data, char *dest, int destlen, int maxaccesslevel, qboolean expandmacros) +char *Cmd_ExpandString (char *data, char *dest, int destlen, int maxaccesslevel, qboolean expandcvars, qboolean expandmacros) { unsigned int c; char buf[255]; @@ -1341,7 +1341,7 @@ void Cmd_TokenizeString (char *text, qboolean expandmacros, qboolean qctokenize) while (1) { -// skip whitespace up to a /n +// skip whitespace up to a \n while (*text && (unsigned)*text <= ' ' && *text != '\n') { text++; @@ -1838,7 +1838,7 @@ void Cmd_ExecuteString (char *text, int level) Cmd_ExecLevel = level; - text = Cmd_ExpandString(text, dest, sizeof(dest), level, !Cmd_IsInsecure()?true:false); + text = Cmd_ExpandString(text, dest, sizeof(dest), level, !Cmd_IsInsecure()?true:false, true); Cmd_TokenizeString (text, level == RESTRICT_LOCAL?true:false, false); // execute the command line diff --git a/engine/common/cmd.h b/engine/common/cmd.h index 2676cbc49..87e1b61a1 100644 --- a/engine/common/cmd.h +++ b/engine/common/cmd.h @@ -141,7 +141,7 @@ void Cmd_MessageTrigger (char *message, int type); void Cmd_ShiftArgs (int ammount, qboolean expandstring); -char *Cmd_ExpandString (char *data, char *dest, int destlen, int maxaccesslevel, qboolean expandmacros); +char *Cmd_ExpandString (char *data, char *dest, int destlen, int maxaccesslevel, qboolean expandcvars, qboolean expandmacros); extern cvar_t rcon_level; diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 4949f4036..a93dd4b89 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -17,7 +17,8 @@ #endif extern cvar_t gl_part_flame, r_fullbrightSkins, r_fb_models; -extern cvar_t r_noaliasshadows; +extern cvar_t r_noaliasshadows; +extern cvar_t r_skin_overlays; extern cvar_t mod_md3flags; @@ -1096,7 +1097,11 @@ void Mod_LoadSkinFile(galiastexnum_t *texnum, char *surfacename, int skinnumber, texnum->shader = R_RegisterSkin(shadername); #endif - texnum->base = Mod_LoadHiResTexture(shadername, "models", true, true, true); + texnum->base = Mod_LoadHiResTexture(shadername, "models", true, true, true); + + //13/4/08 IMPLEMENTME + texnum->loweroverlay = 0; + texnum->upperoverlay = 0; } #endif @@ -1437,7 +1442,19 @@ static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, qboolean alpha) texnums->base = texture; texnums->fullbright = fbtexture; - texnums->bump = bumptexture; + texnums->bump = bumptexture; + + //13/4/08 IMPLEMENTME + if (r_skin_overlays.value) + { + snprintf(skinname, sizeof(skinname), "%s_%i", loadname, i); + texture = R_LoadTexture8(skinname, outskin->skinwidth, outskin->skinheight, saved, true, alpha); + } + else + { + texnums->loweroverlay = 0; + texnums->upperoverlay = 0; + } pskintype = (daliasskintype_t *)((char *)(pskintype+1)+s); break; @@ -1523,7 +1540,11 @@ static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, qboolean alpha) #endif texnums->base = texture; - texnums->fullbright = fbtexture; + texnums->fullbright = fbtexture; + + //13/4/08 IMPLEMENTME + texnums->loweroverlay = 0; + texnums->upperoverlay = 0; } pskintype = (daliasskintype_t *)data; break; @@ -2651,7 +2672,11 @@ qboolean Mod_LoadQ3Model(model_t *mod, void *buffer) texnum->fullbright = Mod_LoadBumpmapTexture(name, "models"); } } -#endif +#endif + + //13/4/08 IMPLEMENTME + texnum->loweroverlay = 0; + texnum->upperoverlay = 0; inshader++; skin++; diff --git a/engine/common/com_mesh.h b/engine/common/com_mesh.h index 769f3b6ec..8549c4928 100644 --- a/engine/common/com_mesh.h +++ b/engine/common/com_mesh.h @@ -101,7 +101,9 @@ typedef struct { typedef struct { int base; int bump; - int fullbright; + int fullbright; + int upperoverlay; + int loweroverlay; #ifdef Q3SHADERS shader_t *shader; diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index 310820f00..d25c51cea 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -1046,6 +1046,9 @@ void *Mod_LoadWall(char *name) COM_FileBase(name, ln, sizeof(ln)); + if (!CL_CheckOrEnqueDownloadFile(name, NULL)) + return NULL; + wal = (void *)COM_LoadMallocFile (name); if (!wal) return NULL; diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 589a12a7d..004910ba0 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -207,49 +207,52 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //FTE extended svcs #ifdef PEXT_VIEW2 -#define svc_view2 56 +//#define svcfte_view2 56 #endif #ifdef PEXT_LIGHTSTYLECOL -#define svc_lightstylecol 57 +#define svcfte_lightstylecol 57 #endif #ifdef PEXT_BULLETENS -#define svc_bulletentext 58 +#define svcfte_bulletentext 58 #endif #ifdef PEXT_LIGHTUPDATES -#define svc_lightnings 59 +#define svcfte_lightnings 59 #endif #ifdef PEXT_MODELDBL -#define svc_modellistshort 60 // [strings] +#define svcfte_modellistshort 60 // [strings] #endif #define svc_ftesetclientpersist 61 //ushort DATA #define svc_setportalstate 62 -#define svc_particle2 63 -#define svc_particle3 64 -#define svc_particle4 65 -#define svc_spawnbaseline2 66 +#define svcfte_particle2 63 +#define svcfte_particle3 64 +#define svcfte_particle4 65 +#define svcfte_spawnbaseline2 66 -#define svc_customtempent 67 +#define svcfte_customtempent 67 -#define svc_choosesplitclient 68 -#define svc_showpic 69 -#define svc_hidepic 70 -#define svc_movepic 71 -#define svc_updatepic 72 +#define svcfte_choosesplitclient 68 +#define svcfte_showpic 69 +#define svcfte_hidepic 70 +#define svcfte_movepic 71 +#define svcfte_updatepic 72 -#define svcqw_effect 74 // [vector] org [byte] modelindex [byte] startframe [byte] framecount [byte] framerate -#define svcqw_effect2 75 // [vector] org [short] modelindex [short] startframe [byte] framecount [byte] framerate +#define svcfte_effect 74 // [vector] org [byte] modelindex [byte] startframe [byte] framecount [byte] framerate +#define svcfte_effect2 75 // [vector] org [short] modelindex [short] startframe [byte] framecount [byte] framerate #ifdef PEXT_CSQC -#define svc_csqcentities 76 //entity lump for csqc +#define svcfte_csqcentities 76 //entity lump for csqc #endif -#define svc_precache 77 +#define svcfte_precache 77 + +#define svcfte_updatestatstring 78 +#define svcfte_updatestatfloat 79 //DP extended svcs diff --git a/engine/common/q1bsp.c b/engine/common/q1bsp.c index 7e1b8a55d..94f30b20e 100644 --- a/engine/common/q1bsp.c +++ b/engine/common/q1bsp.c @@ -162,6 +162,13 @@ qboolean Q1BSP_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, while (Q1_HullPointContents (hull, hull->firstclipnode, mid) == Q1CONTENTS_SOLID) { // shouldn't really happen, but does occasionally + if (!(frac < 10000000) && !(frac > -10000000)) + { + trace->fraction = 0; + VectorClear (trace->endpos); + Con_Printf ("nan in traceline\n"); + return false; + } frac -= 0.1; if (frac < 0) { diff --git a/engine/common/qvm.c b/engine/common/qvm.c index 1958f10b0..d91c2b053 100644 --- a/engine/common/qvm.c +++ b/engine/common/qvm.c @@ -421,6 +421,7 @@ qvm_t *QVM_Load(const char *name, sys_callqvm_t syscall) // check file if(header->vmMagic!=VM_MAGIC || header->instructionCount<=0 || header->codeLength<=0) { + Con_Printf("%s: invalid qvm file\n", name); BZ_Free(raw); return NULL; } diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index e3c0b73e8..51e4259fc 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -15,30 +15,24 @@ #include "quakedef.h" #ifdef RGLQUAKE -#include "glquake.h" + #include "glquake.h" #endif #if defined(RGLQUAKE) || defined(SERVERONLY) -#include "shader.h" -#include "hash.h" - -#if defined(ZYMOTICMODELS) || defined(MD5MODELS) -#define SKELETALMODELS - -#include -#endif #ifdef _WIN32 -#include + #include #else -#include + #include #endif #define MAX_BONES 256 #ifndef SERVERONLY -static model_t *loadmodel; + static model_t *loadmodel; #endif +#include "com_mesh.h" + //FIXME typedef struct { @@ -97,113 +91,7 @@ vec3_t *tempNormals; extern cvar_t gl_ati_truform; extern cvar_t r_vertexdlights; extern cvar_t mod_md3flags; - -typedef struct { - int ofs_indexes; - int numindexes; - - int ofs_trineighbours; - - int numskins; -#ifndef SERVERONLY - int ofsskins; -#endif - - qboolean sharesverts; //used with models with two shaders using the same vertex - use last mesh's verts - qboolean sharesbones; //use last mesh's bones (please, never set this on the first mesh!) - - int numverts; - -#ifndef SERVERONLY - int ofs_st_array; -#endif - - int groups; - int groupofs; - - int nextsurf; - -#ifdef SKELETALMODELS - int numbones; - int ofsbones; - int numtransforms; - int ofstransforms; -#endif - -//these exist only in the root mesh. - int numtagframes; - int numtags; - int ofstags; -} galiasinfo_t; - -//frame is an index into this -typedef struct { -#ifdef SKELETALMODELS - qboolean isheirachical; //for models with transforms, states that bones need to be transformed from their parent. - //this is actually bad, and can result in bones shortening as they interpolate. -#endif - qboolean loop; - int numposes; - float rate; - int poseofs; - char name[64]; -} galiasgroup_t; - -typedef struct { - int ofsverts; -#ifndef SERVERONLY - int ofsnormals; -#endif - - vec3_t scale; - vec3_t scale_origin; -} galiaspose_t; - -#ifdef SKELETALMODELS -typedef struct { - char name[32]; - int parent; -} galiasbone_t; - -typedef struct { - //skeletal poses refer to this. - int vertexindex; - int boneindex; - vec4_t org; -} galisskeletaltransforms_t; -#endif - -//we can't be bothered with animating skins. -//We'll load up to four of them but after that you're on your own -#ifndef SERVERONLY -typedef struct { - int skinwidth; - int skinheight; - int ofstexels; //this is 8bit for frame 0 only. only valid in q1 models without replacement textures, used for colourising player skins. - float skinspeed; - int texnums; - int ofstexnums; - char name [MAX_QPATH]; -} galiasskin_t; - -typedef struct { - int base; - int bump; - int fullbright; - -#ifdef Q3SHADERS - shader_t *shader; -#endif -} galiastexnum_t; - -typedef struct { - char name[MAX_QPATH]; - galiastexnum_t texnum; - int colour; - int skinnum; - bucket_t bucket; -} galiascolourmapped_t; -#endif +extern cvar_t r_skin_overlays; #ifdef SKELETALMODELS static void R_LerpBones(float *plerp, float **pose, int poses, galiasbone_t *bones, int bonecount, float bonepose[MAX_BONES][12]); @@ -1039,7 +927,9 @@ static galiastexnum_t *GL_ChooseSkin(galiasinfo_t *inf, char *modelname, int sur char hashname[512]; cc = (tc<<4)|bc; - if (forced) +// if (e->scoreboard->skin->cachedbpp + + /* if (cls.protocol == CP_QUAKE2) { if (e->scoreboard && e->scoreboard->skin) snprintf(hashname, sizeof(hashname), "%s$%s$%i", modelname, e->scoreboard->skin->name, surfnum); @@ -1047,7 +937,7 @@ static galiastexnum_t *GL_ChooseSkin(galiasinfo_t *inf, char *modelname, int sur snprintf(hashname, sizeof(hashname), "%s$%i", modelname, surfnum); skinname = hashname; } - else + else */ { if (e->scoreboard && e->scoreboard->skin) { @@ -2090,6 +1980,20 @@ void R_DrawGAliasModel (entity_t *e) // else GL_DrawAliasMesh(&mesh, skin->base); + if (skin->loweroverlay && r_skin_overlays.value) + { + qglEnable(GL_BLEND); + qglColor4f(shadelight[0]/255, shadelight[1]/255, shadelight[2]/255, e->shaderRGBAf[3]); + c_alias_polys += mesh.numindexes/3; + GL_DrawAliasMesh(&mesh, skin->loweroverlay); + } + if (skin->upperoverlay && r_skin_overlays.value) + { + qglEnable(GL_BLEND); + qglColor4f(shadelight[0]/255, shadelight[1]/255, shadelight[2]/255, e->shaderRGBAf[3]); + c_alias_polys += mesh.numindexes/3; + GL_DrawAliasMesh(&mesh, skin->upperoverlay); + } if (skin->fullbright && r_fb_models.value && cls.allow_luma) { mesh.colors_array = NULL; diff --git a/engine/gl/gl_vidnt.c b/engine/gl/gl_vidnt.c index e96a06247..586fcde16 100644 --- a/engine/gl/gl_vidnt.c +++ b/engine/gl/gl_vidnt.c @@ -1222,6 +1222,7 @@ BOOL bSetupPixelFormat(HDC hDC) if (SetPixelFormat(hDC, pixelformat, &pfd)) { TRACE(("dbg: bSetupPixelFormat: we can use the stencil buffer. woot\n")); + DescribePixelFormat(hDC, pixelformat, sizeof(pfd), &pfd); FixPaletteInDescriptor(hDC, &pfd); gl_canstencil = pfd.cStencilBits; return TRUE; diff --git a/engine/server/net_preparse.c b/engine/server/net_preparse.c index 257496953..1cf7e1d37 100644 --- a/engine/server/net_preparse.c +++ b/engine/server/net_preparse.c @@ -136,13 +136,13 @@ void NPP_NQFlush(void) break; case svcdp_hidelmp: requireextension = PEXT_SHOWPIC; - buffer[0] = svc_hidepic; + buffer[0] = svcfte_hidepic; break; case svcdp_showlmp: requireextension = PEXT_SHOWPIC; memmove(buffer+2, buffer+1, bufferlen-1); bufferlen++; - buffer[0] = svc_showpic; + buffer[0] = svcfte_showpic; buffer[1] = 0; //top left //pad the bytes to shorts. buffer[bufferlen] = buffer[bufferlen-1]; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 3ec8f2c5a..9d68b7386 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -2001,7 +2001,7 @@ void PF_setmodel_Internal (progfuncs_t *prinst, edict_t *e, char *m) if (sv.state != ss_loading) { - MSG_WriteByte(&sv.reliable_datagram, svc_precache); + MSG_WriteByte(&sv.reliable_datagram, svcfte_precache); MSG_WriteShort(&sv.reliable_datagram, i); MSG_WriteString(&sv.reliable_datagram, m); #ifdef NQPROT @@ -2264,7 +2264,7 @@ void PF_centerprint (progfuncs_t *prinst, struct globalvars_s *pr_globals) } sp = cl->controller; - ClientReliableWrite_Begin (sp, svc_choosesplitclient, 4 + slen); + ClientReliableWrite_Begin (sp, svcfte_choosesplitclient, 4 + slen); ClientReliableWrite_Byte (sp, pnum); ClientReliableWrite_Byte (sp, svc_centerprint); ClientReliableWrite_String (sp, s); @@ -2620,7 +2620,7 @@ static void PF_particle2 (progfuncs_t *prinst, globalvars_t *pr_globals) effect = G_FLOAT(OFS_PARM4); count = G_FLOAT(OFS_PARM5); - MSG_WriteByte (&sv.multicast, svc_particle2); + MSG_WriteByte (&sv.multicast, svcfte_particle2); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); @@ -2659,7 +2659,7 @@ static void PF_particle3 (progfuncs_t *prinst, globalvars_t *pr_globals) effect = G_FLOAT(OFS_PARM3); count = G_FLOAT(OFS_PARM4); - MSG_WriteByte (&sv.multicast, svc_particle3); + MSG_WriteByte (&sv.multicast, svcfte_particle3); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); @@ -2695,7 +2695,7 @@ static void PF_particle4 (progfuncs_t *prinst, globalvars_t *pr_globals) effect = G_FLOAT(OFS_PARM3); count = G_FLOAT(OFS_PARM4); - MSG_WriteByte (&sv.multicast, svc_particle4); + MSG_WriteByte (&sv.multicast, svcfte_particle4); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); @@ -3247,7 +3247,7 @@ void PF_stuffcmd (progfuncs_t *prinst, struct globalvars_s *pr_globals) } sp = cl->controller; - ClientReliableWrite_Begin (sp, svc_choosesplitclient, 4 + slen); + ClientReliableWrite_Begin (sp, svcfte_choosesplitclient, 4 + slen); ClientReliableWrite_Byte (sp, pnum); ClientReliableWrite_Byte (sp, svc_stufftext); ClientReliableWrite_String (sp, str); @@ -3744,7 +3744,7 @@ void PF_precache_sound_Internal (progfuncs_t *prinst, char *s) if (sv.state != ss_loading) { - MSG_WriteByte(&sv.reliable_datagram, svc_precache); + MSG_WriteByte(&sv.reliable_datagram, svcfte_precache); MSG_WriteShort(&sv.reliable_datagram, i+32768); MSG_WriteString(&sv.reliable_datagram, s); #ifdef NQPROT @@ -3802,7 +3802,7 @@ void PF_precache_model_Internal (progfuncs_t *prinst, char *s) if (sv.state != ss_loading) { - MSG_WriteByte(&sv.reliable_datagram, svc_precache); + MSG_WriteByte(&sv.reliable_datagram, svcfte_precache); MSG_WriteShort(&sv.reliable_datagram, i); MSG_WriteString(&sv.reliable_datagram, s); #ifdef NQPROT @@ -4049,7 +4049,7 @@ void PF_applylightstyle(int style, char *val, int col) #ifdef PEXT_LIGHTSTYLECOL if ((client->fteprotocolextensions & PEXT_LIGHTSTYLECOL) && col!=7) { - ClientReliableWrite_Begin (client, svc_lightstylecol, strlen(val)+4); + ClientReliableWrite_Begin (client, svcfte_lightstylecol, strlen(val)+4); ClientReliableWrite_Byte (client, style); ClientReliableWrite_Char (client, col); ClientReliableWrite_String (client, val); @@ -5537,7 +5537,7 @@ void PF_bulleten (progfuncs_t *prinst, struct globalvars_s *pr_globals) if (client->fteprotocolextensions & PEXT_BULLETENS) { ClientReliableCheckBlock(client, msglen+1); - ClientReliableWrite_Byte(client, svc_bulletentext); + ClientReliableWrite_Byte(client, svcfte_bulletentext); ClientReliableWrite_Byte(client, board); ClientReliableWrite_String(client, msg); } @@ -8501,7 +8501,7 @@ void PF_CustomTEnt(progfuncs_t *prinst, struct globalvars_s *pr_globals) if (type < 0 || type >= 255) return; - MSG_WriteByte(&sv.multicast, svc_customtempent); + MSG_WriteByte(&sv.multicast, svcfte_customtempent); MSG_WriteByte(&sv.multicast, type); MSG_WriteCoord(&sv.multicast, org[0]); MSG_WriteCoord(&sv.multicast, org[1]); @@ -9148,7 +9148,7 @@ static void PF_effect(progfuncs_t *prinst, struct globalvars_s *pr_globals) if (startframe>255 || index>255) { - MSG_WriteByte (&sv.multicast, svcqw_effect2); + MSG_WriteByte (&sv.multicast, svcfte_effect2); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); @@ -9170,7 +9170,7 @@ static void PF_effect(progfuncs_t *prinst, struct globalvars_s *pr_globals) } else { - MSG_WriteByte (&sv.multicast, svcqw_effect); + MSG_WriteByte (&sv.multicast, svcfte_effect); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); @@ -9314,7 +9314,7 @@ static client_t *DirectSplit(client_t *cl, int svc, int svclen) } sp = cl->controller; - ClientReliableWrite_Begin (sp, svc_choosesplitclient, 2+svclen); + ClientReliableWrite_Begin (sp, svcfte_choosesplitclient, 2+svclen); ClientReliableWrite_Byte (sp, pnum); ClientReliableWrite_Byte (sp, svc); return sp; @@ -9362,7 +9362,7 @@ void PF_ShowPic(progfuncs_t *prinst, struct globalvars_s *pr_globals) if (!(svs.clients[entnum].fteprotocolextensions & PEXT_SHOWPIC)) return; //need an extension for this. duh. - cl = DirectSplit(&svs.clients[entnum], svc_showpic, 8 + strlen(slot)+strlen(picname)); + cl = DirectSplit(&svs.clients[entnum], svcfte_showpic, 8 + strlen(slot)+strlen(picname)); ClientReliableWrite_Byte(cl, zone); ClientReliableWrite_String(cl, slot); ClientReliableWrite_String(cl, picname); @@ -9395,7 +9395,7 @@ void PF_HidePic(progfuncs_t *prinst, struct globalvars_s *pr_globals) if (!(svs.clients[entnum].fteprotocolextensions & PEXT_SHOWPIC)) return; //need an extension for this. duh. - cl = DirectSplit(&svs.clients[entnum], svc_hidepic, 2 + strlen(slot)); + cl = DirectSplit(&svs.clients[entnum], svcfte_hidepic, 2 + strlen(slot)); ClientReliableWrite_String(cl, slot); } else @@ -9430,7 +9430,7 @@ void PF_MovePic(progfuncs_t *prinst, struct globalvars_s *pr_globals) if (!(svs.clients[entnum].fteprotocolextensions & PEXT_SHOWPIC)) return; //need an extension for this. duh. - cl = DirectSplit(&svs.clients[entnum], svc_movepic, 6 + strlen(slot)); + cl = DirectSplit(&svs.clients[entnum], svcfte_movepic, 6 + strlen(slot)); ClientReliableWrite_String(cl, slot); ClientReliableWrite_Byte(cl, zone); ClientReliableWrite_Short(cl, x); @@ -9463,7 +9463,7 @@ void PF_ChangePic(progfuncs_t *prinst, struct globalvars_s *pr_globals) if (!(svs.clients[entnum].fteprotocolextensions & PEXT_SHOWPIC)) return; //need an extension for this. duh. - cl = DirectSplit(&svs.clients[entnum], svc_updatepic, 3 + strlen(slot)+strlen(newpic)); + cl = DirectSplit(&svs.clients[entnum], svcfte_updatepic, 3 + strlen(slot)+strlen(newpic)); ClientReliableWrite_String(cl, slot); ClientReliableWrite_String(cl, newpic); } @@ -10766,6 +10766,7 @@ void PR_RegisterFields(void) //it's just easier to do it this way. fieldxfunction(SendEntity); fieldxfloat(Version); + fieldxfloat(pvsflags); //Tell the qc library to split the entity fields each side. //the fields above become < 0, the remaining fields specified by the qc stay where the mod specified, as far as possible (with addons at least). diff --git a/engine/server/pr_q1qvm.c b/engine/server/pr_q1qvm.c index c59f44d1f..2bca729d6 100755 --- a/engine/server/pr_q1qvm.c +++ b/engine/server/pr_q1qvm.c @@ -1,1483 +1,1498 @@ -/* -Copyright (C) 1996-1997 Id Software, Inc. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "quakedef.h" -#include "qwsvdef.h" - -#ifdef VM_Q1 - -#define GAME_API_VERSION 12 -#define MAX_Q1QVM_EDICTS 768 //according to ktx at api version 12 (fte's protocols go to 2048) - -#define VMFSID_Q1QVM 57235 //a cookie - -#define WASTED_EDICT_T_SIZE 112 //qclib has split edict_t and entvars_t. - //mvdsv and the api we're implementing has them in one lump - //so we need to bias our entvars_t and fake the offsets a little. - -//=============================================================== - -// -// system traps provided by the main engine -// -typedef enum -{ - //============== general Quake services ================== - - G_GETAPIVERSION, // ( void); //0 - - G_DPRINT, // ( const char *string ); //1 - // print message on the local console - - G_ERROR, // ( const char *string ); //2 - // abort the game - G_GetEntityToken, //3 - - G_SPAWN_ENT, //4 - G_REMOVE_ENT, //5 - G_PRECACHE_SOUND, - G_PRECACHE_MODEL, - G_LIGHTSTYLE, - G_SETORIGIN, - G_SETSIZE, //10 - G_SETMODEL, - G_BPRINT, - G_SPRINT, - G_CENTERPRINT, - G_AMBIENTSOUND, //15 - G_SOUND, - G_TRACELINE, - G_CHECKCLIENT, - G_STUFFCMD, - G_LOCALCMD, //20 - G_CVAR, - G_CVAR_SET, - G_FINDRADIUS, - G_WALKMOVE, - G_DROPTOFLOOR, //25 - G_CHECKBOTTOM, - G_POINTCONTENTS, - G_NEXTENT, - G_AIM, - G_MAKESTATIC, //30 - G_SETSPAWNPARAMS, - G_CHANGELEVEL, - G_LOGFRAG, - G_GETINFOKEY, - G_MULTICAST, //35 - G_DISABLEUPDATES, - G_WRITEBYTE, - G_WRITECHAR, - G_WRITESHORT, - G_WRITELONG, //40 - G_WRITEANGLE, - G_WRITECOORD, - G_WRITESTRING, - G_WRITEENTITY, - G_FLUSHSIGNON, //45 - g_memset, - g_memcpy, - g_strncpy, - g_sin, - g_cos, //50 - g_atan2, - g_sqrt, - g_floor, - g_ceil, - g_acos, //55 - G_CMD_ARGC, - G_CMD_ARGV, - G_TraceCapsule, - G_FS_OpenFile, - G_FS_CloseFile, //60 - G_FS_ReadFile, - G_FS_WriteFile, - G_FS_SeekFile, - G_FS_TellFile, - G_FS_GetFileList, //65 - G_CVAR_SET_FLOAT, - G_CVAR_STRING, - G_Map_Extension, - G_strcmp, - G_strncmp, //70 - G_stricmp, - G_strnicmp, - G_Find, - G_executecmd, - G_conprint, //75 - G_readcmd, - G_redirectcmd, - G_Add_Bot, - G_Remove_Bot, - G_SetBotUserInfo, //80 - G_SetBotCMD, - - G_strftime, - G_CMD_ARGS, - G_CMD_TOKENIZE, - G_strlcpy, //85 - G_strlcat, - G_MAKEVECTORS, - G_NEXTCLIENT, - - - G_MAX -} gameImport_t; - - -// -// functions exported by the game subsystem -// -typedef enum -{ - GAME_INIT, // ( int levelTime, int randomSeed, int restart ); - // init and shutdown will be called every single level - // The game should call G_GET_ENTITY_TOKEN to parse through all the - // entity configuration text and spawn gentities. - GAME_LOADENTS, - GAME_SHUTDOWN, // (void); - - GAME_CLIENT_CONNECT, // ( int clientNum ,int isSpectator); - GAME_PUT_CLIENT_IN_SERVER, - - GAME_CLIENT_USERINFO_CHANGED, // ( int clientNum,int isSpectator ); - - GAME_CLIENT_DISCONNECT, // ( int clientNum,int isSpectator ); - - GAME_CLIENT_COMMAND, // ( int clientNum,int isSpectator ); - - GAME_CLIENT_PRETHINK, - GAME_CLIENT_THINK, // ( int clientNum,int isSpectator ); - GAME_CLIENT_POSTTHINK, - - GAME_START_FRAME, // ( int levelTime ); - GAME_SETCHANGEPARMS, //self - GAME_SETNEWPARMS, - GAME_CONSOLE_COMMAND, // ( void ); - GAME_EDICT_TOUCH, //(self,other) - GAME_EDICT_THINK, //(self,other=world,time) - GAME_EDICT_BLOCKED, //(self,other) - GAME_CLIENT_SAY, //(int isteam) -} gameExport_t; - - -typedef enum -{ - F_INT, - F_FLOAT, - F_LSTRING, // string on disk, pointer in memory, TAG_LEVEL -// F_GSTRING, // string on disk, pointer in memory, TAG_GAME - F_VECTOR, - F_ANGLEHACK, -// F_ENTITY, // index on disk, pointer in memory -// F_ITEM, // index on disk, pointer in memory -// F_CLIENT, // index on disk, pointer in memory - F_IGNORE -} fieldtype_t; - -typedef struct -{ - string_t name; - int ofs; - fieldtype_t type; -// int flags; -} field_t; - - - -typedef struct { - int pad[28]; - int self; - int other; - int world; - float time; - float frametime; - int newmis; - float force_retouch; - string_t mapname; - float serverflags; - float total_secrets; - float total_monsters; - float found_secrets; - float killed_monsters; - float parm1; - float parm2; - float parm3; - float parm4; - float parm5; - float parm6; - float parm7; - float parm8; - float parm9; - float parm10; - float parm11; - float parm12; - float parm13; - float parm14; - float parm15; - float parm16; - vec3_t v_forward; - vec3_t v_up; - vec3_t v_right; - float trace_allsolid; - float trace_startsolid; - float trace_fraction; - vec3_t trace_endpos; - vec3_t trace_plane_normal; - float trace_plane_dist; - int trace_ent; - float trace_inopen; - float trace_inwater; - int msg_entity; - func_t main; - func_t StartFrame; - func_t PlayerPreThink; - func_t PlayerPostThink; - func_t ClientKill; - func_t ClientConnect; - func_t PutClientInServer; - func_t ClientDisconnect; - func_t SetNewParms; - func_t SetChangeParms; -} 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; - int sizeofent; - q1qvmglobalvars_t *global; - field_t *fields; - int APIversion; -} gameDataN_t; - -typedef struct -{ - unsigned int ents; - int sizeofent; - unsigned int global; - unsigned int fields; - int APIversion; -} gameData32_t; - -typedef int fileHandle_t; - -typedef enum { - FS_READ_BIN, - FS_READ_TXT, - FS_WRITE_BIN, - FS_WRITE_TXT, - FS_APPEND_BIN, - FS_APPEND_TXT -} fsMode_t; - -typedef enum { - FS_SEEK_CUR, - FS_SEEK_END, - FS_SEEK_SET -} fsOrigin_t; - - - - - - - - - - - -static field_t *qvmfields; -static char *q1qvmentstring; -static vm_t *q1qvm; -static progfuncs_t q1qvmprogfuncs; -static edict_t *q1qvmedicts[MAX_Q1QVM_EDICTS]; - - -static void *evars; //pointer to the gamecodes idea of an edict_t -static int vevars; //offset into the vm base of evars - -/* -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= sv.max_edicts) - return NULL; - - e = q1qvmedicts[num]; - if (!e) - { - e = q1qvmedicts[num] = Z_TagMalloc(sizeof(edict_t)+sizeof(extentvars_t), VMFSID_Q1QVM); - e->v = (stdentvars_t*)((char*)evars + (num * pr_edict_size) + WASTED_EDICT_T_SIZE); - e->xv = (extentvars_t*)(e+1); - e->entnum = num; - } - return e; -} - -static unsigned int Q1QVMPF_NumForEdict(progfuncs_t *pf, edict_t *e) -{ - return e->entnum; -} - -static int Q1QVMPF_EdictToProgs(progfuncs_t *pf, edict_t *e) -{ - return e->entnum*pr_edict_size; -} -static edict_t *Q1QVMPF_ProgsToEdict(progfuncs_t *pf, int num) -{ - if (num % pr_edict_size) - Con_Printf("Edict To Progs with remainder\n"); - num /= pr_edict_size; - - return Q1QVMPF_EdictNum(pf, num); -} - -void Q1QVMED_ClearEdict (edict_t *e, qboolean wipe) -{ - int num = e->entnum; - if (wipe) - memset (e->v, 0, pr_edict_size - WASTED_EDICT_T_SIZE); - e->isfree = false; - e->entnum = num; -} - -static void Q1QVMPF_EntRemove(progfuncs_t *pf, edict_t *e) -{ - if (!ED_CanFree(e)) - return; - e->isfree = true; - e->freetime = sv.time; -} - -static edict_t *Q1QVMPF_EntAlloc(progfuncs_t *pf) -{ - int i; - edict_t *e; - for ( i=0 ; iisfree && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) )) - { - Q1QVMED_ClearEdict (e, true); - - ED_Spawned((struct edict_s *) e); - return (struct edict_s *)e; - } - } - - if (i >= sv.max_edicts-1) //try again, but use timed out ents. - { - for ( i=0 ; iisfree)) - { - Q1QVMED_ClearEdict (e, true); - - ED_Spawned((struct edict_s *) e); - return (struct edict_s *)e; - } - } - - if (i >= sv.max_edicts-1) - { - Sys_Error ("ED_Alloc: no free edicts"); - } - } - - sv.num_edicts++; - e = (edict_t*)EDICT_NUM(pf, i); - -// new ents come ready wiped -// Q1QVMED_ClearEdict (e, false); - - ED_Spawned((struct edict_s *) e); - - return (struct edict_s *)e; -} - -static int Q1QVMPF_LoadEnts(progfuncs_t *pf, char *mapstring, float spawnflags) -{ - q1qvmentstring = mapstring; - VM_Call(q1qvm, GAME_LOADENTS); - q1qvmentstring = NULL; - return pr_edict_size; -} - -static eval_t *Q1QVMPF_GetEdictFieldValue(progfuncs_t *pf, edict_t *e, char *fieldname, evalc_t *cache) -{ - if (!strcmp(fieldname, "message")) - { - return (eval_t*)&e->v->message; - } - return NULL; -} - -static eval_t *Q1QVMPF_FindGlobal (progfuncs_t *prinst, char *name, progsnum_t num) -{ - return NULL; -} - -static globalvars_t *Q1QVMPF_Globals(progfuncs_t *prinst, int prnum) -{ - return NULL; -} - -static string_t Q1QVMPF_StringToProgs(progfuncs_t *prinst, char *str) -{ - return (string_t)(str - (char*)VM_MemoryBase(q1qvm)); -} - -static char *Q1QVMPF_StringToNative(progfuncs_t *prinst, string_t str) -{ - return (char*)VM_MemoryBase(q1qvm) + str; -} - -void PF_WriteByte (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_WriteChar (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_WriteShort (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_WriteLong (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_WriteAngle (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_WriteCoord (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_WriteFloat (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_WriteString (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_WriteEntity (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_multicast (progfuncs_t *prinst, struct globalvars_s *pr_globals); - -void PF_svtraceline (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_changelevel (progfuncs_t *prinst, struct globalvars_s *pr_globals); - -void PF_cvar_set (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_cvar_setf (progfuncs_t *prinst, struct globalvars_s *pr_globals); - -void PF_lightstyle (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_ambientsound (progfuncs_t *prinst, struct globalvars_s *pr_globals); - -void PF_makestatic (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_logfrag (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_centerprint (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_localcmd (progfuncs_t *prinst, struct globalvars_s *pr_globals); -void PF_ExecuteCommand (progfuncs_t *prinst, struct globalvars_s *pr_globals); -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); - -static int WrapQCBuiltin(builtin_t func, void *offset, unsigned int mask, const int *arg, char *argtypes) -{ - globalvars_t gv; - int argnum=0; - while(*argtypes) - { - switch(*argtypes++) - { - case 'f': - gv.param[argnum++].f = VM_FLOAT(*arg++); - break; - case 'i': - gv.param[argnum++].f = VM_LONG(*arg++); - break; - case 'n': //ent num - gv.param[argnum++].i = EDICT_TO_PROG(svprogfuncs, Q1QVMPF_EdictNum(svprogfuncs, VM_LONG(*arg++))); - break; - case 'v': //three seperate args -> 1 vector - gv.param[argnum].vec[0] = VM_FLOAT(*arg++); - gv.param[argnum].vec[1] = VM_FLOAT(*arg++); - gv.param[argnum].vec[2] = VM_FLOAT(*arg++); - argnum++; - break; - case 's': //three seperate args -> 1 vector - gv.param[argnum].i = VM_LONG(*arg++); - argnum++; - break; - } - } - svprogfuncs->callargc = &argnum; - gv.ret.i = 0; - func(svprogfuncs, &gv); - return gv.ret.i; -} - -#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. -static int syscallqvm (void *offset, unsigned int mask, int fn, const int *arg) -{ - switch (fn) - { - case G_GETAPIVERSION: - return GAME_API_VERSION; - - case G_DPRINT: - Con_Printf("%s", VM_POINTER(arg[0])); - break; - - case G_ERROR: - SV_Error("Q1QVM: %s", VM_POINTER(arg[0])); - break; - - case G_GetEntityToken: - { - if (VM_OOB(arg[0], arg[1])) - return false; - if (q1qvmentstring) - { - char *ret = VM_POINTER(arg[0]); - q1qvmentstring = COM_Parse(q1qvmentstring); - Q_strncpyz(ret, com_token, VM_LONG(arg[1])); - return *com_token != 0; - } - else - { - char *ret = VM_POINTER(arg[0]); - strcpy(ret, ""); - return false; - } - } - break; - - case G_SPAWN_ENT: - return Q1QVMPF_EntAlloc(svprogfuncs)->entnum; - - case G_REMOVE_ENT: - if (arg[0] >= sv.max_edicts) - return false; - Q1QVMPF_EntRemove(svprogfuncs, q1qvmedicts[arg[0]]); - return true; - - case G_PRECACHE_SOUND: - PF_precache_sound_Internal(svprogfuncs, VM_POINTER(arg[0])); - break; - - case G_PRECACHE_MODEL: - PF_precache_model_Internal(svprogfuncs, VM_POINTER(arg[0])); - break; - - case G_LIGHTSTYLE: - WrapQCBuiltin(PF_lightstyle, offset, mask, arg, "is"); - break; - - case G_SETORIGIN: - { - edict_t *e = Q1QVMPF_EdictNum(svprogfuncs, arg[0]); - if (!e || e->isfree) - return false; - - e->v->origin[0] = VM_FLOAT(arg[1]); - e->v->origin[1] = VM_FLOAT(arg[2]); - e->v->origin[2] = VM_FLOAT(arg[3]); - SV_LinkEdict (e, false); - return true; - } - break; - - case G_SETSIZE: - { - edict_t *e = Q1QVMPF_EdictNum(svprogfuncs, arg[0]); - if (!e || e->isfree) - return false; - - e->v->mins[0] = VM_FLOAT(arg[1]); - e->v->mins[1] = VM_FLOAT(arg[2]); - e->v->mins[2] = VM_FLOAT(arg[3]); - - e->v->maxs[0] = VM_FLOAT(arg[4]); - e->v->maxs[1] = VM_FLOAT(arg[5]); - e->v->maxs[2] = VM_FLOAT(arg[6]); - - VectorSubtract (e->v->maxs, e->v->mins, e->v->size); - SV_LinkEdict (e, false); - return true; - } - case G_SETMODEL: - { - edict_t *e = Q1QVMPF_EdictNum(svprogfuncs, arg[0]); - PF_setmodel_Internal(svprogfuncs, e, VM_POINTER(arg[1])); - } - break; - - case G_BPRINT: - SV_BroadcastPrintf(arg[0], "%s", VM_POINTER(arg[1])); - break; - - case G_SPRINT: - if ((unsigned)VM_LONG(arg[0]) > sv.allocated_client_slots) - return 0; - SV_ClientPrintf(&svs.clients[VM_LONG(arg[0])-1], VM_LONG(arg[1]), "%s", VM_POINTER(arg[2])); - break; - - case G_CENTERPRINT: - WrapQCBuiltin(PF_centerprint, offset, mask, arg, "ns"); - break; - - case G_AMBIENTSOUND: - WrapQCBuiltin(PF_ambientsound, offset, mask, arg, "vsff"); - break; - - case G_SOUND: -// ( int edn, int channel, char *samp, float vol, float att ) - SV_StartSound (Q1QVMPF_EdictNum(svprogfuncs, VM_LONG(arg[0])), VM_LONG(arg[1]), VM_POINTER(arg[2]), VM_FLOAT(arg[3])*255, VM_FLOAT(arg[4])); - break; - - case G_TRACELINE: - WrapQCBuiltin(PF_svtraceline, offset, mask, arg, "vvin"); - break; - - case G_CHECKCLIENT: - return PF_checkclient_Internal(svprogfuncs); - - case G_STUFFCMD: - { - char *s; - client_t *cl; - if ((unsigned)VM_LONG(arg[0]) > sv.allocated_client_slots) - return -1; - cl = &svs.clients[VM_LONG(arg[0])-1]; - if (cl->state != cs_spawned) - return -1; - s = VM_POINTER(arg[1]); - - ClientReliableWrite_Begin (cl, svc_stufftext, 3+strlen(s)); - ClientReliableWrite_String(cl, s); - } - break; - - case G_LOCALCMD: - WrapQCBuiltin(PF_localcmd, offset, mask, arg, "s"); - break; - - case G_CVAR: - { - int i; - cvar_t *c; - c = Cvar_Get(VM_POINTER(arg[0]), "", 0, "Gamecode"); - i = VM_LONG(c->value); - return i; - } - - case G_CVAR_SET: - WrapQCBuiltin(PF_cvar_set, offset, mask, arg, "ss"); - break; - - case G_FINDRADIUS: - { - int start = ((char*)VM_POINTER(arg[0]) - (char*)evars) / pr_edict_size; - edict_t *ed; - vec3_t diff; - float *org = VM_POINTER(arg[1]); - float rad = VM_FLOAT(arg[2]); - rad *= rad; - for(start++; start < sv.num_edicts; start++) - { - ed = EDICT_NUM(svprogfuncs, start); - if (ed->isfree) - continue; - VectorSubtract(ed->v->origin, org, diff); - if (rad > DotProduct(diff, diff)) - return (int)(vevars + start*pr_edict_size); - } - return 0; - } - - case G_WALKMOVE: - return WrapQCBuiltin(PF_cvar_set, offset, mask, arg, "ff"); - - case G_DROPTOFLOOR: - { - edict_t *ent; - vec3_t end; - vec3_t start; - trace_t trace; - extern cvar_t pr_droptofloorunits; - - ent = PROG_TO_EDICT(svprogfuncs, pr_global_struct->self); - - VectorCopy (ent->v->origin, end); - if (pr_droptofloorunits.value > 0) - end[2] -= pr_droptofloorunits.value; - else - end[2] -= 256; - - VectorCopy (ent->v->origin, start); - trace = SV_Move (start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent); - - if (trace.fraction == 1 || trace.allsolid) - return false; - else - { - VectorCopy (trace.endpos, ent->v->origin); - SV_LinkEdict (ent, false); - ent->v->flags = (int)ent->v->flags | FL_ONGROUND; - ent->v->groundentity = EDICT_TO_PROG(svprogfuncs, trace.ent); - return true; - } - } - break; - - case G_CHECKBOTTOM: - return SV_CheckBottom(EDICT_NUM(svprogfuncs, VM_LONG(arg[0]))); - - case G_POINTCONTENTS: - { - vec3_t v; - v[0] = VM_FLOAT(arg[0]); - v[1] = VM_FLOAT(arg[1]); - v[2] = VM_FLOAT(arg[2]); - return sv.worldmodel->funcs.PointContents(sv.worldmodel, v); - } - break; - - case G_NEXTENT: - { //input output are entity numbers - unsigned int i; - edict_t *ent; - - i = VM_LONG(arg[0]); - while (1) - { - i++; - if (i >= sv.num_edicts) - { - return 0; - } - ent = EDICT_NUM(svprogfuncs, i); - if (!ent->isfree) - { - return i; - } - } - break; - } - /* - case G_AIM: //not in mvdsv anyway - break; -*/ - case G_MAKESTATIC: - WrapQCBuiltin(PF_makestatic, offset, mask, arg, "n"); - break; - - case G_SETSPAWNPARAMS: - WrapQCBuiltin(PF_setspawnparms, offset, mask, arg, "n"); - break; - - case G_CHANGELEVEL: - WrapQCBuiltin(PF_changelevel, offset, mask, arg, "s"); - break; - - case G_LOGFRAG: - WrapQCBuiltin(PF_logfrag, offset, mask, arg, "nn"); - break; - - case G_GETINFOKEY: - { - char *v; - if (VM_OOB(arg[2], arg[3])) - return -1; - v = PF_infokey_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1])); - Q_strncpyz(VM_POINTER(arg[2]), v, VM_LONG(arg[3])); - } - break; - - case G_MULTICAST: - WrapQCBuiltin(PF_multicast, offset, mask, arg, "vi"); - break; - - case G_DISABLEUPDATES: - //FIXME: remember to ask mvdsv people why this is useful - Con_Printf("G_DISABLEUPDATES: not supported\n"); - break; - - case G_WRITEBYTE: - WrapQCBuiltin(PF_WriteByte, offset, mask, arg, "ii"); - break; - case G_WRITECHAR: - WrapQCBuiltin(PF_WriteChar, offset, mask, arg, "ii"); - break; - case G_WRITESHORT: - WrapQCBuiltin(PF_WriteShort, offset, mask, arg, "ii"); - break; - case G_WRITELONG: - WrapQCBuiltin(PF_WriteLong, offset, mask, arg, "ii"); - break; - case G_WRITEANGLE: - WrapQCBuiltin(PF_WriteAngle, offset, mask, arg, "if"); - break; - case G_WRITECOORD: - WrapQCBuiltin(PF_WriteCoord, offset, mask, arg, "if"); - break; - case G_WRITESTRING: - WrapQCBuiltin(PF_WriteString, offset, mask, arg, "is"); - break; - case G_WRITEENTITY: - WrapQCBuiltin(PF_WriteEntity, offset, mask, arg, "in"); - break; - - case G_FLUSHSIGNON: - SV_FlushSignon (); - break; - - case g_memset: - { - void *dst = VM_POINTER(arg[0]); - VALIDATEPOINTER(arg[0], arg[2]); - memset(dst, arg[1], arg[2]); - return arg[0]; - } - case g_memcpy: - { - 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]); - Q_strncpyS(VM_POINTER(arg[0]), VM_POINTER(arg[1]), arg[2]); - return arg[0]; - case g_sin: - VM_FLOAT(fn)=(float)sin(VM_FLOAT(arg[0])); - return fn; - case g_cos: - VM_FLOAT(fn)=(float)cos(VM_FLOAT(arg[0])); - return fn; - case g_atan2: - VM_FLOAT(fn)=(float)atan2(VM_FLOAT(arg[0]), VM_FLOAT(arg[1])); - return fn; - case g_sqrt: - VM_FLOAT(fn)=(float)sqrt(VM_FLOAT(arg[0])); - return fn; - case g_floor: - VM_FLOAT(fn)=(float)floor(VM_FLOAT(arg[0])); - return fn; - case g_ceil: - VM_FLOAT(fn)=(float)ceil(VM_FLOAT(arg[0])); - return fn; - case g_acos: - VM_FLOAT(fn)=(float)acos(VM_FLOAT(arg[0])); - return fn; - - case G_CMD_ARGC: - return Cmd_Argc(); - case G_CMD_ARGV: - { - char *c; - c = Cmd_Argv(VM_LONG(arg[0])); - if (VM_OOB(arg[1], arg[2])) - return -1; - Q_strncpyz(VM_POINTER(arg[1]), c, VM_LONG(arg[2])); - } - break; - - case G_TraceCapsule: - WrapQCBuiltin(PF_svtraceline, offset, mask, arg, "vvinvv"); - break; - - case G_FS_OpenFile: - //0 = name - //1 = &handle - //2 = mode - //ret = filesize or -1 - - // Con_Printf("G_FSOpenFile: %s (mode %i)\n", VM_POINTER(arg[0]), arg[2]); - - return VM_fopen(VM_POINTER(arg[0]), VM_POINTER(arg[1]), arg[2]/2, VMFSID_Q1QVM); - - case G_FS_CloseFile: - VM_fclose(arg[0], VMFSID_Q1QVM); - break; - - case G_FS_ReadFile: - if (VM_OOB(arg[0], arg[1])) - return 0; - return VM_FRead(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VMFSID_Q1QVM); -/* - //not supported, open will fail anyway - case G_FS_WriteFile: - return VM_FWrite(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VMFSID_Q1QVM); - break; -*/ -/* - case G_FS_SeekFile: -// int trap_FS_SeekFile( fileHandle_t handle, int offset, int type ) - return VM_FSeek(VM_LONG(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2])); - break; -*/ -/* - case G_FS_TellFile: - break; -*/ - - case G_FS_GetFileList: - if (VM_OOB(arg[2], arg[3])) - return 0; - return VM_GetFileList(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3])); - break; - - case G_CVAR_SET_FLOAT: - WrapQCBuiltin(PF_cvar_setf, offset, mask, arg, "sf"); - break; - - case G_CVAR_STRING: - { - cvar_t *cv; - if (VM_OOB(arg[1], arg[2])) - return -1; - cv = Cvar_Get(VM_POINTER(arg[0]), "", 0, "QC variables"); - Q_strncpyz(VM_POINTER(arg[1]), cv->string, VM_LONG(arg[2])); - } - break; - - case G_Map_Extension: - //yes, this does exactly match mvdsv... - if (VM_LONG(arg[1]) < G_MAX) - return -2; //can't map that there - else - return -1; //extension not known - - case G_strcmp: - { - char *a = VM_POINTER(arg[0]); - char *b = VM_POINTER(arg[1]); - return strcmp(a, b); - } - case G_strncmp: - { - char *a = VM_POINTER(arg[0]); - char *b = VM_POINTER(arg[1]); - return strncmp(a, b, VM_LONG(arg[2])); - } - case G_stricmp: - { - char *a = VM_POINTER(arg[0]); - char *b = VM_POINTER(arg[1]); - return stricmp(a, b); - } - case G_strnicmp: - { - char *a = VM_POINTER(arg[0]); - char *b = VM_POINTER(arg[1]); - return strnicmp(a, b, VM_LONG(arg[2])); - } - - case G_Find: - { - edict_t *e = VM_POINTER(arg[0]); - int ofs = VM_LONG(arg[1]) - WASTED_EDICT_T_SIZE; - char *match = VM_POINTER(arg[2]); - char *field; - int first = e?((char*)e - (char*)evars)/pr_edict_size:0; - int i; - if (!match) - match = ""; - for (i = first+1; i < sv.num_edicts; i++) - { - e = q1qvmedicts[i]; - field = VM_POINTER(*((string_t*)e->v + ofs/4)); - if (field == NULL) - { - if (*match == '\0') - return ((char*)e->v - (char*)offset)-WASTED_EDICT_T_SIZE; - } - else - { - if (!strcmp(field, match)) - return ((char*)e->v - (char*)offset)-WASTED_EDICT_T_SIZE; - } - } - } - return 0; - - case G_executecmd: - WrapQCBuiltin(PF_ExecuteCommand, offset, mask, arg, ""); - break; - - case G_conprint: - Con_Printf("%s", VM_POINTER(arg[0])); - break; - - case G_readcmd: - { - extern char outputbuf[]; - extern redirect_t sv_redirected; - extern int sv_redirectedlang; - redirect_t old; - int oldl; - - char *s = VM_POINTER(arg[0]); - char *output = VM_POINTER(arg[1]); - int outputlen = VM_LONG(arg[2]); - - if (VM_OOB(arg[1], arg[2])) - return -1; - - Cbuf_Execute(); //FIXME: this code is flawed - Cbuf_AddText (s, RESTRICT_LOCAL); - - old = sv_redirected; - oldl = sv_redirectedlang; - if (old != RD_NONE) - SV_EndRedirect(); - - SV_BeginRedirect(RD_OBLIVION, LANGDEFAULT); - Cbuf_Execute(); - Q_strncpyz(output, outputbuf, outputlen); - SV_EndRedirect(); - - if (old != RD_NONE) - SV_BeginRedirect(old, oldl); - -Con_DPrintf("PF_readcmd: %s\n%s", s, output); - - } - break; - - case G_redirectcmd: - //FIXME: KTX uses this, along with a big fat warning. - //it shouldn't be vital to the normal functionality - //just restricts admin a little (did these guys never hear of rcon?) - //I'm too lazy to implement it though. - return 0; - - case G_Add_Bot: - //FIXME: not implemented, always returns failure. - //the other bot functions only ever work on bots anyway, so don't need to be implemented until this one is - return 0; -/* - case G_Remove_Bot: - break; - case G_SetBotUserInfo: - break; - case G_SetBotCMD: - break; -*/ - case G_strftime: - { - char *out = VM_POINTER(arg[0]); - char *fmt = VM_POINTER(arg[2]); - time_t curtime; - struct tm *local; - if (VM_OOB(arg[0], arg[1]) || !out) - return -1; //please don't corrupt me - time(&curtime); - curtime += VM_LONG(arg[3]); - local = localtime(&curtime); - strftime(out, VM_LONG(arg[1]), fmt, local); - } - break; - case G_CMD_ARGS: - { - char *c; - c = Cmd_Args(); - if (VM_OOB(arg[0], arg[1])) - return -1; - Q_strncpyz(VM_POINTER(arg[0]), c, VM_LONG(arg[1])); - } - break; - case G_CMD_TOKENIZE: - { - char *str = VM_POINTER(arg[0]); - Cmd_TokenizeString(str, false, false); - return Cmd_Argc(); - } - break; - case G_strlcpy: - { - char *dst = VM_POINTER(arg[0]); - char *src = VM_POINTER(arg[1]); - if (VM_OOB(arg[0], arg[2])) - return -1; - Q_strncpyz(dst, src, VM_LONG(arg[2])); - //WARNING: no return value - } - break; - case G_strlcat: - { - char *dst = VM_POINTER(arg[0]); - char *src = VM_POINTER(arg[1]); - if (VM_OOB(arg[0], arg[2])) - return -1; - Q_strncatz(dst, src, VM_LONG(arg[2])); - //WARNING: no return value - } - break; - - case G_MAKEVECTORS: - AngleVectors(VM_POINTER(arg[0]), P_VEC(v_forward), P_VEC(v_right), P_VEC(v_up)); - break; - - case G_NEXTCLIENT: - { - 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 (int)(vevars + (start+1) * pr_edict_size); - start++; - } - return 0; - } - break; - - default: - SV_Error("Q1QVM: Trap %i not implemented\n", fn); - break; - } - return 0; -} - -static int EXPORT_FN syscallnative (int arg, ...) -{ - int args[13]; - va_list argptr; - - va_start(argptr, arg); - args[0]=va_arg(argptr, int); - args[1]=va_arg(argptr, int); - args[2]=va_arg(argptr, int); - args[3]=va_arg(argptr, int); - args[4]=va_arg(argptr, int); - args[5]=va_arg(argptr, int); - args[6]=va_arg(argptr, int); - args[7]=va_arg(argptr, int); - args[8]=va_arg(argptr, int); - args[9]=va_arg(argptr, int); - args[10]=va_arg(argptr, int); - args[11]=va_arg(argptr, int); - args[12]=va_arg(argptr, int); - va_end(argptr); - - return syscallqvm(NULL, ~0, arg, args); -} - -void Q1QVM_Shutdown(void) -{ - int i; - for (i = 0; i < MAX_CLIENTS; i++) - { - if (svs.clients[i].name) - Q_strncpyz(svs.clients[i].namebuf, svs.clients[i].name, sizeof(svs.clients[i].namebuf)); - svs.clients[i].name = svs.clients[i].namebuf; - } - if (q1qvm) - VM_Destroy(q1qvm); - q1qvm = NULL; - VM_fcloseall(VMFSID_Q1QVM); - if (svprogfuncs == &q1qvmprogfuncs) - svprogfuncs = NULL; - Z_FreeTags(VMFSID_Q1QVM); -} - -qboolean PR_LoadQ1QVM(void) -{ - static float writable; - int i; - gameDataN_t *gd, gdm; - gameData32_t *gd32; - int ret; - - if (q1qvm) - VM_Destroy(q1qvm); - - q1qvm = VM_Create(NULL, "qwprogs", syscallnative, syscallqvm); - if (!q1qvm) - return false; - - - progstype = PROG_QW; - - - svprogfuncs = &q1qvmprogfuncs; - - -// 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; - q1qvmprogfuncs.ProgsToEdict = Q1QVMPF_ProgsToEdict; - q1qvmprogfuncs.EntAlloc = Q1QVMPF_EntAlloc; - q1qvmprogfuncs.EntFree = Q1QVMPF_EntRemove; - q1qvmprogfuncs.FindGlobal = Q1QVMPF_FindGlobal; - q1qvmprogfuncs.load_ents = Q1QVMPF_LoadEnts; - q1qvmprogfuncs.globals = Q1QVMPF_Globals; - q1qvmprogfuncs.GetEdictFieldValue = Q1QVMPF_GetEdictFieldValue; - q1qvmprogfuncs.StringToProgs = Q1QVMPF_StringToProgs; - q1qvmprogfuncs.StringToNative = Q1QVMPF_StringToNative; - - sv.num_edicts = 0; //we're not ready for most of the builtins yet - sv.max_edicts = 0; //so clear these out, just in case - pr_edict_size = 0; //if we get a division by zero, then at least its a safe crash - - memset(q1qvmedicts, 0, sizeof(q1qvmedicts)); - - q1qvmprogfuncs.stringtable = VM_MemoryBase(q1qvm); - - ret = VM_Call(q1qvm, GAME_INIT, (int)(sv.time*1000), rand()); - if (!ret) - { - Q1QVM_Shutdown(); - return false; - } - 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]); - -//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); - globalfloat (true, time); - globalfloat (true, frametime); - globalint (false, newmis); //not always in nq. - globalfloat (false, force_retouch); - globalstring (true, mapname); -// globalfloat (false, deathmatch); -// globalfloat (false, coop); -// globalfloat (false, teamplay); - globalfloat (true, serverflags); - globalfloat (true, total_secrets); - globalfloat (true, total_monsters); - globalfloat (true, found_secrets); - globalfloat (true, killed_monsters); - globalvec (true, v_forward); - globalvec (true, v_up); - globalvec (true, v_right); - globalfloat (true, trace_allsolid); - globalfloat (true, trace_startsolid); - globalfloat (true, trace_fraction); - globalvec (true, trace_endpos); - globalvec (true, trace_plane_normal); - globalfloat (true, trace_plane_dist); - globalint (true, trace_ent); - globalfloat (true, trace_inopen); - globalfloat (true, trace_inwater); -// globalfloat (false, trace_endcontents); -// globalfloat (false, trace_surfaceflags); -// globalfloat (false, cycle_wrapped); - globalint (false, msg_entity); - globalfunc (false, main); - globalfunc (true, StartFrame); - globalfunc (true, PlayerPreThink); - globalfunc (true, PlayerPostThink); - globalfunc (true, ClientKill); - globalfunc (true, ClientConnect); - globalfunc (true, PutClientInServer); - globalfunc (true, ClientDisconnect); - globalfunc (false, SetNewParms); - globalfunc (false, SetChangeParms); - - pr_nqglobal_struct->trace_surfaceflags = &writable; - pr_nqglobal_struct->trace_endcontents = &writable; - - for (i = 0; i < 16; i++) - spawnparamglobals[i] = (float*)((char*)VM_MemoryBase(q1qvm)+(long)(&gd->global->parm1 + i)); - for (; i < NUM_SPAWN_PARMS; i++) - spawnparamglobals[i] = NULL; - - - sv.edicts = EDICT_NUM(svprogfuncs, 0); - - return true; -} - - - - -void Q1QVM_ClientConnect(client_t *cl) -{ - if (cl->edict->v->netname) - { - strcpy(cl->namebuf, cl->name); - cl->name = Q1QVMPF_StringToNative(svprogfuncs, cl->edict->v->netname); - //FIXME: check this pointer - strcpy(cl->name, cl->namebuf); - } - // call the spawn function - pr_global_struct->time = sv.time; - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict); - VM_Call(q1qvm, GAME_CLIENT_CONNECT, cl->spectator); - - // actually spawn the player - pr_global_struct->time = sv.time; - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict); - 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); -} - -void Q1QVM_RunPlayerThink(void) -{ - VM_Call(q1qvm, GAME_EDICT_THINK); - VM_Call(q1qvm, GAME_CLIENT_THINK, host_client->spectator); -} - -void Q1QVM_PostThink(void) -{ - VM_Call(q1qvm, GAME_CLIENT_POSTTHINK, host_client->spectator); -} - -void Q1QVM_StartFrame(void) -{ - VM_Call(q1qvm, GAME_START_FRAME, (int)(sv.time*1000)); -} - -void Q1QVM_Touch(void) -{ - VM_Call(q1qvm, GAME_EDICT_TOUCH); -} - -void Q1QVM_Think(void) -{ - VM_Call(q1qvm, GAME_EDICT_THINK); -} - -void Q1QVM_Blocked(void) -{ - VM_Call(q1qvm, GAME_EDICT_BLOCKED); -} - -void Q1QVM_SetNewParms(void) -{ - VM_Call(q1qvm, GAME_SETNEWPARMS); -} - -void Q1QVM_SetChangeParms(void) -{ - VM_Call(q1qvm, GAME_SETCHANGEPARMS); -} - -void Q1QVM_ClientCommand(void) -{ - VM_Call(q1qvm, GAME_CLIENT_COMMAND); -} - -void Q1QVM_DropClient(client_t *cl) -{ - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict); - VM_Call(q1qvm, GAME_CLIENT_DISCONNECT); -} - -void Q1QVM_ChainMoved(void) -{ -} -void Q1QVM_EndFrame(void) -{ -} - -#endif - +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "quakedef.h" +#include "qwsvdef.h" + +#ifdef VM_Q1 + +#define GAME_API_VERSION 12 +#define MAX_Q1QVM_EDICTS 768 //according to ktx at api version 12 (fte's protocols go to 2048) + +#define VMFSID_Q1QVM 57235 //a cookie + +#define WASTED_EDICT_T_SIZE 112 //qclib has split edict_t and entvars_t. + //mvdsv and the api we're implementing has them in one lump + //so we need to bias our entvars_t and fake the offsets a little. + +//=============================================================== + +// +// system traps provided by the main engine +// +typedef enum +{ + //============== general Quake services ================== + + G_GETAPIVERSION, // ( void); //0 + + G_DPRINT, // ( const char *string ); //1 + // print message on the local console + + G_ERROR, // ( const char *string ); //2 + // abort the game + G_GetEntityToken, //3 + + G_SPAWN_ENT, //4 + G_REMOVE_ENT, //5 + G_PRECACHE_SOUND, + G_PRECACHE_MODEL, + G_LIGHTSTYLE, + G_SETORIGIN, + G_SETSIZE, //10 + G_SETMODEL, + G_BPRINT, + G_SPRINT, + G_CENTERPRINT, + G_AMBIENTSOUND, //15 + G_SOUND, + G_TRACELINE, + G_CHECKCLIENT, + G_STUFFCMD, + G_LOCALCMD, //20 + G_CVAR, + G_CVAR_SET, + G_FINDRADIUS, + G_WALKMOVE, + G_DROPTOFLOOR, //25 + G_CHECKBOTTOM, + G_POINTCONTENTS, + G_NEXTENT, + G_AIM, + G_MAKESTATIC, //30 + G_SETSPAWNPARAMS, + G_CHANGELEVEL, + G_LOGFRAG, + G_GETINFOKEY, + G_MULTICAST, //35 + G_DISABLEUPDATES, + G_WRITEBYTE, + G_WRITECHAR, + G_WRITESHORT, + G_WRITELONG, //40 + G_WRITEANGLE, + G_WRITECOORD, + G_WRITESTRING, + G_WRITEENTITY, + G_FLUSHSIGNON, //45 + g_memset, + g_memcpy, + g_strncpy, + g_sin, + g_cos, //50 + g_atan2, + g_sqrt, + g_floor, + g_ceil, + g_acos, //55 + G_CMD_ARGC, + G_CMD_ARGV, + G_TraceCapsule, + G_FS_OpenFile, + G_FS_CloseFile, //60 + G_FS_ReadFile, + G_FS_WriteFile, + G_FS_SeekFile, + G_FS_TellFile, + G_FS_GetFileList, //65 + G_CVAR_SET_FLOAT, + G_CVAR_STRING, + G_Map_Extension, + G_strcmp, + G_strncmp, //70 + G_stricmp, + G_strnicmp, + G_Find, + G_executecmd, + G_conprint, //75 + G_readcmd, + G_redirectcmd, + G_Add_Bot, + G_Remove_Bot, + G_SetBotUserInfo, //80 + G_SetBotCMD, + + G_strftime, + G_CMD_ARGS, + G_CMD_TOKENIZE, + G_strlcpy, //85 + G_strlcat, + G_MAKEVECTORS, + G_NEXTCLIENT, + + G_PRECAHCE_VWEP_MODEL, + G_SETPAUSE, + G_SETUSERINFO, + + + G_MAX +} gameImport_t; + + +// +// functions exported by the game subsystem +// +typedef enum +{ + GAME_INIT, // ( int levelTime, int randomSeed, int restart ); + // init and shutdown will be called every single level + // The game should call G_GET_ENTITY_TOKEN to parse through all the + // entity configuration text and spawn gentities. + GAME_LOADENTS, + GAME_SHUTDOWN, // (void); + + GAME_CLIENT_CONNECT, // ( int clientNum ,int isSpectator); + GAME_PUT_CLIENT_IN_SERVER, + + GAME_CLIENT_USERINFO_CHANGED, // ( int clientNum,int isSpectator ); + + GAME_CLIENT_DISCONNECT, // ( int clientNum,int isSpectator ); + + GAME_CLIENT_COMMAND, // ( int clientNum,int isSpectator ); + + GAME_CLIENT_PRETHINK, + GAME_CLIENT_THINK, // ( int clientNum,int isSpectator ); + GAME_CLIENT_POSTTHINK, + + GAME_START_FRAME, // ( int levelTime ); + GAME_SETCHANGEPARMS, //self + GAME_SETNEWPARMS, + GAME_CONSOLE_COMMAND, // ( void ); + GAME_EDICT_TOUCH, //(self,other) + GAME_EDICT_THINK, //(self,other=world,time) + GAME_EDICT_BLOCKED, //(self,other) + GAME_CLIENT_SAY, //(int isteam) +} gameExport_t; + + +typedef enum +{ + F_INT, + F_FLOAT, + F_LSTRING, // string on disk, pointer in memory, TAG_LEVEL +// F_GSTRING, // string on disk, pointer in memory, TAG_GAME + F_VECTOR, + F_ANGLEHACK, +// F_ENTITY, // index on disk, pointer in memory +// F_ITEM, // index on disk, pointer in memory +// F_CLIENT, // index on disk, pointer in memory + F_IGNORE +} fieldtype_t; + +typedef struct +{ + string_t name; + int ofs; + fieldtype_t type; +// int flags; +} field_t; + + + +typedef struct { + int pad[28]; + int self; + int other; + int world; + float time; + float frametime; + int newmis; + float force_retouch; + string_t mapname; + float serverflags; + float total_secrets; + float total_monsters; + float found_secrets; + float killed_monsters; + float parm1; + float parm2; + float parm3; + float parm4; + float parm5; + float parm6; + float parm7; + float parm8; + float parm9; + float parm10; + float parm11; + float parm12; + float parm13; + float parm14; + float parm15; + float parm16; + vec3_t v_forward; + vec3_t v_up; + vec3_t v_right; + float trace_allsolid; + float trace_startsolid; + float trace_fraction; + vec3_t trace_endpos; + vec3_t trace_plane_normal; + float trace_plane_dist; + int trace_ent; + float trace_inopen; + float trace_inwater; + int msg_entity; + func_t main; + func_t StartFrame; + func_t PlayerPreThink; + func_t PlayerPostThink; + func_t ClientKill; + func_t ClientConnect; + func_t PutClientInServer; + func_t ClientDisconnect; + func_t SetNewParms; + func_t SetChangeParms; +} 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; + int sizeofent; + q1qvmglobalvars_t *global; + field_t *fields; + int APIversion; +} gameDataN_t; + +typedef struct +{ + unsigned int ents; + int sizeofent; + unsigned int global; + unsigned int fields; + int APIversion; +} gameData32_t; + +typedef int fileHandle_t; + +typedef enum { + FS_READ_BIN, + FS_READ_TXT, + FS_WRITE_BIN, + FS_WRITE_TXT, + FS_APPEND_BIN, + FS_APPEND_TXT +} fsMode_t; + +typedef enum { + FS_SEEK_CUR, + FS_SEEK_END, + FS_SEEK_SET +} fsOrigin_t; + + + + + + + + + + + +static field_t *qvmfields; +static char *q1qvmentstring; +static vm_t *q1qvm; +static progfuncs_t q1qvmprogfuncs; +static edict_t *q1qvmedicts[MAX_Q1QVM_EDICTS]; + + +static void *evars; //pointer to the gamecodes idea of an edict_t +static int vevars; //offset into the vm base of evars + +/* +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= sv.max_edicts) + return NULL; + + e = q1qvmedicts[num]; + if (!e) + { + e = q1qvmedicts[num] = Z_TagMalloc(sizeof(edict_t)+sizeof(extentvars_t), VMFSID_Q1QVM); + e->v = (stdentvars_t*)((char*)evars + (num * pr_edict_size) + WASTED_EDICT_T_SIZE); + e->xv = (extentvars_t*)(e+1); + e->entnum = num; + } + return e; +} + +static unsigned int Q1QVMPF_NumForEdict(progfuncs_t *pf, edict_t *e) +{ + return e->entnum; +} + +static int Q1QVMPF_EdictToProgs(progfuncs_t *pf, edict_t *e) +{ + return e->entnum*pr_edict_size; +} +static edict_t *Q1QVMPF_ProgsToEdict(progfuncs_t *pf, int num) +{ + if (num % pr_edict_size) + Con_Printf("Edict To Progs with remainder\n"); + num /= pr_edict_size; + + return Q1QVMPF_EdictNum(pf, num); +} + +void Q1QVMED_ClearEdict (edict_t *e, qboolean wipe) +{ + int num = e->entnum; + if (wipe) + memset (e->v, 0, pr_edict_size - WASTED_EDICT_T_SIZE); + e->isfree = false; + e->entnum = num; +} + +static void Q1QVMPF_EntRemove(progfuncs_t *pf, edict_t *e) +{ + if (!ED_CanFree(e)) + return; + e->isfree = true; + e->freetime = sv.time; +} + +static edict_t *Q1QVMPF_EntAlloc(progfuncs_t *pf) +{ + int i; + edict_t *e; + for ( i=0 ; iisfree && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) )) + { + Q1QVMED_ClearEdict (e, true); + + ED_Spawned((struct edict_s *) e); + return (struct edict_s *)e; + } + } + + if (i >= sv.max_edicts-1) //try again, but use timed out ents. + { + for ( i=0 ; iisfree)) + { + Q1QVMED_ClearEdict (e, true); + + ED_Spawned((struct edict_s *) e); + return (struct edict_s *)e; + } + } + + if (i >= sv.max_edicts-1) + { + Sys_Error ("ED_Alloc: no free edicts"); + } + } + + sv.num_edicts++; + e = (edict_t*)EDICT_NUM(pf, i); + +// new ents come ready wiped +// Q1QVMED_ClearEdict (e, false); + + ED_Spawned((struct edict_s *) e); + + return (struct edict_s *)e; +} + +static int Q1QVMPF_LoadEnts(progfuncs_t *pf, char *mapstring, float spawnflags) +{ + q1qvmentstring = mapstring; + VM_Call(q1qvm, GAME_LOADENTS); + q1qvmentstring = NULL; + return pr_edict_size; +} + +static eval_t *Q1QVMPF_GetEdictFieldValue(progfuncs_t *pf, edict_t *e, char *fieldname, evalc_t *cache) +{ + if (!strcmp(fieldname, "message")) + { + return (eval_t*)&e->v->message; + } + return NULL; +} + +static eval_t *Q1QVMPF_FindGlobal (progfuncs_t *prinst, char *name, progsnum_t num) +{ + return NULL; +} + +static globalvars_t *Q1QVMPF_Globals(progfuncs_t *prinst, int prnum) +{ + return NULL; +} + +static string_t Q1QVMPF_StringToProgs(progfuncs_t *prinst, char *str) +{ + return (string_t)(str - (char*)VM_MemoryBase(q1qvm)); +} + +static char *Q1QVMPF_StringToNative(progfuncs_t *prinst, string_t str) +{ + return (char*)VM_MemoryBase(q1qvm) + str; +} + +void PF_WriteByte (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_WriteChar (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_WriteShort (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_WriteLong (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_WriteAngle (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_WriteCoord (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_WriteFloat (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_WriteString (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_WriteEntity (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_multicast (progfuncs_t *prinst, struct globalvars_s *pr_globals); + +void PF_svtraceline (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_changelevel (progfuncs_t *prinst, struct globalvars_s *pr_globals); + +void PF_cvar_set (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_cvar_setf (progfuncs_t *prinst, struct globalvars_s *pr_globals); + +void PF_lightstyle (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_ambientsound (progfuncs_t *prinst, struct globalvars_s *pr_globals); + +void PF_makestatic (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_logfrag (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_centerprint (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_localcmd (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_ExecuteCommand (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_setspawnparms (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_walkmove (progfuncs_t *prinst, struct globalvars_s *pr_globals); +void PF_ForceInfoKey(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); + +static int WrapQCBuiltin(builtin_t func, void *offset, unsigned int mask, const int *arg, char *argtypes) +{ + globalvars_t gv; + int argnum=0; + while(*argtypes) + { + switch(*argtypes++) + { + case 'f': + gv.param[argnum++].f = VM_FLOAT(*arg++); + break; + case 'i': + gv.param[argnum++].f = VM_LONG(*arg++); + break; + case 'n': //ent num + gv.param[argnum++].i = EDICT_TO_PROG(svprogfuncs, Q1QVMPF_EdictNum(svprogfuncs, VM_LONG(*arg++))); + break; + case 'v': //three seperate args -> 1 vector + gv.param[argnum].vec[0] = VM_FLOAT(*arg++); + gv.param[argnum].vec[1] = VM_FLOAT(*arg++); + gv.param[argnum].vec[2] = VM_FLOAT(*arg++); + argnum++; + break; + case 's': //three seperate args -> 1 vector + gv.param[argnum].i = VM_LONG(*arg++); + argnum++; + break; + } + } + svprogfuncs->callargc = &argnum; + gv.ret.i = 0; + func(svprogfuncs, &gv); + return gv.ret.i; +} + +#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. +static int syscallqvm (void *offset, unsigned int mask, int fn, const int *arg) +{ + switch (fn) + { + case G_GETAPIVERSION: + return GAME_API_VERSION; + + case G_DPRINT: + Con_Printf("%s", VM_POINTER(arg[0])); + break; + + case G_ERROR: + SV_Error("Q1QVM: %s", VM_POINTER(arg[0])); + break; + + case G_GetEntityToken: + { + if (VM_OOB(arg[0], arg[1])) + return false; + if (q1qvmentstring) + { + char *ret = VM_POINTER(arg[0]); + q1qvmentstring = COM_Parse(q1qvmentstring); + Q_strncpyz(ret, com_token, VM_LONG(arg[1])); + return *com_token != 0; + } + else + { + char *ret = VM_POINTER(arg[0]); + strcpy(ret, ""); + return false; + } + } + break; + + case G_SPAWN_ENT: + return Q1QVMPF_EntAlloc(svprogfuncs)->entnum; + + case G_REMOVE_ENT: + if (arg[0] >= sv.max_edicts) + return false; + Q1QVMPF_EntRemove(svprogfuncs, q1qvmedicts[arg[0]]); + return true; + + case G_PRECACHE_SOUND: + PF_precache_sound_Internal(svprogfuncs, VM_POINTER(arg[0])); + break; + + case G_PRECACHE_MODEL: + PF_precache_model_Internal(svprogfuncs, VM_POINTER(arg[0])); + break; + + case G_LIGHTSTYLE: + WrapQCBuiltin(PF_lightstyle, offset, mask, arg, "is"); + break; + + case G_SETORIGIN: + { + edict_t *e = Q1QVMPF_EdictNum(svprogfuncs, arg[0]); + if (!e || e->isfree) + return false; + + e->v->origin[0] = VM_FLOAT(arg[1]); + e->v->origin[1] = VM_FLOAT(arg[2]); + e->v->origin[2] = VM_FLOAT(arg[3]); + SV_LinkEdict (e, false); + return true; + } + break; + + case G_SETSIZE: + { + edict_t *e = Q1QVMPF_EdictNum(svprogfuncs, arg[0]); + if (!e || e->isfree) + return false; + + e->v->mins[0] = VM_FLOAT(arg[1]); + e->v->mins[1] = VM_FLOAT(arg[2]); + e->v->mins[2] = VM_FLOAT(arg[3]); + + e->v->maxs[0] = VM_FLOAT(arg[4]); + e->v->maxs[1] = VM_FLOAT(arg[5]); + e->v->maxs[2] = VM_FLOAT(arg[6]); + + VectorSubtract (e->v->maxs, e->v->mins, e->v->size); + SV_LinkEdict (e, false); + return true; + } + case G_SETMODEL: + { + edict_t *e = Q1QVMPF_EdictNum(svprogfuncs, arg[0]); + PF_setmodel_Internal(svprogfuncs, e, VM_POINTER(arg[1])); + } + break; + + case G_BPRINT: + SV_BroadcastPrintf(arg[0], "%s", VM_POINTER(arg[1])); + break; + + case G_SPRINT: + if ((unsigned)VM_LONG(arg[0]) > sv.allocated_client_slots) + return 0; + SV_ClientPrintf(&svs.clients[VM_LONG(arg[0])-1], VM_LONG(arg[1]), "%s", VM_POINTER(arg[2])); + break; + + case G_CENTERPRINT: + WrapQCBuiltin(PF_centerprint, offset, mask, arg, "ns"); + break; + + case G_AMBIENTSOUND: + WrapQCBuiltin(PF_ambientsound, offset, mask, arg, "vsff"); + break; + + case G_SOUND: +// ( int edn, int channel, char *samp, float vol, float att ) + SV_StartSound (Q1QVMPF_EdictNum(svprogfuncs, VM_LONG(arg[0])), VM_LONG(arg[1]), VM_POINTER(arg[2]), VM_FLOAT(arg[3])*255, VM_FLOAT(arg[4])); + break; + + case G_TRACELINE: + WrapQCBuiltin(PF_svtraceline, offset, mask, arg, "vvin"); + break; + + case G_CHECKCLIENT: + return PF_checkclient_Internal(svprogfuncs); + + case G_STUFFCMD: + { + char *s; + client_t *cl; + if ((unsigned)VM_LONG(arg[0]) > sv.allocated_client_slots) + return -1; + cl = &svs.clients[VM_LONG(arg[0])-1]; + if (cl->state != cs_spawned) + return -1; + s = VM_POINTER(arg[1]); + + ClientReliableWrite_Begin (cl, svc_stufftext, 3+strlen(s)); + ClientReliableWrite_String(cl, s); + } + break; + + case G_LOCALCMD: + WrapQCBuiltin(PF_localcmd, offset, mask, arg, "s"); + break; + + case G_CVAR: + { + int i; + cvar_t *c; + c = Cvar_Get(VM_POINTER(arg[0]), "", 0, "Gamecode"); + i = VM_LONG(c->value); + return i; + } + + case G_CVAR_SET: + WrapQCBuiltin(PF_cvar_set, offset, mask, arg, "ss"); + break; + + case G_FINDRADIUS: + { + int start = ((char*)VM_POINTER(arg[0]) - (char*)evars) / pr_edict_size; + edict_t *ed; + vec3_t diff; + float *org = VM_POINTER(arg[1]); + float rad = VM_FLOAT(arg[2]); + rad *= rad; + for(start++; start < sv.num_edicts; start++) + { + ed = EDICT_NUM(svprogfuncs, start); + if (ed->isfree) + continue; + VectorSubtract(ed->v->origin, org, diff); + if (rad > DotProduct(diff, diff)) + return (int)(vevars + start*pr_edict_size); + } + return 0; + } + + case G_WALKMOVE: + return WrapQCBuiltin(PF_cvar_set, offset, mask, arg, "ff"); + + case G_DROPTOFLOOR: + { + edict_t *ent; + vec3_t end; + vec3_t start; + trace_t trace; + extern cvar_t pr_droptofloorunits; + + ent = PROG_TO_EDICT(svprogfuncs, pr_global_struct->self); + + VectorCopy (ent->v->origin, end); + if (pr_droptofloorunits.value > 0) + end[2] -= pr_droptofloorunits.value; + else + end[2] -= 256; + + VectorCopy (ent->v->origin, start); + trace = SV_Move (start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent); + + if (trace.fraction == 1 || trace.allsolid) + return false; + else + { + VectorCopy (trace.endpos, ent->v->origin); + SV_LinkEdict (ent, false); + ent->v->flags = (int)ent->v->flags | FL_ONGROUND; + ent->v->groundentity = EDICT_TO_PROG(svprogfuncs, trace.ent); + return true; + } + } + break; + + case G_CHECKBOTTOM: + return SV_CheckBottom(EDICT_NUM(svprogfuncs, VM_LONG(arg[0]))); + + case G_POINTCONTENTS: + { + vec3_t v; + v[0] = VM_FLOAT(arg[0]); + v[1] = VM_FLOAT(arg[1]); + v[2] = VM_FLOAT(arg[2]); + return sv.worldmodel->funcs.PointContents(sv.worldmodel, v); + } + break; + + case G_NEXTENT: + { //input output are entity numbers + unsigned int i; + edict_t *ent; + + i = VM_LONG(arg[0]); + while (1) + { + i++; + if (i >= sv.num_edicts) + { + return 0; + } + ent = EDICT_NUM(svprogfuncs, i); + if (!ent->isfree) + { + return i; + } + } + break; + } + /* + case G_AIM: //not in mvdsv anyway + break; +*/ + case G_MAKESTATIC: + WrapQCBuiltin(PF_makestatic, offset, mask, arg, "n"); + break; + + case G_SETSPAWNPARAMS: + WrapQCBuiltin(PF_setspawnparms, offset, mask, arg, "n"); + break; + + case G_CHANGELEVEL: + WrapQCBuiltin(PF_changelevel, offset, mask, arg, "s"); + break; + + case G_LOGFRAG: + WrapQCBuiltin(PF_logfrag, offset, mask, arg, "nn"); + break; + + case G_GETINFOKEY: + { + char *v; + if (VM_OOB(arg[2], arg[3])) + return -1; + v = PF_infokey_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1])); + Q_strncpyz(VM_POINTER(arg[2]), v, VM_LONG(arg[3])); + } + break; + + case G_MULTICAST: + WrapQCBuiltin(PF_multicast, offset, mask, arg, "vi"); + break; + + case G_DISABLEUPDATES: + //FIXME: remember to ask mvdsv people why this is useful + Con_Printf("G_DISABLEUPDATES: not supported\n"); + break; + + case G_WRITEBYTE: + WrapQCBuiltin(PF_WriteByte, offset, mask, arg, "ii"); + break; + case G_WRITECHAR: + WrapQCBuiltin(PF_WriteChar, offset, mask, arg, "ii"); + break; + case G_WRITESHORT: + WrapQCBuiltin(PF_WriteShort, offset, mask, arg, "ii"); + break; + case G_WRITELONG: + WrapQCBuiltin(PF_WriteLong, offset, mask, arg, "ii"); + break; + case G_WRITEANGLE: + WrapQCBuiltin(PF_WriteAngle, offset, mask, arg, "if"); + break; + case G_WRITECOORD: + WrapQCBuiltin(PF_WriteCoord, offset, mask, arg, "if"); + break; + case G_WRITESTRING: + WrapQCBuiltin(PF_WriteString, offset, mask, arg, "is"); + break; + case G_WRITEENTITY: + WrapQCBuiltin(PF_WriteEntity, offset, mask, arg, "in"); + break; + + case G_FLUSHSIGNON: + SV_FlushSignon (); + break; + + case g_memset: + { + void *dst = VM_POINTER(arg[0]); + VALIDATEPOINTER(arg[0], arg[2]); + memset(dst, arg[1], arg[2]); + return arg[0]; + } + case g_memcpy: + { + 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]); + Q_strncpyS(VM_POINTER(arg[0]), VM_POINTER(arg[1]), arg[2]); + return arg[0]; + case g_sin: + VM_FLOAT(fn)=(float)sin(VM_FLOAT(arg[0])); + return fn; + case g_cos: + VM_FLOAT(fn)=(float)cos(VM_FLOAT(arg[0])); + return fn; + case g_atan2: + VM_FLOAT(fn)=(float)atan2(VM_FLOAT(arg[0]), VM_FLOAT(arg[1])); + return fn; + case g_sqrt: + VM_FLOAT(fn)=(float)sqrt(VM_FLOAT(arg[0])); + return fn; + case g_floor: + VM_FLOAT(fn)=(float)floor(VM_FLOAT(arg[0])); + return fn; + case g_ceil: + VM_FLOAT(fn)=(float)ceil(VM_FLOAT(arg[0])); + return fn; + case g_acos: + VM_FLOAT(fn)=(float)acos(VM_FLOAT(arg[0])); + return fn; + + case G_CMD_ARGC: + return Cmd_Argc(); + case G_CMD_ARGV: + { + char *c; + c = Cmd_Argv(VM_LONG(arg[0])); + if (VM_OOB(arg[1], arg[2])) + return -1; + Q_strncpyz(VM_POINTER(arg[1]), c, VM_LONG(arg[2])); + } + break; + + case G_TraceCapsule: + WrapQCBuiltin(PF_svtraceline, offset, mask, arg, "vvinvv"); + break; + + case G_FS_OpenFile: + //0 = name + //1 = &handle + //2 = mode + //ret = filesize or -1 + + // Con_Printf("G_FSOpenFile: %s (mode %i)\n", VM_POINTER(arg[0]), arg[2]); + + return VM_fopen(VM_POINTER(arg[0]), VM_POINTER(arg[1]), arg[2]/2, VMFSID_Q1QVM); + + case G_FS_CloseFile: + VM_fclose(arg[0], VMFSID_Q1QVM); + break; + + case G_FS_ReadFile: + if (VM_OOB(arg[0], arg[1])) + return 0; + return VM_FRead(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VMFSID_Q1QVM); +/* + //not supported, open will fail anyway + case G_FS_WriteFile: + return VM_FWrite(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VMFSID_Q1QVM); + break; +*/ +/* + case G_FS_SeekFile: +// int trap_FS_SeekFile( fileHandle_t handle, int offset, int type ) + return VM_FSeek(VM_LONG(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2])); + break; +*/ +/* + case G_FS_TellFile: + break; +*/ + + case G_FS_GetFileList: + if (VM_OOB(arg[2], arg[3])) + return 0; + return VM_GetFileList(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3])); + break; + + case G_CVAR_SET_FLOAT: + WrapQCBuiltin(PF_cvar_setf, offset, mask, arg, "sf"); + break; + + case G_CVAR_STRING: + { + cvar_t *cv; + if (VM_OOB(arg[1], arg[2])) + return -1; + cv = Cvar_Get(VM_POINTER(arg[0]), "", 0, "QC variables"); + Q_strncpyz(VM_POINTER(arg[1]), cv->string, VM_LONG(arg[2])); + } + break; + + case G_Map_Extension: + //yes, this does exactly match mvdsv... + if (VM_LONG(arg[1]) < G_MAX) + return -2; //can't map that there + else + return -1; //extension not known + + case G_strcmp: + { + char *a = VM_POINTER(arg[0]); + char *b = VM_POINTER(arg[1]); + return strcmp(a, b); + } + case G_strncmp: + { + char *a = VM_POINTER(arg[0]); + char *b = VM_POINTER(arg[1]); + return strncmp(a, b, VM_LONG(arg[2])); + } + case G_stricmp: + { + char *a = VM_POINTER(arg[0]); + char *b = VM_POINTER(arg[1]); + return stricmp(a, b); + } + case G_strnicmp: + { + char *a = VM_POINTER(arg[0]); + char *b = VM_POINTER(arg[1]); + return strnicmp(a, b, VM_LONG(arg[2])); + } + + case G_Find: + { + edict_t *e = VM_POINTER(arg[0]); + int ofs = VM_LONG(arg[1]) - WASTED_EDICT_T_SIZE; + char *match = VM_POINTER(arg[2]); + char *field; + int first = e?((char*)e - (char*)evars)/pr_edict_size:0; + int i; + if (!match) + match = ""; + for (i = first+1; i < sv.num_edicts; i++) + { + e = q1qvmedicts[i]; + field = VM_POINTER(*((string_t*)e->v + ofs/4)); + if (field == NULL) + { + if (*match == '\0') + return ((char*)e->v - (char*)offset)-WASTED_EDICT_T_SIZE; + } + else + { + if (!strcmp(field, match)) + return ((char*)e->v - (char*)offset)-WASTED_EDICT_T_SIZE; + } + } + } + return 0; + + case G_executecmd: + WrapQCBuiltin(PF_ExecuteCommand, offset, mask, arg, ""); + break; + + case G_conprint: + Con_Printf("%s", VM_POINTER(arg[0])); + break; + + case G_readcmd: + { + extern char outputbuf[]; + extern redirect_t sv_redirected; + extern int sv_redirectedlang; + redirect_t old; + int oldl; + + char *s = VM_POINTER(arg[0]); + char *output = VM_POINTER(arg[1]); + int outputlen = VM_LONG(arg[2]); + + if (VM_OOB(arg[1], arg[2])) + return -1; + + Cbuf_Execute(); //FIXME: this code is flawed + Cbuf_AddText (s, RESTRICT_LOCAL); + + old = sv_redirected; + oldl = sv_redirectedlang; + if (old != RD_NONE) + SV_EndRedirect(); + + SV_BeginRedirect(RD_OBLIVION, LANGDEFAULT); + Cbuf_Execute(); + Q_strncpyz(output, outputbuf, outputlen); + SV_EndRedirect(); + + if (old != RD_NONE) + SV_BeginRedirect(old, oldl); + +Con_DPrintf("PF_readcmd: %s\n%s", s, output); + + } + break; + + case G_redirectcmd: + //FIXME: KTX uses this, along with a big fat warning. + //it shouldn't be vital to the normal functionality + //just restricts admin a little (did these guys never hear of rcon?) + //I'm too lazy to implement it though. + return 0; + + case G_Add_Bot: + //FIXME: not implemented, always returns failure. + //the other bot functions only ever work on bots anyway, so don't need to be implemented until this one is + return 0; +/* + case G_Remove_Bot: + break; + case G_SetBotCMD: + break; +*/ + case G_SETUSERINFO: + { + char *key = VM_POINTER(arg[1]); + if (*key == '*' && (VM_LONG(arg[3])&1)) + return -1; //denied! + } + //fallthrough + case G_SetBotUserInfo: + WrapQCBuiltin(PF_ForceInfoKey, offset, mask, arg, "ess"); + return 0; + + case G_strftime: + { + char *out = VM_POINTER(arg[0]); + char *fmt = VM_POINTER(arg[2]); + time_t curtime; + struct tm *local; + if (VM_OOB(arg[0], arg[1]) || !out) + return -1; //please don't corrupt me + time(&curtime); + curtime += VM_LONG(arg[3]); + local = localtime(&curtime); + strftime(out, VM_LONG(arg[1]), fmt, local); + } + break; + case G_CMD_ARGS: + { + char *c; + c = Cmd_Args(); + if (VM_OOB(arg[0], arg[1])) + return -1; + Q_strncpyz(VM_POINTER(arg[0]), c, VM_LONG(arg[1])); + } + break; + case G_CMD_TOKENIZE: + { + char *str = VM_POINTER(arg[0]); + Cmd_TokenizeString(str, false, false); + return Cmd_Argc(); + } + break; + case G_strlcpy: + { + char *dst = VM_POINTER(arg[0]); + char *src = VM_POINTER(arg[1]); + if (VM_OOB(arg[0], arg[2])) + return -1; + Q_strncpyz(dst, src, VM_LONG(arg[2])); + //WARNING: no return value + } + break; + case G_strlcat: + { + char *dst = VM_POINTER(arg[0]); + char *src = VM_POINTER(arg[1]); + if (VM_OOB(arg[0], arg[2])) + return -1; + Q_strncatz(dst, src, VM_LONG(arg[2])); + //WARNING: no return value + } + break; + + case G_MAKEVECTORS: + AngleVectors(VM_POINTER(arg[0]), P_VEC(v_forward), P_VEC(v_right), P_VEC(v_up)); + break; + + case G_NEXTCLIENT: + { + 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 (int)(vevars + (start+1) * pr_edict_size); + start++; + } + return 0; + } + break; + + default: + SV_Error("Q1QVM: Trap %i not implemented\n", fn); + break; + } + return 0; +} + +static int EXPORT_FN syscallnative (int arg, ...) +{ + int args[13]; + va_list argptr; + + va_start(argptr, arg); + args[0]=va_arg(argptr, int); + args[1]=va_arg(argptr, int); + args[2]=va_arg(argptr, int); + args[3]=va_arg(argptr, int); + args[4]=va_arg(argptr, int); + args[5]=va_arg(argptr, int); + args[6]=va_arg(argptr, int); + args[7]=va_arg(argptr, int); + args[8]=va_arg(argptr, int); + args[9]=va_arg(argptr, int); + args[10]=va_arg(argptr, int); + args[11]=va_arg(argptr, int); + args[12]=va_arg(argptr, int); + va_end(argptr); + + return syscallqvm(NULL, ~0, arg, args); +} + +void Q1QVM_Shutdown(void) +{ + int i; + if (q1qvm) + { + for (i = 0; i < MAX_CLIENTS; i++) + { + if (svs.clients[i].name) + Q_strncpyz(svs.clients[i].namebuf, svs.clients[i].name, sizeof(svs.clients[i].namebuf)); + svs.clients[i].name = svs.clients[i].namebuf; + } + VM_Destroy(q1qvm); + } + q1qvm = NULL; + VM_fcloseall(VMFSID_Q1QVM); + if (svprogfuncs == &q1qvmprogfuncs) + svprogfuncs = NULL; + Z_FreeTags(VMFSID_Q1QVM); +} + +qboolean PR_LoadQ1QVM(void) +{ + static float writable; + int i; + gameDataN_t *gd, gdm; + gameData32_t *gd32; + int ret; + + if (q1qvm) + VM_Destroy(q1qvm); + + q1qvm = VM_Create(NULL, "qwprogs", syscallnative, syscallqvm); + if (!q1qvm) + return false; + + + progstype = PROG_QW; + + + svprogfuncs = &q1qvmprogfuncs; + + +// 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; + q1qvmprogfuncs.ProgsToEdict = Q1QVMPF_ProgsToEdict; + q1qvmprogfuncs.EntAlloc = Q1QVMPF_EntAlloc; + q1qvmprogfuncs.EntFree = Q1QVMPF_EntRemove; + q1qvmprogfuncs.FindGlobal = Q1QVMPF_FindGlobal; + q1qvmprogfuncs.load_ents = Q1QVMPF_LoadEnts; + q1qvmprogfuncs.globals = Q1QVMPF_Globals; + q1qvmprogfuncs.GetEdictFieldValue = Q1QVMPF_GetEdictFieldValue; + q1qvmprogfuncs.StringToProgs = Q1QVMPF_StringToProgs; + q1qvmprogfuncs.StringToNative = Q1QVMPF_StringToNative; + + sv.num_edicts = 0; //we're not ready for most of the builtins yet + sv.max_edicts = 0; //so clear these out, just in case + pr_edict_size = 0; //if we get a division by zero, then at least its a safe crash + + memset(q1qvmedicts, 0, sizeof(q1qvmedicts)); + + q1qvmprogfuncs.stringtable = VM_MemoryBase(q1qvm); + + ret = VM_Call(q1qvm, GAME_INIT, (int)(sv.time*1000), rand()); + if (!ret) + { + Q1QVM_Shutdown(); + return false; + } + 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]); + +//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); + globalfloat (true, time); + globalfloat (true, frametime); + globalint (false, newmis); //not always in nq. + globalfloat (false, force_retouch); + globalstring (true, mapname); +// globalfloat (false, deathmatch); +// globalfloat (false, coop); +// globalfloat (false, teamplay); + globalfloat (true, serverflags); + globalfloat (true, total_secrets); + globalfloat (true, total_monsters); + globalfloat (true, found_secrets); + globalfloat (true, killed_monsters); + globalvec (true, v_forward); + globalvec (true, v_up); + globalvec (true, v_right); + globalfloat (true, trace_allsolid); + globalfloat (true, trace_startsolid); + globalfloat (true, trace_fraction); + globalvec (true, trace_endpos); + globalvec (true, trace_plane_normal); + globalfloat (true, trace_plane_dist); + globalint (true, trace_ent); + globalfloat (true, trace_inopen); + globalfloat (true, trace_inwater); +// globalfloat (false, trace_endcontents); +// globalfloat (false, trace_surfaceflags); +// globalfloat (false, cycle_wrapped); + globalint (false, msg_entity); + globalfunc (false, main); + globalfunc (true, StartFrame); + globalfunc (true, PlayerPreThink); + globalfunc (true, PlayerPostThink); + globalfunc (true, ClientKill); + globalfunc (true, ClientConnect); + globalfunc (true, PutClientInServer); + globalfunc (true, ClientDisconnect); + globalfunc (false, SetNewParms); + globalfunc (false, SetChangeParms); + + pr_nqglobal_struct->trace_surfaceflags = &writable; + pr_nqglobal_struct->trace_endcontents = &writable; + + for (i = 0; i < 16; i++) + spawnparamglobals[i] = (float*)((char*)VM_MemoryBase(q1qvm)+(long)(&gd->global->parm1 + i)); + for (; i < NUM_SPAWN_PARMS; i++) + spawnparamglobals[i] = NULL; + + + sv.edicts = EDICT_NUM(svprogfuncs, 0); + + return true; +} + + + + +void Q1QVM_ClientConnect(client_t *cl) +{ + if (cl->edict->v->netname) + { + strcpy(cl->namebuf, cl->name); + cl->name = Q1QVMPF_StringToNative(svprogfuncs, cl->edict->v->netname); + //FIXME: check this pointer + strcpy(cl->name, cl->namebuf); + } + // call the spawn function + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict); + VM_Call(q1qvm, GAME_CLIENT_CONNECT, cl->spectator); + + // actually spawn the player + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict); + 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); +} + +void Q1QVM_RunPlayerThink(void) +{ + VM_Call(q1qvm, GAME_EDICT_THINK); + VM_Call(q1qvm, GAME_CLIENT_THINK, host_client->spectator); +} + +void Q1QVM_PostThink(void) +{ + VM_Call(q1qvm, GAME_CLIENT_POSTTHINK, host_client->spectator); +} + +void Q1QVM_StartFrame(void) +{ + VM_Call(q1qvm, GAME_START_FRAME, (int)(sv.time*1000)); +} + +void Q1QVM_Touch(void) +{ + VM_Call(q1qvm, GAME_EDICT_TOUCH); +} + +void Q1QVM_Think(void) +{ + VM_Call(q1qvm, GAME_EDICT_THINK); +} + +void Q1QVM_Blocked(void) +{ + VM_Call(q1qvm, GAME_EDICT_BLOCKED); +} + +void Q1QVM_SetNewParms(void) +{ + VM_Call(q1qvm, GAME_SETNEWPARMS); +} + +void Q1QVM_SetChangeParms(void) +{ + VM_Call(q1qvm, GAME_SETCHANGEPARMS); +} + +void Q1QVM_ClientCommand(void) +{ + VM_Call(q1qvm, GAME_CLIENT_COMMAND); +} + +void Q1QVM_DropClient(client_t *cl) +{ + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict); + VM_Call(q1qvm, GAME_CLIENT_DISCONNECT); +} + +void Q1QVM_ChainMoved(void) +{ +} +void Q1QVM_EndFrame(void) +{ +} + +#endif diff --git a/engine/server/progdefs.h b/engine/server/progdefs.h index da73d08f6..e3bcea54a 100644 --- a/engine/server/progdefs.h +++ b/engine/server/progdefs.h @@ -237,6 +237,7 @@ typedef struct extentvars_s //csqc stuph. func_t SendEntity; float Version; + float pvsflags; #ifdef VM_Q1 } extentvars_t; diff --git a/engine/server/server.h b/engine/server/server.h index 2b07d9ac9..830e4cc27 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -110,6 +110,7 @@ typedef struct float gamespeed; //time progression multiplier, fixed per-level. qboolean csqcdebug; + unsigned int csqcchecksum; qboolean mapchangelocked; double time; @@ -405,7 +406,9 @@ typedef struct client_s // client known data for deltas int old_frags; - int stats[MAX_CL_STATS]; + int statsi[MAX_CL_STATS]; + float statsf[MAX_CL_STATS]; + char *statss[MAX_CL_STATS]; union{ //save space client_frame_t *frames; // updates can be deltad from here @@ -821,6 +824,13 @@ typedef struct #define FF_CROUCHING 1 //fte flags. seperate from flags #define FF_LADDER 2 //fte flags. seperate from flags +#define PVSF_NORMALPVS 0x0 +#define PVSF_NOTRACECHECK 0x1 +#define PVSF_USEPHS 0x2 +#define PVSF_IGNOREPVS 0x3 +#define PVSF_MODE_MASK 0x3 +#define PVSF_NOREMOVE 0x80 + // entity effects //define EF_BRIGHTFIELD 1 diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 524ac0e2e..cd204e1a0 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -701,13 +701,59 @@ void SV_BanName_f (void) Con_TPrintf (STL_USERDOESNTEXIST, Cmd_Argv(1)); } +void SV_KickBanIP(netadr_t banadr, netadr_t banmask, char *reason) +{ + qboolean shouldkick; + client_t *cl; + int i; + unsigned int reasonsize; + bannedips_t *nb; + + if (reason) + reasonsize = strlen(reason); + else + reasonsize = 0; + + // loop through clients and kick the ones that match + for (i = 0, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++) + { + if (cl->state<=cs_zombie) + continue; + + shouldkick = false; + + if (NET_CompareAdrMasked(cl->netchan.remote_address, banadr, banmask)) + shouldkick = true; + else if (cl->realip_status >= 1) + if (NET_CompareAdrMasked(cl->realip, banadr, banmask)) + shouldkick = true; + + if (shouldkick) + { + // match, so kick + SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTWASBANNED, cl->name); + // print directly, because the dropped client won't get the + // SV_BroadcastPrintf message + SV_ClientTPrintf (cl, PRINT_HIGH, STL_YOUWEREBANNED); + SV_LogPlayer(cl, "banned ip"); + SV_DropClient (cl); + } + } + + // add IP and mask to ban list + nb = Z_Malloc(sizeof(bannedips_t)+reasonsize); + nb->next = svs.bannedips; + nb->adr = banadr; + nb->adrmask = banmask; + svs.bannedips = nb; + if (reasonsize) + Q_strcpy(nb->reason, reason); +} + void SV_BanIP_f (void) { netadr_t banadr; netadr_t banmask; - int i; - client_t *cl; - bannedips_t *nb; char *reason = NULL; int reasonsize = 0; @@ -730,37 +776,39 @@ void SV_BanIP_f (void) } if (Cmd_Argc() > 2) - { reason = Cmd_Argv(2); - reasonsize = strlen(reason); - } - // loop through clients and kick the ones that match - for (i = 0, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++) + SV_KickBanIP(banadr, banmask, reason); +} + +void SV_BanClientIP_f (void) +{ + netadr_t banmask; + client_t *cl; + char *reason = NULL; + int clnum=-1; + + while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) { - if (cl->state<=cs_zombie) - continue; - - if (NET_CompareAdrMasked(cl->netchan.remote_address, banadr, banmask)) + if (NET_IsLoopBackAddress(cl->netchan.remote_address)) { - // match, so kick - SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTWASBANNED, cl->name); - // print directly, because the dropped client won't get the - // SV_BroadcastPrintf message - SV_ClientTPrintf (cl, PRINT_HIGH, STL_YOUWEREBANNED); - SV_LogPlayer(cl, "banned ip"); - SV_DropClient (cl); + Con_Printf("You're not allowed to ban loopback!\n"); + continue; + } + + if (cl->realip_status>0) + { + memset(&banmask.address, 0xff, sizeof(banmask.address)); + banmask.type = cl->netchan.remote_address.type; + SV_KickBanIP(cl->realip, banmask, reason); + } + else + { + memset(&banmask.address, 0xff, sizeof(banmask.address)); + banmask.type = cl->netchan.remote_address.type; + SV_KickBanIP(cl->netchan.remote_address, banmask, reason); } } - - // add IP and mask to ban list - nb = Z_Malloc(sizeof(bannedips_t)+reasonsize); - nb->next = svs.bannedips; - nb->adr = banadr; - nb->adrmask = banmask; - svs.bannedips = nb; - if (reasonsize) - Q_strcpy(nb->reason, reason); } void SV_FilterIP_f (void) @@ -1245,6 +1293,8 @@ void SV_Status_f (void) { if (net_local_sv_ipadr.type != NA_LOOPBACK) Con_Printf ("ip address : %s\n",NET_AdrToString (net_local_sv_ipadr)); + if (net_local_sv_ip6adr.type != NA_LOOPBACK) + Con_Printf ("ipv6 address : %s\n",NET_AdrToString (net_local_sv_ip6adr)); Con_Printf("Server is not running\n"); return; @@ -1262,7 +1312,7 @@ void SV_Status_f (void) if (svs.socketip != INVALID_SOCKET && net_local_sv_ipadr.type != NA_LOOPBACK) { extern cvar_t pr_imitatemvdsv; - if (pr_imitatemvdsv.value) + if (pr_imitatemvdsv.value) //ktpro requires 'net address' for some reason that I don't remember Con_Printf ("net address : %s\n",NET_AdrToString (net_local_sv_ipadr)); else Con_Printf ("ip address : %s\n",NET_AdrToString (net_local_sv_ipadr)); @@ -1973,6 +2023,7 @@ void SV_InitOperatorCommands (void) Cmd_AddCommand ("banname", SV_BanName_f); Cmd_AddCommand ("banlist", SV_BanList_f); Cmd_AddCommand ("banip", SV_BanIP_f); + Cmd_AddCommand ("ban", SV_BanClientIP_f); Cmd_AddCommand ("unban", SV_Unban_f); // Cmd_AddCommand ("ban", SV_BanName_f); Cmd_AddCommand ("status", SV_Status_f); diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index a5bbc7eb1..0d2ce1507 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -115,6 +115,8 @@ extern int sv_lightningmodel; static edict_t *csqcent[MAX_EDICTS]; static int csqcnuments; +static edict_t *ssqcent[MAX_EDICTS]; + qboolean SV_AddNailUpdate (edict_t *ent) { if (ent->v->modelindex != sv_nailmodel @@ -348,7 +350,7 @@ void SV_EmitCSQCUpdate(client_t *client, sizebuf_t *msg) if (client->protocol != SCP_QUAKEWORLD) MSG_WriteByte(msg, svcdp_csqcentities); else - MSG_WriteByte(msg, svc_csqcentities); + MSG_WriteByte(msg, svcfte_csqcentities); } MSG_WriteShort(msg, ent->entnum); if (sv.csqcdebug) //optional extra. @@ -362,12 +364,12 @@ void SV_EmitCSQCUpdate(client_t *client, sizebuf_t *msg) // Con_Printf("Sending update packet %i\n", ent->entnum); } - else if (sv.csqcentversion[ent->entnum]) + else if (sv.csqcentversion[ent->entnum] && !((int)ent->xv->pvsflags & PVSF_NOREMOVE)) { //Don't want to send. if (!writtenheader) { writtenheader=true; - MSG_WriteByte(msg, svc_csqcentities); + MSG_WriteByte(msg, svcfte_csqcentities); } mask = (unsigned)ent->entnum | 0x8000; @@ -379,9 +381,9 @@ void SV_EmitCSQCUpdate(client_t *client, sizebuf_t *msg) } for (en = 1; en < sv.num_edicts; en++) { - if (client->csqcentversions[en] > 0 && (client->csqcentversions[en] != sv.csqcentversion[en])) + ent = EDICT_NUM(svprogfuncs, en); + if (client->csqcentversions[en] > 0 && (client->csqcentversions[en] != sv.csqcentversion[en]) && !((int)ent->xv->pvsflags & PVSF_NOREMOVE)) { - ent = EDICT_NUM(svprogfuncs, en); // if (!ent->isfree) // continue; @@ -393,7 +395,7 @@ void SV_EmitCSQCUpdate(client_t *client, sizebuf_t *msg) if (!writtenheader) { writtenheader=true; - MSG_WriteByte(msg, svc_csqcentities); + MSG_WriteByte(msg, svcfte_csqcentities); } // Con_Printf("Sending remove packet %i\n", en); @@ -2163,6 +2165,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore edict_t *clent; client_frame_t *frame; entity_state_t *state; + int pvsflags; client_t *split; @@ -2184,33 +2187,6 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore #endif for (split = client->controlled; split; split = split->controlled) sv.worldmodel->funcs.FatPVS(sv.worldmodel, split->edict->v->origin, true); -/* - if (sv.worldmodel->fromgame == fg_doom) - { - } - else -#ifdef Q2BSPS - if (sv.worldmodel->fromgame == fg_quake2 || sv.worldmodel->fromgame == fg_quake3) - { - leafnum = CM_PointLeafnum (org); - clientarea = CM_LeafArea (leafnum); - clientcluster = CM_LeafCluster (leafnum); - - SV_Q2BSP_FatPVS (org); - } - else -#endif - { - SV_Q1BSP_FatPVS (org); - -#ifdef PEXT_VIEW2 - if (clent->xv->view2) - SV_Q1BSP_AddToFatPVS (PROG_TO_EDICT(svprogfuncs, clent->xv->view2)->v->origin, sv.worldmodel->nodes); //add a little more... -#endif - for (split = client->controlled; split; split = split->controlled) - SV_Q1BSP_AddToFatPVS (split->edict->v->origin, sv.worldmodel->nodes); //add a little more... - } -*/ } else clent = NULL; @@ -2369,6 +2345,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore if (!ent->xv->SendEntity && (!ent->v->modelindex || !*PR_GetString(svprogfuncs, ent->v->model)) && !((int)ent->xv->pflags & PFLAGS_FULLDYNAMIC)) continue; + pvsflags = ent->xv->pvsflags; if (progstype != PROG_QW) { // if (progstype == PROG_H2) @@ -2388,26 +2365,43 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore if (!ignorepvs && ent != clent) { - //branch out to the pvs testing. - if (ent->xv->viewmodelforclient == EDICT_TO_PROG(svprogfuncs, client->edict)) + if ((pvsflags & PVSF_MODE_MASK) < PVSF_USEPHS) { - //unconditional - } - else if (ent->xv->tag_entity) - { - edict_t *p = ent; - int c = 10; - while(p->xv->tag_entity&&c-->0) + //branch out to the pvs testing. + if (ent->xv->viewmodelforclient == EDICT_TO_PROG(svprogfuncs, client->edict)) { - p = EDICT_NUM(svprogfuncs, p->xv->tag_entity); + //unconditional + } + else if (ent->xv->tag_entity) + { + edict_t *p = ent; + int c = 10; + while(p->xv->tag_entity&&c-->0) + { + p = EDICT_NUM(svprogfuncs, p->xv->tag_entity); + } + if (!sv.worldmodel->funcs.EdictInFatPVS(sv.worldmodel, p)) + continue; + } + else + { + if (!sv.worldmodel->funcs.EdictInFatPVS(sv.worldmodel, ent)) + continue; } - if (!sv.worldmodel->funcs.EdictInFatPVS(sv.worldmodel, p)) - continue; } - else + else if ((pvsflags & PVSF_MODE_MASK) == PVSF_USEPHS && sv.worldmodel->fromgame == fg_quake) { - if (!sv.worldmodel->funcs.EdictInFatPVS(sv.worldmodel, ent)) + int leafnum; + unsigned char *mask; + leafnum = sv.worldmodel->funcs.LeafnumForPoint(sv.worldmodel, host_client->edict->v->origin); + mask = sv.phs + leafnum * 4*((sv.worldmodel->numleafs+31)>>5); + + leafnum = sv.worldmodel->funcs.LeafnumForPoint (sv.worldmodel, ent->v->origin)-1; + if ( !(mask[leafnum>>3] & (1<<(leafnum&7)) ) ) + { + Con_Printf ("PHS supressed entity\n"); continue; + } } } @@ -2444,7 +2438,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore continue; //not in this dimension - sorry... - if (!ignorepvs && ent != clent) + if (!ignorepvs && ent != clent && (pvsflags & PVSF_MODE_MASK)==PVSF_NORMALPVS) { //more expensive culling if ((e <= sv.allocated_client_slots && sv_cullplayers_trace.value) || sv_cullentities_trace.value) if (Cull_Traceline(clent, ent)) diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index b31645211..1290eb504 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -831,11 +831,15 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us if (file) { char text[64]; - sprintf(text, "0x%x", Com_BlockChecksum(file, com_filesize)); + sv.csqcchecksum = Com_BlockChecksum(file, com_filesize); + sprintf(text, "0x%x", sv.csqcchecksum); Info_SetValueForStarKey(svs.info, "*csprogs", text, MAX_SERVERINFO_STRING); } else + { + sv.csqcchecksum = 0; Info_SetValueForStarKey(svs.info, "*csprogs", "", MAX_SERVERINFO_STRING); + } sv.csqcdebug = sv_csqcdebug.value; if (sv.csqcdebug) diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index c26c92874..5755a3e3e 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -2279,6 +2279,8 @@ void SVC_RealIP (void) { unsigned int slotnum; int cookie; + bannedips_t *banip; + slotnum = atoi(Cmd_Argv(1)); cookie = atoi(Cmd_Argv(2)); @@ -2298,6 +2300,24 @@ void SVC_RealIP (void) if (svs.clients[slotnum].realip_status) return; + if (NET_AddressSmellsFunny(net_from)) + { + Con_Printf("funny realip address: %s, ", NET_AdrToString(net_from)); + Con_Printf("proxy address: %s\n", NET_AdrToString(svs.clients[slotnum].netchan.remote_address)); + return; + } + + banip = SV_BannedAddress(&net_from); + if (banip) + { + Con_Printf("%s has a banned realip\n", svs.clients[slotnum].name); + if (banip->reason) + SV_ClientPrintf(&svs.clients[slotnum], PRINT_CHAT, "You were banned.\nReason: %s\n", banip->reason); + else + SV_ClientPrintf(&svs.clients[slotnum], PRINT_CHAT, "You were banned.\n"); + SV_DropClient(&svs.clients[slotnum]); + return; + } svs.clients[slotnum].realip_status = 1; svs.clients[slotnum].realip = net_from; diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 98486e8d0..7c17c2bdb 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -938,7 +938,7 @@ void SV_WriteEntityDataToMessage (client_t *client, sizebuf_t *msg, int pnum) other = PROG_TO_EDICT(svprogfuncs, ent->v->dmg_inflictor); if (pnum) { - MSG_WriteByte(msg, svc_choosesplitclient); + MSG_WriteByte(msg, svcfte_choosesplitclient); MSG_WriteByte(msg, pnum); } MSG_WriteByte (msg, svc_damage); @@ -956,7 +956,7 @@ void SV_WriteEntityDataToMessage (client_t *client, sizebuf_t *msg, int pnum) { if (pnum) { - MSG_WriteByte(msg, svc_choosesplitclient); + MSG_WriteByte(msg, svcfte_choosesplitclient); MSG_WriteByte(msg, pnum); } MSG_WriteByte (msg, svc_setangle); @@ -1237,7 +1237,7 @@ void SV_ClearQCStats(void) numqcstats = 0; } -void SV_UpdateQCStats(edict_t *ent, int *stats) +void SV_UpdateQCStats(edict_t *ent, int *statsi, char **statss, float *statsf) { char *s; int i; @@ -1252,20 +1252,21 @@ void SV_UpdateQCStats(edict_t *ent, int *stats) switch(qcstats[i].type) { case ev_float: - stats[qcstats[i].statnum] = eval->_float; + statsf[qcstats[i].statnum] = eval->_float; break; case ev_integer: - stats[qcstats[i].statnum] = eval->_int; + statsi[qcstats[i].statnum] = eval->_int; break; case ev_entity: - stats[qcstats[i].statnum] = NUM_FOR_EDICT(svprogfuncs, PROG_TO_EDICT(svprogfuncs, eval->edict)); + statsi[qcstats[i].statnum] = NUM_FOR_EDICT(svprogfuncs, PROG_TO_EDICT(svprogfuncs, eval->edict)); break; case ev_string: s = PR_GetString(svprogfuncs, eval->string); - stats[qcstats[i].statnum+0] = LittleLong(((int*)s)[0]); //so the network is sent out correctly as a string. - stats[qcstats[i].statnum+1] = LittleLong(((int*)s)[1]); - stats[qcstats[i].statnum+2] = LittleLong(((int*)s)[2]); - stats[qcstats[i].statnum+3] = LittleLong(((int*)s)[3]); + statss[qcstats[i].statnum] = s; +// statsi[qcstats[i].statnum+0] = LittleLong(((int*)s)[0]); //so the network is sent out correctly as a string. +// statsi[qcstats[i].statnum+1] = LittleLong(((int*)s)[1]); +// statsi[qcstats[i].statnum+2] = LittleLong(((int*)s)[2]); +// statsi[qcstats[i].statnum+3] = LittleLong(((int*)s)[3]); break; } } @@ -1282,88 +1283,92 @@ when a reliable message can be delivered this frame. void SV_UpdateClientStats (client_t *client, int pnum) { edict_t *ent; - int stats[MAX_CL_STATS]; + int statsi[MAX_CL_STATS]; + float statsf[MAX_CL_STATS]; + char *statss[MAX_CL_STATS]; int i, m; globalvars_t *pr_globals; extern qboolean pr_items2; ent = client->edict; - memset (stats, 0, sizeof(stats)); + memset (statsi, 0, sizeof(statsi)); + memset (statsf, 0, sizeof(statsf)); + memset (statss, 0, sizeof(statss)); // if we are a spectator and we are tracking a player, we get his stats // so our status bar reflects his if (client->spectator && client->spec_track > 0) ent = svs.clients[client->spec_track - 1].edict; - stats[STAT_HEALTH] = ent->v->health; - stats[STAT_WEAPON] = SV_ModelIndex(PR_GetString(svprogfuncs, ent->v->weaponmodel)); + statsf[STAT_HEALTH] = ent->v->health; //sorry, but mneh + statsi[STAT_WEAPON] = SV_ModelIndex(PR_GetString(svprogfuncs, ent->v->weaponmodel)); if (host_client->fteprotocolextensions & PEXT_MODELDBL) { - if ((unsigned)stats[STAT_WEAPON] >= 512) - stats[STAT_WEAPON] = 0; + if ((unsigned)statsi[STAT_WEAPON] >= 512) + statsi[STAT_WEAPON] = 0; } else { - if ((unsigned)stats[STAT_WEAPON] >= 256) - stats[STAT_WEAPON] = 0; + if ((unsigned)statsi[STAT_WEAPON] >= 256) + statsi[STAT_WEAPON] = 0; } - stats[STAT_AMMO] = ent->v->currentammo; - stats[STAT_ARMOR] = ent->v->armorvalue; - stats[STAT_SHELLS] = ent->v->ammo_shells; - stats[STAT_NAILS] = ent->v->ammo_nails; - stats[STAT_ROCKETS] = ent->v->ammo_rockets; - stats[STAT_CELLS] = ent->v->ammo_cells; + statsf[STAT_AMMO] = ent->v->currentammo; + statsf[STAT_ARMOR] = ent->v->armorvalue; + statsf[STAT_SHELLS] = ent->v->ammo_shells; + statsf[STAT_NAILS] = ent->v->ammo_nails; + statsf[STAT_ROCKETS] = ent->v->ammo_rockets; + statsf[STAT_CELLS] = ent->v->ammo_cells; if (!client->spectator) { - stats[STAT_ACTIVEWEAPON] = ent->v->weapon; + statsi[STAT_ACTIVEWEAPON] = ent->v->weapon; if (client->csqcactive) - stats[STAT_WEAPONFRAME] = ent->v->weaponframe; + statsi[STAT_WEAPONFRAME] = ent->v->weaponframe; } // stuff the sigil bits into the high bits of items for sbar if (pr_items2) - stats[STAT_ITEMS] = (int)ent->v->items | ((int)ent->xv->items2 << 23); + statsi[STAT_ITEMS] = (int)ent->v->items | ((int)ent->xv->items2 << 23); else - stats[STAT_ITEMS] = (int)ent->v->items | ((int)pr_global_struct->serverflags << 28); + statsi[STAT_ITEMS] = (int)ent->v->items | ((int)pr_global_struct->serverflags << 28); - stats[STAT_VIEWHEIGHT] = ent->v->view_ofs[2]; + statsf[STAT_VIEWHEIGHT] = ent->v->view_ofs[2]; #ifdef PEXT_VIEW2 if (ent->xv->view2) - stats[STAT_VIEW2] = NUM_FOR_EDICT(svprogfuncs, PROG_TO_EDICT(svprogfuncs, ent->xv->view2)); + statsi[STAT_VIEW2] = NUM_FOR_EDICT(svprogfuncs, PROG_TO_EDICT(svprogfuncs, ent->xv->view2)); else - stats[STAT_VIEW2] = 0; + statsi[STAT_VIEW2] = 0; #endif if (!ent->xv->viewzoom) - stats[STAT_VIEWZOOM] = 255; + statsf[STAT_VIEWZOOM] = 255; else - stats[STAT_VIEWZOOM] = ent->xv->viewzoom*255; + statsf[STAT_VIEWZOOM] = ent->xv->viewzoom*255; if (host_client->protocol == SCP_DARKPLACES7) { - float *statsf = (float*)stats; -// statsf[STAT_MOVEVARS_WALLFRICTION] = sv_wall - statsf[STAT_MOVEVARS_FRICTION] = sv_friction.value; - statsf[STAT_MOVEVARS_WATERFRICTION] = sv_waterfriction.value; - statsf[STAT_MOVEVARS_TICRATE] = 72; - statsf[STAT_MOVEVARS_TIMESCALE] = sv_gamespeed.value; - statsf[STAT_MOVEVARS_GRAVITY] = sv_gravity.value; - statsf[STAT_MOVEVARS_STOPSPEED] = sv_stopspeed.value; - statsf[STAT_MOVEVARS_MAXSPEED] = host_client->maxspeed; - statsf[STAT_MOVEVARS_SPECTATORMAXSPEED] = sv_spectatormaxspeed.value; - statsf[STAT_MOVEVARS_ACCELERATE] = sv_accelerate.value; - statsf[STAT_MOVEVARS_AIRACCELERATE] = sv_airaccelerate.value; - statsf[STAT_MOVEVARS_WATERACCELERATE] = sv_wateraccelerate.value; - statsf[STAT_MOVEVARS_ENTGRAVITY] = host_client->entgravity; - statsf[STAT_MOVEVARS_JUMPVELOCITY] = 280;//sv_jumpvelocity.value; //bah - statsf[STAT_MOVEVARS_EDGEFRICTION] = sv_edgefriction.value; - statsf[STAT_MOVEVARS_MAXAIRSPEED] = host_client->maxspeed; - statsf[STAT_MOVEVARS_STEPHEIGHT] = 18; - statsf[STAT_MOVEVARS_AIRACCEL_QW] = 1; - statsf[STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION] = sv_gravity.value; + float *statsfi = (float*)statsi; +// statsfi[STAT_MOVEVARS_WALLFRICTION] = sv_wall + statsfi[STAT_MOVEVARS_FRICTION] = sv_friction.value; + statsfi[STAT_MOVEVARS_WATERFRICTION] = sv_waterfriction.value; + statsfi[STAT_MOVEVARS_TICRATE] = 72; + statsfi[STAT_MOVEVARS_TIMESCALE] = sv_gamespeed.value; + statsfi[STAT_MOVEVARS_GRAVITY] = sv_gravity.value; + statsfi[STAT_MOVEVARS_STOPSPEED] = sv_stopspeed.value; + statsfi[STAT_MOVEVARS_MAXSPEED] = host_client->maxspeed; + statsfi[STAT_MOVEVARS_SPECTATORMAXSPEED] = sv_spectatormaxspeed.value; + statsfi[STAT_MOVEVARS_ACCELERATE] = sv_accelerate.value; + statsfi[STAT_MOVEVARS_AIRACCELERATE] = sv_airaccelerate.value; + statsfi[STAT_MOVEVARS_WATERACCELERATE] = sv_wateraccelerate.value; + statsfi[STAT_MOVEVARS_ENTGRAVITY] = host_client->entgravity; + statsfi[STAT_MOVEVARS_JUMPVELOCITY] = 280;//sv_jumpvelocity.value; //bah + statsfi[STAT_MOVEVARS_EDGEFRICTION] = sv_edgefriction.value; + statsfi[STAT_MOVEVARS_MAXAIRSPEED] = host_client->maxspeed; + statsfi[STAT_MOVEVARS_STEPHEIGHT] = 18; + statsfi[STAT_MOVEVARS_AIRACCEL_QW] = 1; + statsfi[STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION] = sv_gravity.value; } - SV_UpdateQCStats(ent, stats); + SV_UpdateQCStats(ent, statsi, statss, statsf); //dmw tweek for stats pr_globals = PR_globals(svprogfuncs, PR_CURRENT); @@ -1378,71 +1383,106 @@ void SV_UpdateClientStats (client_t *client, int pnum) //dmw tweek for stats if (getplayerstati[i]) { - G_INT(OFS_PARM0) = stats[i]; + G_INT(OFS_PARM0) = statsf[i]; PR_ExecuteProgram(svprogfuncs, getplayerstati[i]); - stats[i] = G_INT(OFS_RETURN); + statsf[i] = G_INT(OFS_RETURN); } else if (getplayerstat[i]) { - G_FLOAT(OFS_PARM0) = stats[i]; + G_FLOAT(OFS_PARM0) = statsf[i]; PR_ExecuteProgram(svprogfuncs, getplayerstat[i]); - stats[i] = G_FLOAT(OFS_RETURN); + statsf[i] = G_FLOAT(OFS_RETURN); } if (sv.demofile) { if (!client->spec_track) { - stats[i] = 0; + statsf[i] = 0; if (i == STAT_HEALTH) - stats[i] = 100; + statsf[i] = 100; } else - stats[i] = sv.recordedplayer[client->spec_track - 1].stats[i]; - } - if (stats[i] != client->stats[i]) - { - client->stats[i] = stats[i]; -#ifdef NQPROT - if (!ISQWCLIENT(client)) { + statsf[i] = sv.recordedplayer[client->spec_track - 1].stats[i]; + statsi[i] = sv.recordedplayer[client->spec_track - 1].stats[i]; + } + } + if (!ISQWCLIENT(client)) + { + if (!statsi[i]) + statsi[i] = *(int*)&statsf[i]; + if (statsi[i] != client->statsi[i]) + { + client->statsi[i] = statsi[i]; ClientReliableWrite_Begin(client, svc_updatestat, 3); ClientReliableWrite_Byte(client, i); - ClientReliableWrite_Long(client, stats[i]); + ClientReliableWrite_Long(client, statsi[i]); } - else -#endif - - if (pnum) + } + else + { + if ((client->fteprotocolextensions & PEXT_CSQC) && sv.csqcchecksum) { - if (stats[i] >=0 && stats[i] <= 255) + if (statsf[i] && statsf[i] - (float)(int)statsf[i] == 0) { - ClientReliableWrite_Begin(client->controller, svc_choosesplitclient, 5); + statsi[i] = statsf[i]; + statsf[i] = 0; + } + else if (statsf[i] != client->statsf[i]) + { + client->statsf[i] = statsf[i]; +// client->statsi[i] = statsi[i]; + if (pnum) + { + ClientReliableWrite_Begin(client->controller, svcfte_choosesplitclient, 8); + ClientReliableWrite_Byte(client->controller, pnum); + } + ClientReliableWrite_Begin(client, svcfte_updatestatfloat, 6); + ClientReliableWrite_Byte(client, i); + ClientReliableWrite_Float(client, statsf[i]); + } + + if (statss[i] || client->statss[i]) + if (strcmp(statss[i]?statss[i]:"", client->statss[i]?client->statss[i]:"")) + { + client->statss[i] = statss[i]; + if (pnum) + { + ClientReliableWrite_Begin(client->controller, svcfte_choosesplitclient, 5+strlen(statss[i])); + ClientReliableWrite_Byte(client->controller, pnum); + ClientReliableWrite_Byte(client->controller, svcfte_updatestatstring); + } + else + ClientReliableWrite_Begin(client, svcfte_updatestatstring, 3+strlen(statss[i])); + ClientReliableWrite_Byte(client, i); + ClientReliableWrite_String(client, statss[i]); + } + } + else if (!statsi[i]) + statsi[i] = statsf[i]; + if (statsi[i] != client->statsi[i]) + { + client->statsi[i] = statsi[i]; + client->statsf[i] = 0; + + if (pnum) + { + ClientReliableWrite_Begin(client->controller, svcfte_choosesplitclient, 8); ClientReliableWrite_Byte(client->controller, pnum); - ClientReliableWrite_Byte(client->controller, svc_updatestat); - ClientReliableWrite_Byte(client->controller, i); - ClientReliableWrite_Byte(client->controller, stats[i]); + } + if (statsi[i] >=0 && statsi[i] <= 255) + { + ClientReliableWrite_Begin(client, svc_updatestat, 3); + ClientReliableWrite_Byte(client, i); + ClientReliableWrite_Byte(client, statsi[i]); } else { - ClientReliableWrite_Begin(client->controller, svc_choosesplitclient, 8); - ClientReliableWrite_Byte(client->controller, pnum); - ClientReliableWrite_Byte(client->controller, svc_updatestatlong); - ClientReliableWrite_Byte(client->controller, i); - ClientReliableWrite_Long(client->controller, stats[i]); + ClientReliableWrite_Begin(client, svc_updatestatlong, 6); + ClientReliableWrite_Byte(client, i); + ClientReliableWrite_Long(client, statsi[i]); } } - else if (stats[i] >=0 && stats[i] <= 255) - { - ClientReliableWrite_Begin(client, svc_updatestat, 3); - ClientReliableWrite_Byte(client, i); - ClientReliableWrite_Byte(client, stats[i]); - } - else - { - ClientReliableWrite_Begin(client, svc_updatestatlong, 6); - ClientReliableWrite_Byte(client, i); - ClientReliableWrite_Long(client, stats[i]); - } } } } @@ -1534,7 +1574,7 @@ client_t *SV_SplitClientDest(client_t *client, qbyte first, int size) } sp = client->controller; - ClientReliableWrite_Begin (sp, svc_choosesplitclient, size+2); + ClientReliableWrite_Begin (sp, svcfte_choosesplitclient, size+2); ClientReliableWrite_Byte (sp, pnum); ClientReliableWrite_Byte (sp, first); return sp; @@ -1711,7 +1751,7 @@ void SV_UpdateToReliableMessages (void) } sp = host_client->controller; - ClientReliableWrite_Begin (sp, svc_choosesplitclient, 7); + ClientReliableWrite_Begin (sp, svcfte_choosesplitclient, 7); ClientReliableWrite_Byte (sp, pnum); ClientReliableWrite_Byte (sp, svc_maxspeed); ClientReliableWrite_Float(sp, newval); diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index c16fd0a15..f50a17e1a 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -82,6 +82,12 @@ extern cvar_t pm_airstep; extern cvar_t pm_walljump; cvar_t sv_pushplayers = SCVAR("sv_pushplayers", "0"); +//yes, realip cvars need to be fully initialised or realip will be disabled +cvar_t sv_getrealip = SCVAR("sv_getrealip", "0"); +cvar_t sv_realiphostname_ipv4 = SCVAR("sv_realiphostname_ipv4", ""); +cvar_t sv_realiphostname_ipv6 = SCVAR("sv_realiphostname_ipv4", ""); +cvar_t sv_realip_timeout = SCVAR("sv_realip_timeout", "10"); + char sv_votinggroup[] = "server voting"; @@ -108,16 +114,13 @@ host_client and sv_player will be valid. ============================================================ */ -qboolean SV_CheakRealIP(client_t *client, qboolean force) +qboolean SV_CheckRealIP(client_t *client, qboolean force) { //returns true if they have a real ip - cvar_t *sv_getrealip; char *serverip; char *msg; - sv_getrealip = Cvar_Get("sv_getrealip", "0", 0, "Experimental cvars"); - - if (!sv_getrealip || !sv_getrealip->value) + if (!sv_getrealip.value) return true; if (client->netchan.remote_address.type == NA_LOOPBACK) @@ -128,7 +131,6 @@ qboolean SV_CheakRealIP(client_t *client, qboolean force) if (client->realip_status == 2) { ClientReliableWrite_Begin(client, svc_print, 256); - ClientReliableWrite_Byte(client, svc_print); ClientReliableWrite_Byte(client, PRINT_HIGH); ClientReliableWrite_String(client, "Couldn't verify your real ip\n"); return true; //client doesn't support certainty. @@ -136,13 +138,17 @@ qboolean SV_CheakRealIP(client_t *client, qboolean force) if (client->realip_status == -1) return true; //can't get a better answer - if (realtime - host_client->connection_started > 10) + if (realtime - host_client->connection_started > sv_realip_timeout.value) { client->realip_status = -1; ClientReliableWrite_Begin(client, svc_print, 256); - ClientReliableWrite_Byte(client, svc_print); ClientReliableWrite_Byte(client, PRINT_HIGH); ClientReliableWrite_String(client, "Couldn't determine your real ip\n"); + if (sv_getrealip.value == 2) + { + SV_DropClient(client); + return false; + } return true; } @@ -154,9 +160,25 @@ qboolean SV_CheakRealIP(client_t *client, qboolean force) } else { - serverip = NET_AdrToString (net_local_sv_ipadr); + if (client->netchan.remote_address.type == NA_IPV6) + { + serverip = sv_realiphostname_ipv6.string; +// serverip = NET_AdrToString (net_local_sv_ip6adr); + } + else + { + serverip = sv_realiphostname_ipv4.string; +// serverip = NET_AdrToString (net_local_sv_ipadr); + } - ClientReliableWrite_Byte(client, svc_stufftext); + if (!*serverip) + { + Con_Printf("realip not fully configured\n"); + client->realip_status = -2; + return true; + } + + ClientReliableWrite_Begin(client, svc_stufftext, 256); ClientReliableWrite_String(client, va("packet %s \"realip %i %i\"\n", serverip, client-svs.clients, client->realip_num)); } return false; @@ -180,8 +202,6 @@ void SV_New_f (void) if (host_client->state == cs_spawned) return; - SV_CheakRealIP(host_client, false); - /* splitt delay host_client->state = cs_connected; host_client->connection_started = realtime; @@ -264,6 +284,7 @@ void SV_New_f (void) #endif splitnum++; } + if (host_client->fteprotocolextensions & PEXT_SPLITSCREEN) ClientReliableWrite_Byte (host_client, 128); @@ -319,6 +340,9 @@ void SV_New_f (void) host_client->csqcactive = false; + host_client->realip_num = rand()+(host_client->challenge<<16); + SV_CheckRealIP(host_client, false); + // send music ClientReliableCheckBlock(host_client, 2); @@ -873,7 +897,7 @@ void SV_Modellist_f (void) if (n >= 255) { - MSG_WriteByte (&host_client->netchan.message, svc_modellistshort); + MSG_WriteByte (&host_client->netchan.message, svcfte_modellistshort); MSG_WriteShort (&host_client->netchan.message, n); } else @@ -1062,7 +1086,7 @@ void SV_PreSpawn_f (void) } else if (host_client->fteprotocolextensions & PEXT_SPAWNSTATIC2) { - MSG_WriteByte(&host_client->netchan.message, svc_spawnbaseline2); + MSG_WriteByte(&host_client->netchan.message, svcfte_spawnbaseline2); SV_WriteDelta(&from, state, &host_client->netchan.message, true, host_client->fteprotocolextensions); } else if (state->modelindex < 256) @@ -1101,7 +1125,7 @@ void SV_PreSpawn_f (void) if (host_client->fteprotocolextensions & PEXT_CUSTOMTEMPEFFECTS) { - MSG_WriteByte(&host_client->netchan.message, svc_customtempent); + MSG_WriteByte(&host_client->netchan.message, svcfte_customtempent); MSG_WriteByte(&host_client->netchan.message, 255); MSG_WriteByte(&host_client->netchan.message, i); MSG_WriteByte(&host_client->netchan.message, ctent->netstyle); @@ -1219,7 +1243,7 @@ void SV_Spawn_f (void) #ifdef PEXT_LIGHTSTYLECOL if ((host_client->fteprotocolextensions & PEXT_LIGHTSTYLECOL) && sv.strings.lightstylecolours[i]!=7) { - ClientReliableWrite_Begin (host_client, svc_lightstylecol, + ClientReliableWrite_Begin (host_client, svcfte_lightstylecol, 3 + (sv.strings.lightstyles[i] ? strlen(sv.strings.lightstyles[i]) : 1)); ClientReliableWrite_Byte (host_client, (char)i); ClientReliableWrite_Char (host_client, sv.strings.lightstylecolours[i]); @@ -1254,7 +1278,9 @@ void SV_Spawn_f (void) // // force stats to be updated // - memset (host_client->stats, 0, sizeof(host_client->stats)); + memset (host_client->statsi, 0, sizeof(host_client->statsi)); + memset (host_client->statsf, 0, sizeof(host_client->statsf)); + memset (host_client->statss, 0, sizeof(host_client->statss)); } ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); @@ -1322,7 +1348,7 @@ void SV_Begin_f (void) int i; qboolean sendangles=false; - if (!SV_CheakRealIP(host_client, true)) + if (!SV_CheckRealIP(host_client, true)) { if (host_client->protocol == SCP_QUAKE2) ClientReliableWrite_Begin (host_client, svcq2_stufftext, 13+strlen(Cmd_Args())); @@ -3749,7 +3775,9 @@ void SVNQ_Spawn_f (void) // // force stats to be updated // - memset (host_client->stats, 0, sizeof(host_client->stats)); + memset (host_client->statsi, 0, sizeof(host_client->statsi)); + memset (host_client->statsf, 0, sizeof(host_client->statsf)); + memset (host_client->statss, 0, sizeof(host_client->statss)); ClientReliableWrite_Begin (host_client, svc_updatestat, 6); ClientReliableWrite_Byte (host_client, STAT_TOTALSECRETS); @@ -5583,6 +5611,11 @@ void SV_UserInit (void) Cvar_Register (&sv_cheatspeedchecktime, cvargroup_servercontrol); Cvar_Register (&sv_playermodelchecks, cvargroup_servercontrol); + Cvar_Register (&sv_getrealip, cvargroup_servercontrol); + Cvar_Register (&sv_realiphostname_ipv4, cvargroup_servercontrol); + Cvar_Register (&sv_realiphostname_ipv6, cvargroup_servercontrol); + Cvar_Register (&sv_realip_timeout, cvargroup_servercontrol); + Cvar_Register (&sv_pushplayers, cvargroup_servercontrol); Cvar_Register (&sv_floodprotect, cvargroup_servercontrol);