diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fe5643e3..4472a61fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -648,6 +648,41 @@ SET(FTE_CLIENT_FILES engine/vk/vk_init.c ) +ADD_LIBRARY(fteq3bot MODULE + engine/botlib/be_aas_bspq3.c + engine/botlib/be_aas_entity.c + engine/botlib/be_aas_move.c + engine/botlib/be_aas_routealt.c + engine/botlib/be_ai_char.c + engine/botlib/be_ai_goal.c + engine/botlib/be_ai_weight.c + engine/botlib/l_crc.c + engine/botlib/l_memory.c + engine/botlib/l_struct.c + engine/botlib/be_aas_cluster.c + engine/botlib/be_aas_file.c + engine/botlib/be_aas_optimize.c + engine/botlib/be_aas_route.c + engine/botlib/be_ai_chat.c + engine/botlib/be_ai_move.c + engine/botlib/be_ea.c + engine/botlib/l_libvar.c + engine/botlib/l_precomp.c + engine/botlib/be_aas_debug.c + engine/botlib/be_aas_main.c + engine/botlib/be_aas_reach.c + engine/botlib/be_aas_sample.c + engine/botlib/be_ai_gen.c + engine/botlib/be_ai_weap.c + engine/botlib/be_interface.c + engine/botlib/l_log.c + engine/botlib/l_script.c + engine/botlib/standalone.c +) +SET_TARGET_PROPERTIES(fteq3bot PROPERTIES COMPILE_DEFINITIONS "${FTE_LIB_DEFINES};${FTE_DEFINES};${FTE_REVISON};BOTLIB;EXTERNALBOTLIB") +TARGET_LINK_LIBRARIES(fteq3bot ${FTE_LIBS} ) +SET_TARGET_PROPERTIES(fteq3bot PROPERTIES LINK_FLAGS "-Wl,--no-undefined") + FILE(STRINGS "${FTE_BUILD_CONFIG}" BULLET_INTERNAL REGEX "^#define[\t ]+USE_INTERNAL_BULLET") IF(BULLET_INTERNAL) #Built-in bullet physics plugin... diff --git a/engine/botlib/be_interface.c b/engine/botlib/be_interface.c index 2444124cc..73d537d49 100644 --- a/engine/botlib/be_interface.c +++ b/engine/botlib/be_interface.c @@ -868,6 +868,13 @@ static void Init_AI_Export( ai_export_t *ai ) { GetBotLibAPI ============ */ +#ifdef EXTERNALBOTLIB +#ifdef _WIN32 + __declspec(dllexport) +#else + __attribute__((visibility("default"))) +#endif +#endif botlib_export_t *QDECL GetBotLibAPI(int apiVersion, botlib_import_t *import) { assert(import); botimport = *import; diff --git a/engine/botlib/botlib.h b/engine/botlib/botlib.h index 3da1c39a8..ef3050746 100644 --- a/engine/botlib/botlib.h +++ b/engine/botlib/botlib.h @@ -203,6 +203,8 @@ typedef struct botlib_import_s // int (*DebugPolygonCreate)(int color, int numPoints, vec3_t *points); void (*DebugPolygonDelete)(int id); + + void (*Error)(const char *msg); //for unrecoverable errors only. Will quit out. } botlib_import_t; typedef struct aas_export_s diff --git a/engine/botlib/l_precomp.c b/engine/botlib/l_precomp.c index ab995d7fe..082e49752 100644 --- a/engine/botlib/l_precomp.c +++ b/engine/botlib/l_precomp.c @@ -277,7 +277,7 @@ token_t *PC_CopyToken(token_t *token) #ifdef BSPC Error("out of token space\n"); #else - Com_Error(ERR_FATAL, "out of token space"); + botimport.Error("out of token space"); #endif return NULL; } //end if @@ -561,7 +561,7 @@ void PC_PrintDefineHashTable(define_t **definehash) int PC_NameHash(char *name) { - int register hash, i; + register int hash, i; hash = 0; for (i = 0; name[i] != '\0'; i++) diff --git a/engine/botlib/standalone.c b/engine/botlib/standalone.c new file mode 100644 index 000000000..68854cfd6 --- /dev/null +++ b/engine/botlib/standalone.c @@ -0,0 +1,198 @@ +//license GPLv2+ +//this file allows botlib to link as a dynamic lib with no external dependancies + +#include "q_shared.h" +#include "botlib.h" + +vec3_t vec3_origin; +void QDECL AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) +{ + float angle; + float sr, sp, sy, cr, cp, cy; + + angle = angles[YAW] * (M_PI*2 / 360); + sy = sin(angle); + cy = cos(angle); + angle = angles[PITCH] * (M_PI*2 / 360); + sp = sin(angle); + cp = cos(angle); + angle = angles[ROLL] * (M_PI*2 / 360); + sr = sin(angle); + cr = cos(angle); + + if (forward) + { + forward[0] = cp*cy; + forward[1] = cp*sy; + forward[2] = -sp; + } + if (right) + { + right[0] = (-1*sr*sp*cy+-1*cr*-sy); + right[1] = (-1*sr*sp*sy+-1*cr*cy); + right[2] = -1*sr*cp; + } + if (up) + { + up[0] = (cr*sp*cy+-sr*-sy); + up[1] = (cr*sp*sy+-sr*cy); + up[2] = cr*cp; + } +} +void QDECL VectorAngles(float *forward, float *up, float *result, qboolean meshpitch) +{ + float yaw, pitch, roll; + + if (forward[1] == 0 && forward[0] == 0) + { + if (forward[2] > 0) + { + pitch = -M_PI * 0.5; + yaw = up ? atan2(-up[1], -up[0]) : 0; + } + else + { + pitch = M_PI * 0.5; + yaw = up ? atan2(up[1], up[0]) : 0; + } + roll = 0; + } + else + { + yaw = atan2(forward[1], forward[0]); + pitch = -atan2(forward[2], sqrt (forward[0]*forward[0] + forward[1]*forward[1])); + + if (up) + { + vec_t cp = cos(pitch), sp = sin(pitch); + vec_t cy = cos(yaw), sy = sin(yaw); + vec3_t tleft, tup; + tleft[0] = -sy; + tleft[1] = cy; + tleft[2] = 0; + tup[0] = sp*cy; + tup[1] = sp*sy; + tup[2] = cp; + roll = -atan2(DotProduct(up, tleft), DotProduct(up, tup)); + } + else + roll = 0; + } + + pitch *= 180 / M_PI; + yaw *= 180 / M_PI; + roll *= 180 / M_PI; +// if (meshpitch) +// pitch *= r_meshpitch.value; + if (pitch < 0) + pitch += 360; + if (yaw < 0) + yaw += 360; + if (roll < 0) + roll += 360; + + result[0] = pitch; + result[1] = yaw; + result[2] = roll; +} + +vec_t QDECL VectorNormalize2 (const vec3_t v, vec3_t out) +{ + float length, ilength; + + length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; + length = sqrt (length); + if (length) + { + ilength = 1/length; + out[0] = v[0]*ilength; + out[1] = v[1]*ilength; + out[2] = v[2]*ilength; + } + else + { + VectorClear (out); + } + + return length; +} +float QDECL VectorNormalize (vec3_t v) +{ + return VectorNormalize2(v,v); +} + +void QDECL Com_sprintf (char *dest, int size, const char *fmt, ...) +{ + va_list argptr; + + va_start (argptr, fmt); + vsnprintf (dest, size, fmt, argptr); + va_end (argptr); +} + +int Q_strncasecmp (const char *s1, const char *s2, int n) +{ + int c1, c2; + + while (1) + { + c1 = *s1++; + c2 = *s2++; + + if (!n--) + return 0; // strings are equal until end point + + if (c1 != c2) + { + if (c1 >= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') + c2 -= ('a' - 'A'); + if (c1 != c2) + { // strings not equal + if (c1 > c2) + return 1; // strings not equal + return -1; + } + } + if (!c1) + return 0; // strings are equal +// s1++; +// s2++; + } + + return -1; +} +int QDECL Q_stricmp (const char *s1, const char *s2) +{ + return Q_strncasecmp (s1, s2, 0x7fffffff); +} + +void QDECL Q_strncpyz(char *d, const char *s, int n) +{ + int i; + n--; + if (n < 0) + return; //this could be an error + + for (i=0; *s; i++) + { + if (i == n) + break; + *d++ = *s++; + } + *d='\0'; +} + +char *QDECL va(char *format, ...) +{ +#define VA_BUFFER_SIZE 1024 + va_list argptr; + static char string[VA_BUFFER_SIZE]; + + va_start (argptr, format); + vsnprintf (string,sizeof(string)-1, format,argptr); + va_end (argptr); + + return string; +} diff --git a/engine/client/cl_cg.c b/engine/client/cl_cg.c index 45e462213..dfcbc348e 100644 --- a/engine/client/cl_cg.c +++ b/engine/client/cl_cg.c @@ -476,6 +476,99 @@ int CG_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projecti return ctx.numfrags; } + + +//called by the sound code. +static struct +{ + unsigned int entnum; + vec3_t origin; +// vec3_t velocity; + sfx_t *sfx; + qboolean ispersistent; +} *loopers; +static size_t numloopers; +static size_t maxloopers; +unsigned int CG_GatherLoopingSounds(vec3_t *positions, unsigned int *entnums, sfx_t **sounds, unsigned int max) +{ + size_t i; + if (max > numloopers) + max = numloopers; + for (i = 0; i < max; i++) + { + entnums[i] = loopers[i].entnum; + VectorCopy(loopers[i].origin, positions[i]); + sounds[i] = loopers[i].sfx; + } + return i; +} +static void CG_StopLoopingSounds(unsigned int entnum) +{ + size_t i; + for (i = 0; i < numloopers; i++) + { + if (loopers[i].entnum == entnum) + break; + } + if (i == numloopers) + return; + loopers[i] = loopers[numloopers-1]; + numloopers--; +} +static void CG_StartLoopingSounds(unsigned int entnum, float *origin, float *velocity, const char *soundname, qboolean persistent) +{ + size_t i; + for (i = 0; i < numloopers; i++) + { + if (loopers[i].entnum == entnum) + break; + } + + if (i == numloopers) + { + if (numloopers == maxloopers) + Z_ReallocElements((void**)&loopers, &maxloopers, maxloopers+1, sizeof(*loopers)); + numloopers++; + } + loopers[i].entnum = entnum; + VectorCopy(origin, loopers[i].origin); + //VectorCopy(velocity, loopers[i].velocity); + loopers[i].sfx = S_PrecacheSound(soundname); + loopers[i].ispersistent = persistent; +} +static void CG_MoveLoopingSound(unsigned int entnum, float *origin) +{ + size_t i; + for (i = 0; i < numloopers; i++) + { + if (loopers[i].entnum == entnum) + break; + } + + if (i == numloopers) + return; + VectorCopy(origin, loopers[i].origin); +} +static void CG_ClearLoopingSounds(qboolean clearall) +{ + if (clearall) + numloopers = 0; + else + { + size_t i; + for (i = 0; i < numloopers; ) + { + if (!loopers[i].ispersistent) + { + loopers[i] = loopers[numloopers-1]; + numloopers--; + } + else + i++; + } + } +} + int VM_LerpTag(void *out, model_t *model, int f1, int f2, float l2, char *tagname); #define VALIDATEPOINTER(o,l) if ((int)o + l >= mask || VM_POINTER(o) < offset) Host_EndGame("Call to cgame trap %u passes invalid pointer\n", (unsigned int)fn); //out of bounds. @@ -923,15 +1016,23 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con case CG_S_ADDLOOPINGSOUND: //entnum, origin, velocity, sfx -// Con_DPrintf("CG_S_ADDLOOPINGSOUND: not implemented\n"); + CG_StartLoopingSounds(VM_LONG(arg[0])+1, VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_FROMSTRCACHE(arg[3]), false); break; case CG_S_ADDREALLOOPINGSOUND: //entnum, origin, velocity, sfx -// Con_DPrintf("CG_S_ADDREALLOOPINGSOUND: not implemented\n"); + CG_StartLoopingSounds(VM_LONG(arg[0])+1, VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_FROMSTRCACHE(arg[3]), true); break; case CG_S_STOPLOOPINGSOUND: //entnum -// Con_DPrintf("CG_S_STOPLOOPINGSOUND: not implemented\n"); + CG_StopLoopingSounds(VM_LONG(arg[0])+1); + break; + case CG_S_CLEARLOOPINGSOUNDS: + //clearall + CG_ClearLoopingSounds(VM_LONG(arg[0])); + break; + case CG_S_UPDATEENTITYPOSITION://void trap_S_UpdateEntityPosition( int entityNum, const vec3_t origin ); + //entnum, org + CG_MoveLoopingSound(VM_LONG(arg[0])+1, VM_POINTER(arg[1])); break; case CG_S_STARTBACKGROUNDTRACK: @@ -940,23 +1041,16 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con case CG_S_STOPBACKGROUNDTRACK: Media_NamedTrack(NULL, NULL); return 0; - case CG_S_CLEARLOOPINGSOUNDS: - //clearall - Con_DPrintf("CG_S_CLEARLOOPINGSOUNDS: not implemented\n"); - break; - case CG_S_UPDATEENTITYPOSITION://void trap_S_UpdateEntityPosition( int entityNum, const vec3_t origin ); - //entnum, org -// Con_DPrintf("CG_S_UPDATEENTITYPOSITION: not implemented\n"); - break; case CG_S_RESPATIALIZE://void trap_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ); { + int entnum = VM_LONG(arg[0])+1; float *org = VM_POINTER(arg[1]); vec3_t *axis = VM_POINTER(arg[2]); int inwater = VM_LONG(arg[3]); cl.playerview[0].audio.defaulted = false; - cl.playerview[0].audio.entnum = VM_LONG(arg[0])+1; + cl.playerview[0].audio.entnum = entnum; VectorCopy(org, cl.playerview[0].audio.origin); VectorCopy(axis[0], cl.playerview[0].audio.forward); VectorCopy(axis[1], cl.playerview[0].audio.right); @@ -1142,7 +1236,7 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con case CG_FTE_SPAWNPARTICLEEFFECT: return pe->RunParticleEffectState(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_FLOAT(arg[2]), VM_LONG(arg[3]), VM_POINTER(arg[4])); case CG_FTE_SPAWNPARTICLETRAIL: - return pe->ParticleTrail(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]), 0, NULL, VM_POINTER(arg[3])); + return pe->ParticleTrail(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]), 0, 1, NULL, VM_POINTER(arg[3])); case CG_FTE_FREEPARTICLESTATE: pe->DelinkTrailstate(VM_POINTER(arg[0])); break; diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index 84c9a30c4..530b14c32 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -3894,6 +3894,7 @@ void CL_LinkPacketEntities (void) int trailef, trailidx; int modelflags; struct itemtimer_s *timer, **timerlink; + float timestep = host_frametime; pack = cl.currentpackentities; if (!pack) @@ -4023,7 +4024,7 @@ void CL_LinkPacketEntities (void) CL_NewDlight(state->number, ent->origin, radius, 0.1, colour[0], colour[1], colour[2]); } } - if (state->lightpflags & (PFLAGS_FULLDYNAMIC|PFLAGS_CORONA)) + if ((state->lightpflags & (PFLAGS_FULLDYNAMIC|PFLAGS_CORONA)) && ((state->lightpflags&PFLAGS_FULLDYNAMIC)||state->light[3])) { vec3_t colour; if (!state->light[0] && !state->light[1] && !state->light[2]) @@ -4380,9 +4381,9 @@ void CL_LinkPacketEntities (void) //and emit it // if (lasttime != cl.currentpacktime) { - if (trailef == P_INVALID || pe->ParticleTrail (old_origin, ent->origin, trailef, ent->keynum, ent->axis, &(le->trailstate))) + if (trailef == P_INVALID || pe->ParticleTrail (old_origin, ent->origin, trailef, timestep, ent->keynum, ent->axis, &(le->trailstate))) if (model->traildefaultindex >= 0) - pe->ParticleTrailIndex(old_origin, ent->origin, P_INVALID, trailidx, 0, &(le->trailstate)); + pe->ParticleTrailIndex(old_origin, ent->origin, P_INVALID, timestep, trailidx, 0, &(le->trailstate)); //dlights are not so customisable. if (r_rocketlight.value && (modelflags & MF_ROCKET) && !(state->lightpflags & (PFLAGS_FULLDYNAMIC|PFLAGS_CORONA))) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 8caeceef7..03dfdb9b4 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -55,7 +55,7 @@ cvar_t cl_timeout = CVAR("cl_timeout", "60"); cvar_t cl_shownet = CVARD("cl_shownet","0", "Debugging var. 0 shows nothing. 1 shows incoming packet sizes. 2 shows individual messages. 3 shows entities too."); // can be 0, 1, or 2 -cvar_t cl_disconnectreason = CVARFD("_cl_disconnectreason", "", CVAR_NOSAVE, "This cvar contains the reason for the last disconnection, so that mod menus can know why things failed."); +cvar_t cl_disconnectreason = CVARAFD("_cl_disconnectreason", "", "com_errorMessage", CVAR_NOSAVE, "This cvar contains the reason for the last disconnection, so that mod menus can know why things failed."); cvar_t cl_pure = CVARD("cl_pure", "0", "0=standard quake rules.\n1=clients should prefer files within packages present on the server.\n2=clients should use *only* files within packages present on the server.\nDue to quake 1.01/1.06 differences, a setting of 2 is only reliable with total conversions.\nIf sv_pure is set, the client will prefer the highest value set."); cvar_t cl_sbar = CVARFC("cl_sbar", "0", CVAR_ARCHIVE, CL_Sbar_Callback); @@ -1046,6 +1046,7 @@ void CL_CheckForResend (void) if (!NET_StringToAdr (host, connectinfo.defaultport, &connectinfo.adr)) { + Cvar_Set(&cl_disconnectreason, va("Bad server address \"%s\"", host)); Con_TPrintf ("Bad server address \"%s\"\n", host); connectinfo.trying = false; SCR_EndLoadingPlaque(); @@ -1072,6 +1073,7 @@ void CL_CheckForResend (void) } if (!NET_IsClientLegal(&connectinfo.adr)) { + Cvar_Set(&cl_disconnectreason, va("Illegal server address")); Con_TPrintf ("Illegal server address\n"); SCR_EndLoadingPlaque(); connectinfo.trying = false; @@ -1101,6 +1103,7 @@ void CL_CheckForResend (void) if (connectinfo.tries == 0) if (!NET_EnsureRoute(cls.sockets, "conn", cls.servername, &connectinfo.adr)) { + Cvar_Set(&cl_disconnectreason, va("Unable to establish connection to %s\n", cls.servername)); Con_Printf ("Unable to establish connection to %s\n", cls.servername); connectinfo.trying = false; SCR_EndLoadingPlaque(); @@ -1171,6 +1174,7 @@ void CL_CheckForResend (void) if (!keeptrying) { + Cvar_Set(&cl_disconnectreason, va("No route to \"%s\", giving up\n", cls.servername)); Con_TPrintf ("No route to host, giving up\n"); connectinfo.trying = false; SCR_EndLoadingPlaque(); @@ -2960,18 +2964,27 @@ void CL_ConnectionlessPacket (void) char *data = MSG_ReadStringLine(); Con_Printf ("reject\n%s\n", data); if (NET_CompareAdr(&connectinfo.adr, &net_from)) + { + Cvar_Set(&cl_disconnectreason, va("%s\n", data)); connectinfo.trying = false; + } return; } else if (!strcmp(s, "badname")) { //rejected purely because of player name if (NET_CompareAdr(&connectinfo.adr, &net_from)) + { + Cvar_Set(&cl_disconnectreason, va("bad player name\n")); connectinfo.trying = false; + } } else if (!strcmp(s, "badaccount")) { //rejected because username or password is wrong if (NET_CompareAdr(&connectinfo.adr, &net_from)) + { + Cvar_Set(&cl_disconnectreason, va("invalid username or password\n")); connectinfo.trying = false; + } } Con_Printf ("f%s\n", s); @@ -3206,6 +3219,7 @@ void CL_ConnectionlessPacket (void) } if (connectinfo.dtlsupgrade == DTLS_REQUIRE) { + Cvar_Set(&cl_disconnectreason, va("Server does not support/allow dtls. not connecting\n")); connectinfo.trying = false; Con_Printf("Server does not support/allow dtls. not connecting.\n"); return; diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 40d17e0f9..edbb4088c 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -43,6 +43,12 @@ static int cl_dp_csqc_progscrc; static int cl_dp_serverextension_download; #endif +//tracks which svcs are using what data (per second) +static size_t packetusage_saved[256]; +static size_t packetusage_pending[256]; +static double packetusageflushtime; +static const double packetusage_interval=10; + #ifdef AVAIL_ZLIB #ifndef ZEXPORT #define ZEXPORT VARGS @@ -51,7 +57,7 @@ static int cl_dp_serverextension_download; #endif -static char *svc_qwstrings[] = +static const char *svc_qwstrings[] = { "svc_bad", "svc_nop", @@ -182,7 +188,7 @@ static char *svc_qwstrings[] = }; #ifdef NQPROT -static char *svc_nqstrings[] = +static const char *svc_nqstrings[] = { "nqsvc_bad", "nqsvc_nop", @@ -6701,6 +6707,7 @@ void CLQW_ParseServerMessage (void) qboolean csqcpacket = false; inframe_t *inf; extern vec3_t demoangles; + unsigned int cmdstart; received_framecount = host_framecount; cl.last_servermessage = realtime; @@ -6751,6 +6758,13 @@ void CLQW_ParseServerMessage (void) } } + if (realtime > packetusageflushtime) + { + memcpy(packetusage_saved, packetusage_pending, sizeof(packetusage_saved)); + memset(packetusage_pending, 0, sizeof(packetusage_pending)); + packetusageflushtime = realtime + packetusage_interval; + } + // // parse the message // @@ -6763,6 +6777,7 @@ void CLQW_ParseServerMessage (void) break; } + cmdstart = msg_readcount; cmd = MSG_ReadByte (); if (cmd == svcfte_choosesplitclient) @@ -7267,6 +7282,8 @@ void CLQW_ParseServerMessage (void) Con_Printf("Unable to parse gamecode packet\n"); break; } + + packetusage_pending[cmd] += msg_readcount-cmdstart; } } @@ -8282,3 +8299,57 @@ void CLNQ_ParseServerMessage (void) } #endif +struct sortedsvcs_s +{ + const char *name; + size_t bytes; +}; +static QDECL int sorttraffic(const void *l, const void *r) +{ + const struct sortedsvcs_s *a=l, *b=r; + + if (a->bytes==b->bytes) + return 0; + if (a->bytes>b->bytes) + return -1; + return 1; +} +void CL_ShowTrafficUsage(float x, float y) +{ + const char **svcnames, *n; + size_t svccount, i, j=0; + size_t total; + struct sortedsvcs_s sorted[256]; + if (cls.protocol == CP_NETQUAKE) + { + svcnames = svc_nqstrings; + svccount = countof(svc_nqstrings); + } + else + { + svcnames = svc_qwstrings; + svccount = countof(svc_qwstrings); + } + total = 0; + for (i = 0; i < 256; i++) + total += packetusage_saved[i]; + for (i = 0; i < 256; i++) + { + if (!packetusage_saved[i]) + continue; //don't show if there's no point. + if (i < svccount) + n = svcnames[i]; + else + n = va("svc %u", (unsigned)i); + sorted[j].name = n; + sorted[j].bytes = packetusage_saved[i]; + j++; + } + qsort(sorted, j, sizeof(*sorted), sorttraffic); + + for (i = 0; i < j; i++) + { + Draw_FunString(x, y, va("%22s:%5.1f%% (%.0f/s)", sorted[i].name, (100.0*sorted[i].bytes)/total, (sorted[i].bytes/packetusage_interval))); + y+=8; + } +} \ No newline at end of file diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index ac4dd8c07..fca868530 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -648,7 +648,7 @@ static char *SCR_CopyCenterPrint(cprint_t *p) //reads the link under the mouse c } #define MAX_CPRINT_LINES 512 -void SCR_DrawCenterString (vrect_t *rect, cprint_t *p, struct font_s *font) +int SCR_DrawCenterString (vrect_t *rect, cprint_t *p, struct font_s *font) { int l; int y, x; @@ -794,6 +794,8 @@ void SCR_DrawCenterString (vrect_t *rect, cprint_t *p, struct font_s *font) } Font_EndString(font); + + return linecount; } qboolean Key_Centerprint(int key, int unicode, unsigned int devid) @@ -904,7 +906,7 @@ void SCR_CheckDrawCenterString (void) } } -void R_DrawTextField(int x, int y, int w, int h, const char *text, unsigned int defaultmask, unsigned int fieldflags, struct font_s *font, vec2_t fontscale) +int R_DrawTextField(int x, int y, int w, int h, const char *text, unsigned int defaultmask, unsigned int fieldflags, struct font_s *font, vec2_t fontscale) { cprint_t p; vrect_t r; @@ -923,7 +925,7 @@ void R_DrawTextField(int x, int y, int w, int h, const char *text, unsigned int p.time_start = cl.time; *p.titleimage = 0; - SCR_DrawCenterString(&r, &p, font); + return SCR_DrawCenterString(&r, &p, font); } qboolean SCR_HardwareCursorIsActive(void) diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 5325976b1..c7c4f2679 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -776,8 +776,8 @@ beam_t *CL_AddBeam (enum beamtype_e tent, int ent, vec3_t start, vec3_t end) //f if (ent < 0 && ent >= -512) //a zquake concept. ent between -1 and -maxplayers is to be taken to be a railtrail from a particular player instead of a beam. { // TODO: add support for those finnicky colored railtrails... - if (P_ParticleTrail(start, end, rtqw_railtrail, -ent, NULL, NULL)) - P_ParticleTrailIndex(start, end, P_INVALID, 208, 8, NULL); + if (P_ParticleTrail(start, end, rtqw_railtrail, 0.1, -ent, NULL, NULL)) + P_ParticleTrailIndex(start, end, P_INVALID, 0.1, 208, 8, NULL); return NULL; } break; @@ -1607,8 +1607,8 @@ void CL_ParseTEnt (void) pos2[1] = MSG_ReadCoord (); pos2[2] = MSG_ReadCoord (); - if (P_ParticleTrail(pos, pos2, rtqw_railtrail, 0, NULL, NULL)) - P_ParticleTrailIndex(pos, pos2, P_INVALID, 208, 8, NULL); + if (P_ParticleTrail(pos, pos2, rtqw_railtrail, 1, 0, NULL, NULL)) + P_ParticleTrailIndex(pos, pos2, P_INVALID, 1, 208, 8, NULL); break; case TEH2_STREAM_LIGHTNING_SMALL: @@ -1751,8 +1751,8 @@ void CL_ParseTEnt (void) MSG_ReadCoord (); MSG_ReadCoord (); - if (P_ParticleTrail(pos, pos2, P_FindParticleType("te_nexbeam"), 0, NULL, NULL)) - P_ParticleTrailIndex(pos, pos2, P_INVALID, 15, 0, NULL); + if (P_ParticleTrail(pos, pos2, P_FindParticleType("te_nexbeam"), 1, 0, NULL, NULL)) + P_ParticleTrailIndex(pos, pos2, P_INVALID, 1, 15, 0, NULL); break; case TEDP_SMOKE: @@ -1933,7 +1933,7 @@ void CL_SpawnCustomTEnt(custtentinst_t *info) } } else - failed = P_ParticleTrail(info->pos, info->pos2, t->particleeffecttype, 0, NULL, NULL); + failed = P_ParticleTrail(info->pos, info->pos2, t->particleeffecttype, 1, 0, NULL, NULL); } else { @@ -2262,8 +2262,8 @@ void CL_ParseTrailParticles(void) else ts = NULL; - if (P_ParticleTrail(start, end, effectindex, entityindex, NULL, ts)) - P_ParticleTrail(start, end, rt_blood, entityindex, NULL, ts); + if (P_ParticleTrail(start, end, effectindex, 1, entityindex, NULL, ts)) + P_ParticleTrail(start, end, rt_blood, 1, entityindex, NULL, ts); } void CL_ParsePointParticles(qboolean compact) @@ -2588,7 +2588,7 @@ void CLQ2_ParseTEnt (void) case Q2TE_BLUEHYPERBLASTER: //TE_BLASTER without model+light MSG_ReadPos (pos); MSG_ReadPos (pos2); - P_ParticleTrail(pos, pos2, pt, 0, NULL, NULL); + P_ParticleTrail(pos, pos2, pt, 1, 0, NULL, NULL); break; case Q2TE_EXPLOSION1: //column case Q2TE_EXPLOSION2: //splits @@ -2674,13 +2674,13 @@ void CLQ2_ParseTEnt (void) MSG_ReadPos (pos); MSG_ReadPos (pos2); color = MSG_ReadByte (); - P_ParticleTrailIndex(pos, pos2, pt, color, 0, NULL); + P_ParticleTrailIndex(pos, pos2, pt, 1, color, 0, NULL); break; case Q2TE_FLASHLIGHT: //white 400-radius dlight MSG_ReadPos(pos); ent = MSG_ReadShort(); - P_ParticleTrail(pos, pos, pt, ent, NULL, NULL); + P_ParticleTrail(pos, pos, pt, 1, ent, NULL, NULL); break; case Q2TE_WIDOWBEAMOUT: /*requires state tracking to keep it splurting constantly for 2.1 secs*/ ent = MSG_ReadShort(); @@ -2737,7 +2737,7 @@ void CLQ2_ParseTEnt (void) case CRTE_BLASTERBEAM: MSG_ReadPos (pos); MSG_ReadPos (pos2); - P_ParticleTrail(pos, pos2, P_FindParticleType("q2part.TR_BLASTERTRAIL2"), 0, NULL, NULL); + P_ParticleTrail(pos, pos2, P_FindParticleType("q2part.TR_BLASTERTRAIL2"), 1, 0, NULL, NULL); break; /* case CRTE_STAIN: Host_EndGame ("CLQ2_ParseTEnt: bad/non-implemented type %i", type); @@ -2969,7 +2969,7 @@ void CL_UpdateBeams (void) } if (ruleset_allow_particle_lightning.ival || !type->modelname) - if (type->ef_beam >= 0 && !P_ParticleTrail(org, b->end, type->ef_beam, b->entity, NULL, &b->trailstate)) + if (type->ef_beam >= 0 && !P_ParticleTrail(org, b->end, type->ef_beam, host_frametime, b->entity, NULL, &b->trailstate)) continue; if (!type->model) { @@ -3151,7 +3151,7 @@ void CL_UpdateExplosions (void) #endif if (ex->traileffect != P_INVALID) - pe->ParticleTrail(ent->oldorigin, ent->origin, ex->traileffect, 0, ent->axis, &(ex->trailstate)); + pe->ParticleTrail(ent->oldorigin, ent->origin, ex->traileffect, frametime, 0, ent->axis, &(ex->trailstate)); if (!(ex->flags & Q2RF_BEAM)) VectorCopy(ent->origin, ex->oldorigin); //don't corrupt q2 beams if (ex->flags & Q2RF_BEAM) diff --git a/engine/client/cl_ui.c b/engine/client/cl_ui.c index 705125778..bd1085f83 100644 --- a/engine/client/cl_ui.c +++ b/engine/client/cl_ui.c @@ -28,6 +28,7 @@ typedef struct { } script_t; static script_t *scripts; static int maxscripts; +static unsigned int ui_width, ui_height; //to track when it needs to be restarted (the api has no video mode changed event) #define Q3SCRIPTPUNCTUATION "(,{})(\':;=!><&|+-\"" void StripCSyntax (char *s) { @@ -1483,6 +1484,12 @@ void UI_DrawMenu(void) { if (uivm) { + if (qrenderer != QR_NONE && (ui_width != vid.width || ui_height != vid.height)) + { + ui_width = vid.width; + ui_height = vid.height; + VM_Call(uivm, UI_INIT); + } VM_Call(uivm, UI_REFRESH, (int)(realtime * 1000)); } } @@ -1585,11 +1592,14 @@ qboolean UI_KeyPress(int key, int unicode, qboolean down) // return result; } -qboolean UI_MousePosition(int xpos, int ypos) +qboolean UI_MousePosition(float xpos, float ypos) { if (uivm && (keycatcher&2)) { - VM_Call(uivm, UI_MOUSE_EVENT, (xpos)*640/(int)vid.width, (ypos)*480/(int)vid.height); + int px, py; + px = (xpos);//*640/(int)vid.width; + py = (ypos);//*480/(int)vid.height; + VM_Call(uivm, UI_MOUSE_EVENT, px, py); return true; } return false; @@ -1625,6 +1635,8 @@ void UI_Start (void) for (i = 0; i < MAX_PINGREQUESTS; i++) ui_pings[i].type = NA_INVALID; + ui_width = vid.width; + ui_height = vid.height; uivm = VM_Create("vm/ui", com_nogamedirnativecode.ival?NULL:UI_SystemCallsNative, UI_SystemCallsVM); if (uivm) { diff --git a/engine/client/client.h b/engine/client/client.h index b8e20aa06..048c3f304 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -1282,6 +1282,7 @@ void CLNQ_ParseServerMessage (void); #ifdef Q2CLIENT void CLQ2_ParseServerMessage (void); #endif +void CL_ShowTrafficUsage(float x, float y); void CL_NewTranslation (int slot); int CL_IsDownloading(const char *localname); diff --git a/engine/client/clq2_ents.c b/engine/client/clq2_ents.c index ae9c176b6..b50d6c0d7 100644 --- a/engine/client/clq2_ents.c +++ b/engine/client/clq2_ents.c @@ -1779,6 +1779,8 @@ static void CLQ2_AddPacketEntities (q2frame_t *frame) unsigned int effects, renderfx; float back, fwds; + float timestep = cl.gametime-cl.oldgametime; + // bonus items rotate at a fixed rate autorotate = anglemod(cl.time*100); @@ -2206,10 +2208,10 @@ static void CLQ2_AddPacketEntities (q2frame_t *frame) if (effects & Q2EF_ROCKET) { //FIXME: cubemap orientation - if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_ROCKET], ent.keynum, NULL, ¢->trailstate)) - if (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_rocket, ent.keynum, NULL, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_ROCKET], timestep, ent.keynum, NULL, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_rocket, timestep, ent.keynum, NULL, ¢->trailstate)) { - P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, 0xdc, 4, ¢->trailstate); + P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 0xdc, 4, ¢->trailstate); V_AddLight (ent.keynum, ent.origin, 200, 0.2, 0.1, 0.05); } } @@ -2220,17 +2222,17 @@ static void CLQ2_AddPacketEntities (q2frame_t *frame) //PGM if (effects & Q2EF_TRACKER) // lame... problematic? { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_BLASTERTRAIL2], ent.keynum, NULL, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_BLASTERTRAIL2], timestep, ent.keynum, NULL, ¢->trailstate)) { - P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, 0xd0, 1, ¢->trailstate); + P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 0xd0, 1, ¢->trailstate); V_AddLight (ent.keynum, ent.origin, 200, 0, 0.2, 0); } } else { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_BLASTERTRAIL], ent.keynum, NULL, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_BLASTERTRAIL], timestep, ent.keynum, NULL, ¢->trailstate)) { - P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, 0xe0, 1, ¢->trailstate); + P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 0xe0, 1, ¢->trailstate); V_AddLight (ent.keynum, ent.origin, 200, 0.2, 0.2, 0); } } @@ -2245,15 +2247,15 @@ static void CLQ2_AddPacketEntities (q2frame_t *frame) } else if (effects & Q2EF_GIB) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_GIB], ent.keynum, NULL, ¢->trailstate)) - if (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_blood, ent.keynum, NULL, ¢->trailstate)) - P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, 0xe8, 8, ¢->trailstate); + if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_GIB], timestep, ent.keynum, NULL, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_blood, timestep, ent.keynum, NULL, ¢->trailstate)) + P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 0xe8, 8, ¢->trailstate); } else if (effects & Q2EF_GRENADE) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_GRENADE], ent.keynum, NULL, ¢->trailstate)) - if (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_grenade, ent.keynum, NULL, ¢->trailstate)) - P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, 4, 8, ¢->trailstate); + if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_GRENADE], timestep, ent.keynum, NULL, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_grenade, timestep, ent.keynum, NULL, ¢->trailstate)) + P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 4, 8, ¢->trailstate); } else if (effects & Q2EF_FLIES) { @@ -2278,21 +2280,21 @@ static void CLQ2_AddPacketEntities (q2frame_t *frame) else if (effects & Q2EF_TRAP) { ent.origin[2] += 32; - P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_TRAP], ent.keynum, NULL, ¢->trailstate); + P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_TRAP], timestep, ent.keynum, NULL, ¢->trailstate); } else if (effects & Q2EF_FLAG1) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_FLAG1], ent.keynum, NULL, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_FLAG1], timestep, ent.keynum, NULL, ¢->trailstate)) { - P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, 242, 1, ¢->trailstate); + P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 242, 1, ¢->trailstate); V_AddLight (ent.keynum, ent.origin, 225, 0.2, 0.05, 0.05); } } else if (effects & Q2EF_FLAG2) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_FLAG2], ent.keynum, NULL, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_FLAG2], timestep, ent.keynum, NULL, ¢->trailstate)) { - P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, 115, 1, ¢->trailstate); + P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 115, 1, ¢->trailstate); V_AddLight (ent.keynum, ent.origin, 225, 0.05, 0.05, 0.2); } } @@ -2300,9 +2302,9 @@ static void CLQ2_AddPacketEntities (q2frame_t *frame) //ROGUE else if (effects & Q2EF_TAGTRAIL) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_TAGTRAIL], ent.keynum, NULL, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_TAGTRAIL], timestep, ent.keynum, NULL, ¢->trailstate)) { - P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, 220, 1, ¢->trailstate); + P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 220, 1, ¢->trailstate); V_AddLight (ent.keynum, ent.origin, 225, 0.2, 0.2, 0.0); } } @@ -2325,9 +2327,9 @@ static void CLQ2_AddPacketEntities (q2frame_t *frame) } else if (effects & Q2EF_TRACKER) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_TRACKER], ent.keynum, NULL, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_TRACKER], timestep, ent.keynum, NULL, ¢->trailstate)) { - P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, 0, 1, ¢->trailstate); + P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 0, 1, ¢->trailstate); V_AddLight (ent.keynum, ent.origin, 200, -0.2, -0.2, -0.2); } } @@ -2336,15 +2338,15 @@ static void CLQ2_AddPacketEntities (q2frame_t *frame) // RAFAEL else if (effects & Q2EF_GREENGIB) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_GREENGIB], ent.keynum, NULL, ¢->trailstate)) - P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, 219, 8, ¢->trailstate); + if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_GREENGIB], timestep, ent.keynum, NULL, ¢->trailstate)) + P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 219, 8, ¢->trailstate); } // RAFAEL else if (effects & Q2EF_IONRIPPER) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_IONRIPPER], ent.keynum, NULL, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_IONRIPPER], timestep, ent.keynum, NULL, ¢->trailstate)) { - P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, 228, 4, ¢->trailstate); + P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 228, 4, ¢->trailstate); V_AddLight (ent.keynum, ent.origin, 100, 0.2, 0.1, 0.1); } } @@ -2358,7 +2360,7 @@ static void CLQ2_AddPacketEntities (q2frame_t *frame) { if (effects & Q2EF_ANIM_ALLFAST) { - P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_PLASMA], ent.keynum, NULL, ¢->trailstate); + P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_PLASMA], timestep, ent.keynum, NULL, ¢->trailstate); } V_AddLight (ent.keynum, ent.origin, 130, 0.2, 0.1, 0.1); } diff --git a/engine/client/clq3_parse.c b/engine/client/clq3_parse.c index 9f5213290..aeaceaafc 100644 --- a/engine/client/clq3_parse.c +++ b/engine/client/clq3_parse.c @@ -549,7 +549,6 @@ void CLQ3_ParseGameState(void) cl.maxpitch = 90; ccs.lastServerCommandNum = MSG_ReadLong(); - ccs.currentServerCommandNum = ccs.lastServerCommandNum; for(;;) { diff --git a/engine/client/clq3defs.h b/engine/client/clq3defs.h index 777f733e6..27a48d795 100644 --- a/engine/client/clq3defs.h +++ b/engine/client/clq3defs.h @@ -219,7 +219,6 @@ typedef struct frame_s { typedef struct { int lastClientCommandNum; int lastServerCommandNum; - int currentServerCommandNum; int numClientCommands; int serverMessageNum; diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 24e27fc9b..9daa90e07 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -1068,11 +1068,28 @@ void FPS_Preset_f (void) if (!stricmp("dp", arg)) { + if (sv.state) + Cbuf_InsertText("echo Be sure to restart your server\n", RESTRICT_LOCAL, false); Cbuf_InsertText( - "set sv_listen_dp 1\n" - "set sv_bigcoords 1\n" - "set r_particledesc \"effectinfo classic\"\n" - "echo you may need to restart the map\n" + //these are for smc+derived mods + "sv_listen_dp 1\n" //awkward, but forces the server to load the effectinfo.txt in advance. + "sv_bigcoords 1\n" //for viewmodel lep precision (would be better to use csqc) + "r_particledesc \"effectinfo high\"\n" //blurgh. + "dpcompat_noretouchground 1\n" //don't call touch functions on entities that already appear onground. this also changes the order that the onground flag is set relative to touch functions. + "cl_nopred 1\n" //DP doesn't predict by default, and DP mods have a nasty habit of clearing .solid values during prethinks, which screws up prediction. so play safe. + "r_dynamic 0\nr_shadow_realtime_dlight 1\n" //fte has separate cvars for everything. which kinda surprises people and makes stuff twice as bright as it should be. + + //general compat stuff + "dpcompat_console 1\n" // + "dpcompat_findradiusarealinks 1\n" //faster findradiuses (but that require things are setorigined properly) + "dpcompat_makeshitup 2\n" //flatten shaders to a single pass, then add new specular etc passes. + //"dpcompat_nopremulpics 1\n" //don't use premultiplied alpha (solving issues with compressed image formats) + "dpcompat_psa_ungroup 1\n" //don't use framegroups with psk models at all. + "dpcompat_set 1\n" //handle 3-arg sets differently + "dpcompat_stats 1\n" //truncate float stats + "dpcompat_strcat_limit 16383\n" //xonotic compat. maximum length of strcat strings. + +// "sv_listen_dp 1\nsv_listen_nq 0\nsv_listen_qw 0\ncl_loopbackprotocol dpp7\ndpcompat_nopreparse 1\n" , RESTRICT_LOCAL, false); return; } diff --git a/engine/client/merged.h b/engine/client/merged.h index 63def1964..8fc080c08 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -139,7 +139,7 @@ extern void SCR_DrawConsole (qboolean noback); extern void SCR_SetUpToDrawConsole (void); extern void SCR_CenterPrint (int pnum, const char *str, qboolean skipgamecode); -void R_DrawTextField(int x, int y, int w, int h, const char *text, unsigned int defaultmask, unsigned int fieldflags, struct font_s *font, vec2_t fontscale); +int R_DrawTextField(int x, int y, int w, int h, const char *text, unsigned int defaultmask, unsigned int fieldflags, struct font_s *font, vec2_t fontscale); #define CPRINT_LALIGN (1<<0) //L #define CPRINT_TALIGN (1<<1) //T #define CPRINT_RALIGN (1<<2) //R diff --git a/engine/client/p_classic.c b/engine/client/p_classic.c index a2818757a..cd1d772d6 100644 --- a/engine/client/p_classic.c +++ b/engine/client/p_classic.c @@ -265,7 +265,7 @@ static void PClassic_RunParticleEffect4 (vec3_t org, float radius, int color, in } //this function is used as a fallback in case a trail effect is unknown. -static void PClassic_ParticleTrailIndex (vec3_t start, vec3_t end, int type, int color, int crnd, trailstate_t **tsk) +static void PClassic_ParticleTrailIndex (vec3_t start, vec3_t end, int type, float timestep, int color, int crnd, trailstate_t **tsk) { } @@ -1130,7 +1130,7 @@ int PClassic_PointFile(int c, vec3_t point) } //builds a trail from here to there. The trail state can be used to remember how far you got last frame. -static int PClassic_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlkey, vec3_t dlaxis[3], trailstate_t **tsk) +static int PClassic_ParticleTrail (vec3_t startpos, vec3_t end, int type, float timestep, int dlkey, vec3_t dlaxis[3], trailstate_t **tsk) { float leftover; diff --git a/engine/client/p_null.c b/engine/client/p_null.c index b18573a67..a0118f1ec 100644 --- a/engine/client/p_null.c +++ b/engine/client/p_null.c @@ -12,7 +12,7 @@ static int PNULL_FindParticleType(const char *name) } static int PNULL_RunParticleEffectTypeString (vec3_t org, vec3_t dir, float count, char *name){return 1;} -static int PNULL_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlkey, vec3_t dlaxis[3], trailstate_t **tsk){return 1;} +static int PNULL_ParticleTrail (vec3_t startpos, vec3_t end, int type, float timestep, int dlkey, vec3_t dlaxis[3], trailstate_t **tsk){return 1;} static int PNULL_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typenum, trailstate_t **tsk){return 1;} static void PNULL_RunParticleWeather(vec3_t minb, vec3_t maxb, vec3_t dir, float count, int colour, char *efname){} static void PNULL_RunParticleCube(int typenum, vec3_t minb, vec3_t maxb, vec3_t dir_min, vec3_t dir_max, float count, int colour, qboolean gravity, float jitter){} @@ -22,7 +22,7 @@ static void PNULL_RunParticleEffect3 (vec3_t org, vec3_t box, int color, int eff static void PNULL_RunParticleEffect4 (vec3_t org, float radius, int color, int effect, int count){} static void PNULL_RunParticleEffectPalette (const char *nameprefix, vec3_t org, vec3_t dir, int color, int count){} -static void PNULL_ParticleTrailIndex (vec3_t start, vec3_t end, int type, int color, int crnd, trailstate_t **tsk){} +static void PNULL_ParticleTrailIndex (vec3_t start, vec3_t end, int type, float timestep, int color, int crnd, trailstate_t **tsk){} static qboolean PNULL_InitParticles (void) { diff --git a/engine/client/p_script.c b/engine/client/p_script.c index 7844de6c8..2ba22f00f 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -264,6 +264,8 @@ typedef struct part_type_s { int countextra; float count; float countrand; + float countspacing; //for trails. + float countoverflow; //for badly-designed effects, instead of depending on trail state. float rainfrequency; int assoc; @@ -1368,12 +1370,14 @@ void P_ParticleEffect_f(void) else if (!strcmp(var, "step")) { + ptype->countspacing = atof(value); ptype->count = 1/atof(value); if (Cmd_Argc()>2) ptype->countrand = 1/atof(Cmd_Argv(2)); } else if (!strcmp(var, "count")) { + ptype->countspacing = 0; ptype->count = atof(value); if (Cmd_Argc()>2) ptype->countrand = atof(Cmd_Argv(2)); @@ -2401,7 +2405,7 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen) Q_strncatz(outstr, va("tcoords %g %g %g %g %g %i %g\n", ptype->s1, ptype->t1, ptype->s2, ptype->t2, 1.0f, ptype->randsmax, ptype->texsstride), outstrlen); } - if (ptype->count || all) + if (ptype->count || ptype->countrand || ptype->countextra || all) Q_strncatz(outstr, va("count %g %g %i\n", ptype->count, ptype->countrand, ptype->countextra), outstrlen); if (ptype->rainfrequency != 1 || all) Q_strncatz(outstr, va("rainfrequency %g\n", ptype->rainfrequency), outstrlen); @@ -3152,7 +3156,10 @@ static void P_ImportEffectInfo(char *config, char *line) else if (!strcmp(arg[0], "velocitymultiplier") && args == 2) ptype->veladd = atof(arg[1]); else if (!strcmp(arg[0], "trailspacing") && args == 2) - ptype->count = 1 / atof(arg[1]); + { + ptype->countspacing = atof(arg[1]); + ptype->count = 1 / ptype->countspacing; + } else if (!strcmp(arg[0], "time") && args == 3) { ptype->die = atof(arg[1]); @@ -5476,7 +5483,7 @@ static void PScript_RunParticleEffectPalette (const char *nameprefix, vec3_t org } } -static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype, trailstate_t **tsk, int dlkey, vec3_t dlaxis[3]) +static void P_ParticleTrailSpawn (vec3_t startpos, vec3_t end, part_type_t *ptype, float timeinterval, trailstate_t **tsk, int dlkey, vec3_t dlaxis[3]) { vec3_t vec, vstep, right, up, start; float len; @@ -5528,9 +5535,9 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype if (ptype->assoc>=0) { if (ts) - P_ParticleTrail(start, end, ptype->assoc, dlkey, NULL, &(ts->assoc)); + P_ParticleTrail(start, end, ptype->assoc, timeinterval, dlkey, NULL, &(ts->assoc)); else - P_ParticleTrail(start, end, ptype->assoc, dlkey, NULL, NULL); + P_ParticleTrail(start, end, ptype->assoc, timeinterval, dlkey, NULL, NULL); } if (r_part_contentswitch.ival && (ptype->flags & (PT_TRUNDERWATER | PT_TROVERWATER)) && cl.worldmodel) @@ -5561,20 +5568,30 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype if (!ptype->die) ts = NULL; - // use ptype step to calc step vector and step size - step = (ptype->count*r_part_density.value) + ptype->countextra; - if (step>0) - { - step = 1/step; - if (step < 0.01) - step = 0.01; - } - else - step = 0; - VectorSubtract (end, start, vec); len = VectorNormalize (vec); + // use ptype step to calc step vector and step size + if (ptype->countspacing) + { + step = ptype->countspacing; //particles per qu + step /= r_part_density.value; //scaled... + + //FIXME: countextra + } + else + { + step = ptype->count * r_part_density.value * timeinterval; + step += ptype->countextra; //particles per frame + step += ptype->countoverflow; + count = (int)step; + ptype->countoverflow = step-count; //the part that we're forgetting, to add to the next frame... + if (count<=0) + return; + else + step = len/count; //particles per second + } + if (ptype->flags & PT_AVERAGETRAIL) { float tavg; @@ -5641,7 +5658,10 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype step = 0; VectorClear(vstep); } - count += ptype->countextra; +// count += ptype->countextra; + +if (count > 1000) +count = 1000; while (count-->0)//len < stop) { @@ -5992,14 +6012,14 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype return; } -static int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlkey, vec3_t axis[3], trailstate_t **tsk) +static int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, float timeinterval, int dlkey, vec3_t axis[3], trailstate_t **tsk) { part_type_t *ptype = &part_type[type]; // TODO: fallback particle system won't have a decent trailstate which will mess up // high fps trails if (type >= FALLBACKBIAS && fallback) - return fallback->ParticleTrail(startpos, end, type-FALLBACKBIAS, dlkey, axis, NULL); + return fallback->ParticleTrail(startpos, end, type-FALLBACKBIAS, timeinterval, dlkey, axis, NULL); if (type < 0 || type >= numparticletypes) return 1; //bad value @@ -6017,11 +6037,11 @@ static int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlk ptype = &part_type[ptype->inwater]; } - P_ParticleTrailDraw (startpos, end, ptype, tsk, dlkey, axis); + P_ParticleTrailSpawn (startpos, end, ptype, timeinterval, tsk, dlkey, axis); return 0; } -static void PScript_ParticleTrailIndex (vec3_t start, vec3_t end, int type, int color, int crnd, trailstate_t **tsk) +static void PScript_ParticleTrailIndex (vec3_t start, vec3_t end, int type, float timeinterval, int color, int crnd, trailstate_t **tsk) { if (type == P_INVALID) type = pe_defaulttrail; @@ -6029,7 +6049,7 @@ static void PScript_ParticleTrailIndex (vec3_t start, vec3_t end, int type, int { part_type[type].colorindex = color; part_type[type].colorrand = crnd; - P_ParticleTrail(start, end, type, 0, NULL, tsk); + P_ParticleTrail(start, end, type, timeinterval, 0, NULL, tsk); } } @@ -7280,7 +7300,7 @@ static void PScript_DrawParticleTypes (void) if (type->emit >= 0) { if (type->emittime < 0) - P_ParticleTrail(oldorg, p->org, type->emit, 0, NULL, &p->state.trailstate); + P_ParticleTrail(oldorg, p->org, type->emit, pframetime, 0, NULL, &p->state.trailstate); else if (p->state.nextemit < particletime) { p->state.nextemit = particletime + type->emittime + frandom()*type->emitrand; diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index ccece8895..384ba0f5e 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -59,6 +59,8 @@ static csqctreadstate_t *csqcthreads; qboolean csqc_resortfrags; world_t csqc_world; +float csqc_starttime; //reset on each csqc reload to restore lost precision of cltime on each map restart. + int csqc_playerseat; //can be negative. static playerview_t *csqc_playerview; qboolean csqc_dp_lastwas3d; //to emulate DP correctly, we need to track whether drawpic/drawfill or clearscene was called last. blame 515. @@ -423,7 +425,7 @@ static void CSQC_FindGlobals(qboolean nofuncs) if (csqcg.simtime) *csqcg.simtime = cl.servertime; if (csqcg.cltime) - *csqcg.cltime = realtime; + *csqcg.cltime = realtime-csqc_starttime; if (!csqcg.global_gravitydir) csqcg.global_gravitydir = defaultgravity; @@ -3199,7 +3201,7 @@ static void QCBUILTIN PF_cs_boxparticles(pubprogfuncs_t *prinst, struct globalva if (flags & 128) //PARTICLES_DRAWASTRAIL { flags &= ~128; - P_ParticleTrail(org_from, org_to, effectnum, 0, NULL, NULL); + P_ParticleTrail(org_from, org_to, effectnum, 1, 0, NULL, NULL); } else { @@ -3235,6 +3237,7 @@ static void QCBUILTIN PF_cs_trailparticles (pubprogfuncs_t *prinst, struct globa csqcedict_t *ent; float *start = G_VECTOR(OFS_PARM2); float *end = G_VECTOR(OFS_PARM3); + float timestep = host_frametime; if ((unsigned int)G_INT(OFS_PARM1) >= MAX_EDICTS) { //ents can't be negative, nor can they be huge (like floats are if expressed as an integer) @@ -3249,9 +3252,9 @@ static void QCBUILTIN PF_cs_trailparticles (pubprogfuncs_t *prinst, struct globa efnum = CL_TranslateParticleFromServer(efnum); if (!ent->entnum) //world trails are non-state-based. - pe->ParticleTrail(start, end, efnum, 0, NULL, NULL); + pe->ParticleTrail(start, end, efnum, timestep, 0, NULL, NULL); else - pe->ParticleTrail(start, end, efnum, -ent->entnum, NULL, &ent->trailstate); + pe->ParticleTrail(start, end, efnum, timestep, -ent->entnum, NULL, &ent->trailstate); } void CSQC_ResetTrails(void) @@ -5064,9 +5067,9 @@ void CSQC_EntStateToCSQC(unsigned int flags, float lerptime, entity_state_t *src //use entnum as a test to see if its new (if the old origin isn't usable) if (ent->xv->entnum) { - if (model->particletrail == P_INVALID || pe->ParticleTrail (ent->v->origin, src->origin, model->particletrail, src->number, NULL, &(le->trailstate))) + if (model->particletrail == P_INVALID || pe->ParticleTrail (ent->v->origin, src->origin, model->particletrail, host_frametime, src->number, NULL, &(le->trailstate))) if (model->traildefaultindex >= 0) - pe->ParticleTrailIndex(ent->v->origin, src->origin, P_INVALID, model->traildefaultindex, 0, &(le->trailstate)); + pe->ParticleTrailIndex(ent->v->origin, src->origin, P_INVALID, host_frametime, model->traildefaultindex, 0, &(le->trailstate)); } } @@ -7331,6 +7334,10 @@ pbool PDECL CSQC_CheckHeaderCrc(pubprogfuncs_t *progs, progsnum_t num, int crc) csqc_isdarkplaces = true; Con_DPrintf(CON_WARNING "Running darkplaces csprogs.dat version\n"); break; + case 23147: + csqc_isdarkplaces = true; + Con_DPrintf(CON_WARNING "Running ^aINVALID^a csprogs.dat version\n"); + break; #endif default: Con_Printf(CON_WARNING "Running unknown csprogs.dat version\n"); @@ -7467,6 +7474,7 @@ qboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int chec int csaddonnum = -1; in_sensitivityscale = 1; csqcmapentitydataloaded = true; + csqc_starttime = realtime; csqcprogs = InitProgs(&csqcprogparms); csqc_world.progs = csqcprogs; csqc_world.usesolidcorpse = true; @@ -8067,7 +8075,7 @@ qboolean CSQC_DrawView(void) CL_PredictMove (); if (csqcg.cltime) - *csqcg.cltime = realtime; + *csqcg.cltime = realtime-csqc_starttime; if (csqcg.simtime) *csqcg.simtime = cl.servertime; if (csqcg.clientcommandframe) @@ -8156,7 +8164,7 @@ qboolean CSQC_DrawHud(playerview_t *pv) if (csqcg.frametime) *csqcg.frametime = host_frametime; if (csqcg.cltime) - *csqcg.cltime = realtime; + *csqcg.cltime = realtime-csqc_starttime; G_FLOAT(OFS_PARM0+0) = r_refdef.grect.width; G_FLOAT(OFS_PARM0+1) = r_refdef.grect.height; @@ -8200,7 +8208,7 @@ qboolean CSQC_DrawScores(playerview_t *pv) if (csqcg.frametime) *csqcg.frametime = host_frametime; if (csqcg.cltime) - *csqcg.cltime = realtime; + *csqcg.cltime = realtime-csqc_starttime; G_FLOAT(OFS_PARM0+0) = r_refdef.grect.width; G_FLOAT(OFS_PARM0+1) = r_refdef.grect.height; @@ -8629,7 +8637,7 @@ void CSQC_Input_Frame(int seat, usercmd_t *cmd) if (csqcg.simtime) *csqcg.simtime = cl.servertime; if (csqcg.cltime) - *csqcg.cltime = realtime; + *csqcg.cltime = realtime-csqc_starttime; if (csqcg.clientcommandframe) *csqcg.clientcommandframe = cl.movesequence; @@ -8743,7 +8751,7 @@ void CSQC_ParseEntities(void) if (csqcg.simtime) //estimated server time *csqcg.simtime = cl.servertime; if (csqcg.cltime) //smooth client time. - *csqcg.cltime = realtime; + *csqcg.cltime = realtime-csqc_starttime; if (csqcg.netnewtime) *csqcg.netnewtime = cl.gametime; diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 88cd50272..c604dc3b9 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -438,7 +438,7 @@ void QCBUILTIN PF_CL_DrawTextField (pubprogfuncs_t *prinst, struct globalvars_s // Oversight ~eukara R2D_ImageColours(1.0f, 1.0f, 1.0f, 1.0f); - R_DrawTextField(pos[0], pos[1], size[0], size[1], text, CON_WHITEMASK, flags, font, scale); + G_FLOAT(OFS_RETURN) = R_DrawTextField(pos[0], pos[1], size[0], size[1], text, CON_WHITEMASK, flags, font, scale); } //float drawstring(vector position, string text, vector scale, float alpha, float flag) = #455; diff --git a/engine/client/r_partset.c b/engine/client/r_partset.c index f9d2ff33e..56b06e278 100644 --- a/engine/client/r_partset.c +++ b/engine/client/r_partset.c @@ -1728,7 +1728,6 @@ char *particle_set_high = "}\n" "#endif\n" "#ifdef FRAGMENT_SHADER\n" -"uniform sampler2D s_t0;\n" "void main(void)\n" "{\n" "vec2 nst;\n" diff --git a/engine/client/r_partset.h b/engine/client/r_partset.h index fc4e468cd..614a030ad 100644 --- a/engine/client/r_partset.h +++ b/engine/client/r_partset.h @@ -29,6 +29,4 @@ extern char *particle_set_q2part; extern char *particle_set_tsshaft; #define R_PARTSET_BUILTINS_tsshaft {"tsshaft", &particle_set_tsshaft}, #define R_PARTSET_BUILTINS R_PARTSET_BUILTINS_spikeset R_PARTSET_BUILTINS_faithful R_PARTSET_BUILTINS_highfps R_PARTSET_BUILTINS_high R_PARTSET_BUILTINS_minimal R_PARTSET_BUILTINS_h2part R_PARTSET_BUILTINS_q2part R_PARTSET_BUILTINS_tsshaft -#else -#define R_PARTSET_BUILTINS #endif diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index 30f43f5df..8a9b6a8af 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -3495,6 +3495,11 @@ static void S_Q2_AddEntitySounds(soundcardinfo_t *sc) if (cls.protocol == CP_QUAKE2) count = CLQ2_GatherSounds(positions, entnums, sounds, countof(sounds)); else +#endif +#ifdef Q3CLIENT + if (cls.protocol == CP_QUAKE3) + count = CG_GatherLoopingSounds(positions, entnums, sounds, countof(sounds)); + else #endif return; @@ -3548,7 +3553,11 @@ static void S_Q2_AddEntitySounds(soundcardinfo_t *sc) } if (sc->ChannelUpdate) { //hardware mixing doesn't support merging + VectorCopy(positions[count], c->origin); SND_Spatialize(sc, c); + + if (c->sfx) + sc->ChannelUpdate(sc, c, false); } else { //merge with any other ents, if we can diff --git a/engine/client/textedit.c b/engine/client/textedit.c index fd6e1e1a7..164d08e58 100644 --- a/engine/client/textedit.c +++ b/engine/client/textedit.c @@ -881,7 +881,7 @@ void Con_Editor_GoToLine(console_t *con, int line) con->display = con->display->newer; } } -console_t *Con_TextEditor(const char *fname, const char *line, qboolean newfile) +console_t *Con_TextEditor(const char *fname, const char *line, pubprogfuncs_t *disasmfuncs) { static int editorcascade; console_t *con; @@ -926,7 +926,26 @@ console_t *Con_TextEditor(const char *fname, const char *line, qboolean newfile) con->close = Con_Editor_Close; con->maxlines = 0x7fffffff; //line limit is effectively unbounded, for a 31-bit process. - if (!newfile) + if (disasmfuncs) + { + int i; + char buffer[65536]; + int start = 1; + int end = INT_MAX; + char *colon = strchr(con->name, ':'); + if (colon && *colon==':') + start = strtol(colon+1, &colon, 0); + if (colon && *colon==':') + end = strtol(colon+1, &colon, 0); + for (i = start; !end || i < end; i++) + { + disasmfuncs->GenerateStatementString(disasmfuncs, i, buffer, sizeof(buffer)); + if (!*buffer) + break; + Con_PrintCon(con, buffer, PFS_FORCEUTF8|PFS_KEEPMARKUP|PFS_NONOTIFY); + } + } + else { file = FS_OpenVFS(fname, "rb", FS_GAME); if (file) @@ -939,10 +958,9 @@ console_t *Con_TextEditor(const char *fname, const char *line, qboolean newfile) } VFS_CLOSE(file); } - - for (l = con->oldest; l; l = l->newer) - Con_Editor_LineChanged(con, l); } + for (l = con->oldest; l; l = l->newer) + Con_Editor_LineChanged(con, l); con->display = con->oldest; con->selstartline = con->selendline = con->oldest; //put the cursor at the start of the file @@ -972,10 +990,10 @@ void Con_TextEditor_f(void) Con_Printf("%s [filename[:line]]: edit a file\n", Cmd_Argv(0)); return; } - Con_TextEditor(fname, line, false); + Con_TextEditor(fname, line, NULL); } -int QCLibEditor(pubprogfuncs_t *prfncs, const char *filename, int *line, int *statement, char *reason, pbool fatal) +int QCLibEditor(pubprogfuncs_t *prfncs, const char *filename, int *line, int *statement, int firststatement, char *reason, pbool fatal) { char newname[MAX_QPATH]; console_t *edit; @@ -983,7 +1001,7 @@ int QCLibEditor(pubprogfuncs_t *prfncs, const char *filename, int *line, int *st if (!strncmp(filename, "./", 2)) filename+=2; - stepasm = !line || *line < 0; + stepasm = !line || *line < 0 || pr_debugger.ival==2; //we can cope with no line info by displaying asm if (editormodal || (stepasm && !statement)) @@ -1001,7 +1019,7 @@ int QCLibEditor(pubprogfuncs_t *prfncs, const char *filename, int *line, int *st return DEBUG_TRACE_OFF; //get lost } - if (qrenderer == QR_NONE || stepasm) + if (qrenderer == QR_NONE)// || stepasm) { //just dump the line of code that's being execed onto the console. int i; char buffer[8192]; @@ -1087,20 +1105,32 @@ int QCLibEditor(pubprogfuncs_t *prfncs, const char *filename, int *line, int *st if (stepasm) { - if (fatal) - return DEBUG_TRACE_ABORT; - return DEBUG_TRACE_OFF; //whoops + if (*statement) + { + char *fname = va(":%#x:%#x", firststatement, firststatement+300); + edit = Con_TextEditor(fname, NULL, prfncs); + if (!edit) + return DEBUG_TRACE_OFF; + firststatement--; //displayed statements are +1 + executionlinenum = *statement - firststatement; + Con_Editor_GoToLine(edit, executionlinenum); + } + else + { + if (fatal) + return DEBUG_TRACE_ABORT; + return DEBUG_TRACE_OFF; //whoops + } } else { - edit = Con_TextEditor(filename, NULL, false); + edit = Con_TextEditor(filename, NULL, NULL); if (!edit) return DEBUG_TRACE_OFF; Con_Editor_GoToLine(edit, *line); + executionlinenum = *line; } - executionlinenum = *line; - { double oldrealtime = realtime; @@ -1138,7 +1168,7 @@ int QCLibEditor(pubprogfuncs_t *prfncs, const char *filename, int *line, int *st { if (line) *line = 0; - *statement = executionlinenum; + *statement = executionlinenum+firststatement; } else if (line) *line = executionlinenum; diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 1b254b055..dcca07ead 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -862,7 +862,7 @@ void R_LightArrays(const entity_t *entity, vecV_t *coords, avec4_t *colours, int //don't include world lights for (lno = rtlights_first; lno < RTL_FIRST; lno++) { - if (cl_dlights[lno].radius) + if (cl_dlights[lno].radius && (cl_dlights[lno].flags & LFLAG_LIGHTMAP)) { VectorSubtract (cl_dlights[lno].origin, entity->origin, @@ -3341,6 +3341,7 @@ static void *Q1MDL_LoadSkins_SV (galiasinfo_t *galias, dmdl_t *pq1inmodel, dalia } #ifndef SERVERONLY +static cvar_t dpcompat_nofloodfill = CVARD("dpcompat_nofloodfill", "0", "Disables the q1mdl floodfill. Setting this to 1 may result in blue seams on the vanilla quake models."); /* ================= Mod_FloodFillSkin @@ -3377,6 +3378,9 @@ static void Mod_FloodFillSkin( qbyte *skin, int skinwidth, int skinheight ) int filledcolor = -1; int i; + if (dpcompat_nofloodfill.ival) + return; + if (filledcolor == -1) { filledcolor = 0; @@ -8541,6 +8545,9 @@ static qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer, size_t f void Alias_Register(void) { #ifdef MD1MODELS +#ifndef SERVERONLY + Cvar_Register(&dpcompat_nofloodfill, NULL); +#endif Mod_RegisterModelFormatMagic(NULL, "Quake1 Model (mdl)", IDPOLYHEADER, Mod_LoadQ1Model); Mod_RegisterModelFormatMagic(NULL, "QuakeForge 16bit Model", (('6'<<24)+('1'<<16)+('D'<<8)+'M'), Mod_LoadQ1Model); #ifdef HEXEN2 diff --git a/engine/common/com_phys_ode.c b/engine/common/com_phys_ode.c index 503054d8c..365944b6a 100644 --- a/engine/common/com_phys_ode.c +++ b/engine/common/com_phys_ode.c @@ -89,9 +89,15 @@ cvar_t r_meshpitch; //#define ODE_DYNAMIC 1 //#endif +//ODE's headers provide version info only as a string, so we don' know when things are deprecated or not. +//this then fucks us over when we try using -Werror +//so until ODE changes its ways, we'll just have to make assumptions and ignore those warnings. +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#define ODEVERSION MAKE2VER(0,15) +#define MAKE2VER(maj,min) (((maj)<<8)|(min)) + // LordHavoc: this large chunk of definitions comes from the ODE library // include files. - #ifdef ODE_STATIC #undef ODE_DYNAMIC #define dDOUBLE @@ -1674,9 +1680,17 @@ static void World_ODE_Frame_JointFromEntity(world_t *world, wedict_t *ed) dJointSetUniversalParam(j, dParamVel2, Vel); break; case JOINTTYPE_HINGE2: - dJointSetHinge2Anchor(j, origin[0], origin[1], origin[2]); + dJointSetHinge2Anchor(j, origin[0], origin[1], origin[2]); +#if ODEVERSION>=MAKE2VER(0,16) + { + dReal a1[]={forward[0], forward[1], forward[2]}, a2[]={velocity[0], velocity[1], velocity[2]}; + dJointSetHinge2Axes(j, a1, a2); + } +#else dJointSetHinge2Axis1(j, forward[0], forward[1], forward[2]); dJointSetHinge2Axis2(j, velocity[0], velocity[1], velocity[2]); +#endif + dJointSetHinge2Param(j, dParamFMax, FMax); dJointSetHinge2Param(j, dParamHiStop, Stop); dJointSetHinge2Param(j, dParamLoStop, -Stop); @@ -1953,8 +1967,15 @@ static void QDECL World_ODE_RagCreateJoint(world_t *world, rbejoint_t *joint, rb break; case JOINTTYPE_HINGE2: dJointSetHinge2Anchor(joint->joint, aaa2[0][0], aaa2[0][1], aaa2[0][2]); +#if ODEVERSION>=MAKE2VER(0,16) + { + dReal a1[]={aaa2[1][0], aaa2[1][1], aaa2[1][2]}, a2[]={aaa2[2][0], aaa2[2][1], aaa2[2][2]}; + dJointSetHinge2Axes(joint->joint, a1, a2); + } +#else dJointSetHinge2Axis1(joint->joint, aaa2[1][0], aaa2[1][1], aaa2[1][2]); dJointSetHinge2Axis2(joint->joint, aaa2[2][0], aaa2[2][1], aaa2[2][2]); +#endif dJointSetHinge2Param(joint->joint, dParamFMax, info->FMax); dJointSetHinge2Param(joint->joint, dParamHiStop, info->HiStop); dJointSetHinge2Param(joint->joint, dParamLoStop, info->LoStop); diff --git a/engine/common/config_fteqw.h b/engine/common/config_fteqw.h index 5318be2ef..f99c87ad4 100644 --- a/engine/common/config_fteqw.h +++ b/engine/common/config_fteqw.h @@ -90,7 +90,7 @@ #define IMAGEFMT_PKM //file format generally written by etcpack or android's etc1tool. doesn't support mips. #define IMAGEFMT_DDS //.dds files embed mipmaps and texture compression. faster to load. //#define IMAGEFMT_BLP //legacy crap -#define IMAGEFMT_BMP //windows bmp. yuck. +#define IMAGEFMT_BMP //windows bmp. yuck. also includes .ico for the luls #define IMAGEFMT_PCX //paletted junk. required for qw player skins, q2 and a few old skyboxes. //#define IMAGEFMT_VTF //hl2 image format #define AVAIL_PNGLIB //.png image format support (read+screenshots) diff --git a/engine/common/particles.h b/engine/common/particles.h index 5fba17dc9..15bcbb68e 100644 --- a/engine/common/particles.h +++ b/engine/common/particles.h @@ -246,7 +246,7 @@ typedef struct { qboolean (*ParticleQuery) (int type, int body, char *outstr, int outstrlen); int (*RunParticleEffectTypeString) (vec3_t org, vec3_t dir, float count, char *name); - int (*ParticleTrail) (vec3_t startpos, vec3_t end, int type, int dlkey, vec3_t dlaxis[3], trailstate_t **tsk); + int (*ParticleTrail) (vec3_t startpos, vec3_t end, int type, float timeinterval, int dlkey, vec3_t dlaxis[3], trailstate_t **tsk); int (*RunParticleEffectState) (vec3_t org, vec3_t dir, float count, int typenum, trailstate_t **tsk); void (*RunParticleWeather) (vec3_t minb, vec3_t maxb, vec3_t dir, float count, int colour, char *efname); void (*RunParticleCube) (int typenum, vec3_t minb, vec3_t maxb, vec3_t dir_min, vec3_t dir_max, float count, int colour, qboolean gravity, float jitter); //typenum may be P_INVALID @@ -256,7 +256,7 @@ typedef struct { void (*RunParticleEffect4) (vec3_t org, float radius, int color, int effect, int count); void (*RunParticleEffectPalette) (const char *nameprefix, vec3_t org, vec3_t dir, int color, int count); - void (*ParticleTrailIndex) (vec3_t start, vec3_t end, int type, int color, int crnd, trailstate_t **tsk); //P_INVALID is fine for the type here, you'll get a default trail. + void (*ParticleTrailIndex) (vec3_t start, vec3_t end, int type, float timeinterval, int color, int crnd, trailstate_t **tsk); //P_INVALID is fine for the type here, you'll get a default trail. qboolean (*InitParticles) (void); void (*ShutdownParticles) (void); void (*DelinkTrailstate) (trailstate_t **tsk); diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 2444e4587..133ce4190 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -186,7 +186,7 @@ static int debuggerstacky; void INS_UpdateGrabs(int fullscreen, int activeapp); #endif -int QCLibEditor(pubprogfuncs_t *prinst, const char *filename, int *line, int *statement, char *error, pbool fatal); +int QCLibEditor(pubprogfuncs_t *prinst, const char *filename, int *line, int *statement, int firststatement, char *error, pbool fatal); void QCLoadBreakpoints(const char *vmname, const char *progsname) { //this asks the gui to reapply any active breakpoints and waits for them so that any spawn functions can be breakpointed properly. extern int isPlugin; @@ -377,7 +377,7 @@ qboolean QCExternalDebuggerCommand(char *text) return true; } -int QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int *statement, char *reason, pbool fatal) +int QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int *statement, int firststatement, char *reason, pbool fatal) { //#if defined(_WIN32) && !defined(FTE_SDL) && !defined(_XBOX) if (isPlugin >= 2) @@ -474,7 +474,7 @@ int QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int //#endif #ifdef TEXTEDITOR - return QCLibEditor(prinst, filename, line, statement, reason, fatal); + return QCLibEditor(prinst, filename, line, statement, firststatement, reason, fatal); #else if (fatal) return DEBUG_TRACE_ABORT; diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index efa2e3051..3926742d7 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -517,7 +517,7 @@ void QCBUILTIN PF_gettime (pubprogfuncs_t *prinst, struct globalvars_s *pr_globa void QCBUILTIN PF_whichpack (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); -int QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int *statement, char *reason, pbool fatal); +int QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int *statement, int firststatement, char *reason, pbool fatal); void PR_Common_Shutdown(pubprogfuncs_t *progs, qboolean errored); void PR_Common_SaveGame(vfsfile_t *f, pubprogfuncs_t *prinst, qboolean binary); qboolean PR_Common_LoadGame(pubprogfuncs_t *prinst, char *command, const char **file); diff --git a/engine/common/vm.h b/engine/common/vm.h index e5e8daebc..f8a4cfcc4 100644 --- a/engine/common/vm.h +++ b/engine/common/vm.h @@ -67,7 +67,7 @@ qboolean UI_OpenMenu(void); void UI_Restart_f(void); qboolean UI_Q2LayoutChanged(void); void UI_StringChanged(int num); -qboolean UI_MousePosition(int xpos, int ypos); +qboolean UI_MousePosition(float xpos, float ypos); int UI_MenuState(void); qboolean UI_KeyPress(int key, int unicode, qboolean down); void UI_Reset(void); diff --git a/engine/gl/gl_ngraph.c b/engine/gl/gl_ngraph.c index 874aeb062..17658adea 100644 --- a/engine/gl/gl_ngraph.c +++ b/engine/gl/gl_ngraph.c @@ -141,6 +141,8 @@ void R_NetGraph (void) M_DrawTextBox (x, y, NET_TIMINGS/8, (NET_GRAPHHEIGHT + textheight)/8); x = 8; + if (r_netgraph.ival > 1) + CL_ShowTrafficUsage(x + NET_TIMINGS + 8, y); y += 8; //top border graphtop = y+textheight; diff --git a/engine/gl/gl_vidlinuxglx.c b/engine/gl/gl_vidlinuxglx.c index 1ff425502..17be27331 100644 --- a/engine/gl/gl_vidlinuxglx.c +++ b/engine/gl/gl_vidlinuxglx.c @@ -3490,28 +3490,34 @@ static void X_StoreIcon(Window wnd) if (!filedata) filedata = FS_LoadMallocFile("icon.jpg", &filesize); #endif +#ifdef IMAGEFMT_BMP if (!filedata) filedata = FS_LoadMallocFile("icon.ico", &filesize); +#endif if (filedata) { int imagewidth, imageheight; - int *iconblob; + unsigned long *iconblob; //yes, long, even on 64bit machines. and even when we claim it to be 32bit. xlib legacy cruft that'll get stripped... uploadfmt_t format; qbyte *imagedata = ReadRawImageFile(filedata, filesize, &imagewidth, &imageheight, &format, true, "icon.png"); Z_Free(filedata); - iconblob = BZ_Malloc(sizeof(int)*(2+imagewidth*imageheight)); - iconblob[0] = imagewidth; - iconblob[1] = imageheight; - //needs to be 0xARGB, rather than RGBA bytes - for (i = 0; i < imagewidth*imageheight; i++) - iconblob[i+2] = (imagedata[i*4+3]<<24) | (imagedata[i*4+0]<<16) | (imagedata[i*4+1]<<8) | (imagedata[i*4+2]<<0); - Z_Free(imagedata); + if (imagedata) + { + iconblob = BZ_Malloc(sizeof(*iconblob)*(2+imagewidth*imageheight)); + iconblob[0] = imagewidth; + iconblob[1] = imageheight; + //needs to be 0xARGB, rather than RGBA bytes + for (i = 0; i < imagewidth*imageheight; i++) + iconblob[i+2] = (imagedata[i*4+3]<<24) | (imagedata[i*4+0]<<16) | (imagedata[i*4+1]<<8) | (imagedata[i*4+2]<<0); + Z_Free(imagedata); - x11.pXChangeProperty(vid_dpy, wnd, propname, proptype, 32, PropModeReplace, (void*)iconblob, 2+imagewidth*imageheight); - BZ_Free(iconblob); + x11.pXChangeProperty(vid_dpy, wnd, propname, proptype, 32, PropModeReplace, (void*)iconblob, 2+imagewidth*imageheight); + BZ_Free(iconblob); + return; + } } - else + { //fall back to the embedded icon. unsigned long data[64*64+2]; diff --git a/engine/partcfgs/high.cfg b/engine/partcfgs/high.cfg index b1f8ec7d4..5bec067e4 100644 --- a/engine/partcfgs/high.cfg +++ b/engine/partcfgs/high.cfg @@ -429,7 +429,6 @@ r_part te_teleport } #endif #ifdef FRAGMENT_SHADER - uniform sampler2D s_t0; void main(void) { vec2 nst; diff --git a/engine/qclib/pr_exec.c b/engine/qclib/pr_exec.c index 8e43313fe..2522889f1 100644 --- a/engine/qclib/pr_exec.c +++ b/engine/qclib/pr_exec.c @@ -180,6 +180,8 @@ void PDECL PR_GenerateStatementString (pubprogfuncs_t *ppf, int statementnum, ch *out = 0; outlen--; + if ((unsigned)statementnum >= current_progstate->progs->numstatements) + return; switch(current_progstate->structtype) { case PST_DEFAULT: @@ -1364,7 +1366,7 @@ static const char *lastfile = NULL; lastfile = file; faultline = lastline; - debugaction = externs->useeditor(&progfuncs->funcs, lastfile, ((lastline!=-1)?&lastline:NULL), &statement, fault, fatal); + debugaction = externs->useeditor(&progfuncs->funcs, lastfile, ((lastline!=-1)?&lastline:NULL), &statement, f->first_statement, fault, fatal); // if (pn != prinst.pr_typecurrent) diff --git a/engine/qclib/progslib.h b/engine/qclib/progslib.h index 6ba015ee5..44a4af3d4 100644 --- a/engine/qclib/progslib.h +++ b/engine/qclib/progslib.h @@ -230,7 +230,7 @@ typedef struct progexterns_s { void *(VARGS *memalloc) (int size); //small string allocation malloced and freed randomly by the executor. (use malloc if you want) void (VARGS *memfree) (void * mem); - int (PDECL *useeditor) (pubprogfuncs_t *prinst, const char *filename, int *line, int *statement, char *reason, pbool fatal); //called on syntax errors or step-by-step debugging. line and statement(if line was set to 0) can be used to change the next line. return value is the new debug state to use/step. + int (PDECL *useeditor) (pubprogfuncs_t *prinst, const char *filename, int *line, int *statement, int funcstart, char *reason, pbool fatal); //called on syntax errors or step-by-step debugging. line and statement(if line was set to 0) can be used to change the next line. return value is the new debug state to use/step. void (PDECL *addressablerelocated) (pubprogfuncs_t *progfuncs, char *oldb, char *newb, int oldlen); //called when the progs memory was resized. you must fix up all pointers to globals, strings, fields, addressable blocks. builtin_t *globalbuiltins; //these are available to all progs diff --git a/engine/qclib/qccguiqt.cpp b/engine/qclib/qccguiqt.cpp index 11d0e61cf..d4532f5fb 100644 --- a/engine/qclib/qccguiqt.cpp +++ b/engine/qclib/qccguiqt.cpp @@ -562,7 +562,7 @@ public: s->SendScintilla(QsciScintillaBase::SCI_STYLESETBACK, QsciScintillaBase::STYLE_BRACEBAD, QColor(0xff, 0xaf, 0xaf)); //SCE_C_WORD - s->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS, static_cast(0), + s->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS, 0ul, "if else for do not while asm break case const continue " "default enum enumflags extern " "float goto __in __out __inout noref " @@ -578,11 +578,11 @@ public: { char buffer[65536]; GenBuiltinsList(buffer, sizeof(buffer)); - s->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS, 1, buffer); + s->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS, 1ul, buffer); } //SCE_C_COMMENTDOCKEYWORDERROR //SCE_C_GLOBALCLASS - s->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS, 3, + s->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS, 3ul, "" ); //preprocessor listing @@ -590,11 +590,11 @@ public: char *deflist = QCC_PR_GetDefinesList(); if (!deflist) deflist = strdup(""); - s->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS, 4, deflist); + s->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS, 4ul, deflist); free(deflist); } //task markers (in comments only) - s->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS, 5, + s->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS, 5ul, "TODO FIXME BUG" ); diff --git a/engine/server/botlib.h b/engine/server/botlib.h index 7a3e74a85..a24ccc10b 100644 --- a/engine/server/botlib.h +++ b/engine/server/botlib.h @@ -202,6 +202,9 @@ typedef struct botlib_import_s // int (QDECL *DebugPolygonCreate)(int color, int numPoints, vec3_t *points); void (QDECL *DebugPolygonDelete)(int id); + + //FTE additions... + void (QDECL *Error)(const char *error); } botlib_import_t; typedef struct aas_export_s diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 1b78a6dc8..16623be38 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -10874,7 +10874,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs //312 //313 //2d (immediate) operations - {"drawtextfield", PF_Fixme, 0, 0, 0, 0/*314*/, D("void(vector pos, vector size, float alignflags, string text)", "Draws a multi-line block of text, including word wrapping and alignment. alignflags bits are RTLB, typically 3.")},// (EXT_CSQC) + {"drawtextfield", PF_Fixme, 0, 0, 0, 0/*314*/, D("float(vector pos, vector size, float alignflags, string text)", "Draws a multi-line block of text, including word wrapping and alignment. alignflags bits are RTLB, typically 3. Returns the total number of lines.")},// (EXT_CSQC) {"drawline", PF_Fixme, 0, 0, 0, 315, D("void(float width, vector pos1, vector pos2, vector rgb, float alpha, optional float drawflag)", "Draws a 2d line between the two 2d points.")},// (EXT_CSQC) {"iscachedpic", PF_Fixme, 0, 0, 0, 316, D("float(string name)", "Checks to see if the image is currently loaded. Engines might lie, or cache between maps.")},// (EXT_CSQC) {"precache_pic", PF_Fixme, 0, 0, 0, 317, D("string(string name, optional float trywad)", "Forces the engine to load the named image. If trywad is specified, the specified name must any lack path and extension.")},// (EXT_CSQC) diff --git a/engine/server/savegame.c b/engine/server/savegame.c index dfc9e8e52..7aae3b9a9 100644 --- a/engine/server/savegame.c +++ b/engine/server/savegame.c @@ -1772,19 +1772,49 @@ qboolean SV_Loadgame (const char *unsafe_savename) #ifndef QUAKETC {"%s.sav"}, #endif + {"%s"} }; - int bd,best; + int bestd=0x7fffffff,best=0; + time_t bestt=0,t; Q_strncpyz(savename, unsafe_savename, sizeof(savename)); if (!*savename || strstr(savename, "..")) - strcpy(savename, "quick"); + { //if no args, or its invalid, try to pick the last one that was saved (of those listed in the menu) + size_t n; + static char *autoload[] = { "quick", "a0", "a1", "a2", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9"}; + strcpy(savename, "quick"); //default... - for (len = 0, bd=0x7fffffff,best=0; len < countof(savefiles); len++) - { - int d = FS_FLocateFile(va(savefiles[len].pattern, savename), FSLF_DONTREFERENCE|FSLF_DEEPONFAILURE, &savefiles[len].loc); - if (bd > d) + for (n = 0; n < countof(autoload); n++) { - bd = d; + for (len = 0; len < countof(savefiles)-1; len++) + { + int d = FS_FLocateFile(va(savefiles[len].pattern, autoload[n]), FSLF_DONTREFERENCE, &savefiles[len].loc); + if (!d) + continue; + FS_GetLocMTime(&savefiles[len].loc, &t); + if (d < bestd || (bestd==d&&t>bestt)) + { + bestd = d; + bestt = t; + best = len; + + strcpy(savename, autoload[n]); + } + } + } + } + + for (len = 0; len < countof(savefiles); len++) + { + int d = FS_FLocateFile(va(savefiles[len].pattern, savename), FSLF_DONTREFERENCE, &savefiles[len].loc); + if (!d) + continue; + FS_GetLocMTime(&savefiles[len].loc, &t); + if (d < bestd || (bestd==d&&t>bestt)) + { + bestd = d; + bestt = t; best = len; } } diff --git a/engine/server/server.h b/engine/server/server.h index 497e56449..05d781d88 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -576,12 +576,14 @@ typedef struct client_s #ifdef Q3SERVER int gamestatesequence; //the sequence number the initial gamestate was sent in. - int last_server_command_num; + int last_client_command_num; - int num_server_commands; - int num_client_commands; - char server_commands[64][1024]; char last_client_command[1024]; + + //quake3 does reliables only via this mechanism. basically just like q1's stuffcmds. + int server_command_ack; //number known to have been received. + int server_command_sequence; //number available. + char server_commands[64][1024]; //the commands, to deal with resends #endif //true/false/persist diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 9c27a915a..3aa4cc536 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -779,11 +779,13 @@ void SV_Map_f (void) // gametype->callback = gtcallback; /* map_restart doesn't need to handle gametype changes - eukara */ - if (Q_strcasecmp(Cmd_Argv(0), "map_restart")) + if (!isrestart) + { if (q3singleplayer) Cvar_ForceSet(gametype, "2");//singleplayer else if (gametype->value == 2) Cvar_ForceSet(gametype, "");//force to ffa deathmatch + } } #endif diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index 6084600f7..c254995dd 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -1480,81 +1480,84 @@ qboolean SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizeb outno = 0; outmax = frame->maxresend; - /*start writing the packet*/ - MSG_WriteByte (msg, svcfte_updateentities); - if (ISNQCLIENT(client) && (client->fteprotocolextensions2 & PEXT2_PREDINFO)) + if (msg->cursize + 52 <= msg->maxsize) { - MSG_WriteShort(msg, client->last_sequence&0xffff); - } -// Con_Printf("Gen sequence %i\n", sequence); - MSG_WriteFloat(msg, sv.world.physicstime); - - if (client->pendingdeltabits[0] & UF_REMOVE) - { - SV_EmitDeltaEntIndex(msg, 0, true, true); - resend[outno].bits = UF_REMOVE; - resend[outno].flags = 0; - resend[outno++].entnum = 0; - - client->pendingdeltabits[0] &= ~UF_REMOVE; - } - for(j = 1; j < client->sentents.num_entities; j++) - { - bits = client->pendingdeltabits[j]; - if (!(bits & ~UF_RESET2)) //skip while there's nothing to send (skip reset2 if there's no other changes, its only to reduce chances of the client getting 'new' entities containing just an origin)*/ - continue; - if (msg->cursize + 50 > msg->maxsize) + /*start writing the packet*/ + MSG_WriteByte (msg, svcfte_updateentities); + if (ISNQCLIENT(client) && (client->fteprotocolextensions2 & PEXT2_PREDINFO)) { - overflow = true; - break; /*give up if it gets full. FIXME: bone data is HUGE.*/ - } - if (outno >= outmax) - { //expand the frames. may need some copying... - SV_ExpandNackFrames(client, outno+1); - break; + MSG_WriteShort(msg, client->last_sequence&0xffff); } + // Con_Printf("Gen sequence %i\n", sequence); + MSG_WriteFloat(msg, sv.world.physicstime); - if (bits & UF_REMOVE) - { //if reset is set, then reset was set eroneously. - SV_EmitDeltaEntIndex(msg, j, true, true); + if (client->pendingdeltabits[0] & UF_REMOVE) + { + SV_EmitDeltaEntIndex(msg, 0, true, true); resend[outno].bits = UF_REMOVE; -// Con_Printf("REMOVE %i @ %i\n", j, sequence); + resend[outno].flags = 0; + resend[outno++].entnum = 0; + + client->pendingdeltabits[0] &= ~UF_REMOVE; } - else if (client->sentents.entities[j].number) /*only send a new copy of the ent if they actually have one already*/ + for(j = 1; j < client->sentents.num_entities; j++) { - //if we didn't reach the end in the last packet, start at that point to avoid spam - //player slots are exempt from this, so they are in every packet (strictly speaking only the local player 'needs' this, but its nice to have it for high-priority targets too) - if (j < client->nextdeltaindex && j > svs.allocated_client_slots) + bits = client->pendingdeltabits[j]; + if (!(bits & ~UF_RESET2)) //skip while there's nothing to send (skip reset2 if there's no other changes, its only to reduce chances of the client getting 'new' entities containing just an origin)*/ continue; - - if (bits & UF_RESET2) + if (msg->cursize + 52 > msg->maxsize) { - /*if reset2, then this is the second packet sent to the client and should have a forced reset (but which isn't tracked)*/ - resend[outno].bits = bits & ~UF_RESET2; - bits = UF_RESET | SVFTE_DeltaCalcBits(&EDICT_NUM_PB(svprogfuncs, j)->baseline, NULL, &client->sentents.entities[j], client->sentents.bonedata); -// Con_Printf("RESET2 %i @ %i\n", j, sequence); + overflow = true; + break; /*give up if it gets full. FIXME: bone data is HUGE.*/ } - else if (bits & UF_RESET) - { - /*flag the entity for the next packet, so we always get two resets when it appears, to reduce the effects of packetloss on seeing rockets etc*/ - client->pendingdeltabits[j] = UF_RESET2; - bits = UF_RESET | SVFTE_DeltaCalcBits(&EDICT_NUM_PB(svprogfuncs, j)->baseline, NULL, &client->sentents.entities[j], client->sentents.bonedata); - resend[outno].bits = UF_RESET; -// Con_Printf("RESET %i @ %i\n", j, sequence); + if (outno >= outmax) + { //expand the frames. may need some copying... + SV_ExpandNackFrames(client, outno+1); + break; } - else - resend[outno].bits = bits; - SV_EmitDeltaEntIndex(msg, j, false, true); - SVFTE_WriteUpdate(bits, &client->sentents.entities[j], msg, client->fteprotocolextensions2, client->sentents.bonedata); + if (bits & UF_REMOVE) + { //if reset is set, then reset was set eroneously. + SV_EmitDeltaEntIndex(msg, j, true, true); + resend[outno].bits = UF_REMOVE; + // Con_Printf("REMOVE %i @ %i\n", j, sequence); + } + else if (client->sentents.entities[j].number) /*only send a new copy of the ent if they actually have one already*/ + { + //if we didn't reach the end in the last packet, start at that point to avoid spam + //player slots are exempt from this, so they are in every packet (strictly speaking only the local player 'needs' this, but its nice to have it for high-priority targets too) + if (j < client->nextdeltaindex && j > svs.allocated_client_slots) + continue; + + if (bits & UF_RESET2) + { + /*if reset2, then this is the second packet sent to the client and should have a forced reset (but which isn't tracked)*/ + resend[outno].bits = bits & ~UF_RESET2; + bits = UF_RESET | SVFTE_DeltaCalcBits(&EDICT_NUM_PB(svprogfuncs, j)->baseline, NULL, &client->sentents.entities[j], client->sentents.bonedata); + // Con_Printf("RESET2 %i @ %i\n", j, sequence); + } + else if (bits & UF_RESET) + { + /*flag the entity for the next packet, so we always get two resets when it appears, to reduce the effects of packetloss on seeing rockets etc*/ + client->pendingdeltabits[j] = UF_RESET2; + bits = UF_RESET | SVFTE_DeltaCalcBits(&EDICT_NUM_PB(svprogfuncs, j)->baseline, NULL, &client->sentents.entities[j], client->sentents.bonedata); + resend[outno].bits = UF_RESET; + // Con_Printf("RESET %i @ %i\n", j, sequence); + } + else + resend[outno].bits = bits; + + SV_EmitDeltaEntIndex(msg, j, false, true); + SVFTE_WriteUpdate(bits, &client->sentents.entities[j], msg, client->fteprotocolextensions2, client->sentents.bonedata); + } + + client->pendingdeltabits[j] = 0; + + resend[outno].flags = 0; + resend[outno++].entnum = j; } - - client->pendingdeltabits[j] = 0; - - resend[outno].flags = 0; - resend[outno++].entnum = j; + MSG_WriteShort(msg, 0); } - MSG_WriteShort(msg, 0); if (j == client->sentents.num_entities) //looks like we sent them all client->nextdeltaindex = 0; //start afresh with the next packet. diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 72680a500..9a39600c4 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -61,6 +61,7 @@ cvar_t sv_gameplayfix_bouncedownslopes = CVARD( "sv_gameplayfix_grenadebouncedo #if !defined(CLIENTONLY) && defined(NQPROT) && !defined(NOLEGACY) cvar_t sv_gameplayfix_spawnbeforethinks = CVARD( "sv_gameplayfix_spawnbeforethinks", "0", "Fixes an issue where player thinks (including Pre+Post) can be called before PutClientInServer. Unfortunately at least one mod depends upon PreThink being called first in order to correctly determine spawn positions."); #endif +cvar_t dpcompat_noretouchground = CVARD( "dpcompat_noretouchground", "0", "Prevents entities that are already standing on an entity from touching the same entity again."); cvar_t sv_sound_watersplash = CVAR( "sv_sound_watersplash", "misc/h2ohit1.wav"); cvar_t sv_sound_land = CVAR( "sv_sound_land", "demon/dland2.wav"); cvar_t sv_stepheight = CVARAFD("pm_stepheight", "", "sv_stepheight", CVAR_SERVERINFO, "If empty, the value "STRINGIFY(PM_DEFAULTSTEPHEIGHT)" will be used instead. This is the size of the step you can step up or down."); @@ -98,6 +99,7 @@ void WPhys_Init(void) Cvar_Register (&sv_gameplayfix_multiplethinks, cvargroup_serverphysics); Cvar_Register (&sv_gameplayfix_stepdown, cvargroup_serverphysics); Cvar_Register (&sv_gameplayfix_bouncedownslopes, cvargroup_serverphysics); + Cvar_Register (&dpcompat_noretouchground, cvargroup_serverphysics); #if !defined(CLIENTONLY) && defined(NQPROT) && !defined(NOLEGACY) Cvar_Register (&sv_gameplayfix_spawnbeforethinks, cvargroup_serverphysics); @@ -450,6 +452,12 @@ static int WPhys_FlyMove (world_t *w, wedict_t *ent, const vec3_t gravitydir, fl if (!trace.ent) Host_Error ("SV_FlyMove: !trace.ent"); + if (dpcompat_noretouchground.ival) + { //note: also sets onground AFTER the touch event. + if (!((int)ent->v->flags&FL_ONGROUND) || ent->v->groundentity!=EDICT_TO_PROG(w->progs, trace.ent)) + WPhys_Impact (w, ent, &trace); + } + if (-DotProduct(gravitydir, trace.plane.normal) > 0.7) { blocked |= 1; // floor @@ -469,7 +477,8 @@ static int WPhys_FlyMove (world_t *w, wedict_t *ent, const vec3_t gravitydir, fl // // run the impact function // - WPhys_Impact (w, ent, &trace); + if (!dpcompat_noretouchground.ival) + WPhys_Impact (w, ent, &trace); if (ED_ISFREE(ent)) break; // removed by the impact function diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 031ede39c..05dcb57eb 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -2566,7 +2566,12 @@ qboolean SV_SendClientDatagram (client_t *client) } if (client->netchan.fragmentsize) - clientlimit = client->netchan.fragmentsize; //try not to overflow + { + if (client->netchan.remote_address.type == NA_LOOPBACK) + clientlimit = countof(buf); //biiiig... + else + clientlimit = client->netchan.fragmentsize; //try not to overflow + } else if (client->protocol == SCP_NETQUAKE) clientlimit = MAX_NQDATAGRAM; //vanilla client is limited. else @@ -2574,6 +2579,8 @@ qboolean SV_SendClientDatagram (client_t *client) if (clientlimit > countof(buf)) clientlimit = countof(buf); msg.maxsize = clientlimit - client->datagram.cursize; + if (msg.maxsize <= 0) + msg.maxsize = clientlimit; //its going to overflow. favour ents over unreliables. its a little less fatal if (sv.world.worldmodel && !client->controller) { diff --git a/engine/server/svq3_game.c b/engine/server/svq3_game.c index 6443c583f..75a407e1e 100644 --- a/engine/server/svq3_game.c +++ b/engine/server/svq3_game.c @@ -34,6 +34,9 @@ static botlib_export_t *FTE_GetBotLibAPI(int apiVersion, botlib_import_t *import {(void**)&pGetBotLibAPI, "GetBotLibAPI"}, {NULL} }; + + if (!botlib && host_parms.binarydir) + botlib = Sys_LoadLibrary(va("%slibfteq3bot", host_parms.binarydir), funcs); if (!botlib) botlib = Sys_LoadLibrary("botlib", funcs); if (!botlib) @@ -764,8 +767,8 @@ void SVQ3_SendServerCommand(client_t *cl, char *str) return; } - cl->num_server_commands++; - Q_strncpyz(cl->server_commands[cl->num_server_commands & TEXTCMD_MASK], str, sizeof(cl->server_commands[0])); + cl->server_command_sequence++; + Q_strncpyz(cl->server_commands[cl->server_command_sequence & TEXTCMD_MASK], str, sizeof(cl->server_commands[0])); } void SVQ3_SendConfigString(client_t *dest, int num, char *string) @@ -832,11 +835,11 @@ static int SVQ3_BotGetConsoleMessage( int client, char *buf, int size ) cl = &svs.clients[client]; // cl->lastPacketTime = svs.time; - if (cl->last_server_command_num == cl->num_server_commands) + if (cl->server_command_ack == cl->server_command_sequence) return false; - cl->last_server_command_num++; - index = cl->last_server_command_num & TEXTCMD_MASK; + cl->server_command_ack++; + index = cl->server_command_ack & TEXTCMD_MASK; if ( !cl->server_commands[index][0] ) return false; @@ -1804,6 +1807,10 @@ static void QDECL BL_DebugLineShow(int line, vec3_t start, vec3_t end, int color static int QDECL BL_DebugPolygonCreate(int color, int numPoints, vec3_t *points) {return 0;} static void QDECL BL_DebugPolygonDelete(int id) {} +void QDECL BL_Error(const char *msg) +{ + Sys_Error("%s", msg); +} #endif static void SV_InitBotLib(void) @@ -1840,6 +1847,8 @@ static void SV_InitBotLib(void) import.DebugPolygonCreate= BL_DebugPolygonCreate; import.DebugPolygonDelete = BL_DebugPolygonDelete; + import.Error = BL_Error; + // Z_FreeTags(Z_TAG_BOTLIB); botlibmemoryavailable = 1024*1024*16; if (bot_enable->value) @@ -2690,6 +2699,23 @@ static void SVQ3Q1_SendGamestateConfigstrings(sizebuf_t *msg) } #endif +static void SVQ3_WriteServerCommandsToClient(client_t *client, sizebuf_t *msg) +{ + int i; + int j, len; + char *str; + + for(i=client->server_command_ack+1; i<=client->server_command_sequence; i++) + { + MSG_WriteBits(msg, svcq3_serverCommand, 8); + MSG_WriteBits(msg, i, 32); + str = client->server_commands[i & TEXTCMD_MASK]; + len = strlen(str); + for (j = 0; j <= len; j++) + MSG_WriteBits(msg, str[j], 8); + } +} + //writes initial gamestate void SVQ3_SendGameState(client_t *client) { @@ -2709,8 +2735,10 @@ void SVQ3_SendGameState(client_t *client) // write last clientCommand number we have processed MSG_WriteBits(&msg, client->last_client_command_num, 32); + SVQ3_WriteServerCommandsToClient(client, &msg); + MSG_WriteBits(&msg, svcq3_gamestate, 8 ); - MSG_WriteBits(&msg, client->num_client_commands, 32); + MSG_WriteBits(&msg, client->server_command_sequence, 32); switch (svs.gametype) { @@ -2781,23 +2809,6 @@ void SVQ3_SendGameState(client_t *client) client->gamestatesequence = client->last_sequence; } -void SVQ3_WriteServerCommandsToClient(client_t *client, sizebuf_t *msg) -{ - int i; - int j, len; - char *str; - - for(i=client->last_server_command_num+1; i<=client->num_server_commands; i++) - { - MSG_WriteBits(msg, svcq3_serverCommand, 8); - MSG_WriteBits(msg, i, 32); - str = client->server_commands[i & TEXTCMD_MASK]; - len = strlen(str); - for (j = 0; j <= len; j++) - MSG_WriteBits(msg, str[j], 8); - } -} - void SVQ3_SendMessage(client_t *client) { qbyte buffer[MAX_OVERALLMSGLEN]; @@ -2992,7 +3003,7 @@ void SVQ3_ParseUsercmd(client_t *client, qboolean delta) return; // was dropped // calculate key for usercmd decryption - string = client->server_commands[client->last_server_command_num & TEXTCMD_MASK]; + string = client->server_commands[client->server_command_ack & TEXTCMD_MASK]; key = client->last_sequence ^ fs_key ^ StringKey(string, 32); // read delta sequenced usercmds @@ -3207,11 +3218,11 @@ void SVQ3_ParseClientMessage(client_t *client) } // read last server command number client received - client->last_server_command_num = MSG_ReadBits(32); - if( client->last_server_command_num <= client->num_server_commands - TEXTCMD_BACKUP ) - client->last_server_command_num = client->num_server_commands - TEXTCMD_BACKUP + 1; - else if( client->last_server_command_num > client->num_server_commands ) - client->last_server_command_num = client->num_server_commands; + client->server_command_ack = MSG_ReadBits(32); + if( client->server_command_ack <= client->server_command_sequence - TEXTCMD_BACKUP ) + client->server_command_ack = client->server_command_sequence - TEXTCMD_BACKUP + 1; //too old + else if( client->server_command_ack > client->server_command_sequence ) + client->server_command_ack = client->server_command_sequence; //client is from the future? o.O make fatal? // check if message is from a previous level if( serverid != svs.spawncount ) @@ -3243,7 +3254,7 @@ void SVQ3_ParseClientMessage(client_t *client) if(msg_readcount > net_message.cursize) { - Con_Printf("corrupted packet from %s\n", client->name); + Con_Printf("corrupt packet from %s\n", client->name); client->drop = true; return; } @@ -3257,7 +3268,7 @@ void SVQ3_ParseClientMessage(client_t *client) switch(c) { default: - Con_Printf("corrupted packet from %s\n", client->name); + Con_Printf("corrupt packet from %s\n", client->name); client->drop = true; return; case clcq3_nop: