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;