diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 19f0cc67e..c626ffef5 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1299,6 +1299,12 @@ void CL_ClearState (void) if (cl.lerpents) BZ_Free(cl.lerpents); + if (cl.particle_ssprecaches) + { + for (i = 0; i < MAX_SSPARTICLESPRE; i++) + if (cl.particle_ssname[i]) + free(cl.particle_ssname[i]); + } { downloadlist_t *next; diff --git a/engine/client/quake.manifest b/engine/client/quake.manifest index 5eafbc15f..730426b03 100644 --- a/engine/client/quake.manifest +++ b/engine/client/quake.manifest @@ -1,9 +1,14 @@ - - + + - + + + + true + + \ No newline at end of file diff --git a/engine/common/common.c b/engine/common/common.c index 0981df9fb..de1cf6176 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -2684,6 +2684,186 @@ static unsigned int koi2wc (unsigned char uc) return uc; } +enum +{ + BIDI_NEUTRAL, + BIDI_LTR, + BIDI_RTL, +}; +static char *bidi_chartype; +static unsigned int bidi_charcount; + + +//semi-colon delimited tokens +char *COM_ParseStringSetSep (const char *data, char sep) +{ + int c; + size_t len; + + len = 0; + com_token[0] = 0; + + if (data) + for (;*data;) + { + if (len >= sizeof(com_token)-1) + { + com_token[len] = 0; + return (char*)data; + } + c = *data++; + if (c == ';') + break; + com_token[len++] = c; + } + + com_token[len] = 0; + return (char*)data; +} +void COM_BiDi_Setup(void) +{ + char *file; + char *line; + char *end; + char *tok; + unsigned int c; + qofs_t size; + + file = FS_MallocFile("bidi.dat", FS_ROOT, &size); + if (file) + { + bidi_chartype = file; + bidi_charcount = size; + return; + } + + file = FS_MallocFile("UnicodeData.txt", FS_ROOT, NULL); + if (!file) + return; + + bidi_charcount = 0xffff; + bidi_chartype = BZ_Malloc(bidi_charcount); + if (!bidi_chartype) + bidi_charcount = 0; + else + { + for (c = 0; c < bidi_charcount; c++) + bidi_chartype[c] = BIDI_NEUTRAL; + for(line = file; line; line = end) + { + end = strchr(line, '\n'); + if (end) + *end++ = 0; + + tok = COM_ParseStringSetSep(line,';'); //number + c = strtoul(com_token, NULL, 16); + tok = COM_ParseStringSetSep(tok,';'); //name + tok = COM_ParseStringSetSep(tok,';'); //class? + tok = COM_ParseStringSetSep(tok,';'); //? + tok = COM_ParseStringSetSep(tok,';'); //bidi + if (c < bidi_charcount) + { + if (!Q_strcasecmp(com_token, "R") || !Q_strcasecmp(com_token, "AL")) + bidi_chartype[c] = BIDI_RTL; + else if (!Q_strcasecmp(com_token, "L")) + bidi_chartype[c] = BIDI_LTR; + else + bidi_chartype[c] = BIDI_NEUTRAL; + } + } + + //trim + while(bidi_charcount>0 && bidi_chartype[bidi_charcount-1] == BIDI_NEUTRAL) + bidi_charcount--; + FS_WriteFile("bidi.dat", bidi_chartype, bidi_charcount, FS_ROOT); + } + BZ_Free(file); +} +//bi-direction text is fun. +//the text is specified in input order. the first in the string is the first entered on the keyboard. +//this makes switching direction mid-line quite awkward. so lets hope you don't do that too often, mmkay? +void COM_BiDi_Parse(conchar_t *fte_restrict start, size_t length) +{ + char fl[2048], next, run, prev, para = BIDI_LTR; + size_t i, runstart, j, k; + unsigned int c; + conchar_t swap; + if (!bidi_charcount || !length || length > sizeof(fl)) + return; + + for (i = 0; i < length; i++) + { + c = start[i] & CON_CHARMASK; + if (c >= bidi_charcount) + fl[i] = BIDI_NEUTRAL; + else + fl[i] = bidi_chartype[c]; + } + + //de-neutralise it + prev = fl[0]; + for (i = 0; i < length; ) + { + if (fl[i] == BIDI_NEUTRAL) + { + next = prev; //trailing weak chars can just use the first side + for (runstart = i; i < length; i++) + { + next = fl[i]; + if (next != BIDI_NEUTRAL) + { + i--; + break; + } + } + //this can happen if the only text is neutral + if (prev == BIDI_NEUTRAL) + run = next; + //if the strong cars are the same direction on both side, we can just use that direction + else if (prev == next) + run = prev; + //if the strong chars differ, we revert to the paragraph's direction. + else + run = para; + + while(runstart <= i) + fl[runstart++] = run; + i++; + } + else + { + prev = fl[i]; + i++; + } + } + + for (run = para, runstart = 0, i = 0; i <= length; i++) + { + if (i >= length) + next = para; + else + next = fl[i]; + if (next != run) + { + if (run == BIDI_NEUTRAL) + break; + if (run == BIDI_RTL) + { //now swap the rtl text + k = (i-runstart)/2; + for (j = 0; j < k; j++) + { + //FIXME: ( -> ) and vice versa. + swap = start[runstart+j]; + start[runstart+j] = start[i-j-1]; + start[i-j-1] = swap; + } + } + run = next; + runstart = i; + } + } +} + //Takes a q3-style fun string, and returns an expanded string-with-flags (actual return value is the null terminator) //outsize parameter is in _BYTES_ (so sizeof is safe). conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t *out, int outsize, int flags) @@ -2698,6 +2878,7 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t conchar_t *linkstart = NULL; conchar_t ext; + conchar_t *oldout = out; if (flags & PFS_FORCEUTF8) utf8 = 2; @@ -3113,6 +3294,8 @@ messedup: } } *out = 0; + + COM_BiDi_Parse(oldout, out - oldout); return out; } @@ -3272,7 +3455,7 @@ char *COM_ParseStringSet (const char *data) com_token[len] = c; data++; len++; - c = *data; + c = *(unsigned char*)data; } while (c>32 && c != ';'); com_token[len] = 0; @@ -4362,6 +4545,8 @@ void COM_Init (void) TranslateInit(); + COM_BiDi_Setup(); + nullentitystate.hexen2flags = SCALE_ORIGIN_ORIGIN; nullentitystate.colormod[0] = 32; diff --git a/engine/common/common.h b/engine/common/common.h index cd8f4f31d..640bb3a57 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -444,6 +444,7 @@ qboolean FS_Remove(const char *fname, enum fs_relative relativeto); //0 on succe qboolean FS_Copy(const char *source, const char *dest, enum fs_relative relativesource, enum fs_relative relativedest); qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out, int outlen); //if you really need to fopen yourself qboolean FS_WriteFile (const char *filename, const void *data, int len, enum fs_relative relativeto); +void *FS_MallocFile(const char *filename, enum fs_relative relativeto, qofs_t *filesize); vfsfile_t *FS_OpenVFS(const char *filename, const char *mode, enum fs_relative relativeto); vfsfile_t *FS_OpenTemp(void); vfsfile_t *FS_OpenTCP(const char *name, int defaultport); diff --git a/engine/common/fs.c b/engine/common/fs.c index cd1677eb9..ceb2d96fe 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -1482,6 +1482,29 @@ void FS_CreatePath(const char *pname, enum fs_relative relativeto) COM_CreatePath(fullname); } +void *FS_MallocFile(const char *filename, enum fs_relative relativeto, qofs_t *filesize) +{ + vfsfile_t *f; + qbyte *buf; + qofs_t len; + + f = FS_OpenVFS(filename, "rb", relativeto); + if (!f) + return NULL; + len = VFS_GETLEN(f); + if (filesize) + *filesize = len; + + buf = (qbyte*)BZ_Malloc(len+1); + if (!buf) + Sys_Error ("FS_MallocFile: out of memory loading %s", filename); + + ((qbyte *)buf)[len] = 0; + + VFS_READ(f, buf, len); + VFS_CLOSE(f); + return buf; +} qboolean FS_WriteFile (const char *filename, const void *data, int len, enum fs_relative relativeto) { vfsfile_t *f; diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 72d56a66f..48f1cf426 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -580,7 +580,10 @@ static void *Terr_GenerateWater(hmsection_t *s, float maxheight) s->water = w; #ifndef SERVERONLY if (!isDedicated) + { w->shader = R_RegisterCustom (s->hmmod->defaultwatershader, SUF_NONE, Shader_DefaultWaterShader, NULL); + R_BuildDefaultTexnums(NULL, w->shader); //this might get expensive. hideously so. + } #endif w->simple = true; w->contentmask = FTECONTENTS_WATER; diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 9456cb9e8..7ba3f8b1f 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -3015,7 +3015,7 @@ void Shader_Shutdown (void) for (i = 0; i < r_numshaders; i++) { shader = r_shaders[i]; - if (!shader || !shader->uses) + if (!shader) continue; Shader_Free(shader); @@ -4886,6 +4886,9 @@ static shader_t *R_LoadShader (const char *name, unsigned int usageflags, shader f = r_numshaders; if (f == r_maxshaders) { + if (!r_maxshaders) + Sys_Error( "R_LoadShader: shader system not inited."); + nm = r_maxshaders * 2; n = realloc(r_shaders, nm*sizeof(*n)); if (!n) diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index f11f04a2a..2435ea686 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -837,7 +837,7 @@ void SV_EvaluatePenalties(client_t *cl) if (delta & BAN_VIP) { delta &= ~BAN_VIP; //don't refer to this as a penalty - if (penalties & p) + if (penalties & BAN_VIP) SV_PrintToClient(cl, PRINT_HIGH, "You are a VIP, apparently\n"); else SV_PrintToClient(cl, PRINT_HIGH, "VIP expired\n"); @@ -1068,7 +1068,7 @@ static void SV_FilterIP_f (void) if (Cmd_Argc() < 2) { Con_Printf("%s
[flags] [+time] [reason]\n", Cmd_Argv(0)); - Con_Printf("allowed flags: ban,safe,cuff,mute,cripple,deaf,lag. time is in seconds (omitting the plus will be taken to mean unix time).\n", Cmd_Argv(0)); + Con_Printf("allowed flags: ban,safe,cuff,mute,cripple,deaf,lag,blind,spec. time is in seconds (omitting the plus will be taken to mean unix time).\n", Cmd_Argv(0)); return; } @@ -1109,6 +1109,8 @@ static void SV_FilterIP_f (void) proto.banflags |= BAN_VIP; else if (!Q_strcasecmp(com_token, "blind")) proto.banflags |= BAN_BLIND; + else if (!Q_strcasecmp(com_token, "spec")) + proto.banflags |= BAN_SPECONLY; else Con_Printf("Unknown ban/penalty flag: %s. ignoring.\n", com_token); } @@ -1176,6 +1178,7 @@ static void SV_FilterList_f (void) "lag", "vip", "blind", + "spec", NULL }; @@ -1256,12 +1259,16 @@ static void SV_Unfilter_f (void) banflags |= BAN_LAGGED; else if (!Q_strcasecmp(com_token, "vip")) banflags |= BAN_VIP; + else if (!Q_strcasecmp(com_token, "blind")) + banflags |= BAN_BLIND; + else if (!Q_strcasecmp(com_token, "spec")) + banflags |= BAN_SPECONLY; else Con_Printf("Unknown ban/penalty flag: %s. ignoring.\n", com_token); } //if no flags were specified, assume all if (!banflags) - banflags = BAN_BAN|BAN_PERMIT|BAN_CUFF|BAN_MUTE|BAN_CRIPPLED|BAN_DEAF|BAN_LAGGED|BAN_VIP; + banflags = BAN_BAN|BAN_PERMIT|BAN_CUFF|BAN_MUTE|BAN_CRIPPLED|BAN_DEAF|BAN_LAGGED|BAN_VIP|BAN_BLIND|BAN_SPECONLY; for (link = &svs.bannedips ; (nb = *link) ; ) { diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index fc6845de7..264fa49de 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -599,6 +599,7 @@ void SV_UpdateMaxPlayers(int newmax) int i; if (newmax != svs.allocated_client_slots) { + client_t *old = svs.clients; for (i = newmax; i < svs.allocated_client_slots; i++) { if (svs.clients[i].state) @@ -618,6 +619,19 @@ void SV_UpdateMaxPlayers(int newmax) svs.clients[i].name = svs.clients[i].namebuf; svs.clients[i].team = svs.clients[i].teambuf; } + for (i = 0; i < min(newmax, svs.allocated_client_slots); i++) + { + if (svs.clients[i].netchan.message.data) + svs.clients[i].netchan.message.data = (qbyte*)&svs.clients[i] + (svs.clients[i].netchan.message.data - (qbyte*)&old[i]); + if (svs.clients[i].datagram.data) + svs.clients[i].datagram.data = (qbyte*)&svs.clients[i] + (svs.clients[i].datagram.data - (qbyte*)&old[i]); + if (svs.clients[i].backbuf.data) + svs.clients[i].backbuf.data = (qbyte*)&svs.clients[i] + (svs.clients[i].backbuf.data - (qbyte*)&old[i]); + if (svs.clients[i].controlled) + svs.clients[i].controlled = svs.clients + (svs.clients[i].controlled - old); + if (svs.clients[i].controller) + svs.clients[i].controller = svs.clients + (svs.clients[i].controller - old); + } svs.allocated_client_slots = sv.allocated_client_slots = newmax; } sv.allocated_client_slots = svs.allocated_client_slots; @@ -852,7 +866,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us if (allow_download_refpackages.ival) FS_ReferenceControl(1, 1); - strcpy (sv.name, server); + Q_strncpyz (sv.name, server, sizeof(sv.name)); #ifndef SERVERONLY current_loading_size+=10; //SCR_BeginLoadingPlaque(); @@ -900,8 +914,8 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us qboolean QDECL Mod_LoadQ2BrushModel (model_t *mod, void *buffer); extern model_t *loadmodel; - strcpy (sv.name, server); - strcpy (sv.modelname, ""); + Q_strncpyz (sv.name, server, sizeof(sv.name)); + Q_strncpyz (sv.modelname, "", sizeof(sv.modelname)); loadmodel = sv.world.worldmodel = Mod_FindName (sv.modelname); loadmodel->needload = !Mod_LoadQ2BrushModel (sv.world.worldmodel, NULL); diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 64ba06dd4..f7e5764e1 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -68,8 +68,8 @@ sv_masterlist_t sv_masterlist[] = { {MP_QUAKEWORLD, CVARC("sv_master8", "", SV_Masterlist_Callback)}, {MP_QUAKEWORLD, CVARC("sv_qwmasterextra1", "qwmaster.ocrana.de:27000", SV_Masterlist_Callback)}, //german. admin unknown - {MP_QUAKEWORLD, CVARC("sv_qwmasterextra2", "masterserver.exhale.de:27000", SV_Masterlist_Callback)}, //german. admin unknown - {MP_QUAKEWORLD, CVARC("sv_qwmasterextra3", "asgaard.morphos-team.net:27000", SV_Masterlist_Callback)}, //german. admin bigfoot + {MP_QUAKEWORLD, CVARC("sv_qwmasterextra2", ""/*"masterserver.exhale.de:27000" seems dead*/, SV_Masterlist_Callback)}, //german. admin unknown + {MP_QUAKEWORLD, CVARC("sv_qwmasterextra3", "asgaard.morphos-team.net:27000", SV_Masterlist_Callback)}, //germany. admin bigfoot {MP_QUAKEWORLD, CVARC("sv_qwmasterextra4", "master.quakeservers.net:27000", SV_Masterlist_Callback)}, //european. admin: raz0? {MP_QUAKEWORLD, CVARC("sv_qwmasterextra5", "qwmaster.fodquake.net:27000", SV_Masterlist_Callback)}, @@ -3577,6 +3577,14 @@ client_t *SVC_DirectConnect(void) SV_EvaluatePenalties(newcl); + if (newcl->penalties & BAN_SPECONLY) + { + if (spectators >= maxspectators.ival) + newcl->drop = true; //oops. + newcl->spectator = spectator = true; + Info_SetValueForStarKey (cl->userinfo, "*spectator", "1", sizeof(cl->userinfo)); + } + newcl->fteprotocolextensions &= ~PEXT_SPLITSCREEN; for (clients = 1; clients < numssclients; clients++) { diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index abf53f061..99470e170 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -4402,6 +4402,12 @@ void Cmd_Join_f (void) return; } + if (host_client->penalties & BAN_SPECONLY) + { + SV_ClientTPrintf(host_client, PRINT_HIGH, "You are banned from joining the game.\n"); + return; + } + // count players already on server numclients = 0; seats = 0;