mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2025-06-03 02:00:59 +00:00
Merge pull request #1203 from BjossiAlfreds/silent-precache
Attempts to speed up level load times
This commit is contained in:
commit
b64207a97e
13 changed files with 551 additions and 194 deletions
|
@ -13,6 +13,7 @@ have been renamed. The prefixes are:
|
|||
* `ogg_`: Ogg/Vorbis music playback.
|
||||
* `r_`: Common to all renderers.
|
||||
* `s_`: Sound system.
|
||||
* `sv_`: Server
|
||||
* `sw_`: Software renderer.
|
||||
* `vid_`: Video backend.
|
||||
|
||||
|
@ -57,6 +58,40 @@ it's `+set busywait 0` (setting the `busywait` cvar) and `-portable`
|
|||
time for the next frame. The latter is more CPU friendly but can be
|
||||
rather inaccurate, especially on Windows. Use with care.
|
||||
|
||||
* **sv_optimize_sp_loadtime**<br />
|
||||
**sv_optimize_mp_loadtime**: These cvars enable/disable optimizations
|
||||
that speed up level load times (or more accurately, client connection).
|
||||
|
||||
sp stands for singleplayer and mp for multiplayer, respectively.
|
||||
The sp version is enabled by default (value 7) while multiplayer is 0.
|
||||
|
||||
The cvar value is a bitmask for 3 optimization features:
|
||||
* **1: Message utilization**<br />
|
||||
When the server sends the client configstrings and other data
|
||||
during the connection process, the message buffer is only used
|
||||
around 50-60%. When this flag is enabled, the message is used
|
||||
much better, dramatically reducing the amount of messages needed
|
||||
to deliver all the data to the client.
|
||||
|
||||
* **2: Server send rate**<br />
|
||||
By default, the server sends messages to clients once every
|
||||
0.1 seconds, roughly. This slows down sending data to clients,
|
||||
especially in singleplayer. This is normal for active clients,
|
||||
but for connecting/inactive clients, this delay is unnecessary.
|
||||
When this flag is set, the server will send messages to
|
||||
inactive clients ~8x more frequently.
|
||||
|
||||
* **4: Reconnection**<br />
|
||||
When the server changes maps, like on level transitions,
|
||||
the server first sends a "changing" command to all clients,
|
||||
and then a "reconnect" command. The delay between these commands
|
||||
can be quite long, ~1 second. This flag will avoid this delay when
|
||||
set, by sending the two commands within the same message.
|
||||
|
||||
Simply add these flag values together to get the cvar value you want.
|
||||
For example, sendrate + reconnect = 2 + 4 = 6.
|
||||
Set to 7 for all optimizations, or 0 to disable them entirely.
|
||||
|
||||
* **cl_maxfps**: The approximate framerate for client/server ("packet")
|
||||
frames if *cl_async* is `1`. If set to `-1` (the default), the engine
|
||||
will choose a packet framerate appropriate for the render framerate.
|
||||
|
|
|
@ -222,9 +222,7 @@ CL_Record_f(void)
|
|||
}
|
||||
|
||||
MSG_WriteByte(&buf, svc_configstring);
|
||||
|
||||
MSG_WriteShort(&buf, i);
|
||||
MSG_WriteString(&buf, cl.configstrings[i]);
|
||||
MSG_WriteConfigString(&buf, i, cl.configstrings[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -444,6 +444,13 @@ CL_Changing_f(void)
|
|||
|
||||
SCR_BeginLoadingPlaque();
|
||||
cls.state = ca_connected; /* not active anymore, but not disconnected */
|
||||
|
||||
/* reset this to 0 just in case it didn't get a chance to settle normally
|
||||
this became a problem with the faster client connection changes
|
||||
but is a good idea to do this regardless
|
||||
*/
|
||||
anykeydown = 0;
|
||||
|
||||
Com_Printf("\nChanging map...\n");
|
||||
|
||||
#ifdef USE_CURL
|
||||
|
|
|
@ -240,7 +240,7 @@ CL_PrepRefresh(void)
|
|||
{
|
||||
char mapname[MAX_QPATH];
|
||||
int i;
|
||||
char name[MAX_QPATH];
|
||||
char *name;
|
||||
float rotate;
|
||||
vec3_t axis;
|
||||
|
||||
|
@ -260,33 +260,25 @@ CL_PrepRefresh(void)
|
|||
Com_Printf("Map: %s\r", mapname);
|
||||
SCR_UpdateScreen();
|
||||
R_BeginRegistration (mapname);
|
||||
Com_Printf(" \r");
|
||||
|
||||
/* precache status bar pics */
|
||||
Com_Printf("pics\r");
|
||||
SCR_UpdateScreen();
|
||||
SCR_TouchPics();
|
||||
Com_Printf(" \r");
|
||||
|
||||
CL_RegisterTEntModels();
|
||||
|
||||
num_cl_weaponmodels = 1;
|
||||
strcpy(cl_weaponmodels[0], "weapon.md2");
|
||||
|
||||
Com_Printf("models\r");
|
||||
SCR_UpdateScreen();
|
||||
|
||||
for (i = 1; i < MAX_MODELS && cl.configstrings[CS_MODELS + i][0]; i++)
|
||||
{
|
||||
strcpy(name, cl.configstrings[CS_MODELS + i]);
|
||||
name[37] = 0; /* never go beyond one line */
|
||||
name = cl.configstrings[CS_MODELS + i];
|
||||
|
||||
if (name[0] != '*')
|
||||
{
|
||||
Com_Printf("%s\r", name);
|
||||
}
|
||||
|
||||
SCR_UpdateScreen();
|
||||
IN_Update();
|
||||
|
||||
if (name[0] == '#')
|
||||
if (*name == '#')
|
||||
{
|
||||
/* special player weapon model */
|
||||
if (num_cl_weaponmodels < MAX_CLIENTWEAPONMODELS)
|
||||
|
@ -294,6 +286,7 @@ CL_PrepRefresh(void)
|
|||
Q_strlcpy(cl_weaponmodels[num_cl_weaponmodels],
|
||||
cl.configstrings[CS_MODELS + i] + 1,
|
||||
sizeof(cl_weaponmodels[num_cl_weaponmodels]));
|
||||
|
||||
num_cl_weaponmodels++;
|
||||
}
|
||||
}
|
||||
|
@ -301,20 +294,9 @@ CL_PrepRefresh(void)
|
|||
{
|
||||
cl.model_draw[i] = R_RegisterModel(cl.configstrings[CS_MODELS + i]);
|
||||
|
||||
if (name[0] == '*')
|
||||
{
|
||||
cl.model_clip[i] = CM_InlineModel(cl.configstrings[CS_MODELS + i]);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
cl.model_clip[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (name[0] != '*')
|
||||
{
|
||||
Com_Printf(" \r");
|
||||
cl.model_clip[i] = (*name == '*') ?
|
||||
CM_InlineModel(name) :
|
||||
NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -324,23 +306,17 @@ CL_PrepRefresh(void)
|
|||
for (i = 1; i < MAX_IMAGES && cl.configstrings[CS_IMAGES + i][0]; i++)
|
||||
{
|
||||
cl.image_precache[i] = Draw_FindPic(cl.configstrings[CS_IMAGES + i]);
|
||||
IN_Update();
|
||||
}
|
||||
|
||||
Com_Printf(" \r");
|
||||
Com_Printf("clients\r");
|
||||
SCR_UpdateScreen();
|
||||
|
||||
for (i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if (!cl.configstrings[CS_PLAYERSKINS + i][0])
|
||||
if (cl.configstrings[CS_PLAYERSKINS + i][0])
|
||||
{
|
||||
continue;
|
||||
CL_ParseClientinfo(i);
|
||||
}
|
||||
|
||||
Com_Printf("client %i\r", i);
|
||||
SCR_UpdateScreen();
|
||||
IN_Update();
|
||||
CL_ParseClientinfo(i);
|
||||
Com_Printf(" \r");
|
||||
}
|
||||
|
||||
CL_LoadClientinfo(&cl.baseclientinfo, "unnamed\\male/grunt");
|
||||
|
@ -351,6 +327,7 @@ CL_PrepRefresh(void)
|
|||
rotate = (float)strtod(cl.configstrings[CS_SKYROTATE], (char **)NULL);
|
||||
sscanf(cl.configstrings[CS_SKYAXIS], "%f %f %f", &axis[0], &axis[1], &axis[2]);
|
||||
R_SetSky(cl.configstrings[CS_SKY], rotate, axis);
|
||||
|
||||
Com_Printf(" \r");
|
||||
|
||||
/* the renderer can now free unneeded stuff */
|
||||
|
|
|
@ -106,6 +106,10 @@ void SZ_Print(sizebuf_t *buf, char *data); /* strcats onto the sizebuf */
|
|||
struct usercmd_s;
|
||||
struct entity_state_s;
|
||||
|
||||
size_t MSG_ConfigString_Size(const char *s);
|
||||
size_t MSG_DeltaEntity_Size(const entity_state_t *from, const entity_state_t *to,
|
||||
qboolean force, qboolean newentity);
|
||||
|
||||
void MSG_WriteChar(sizebuf_t *sb, int c);
|
||||
void MSG_WriteByte(sizebuf_t *sb, int c);
|
||||
void MSG_WriteShort(sizebuf_t *sb, int c);
|
||||
|
@ -116,8 +120,11 @@ void MSG_WriteCoord(sizebuf_t *sb, float f);
|
|||
void MSG_WritePos(sizebuf_t *sb, vec3_t pos);
|
||||
void MSG_WriteAngle(sizebuf_t *sb, float f);
|
||||
void MSG_WriteAngle16(sizebuf_t *sb, float f);
|
||||
void MSG_WriteConfigString(sizebuf_t *sb, short index, const char *s);
|
||||
void MSG_WriteDeltaUsercmd(sizebuf_t *sb, struct usercmd_s *from,
|
||||
struct usercmd_s *cmd);
|
||||
int DeltaEntityBits(const struct entity_state_s *from,
|
||||
const struct entity_state_s *to, qboolean newentity);
|
||||
void MSG_WriteDeltaEntity(struct entity_state_s *from,
|
||||
struct entity_state_s *to, sizebuf_t *msg,
|
||||
qboolean force, qboolean newentity);
|
||||
|
|
|
@ -191,6 +191,170 @@ vec3_t bytedirs[NUMVERTEXNORMALS] = {
|
|||
{-0.688191, -0.587785, -0.425325}
|
||||
};
|
||||
|
||||
size_t
|
||||
MSG_ConfigString_Size(const char *s)
|
||||
{
|
||||
return strlen(s) + 4; /* string length + null char + message type + index */
|
||||
}
|
||||
|
||||
size_t
|
||||
MSG_DeltaEntity_Size(const entity_state_t *from, const entity_state_t *to,
|
||||
qboolean force, qboolean newentity)
|
||||
{
|
||||
size_t sz;
|
||||
int bits = DeltaEntityBits(from, to, newentity);
|
||||
|
||||
if (!bits && !force)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
sz = 1;
|
||||
|
||||
if (bits & 0xff000000)
|
||||
{
|
||||
sz += 3;
|
||||
}
|
||||
else if (bits & 0x00ff0000)
|
||||
{
|
||||
sz += 2;
|
||||
}
|
||||
else if (bits & 0x0000ff00)
|
||||
{
|
||||
sz++;
|
||||
}
|
||||
|
||||
if (bits & U_NUMBER16)
|
||||
{
|
||||
sz += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
sz++;
|
||||
}
|
||||
|
||||
if (bits & U_MODEL)
|
||||
{
|
||||
sz++;
|
||||
}
|
||||
|
||||
if (bits & U_MODEL2)
|
||||
{
|
||||
sz++;
|
||||
}
|
||||
|
||||
if (bits & U_MODEL3)
|
||||
{
|
||||
sz++;
|
||||
}
|
||||
|
||||
if (bits & U_MODEL4)
|
||||
{
|
||||
sz++;
|
||||
}
|
||||
|
||||
if (bits & U_FRAME8)
|
||||
{
|
||||
sz++;
|
||||
}
|
||||
|
||||
if (bits & U_FRAME16)
|
||||
{
|
||||
sz += 2;
|
||||
}
|
||||
|
||||
if ((bits & U_SKIN8) && (bits & U_SKIN16)) /*used for laser colors */
|
||||
{
|
||||
sz += 4;
|
||||
}
|
||||
else if (bits & U_SKIN8)
|
||||
{
|
||||
sz++;
|
||||
}
|
||||
else if (bits & U_SKIN16)
|
||||
{
|
||||
sz += 2;
|
||||
}
|
||||
|
||||
if ((bits & (U_EFFECTS8 | U_EFFECTS16)) == (U_EFFECTS8 | U_EFFECTS16))
|
||||
{
|
||||
sz += 4;
|
||||
}
|
||||
else if (bits & U_EFFECTS8)
|
||||
{
|
||||
sz++;
|
||||
}
|
||||
else if (bits & U_EFFECTS16)
|
||||
{
|
||||
sz += 2;
|
||||
}
|
||||
|
||||
if ((bits & (U_RENDERFX8 | U_RENDERFX16)) == (U_RENDERFX8 | U_RENDERFX16))
|
||||
{
|
||||
sz += 4;
|
||||
}
|
||||
else if (bits & U_RENDERFX8)
|
||||
{
|
||||
sz++;
|
||||
}
|
||||
else if (bits & U_RENDERFX16)
|
||||
{
|
||||
sz += 2;
|
||||
}
|
||||
|
||||
if (bits & U_ORIGIN1)
|
||||
{
|
||||
sz += 2;
|
||||
}
|
||||
|
||||
if (bits & U_ORIGIN2)
|
||||
{
|
||||
sz += 2;
|
||||
}
|
||||
|
||||
if (bits & U_ORIGIN3)
|
||||
{
|
||||
sz += 2;
|
||||
}
|
||||
|
||||
if (bits & U_ANGLE1)
|
||||
{
|
||||
sz++;
|
||||
}
|
||||
|
||||
if (bits & U_ANGLE2)
|
||||
{
|
||||
sz++;
|
||||
}
|
||||
|
||||
if (bits & U_ANGLE3)
|
||||
{
|
||||
sz++;
|
||||
}
|
||||
|
||||
if (bits & U_OLDORIGIN)
|
||||
{
|
||||
sz += 6;
|
||||
}
|
||||
|
||||
if (bits & U_SOUND)
|
||||
{
|
||||
sz++;
|
||||
}
|
||||
|
||||
if (bits & U_EVENT)
|
||||
{
|
||||
sz++;
|
||||
}
|
||||
|
||||
if (bits & U_SOLID)
|
||||
{
|
||||
sz += 2;
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
void
|
||||
MSG_WriteChar(sizebuf_t *sb, int c)
|
||||
{
|
||||
|
@ -286,6 +450,13 @@ MSG_WriteAngle16(sizebuf_t *sb, float f)
|
|||
MSG_WriteShort(sb, ANGLE2SHORT(f));
|
||||
}
|
||||
|
||||
void
|
||||
MSG_WriteConfigString(sizebuf_t *buf, short index, const char *s)
|
||||
{
|
||||
MSG_WriteShort(buf, index);
|
||||
MSG_WriteString(buf, s);
|
||||
}
|
||||
|
||||
void
|
||||
MSG_WriteDeltaUsercmd(sizebuf_t *buf, usercmd_t *from, usercmd_t *cmd)
|
||||
{
|
||||
|
@ -428,28 +599,12 @@ MSG_ReadDir(sizebuf_t *sb, vec3_t dir)
|
|||
* Writes part of a packetentities message.
|
||||
* Can delta from either a baseline or a previous packet_entity
|
||||
*/
|
||||
void
|
||||
MSG_WriteDeltaEntity(entity_state_t *from,
|
||||
entity_state_t *to,
|
||||
sizebuf_t *msg,
|
||||
qboolean force,
|
||||
int
|
||||
DeltaEntityBits(const entity_state_t *from,
|
||||
const entity_state_t *to,
|
||||
qboolean newentity)
|
||||
{
|
||||
int bits;
|
||||
|
||||
if (!to->number)
|
||||
{
|
||||
Com_Error(ERR_FATAL, "Unset entity number");
|
||||
}
|
||||
|
||||
if (to->number >= MAX_EDICTS)
|
||||
{
|
||||
Com_Error(ERR_DROP, "%s: bad entity %d >= %d\n",
|
||||
__func__, to->number, MAX_EDICTS);
|
||||
}
|
||||
|
||||
/* send an update */
|
||||
bits = 0;
|
||||
int bits = 0;
|
||||
|
||||
if (to->number >= 256)
|
||||
{
|
||||
|
@ -594,12 +749,6 @@ MSG_WriteDeltaEntity(entity_state_t *from,
|
|||
bits |= U_OLDORIGIN;
|
||||
}
|
||||
|
||||
/* write the message */
|
||||
if (!bits && !force)
|
||||
{
|
||||
return; /* nothing to send! */
|
||||
}
|
||||
|
||||
if (bits & 0xff000000)
|
||||
{
|
||||
bits |= U_MOREBITS3 | U_MOREBITS2 | U_MOREBITS1;
|
||||
|
@ -615,6 +764,37 @@ MSG_WriteDeltaEntity(entity_state_t *from,
|
|||
bits |= U_MOREBITS1;
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
void
|
||||
MSG_WriteDeltaEntity(entity_state_t *from,
|
||||
entity_state_t *to,
|
||||
sizebuf_t *msg,
|
||||
qboolean force,
|
||||
qboolean newentity)
|
||||
{
|
||||
int bits;
|
||||
|
||||
if (!to->number)
|
||||
{
|
||||
Com_Error(ERR_FATAL, "Unset entity number");
|
||||
}
|
||||
|
||||
if (to->number >= MAX_EDICTS)
|
||||
{
|
||||
Com_Error(ERR_DROP, "%s: bad entity %d >= %d\n",
|
||||
__func__, to->number, MAX_EDICTS);
|
||||
}
|
||||
|
||||
/* send an update */
|
||||
bits = DeltaEntityBits(from, to, newentity);
|
||||
|
||||
if (!bits && !force)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MSG_WriteByte(msg, bits & 255);
|
||||
|
||||
if (bits & 0xff000000)
|
||||
|
|
|
@ -176,8 +176,14 @@ typedef struct
|
|||
FILE *demofile;
|
||||
sizebuf_t demo_multicast;
|
||||
byte demo_multicast_buf[MAX_MSGLEN];
|
||||
|
||||
int gamemode;
|
||||
} server_static_t;
|
||||
|
||||
#define GAMEMODE_SP 1
|
||||
#define GAMEMODE_COOP 2
|
||||
#define GAMEMODE_DM 3
|
||||
|
||||
extern netadr_t net_from;
|
||||
extern sizebuf_t net_message;
|
||||
|
||||
|
@ -186,6 +192,9 @@ extern netadr_t master_adr[MAX_MASTERS]; /* address of the master server */
|
|||
extern server_static_t svs; /* persistant server info */
|
||||
extern server_t sv; /* local server */
|
||||
|
||||
extern cvar_t *sv_optimize_sp_loadtime;
|
||||
extern cvar_t *sv_optimize_mp_loadtime;
|
||||
|
||||
extern cvar_t *sv_paused;
|
||||
extern cvar_t *maxclients;
|
||||
extern cvar_t *sv_noreload; /* don't reload level state when reentering */
|
||||
|
@ -228,6 +237,7 @@ void SV_FlushRedirect(int sv_redirected, char *outputbuf);
|
|||
|
||||
void SV_DemoCompleted(void);
|
||||
void SV_SendClientMessages(void);
|
||||
void SV_SendPrepClientMessages(void);
|
||||
|
||||
void SV_Multicast(vec3_t origin, multicast_t to);
|
||||
void SV_StartSound(vec3_t origin, edict_t *entity, int channel,
|
||||
|
@ -283,5 +293,13 @@ int SV_PointContents(vec3_t p);
|
|||
trace_t SV_Trace(vec3_t start, vec3_t mins, vec3_t maxs,
|
||||
vec3_t end, edict_t *passedict, int contentmask);
|
||||
|
||||
/* loadtime optimizations */
|
||||
|
||||
#define OPTIMIZE_MSGUTIL 1
|
||||
#define OPTIMIZE_SENDRATE 2
|
||||
#define OPTIMIZE_RECONNECT 4
|
||||
|
||||
int SV_Optimizations(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -612,8 +612,7 @@ SV_ServerRecord_f(void)
|
|||
if (sv.configstrings[i][0])
|
||||
{
|
||||
MSG_WriteByte(&buf, svc_configstring);
|
||||
MSG_WriteShort(&buf, i);
|
||||
MSG_WriteString(&buf, sv.configstrings[i]);
|
||||
MSG_WriteConfigString(&buf, i, sv.configstrings[i]);
|
||||
|
||||
if (buf.cursize + 67 >= buf.maxsize)
|
||||
{
|
||||
|
|
|
@ -212,8 +212,7 @@ PF_Configstring(int index, char *val)
|
|||
/* send the update to everyone */
|
||||
SZ_Clear(&sv.multicast);
|
||||
MSG_WriteChar(&sv.multicast, svc_configstring);
|
||||
MSG_WriteShort(&sv.multicast, index);
|
||||
MSG_WriteString(&sv.multicast, val);
|
||||
MSG_WriteConfigString(&sv.multicast, index, val);
|
||||
|
||||
SV_Multicast(vec3_origin, MULTICAST_ALL_R);
|
||||
}
|
||||
|
|
|
@ -26,10 +26,6 @@
|
|||
|
||||
#include "header/server.h"
|
||||
|
||||
#define GAMEMODE_SP 0
|
||||
#define GAMEMODE_COOP 1
|
||||
#define GAMEMODE_DM 2
|
||||
|
||||
server_static_t svs; /* persistant server info */
|
||||
server_t sv; /* local server */
|
||||
|
||||
|
@ -67,8 +63,7 @@ SV_FindIndex(char *name, int start, int max, qboolean create)
|
|||
{
|
||||
/* send the update to everyone */
|
||||
MSG_WriteChar(&sv.multicast, svc_configstring);
|
||||
MSG_WriteShort(&sv.multicast, start + i);
|
||||
MSG_WriteString(&sv.multicast, name);
|
||||
MSG_WriteConfigString(&sv.multicast, start + i, name);
|
||||
SV_Multicast(vec3_origin, MULTICAST_ALL_R);
|
||||
}
|
||||
|
||||
|
@ -436,6 +431,7 @@ SV_InitGame(void)
|
|||
Cvar_FullSet("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH);
|
||||
}
|
||||
|
||||
svs.gamemode = gamemode;
|
||||
svs.spawncount = randk();
|
||||
svs.clients = Z_Malloc(sizeof(client_t) * maxclients->value);
|
||||
svs.num_client_entities = maxclients->value * UPDATE_BACKUP * 64;
|
||||
|
@ -494,6 +490,7 @@ SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame, qboolean isau
|
|||
char *ch;
|
||||
size_t l;
|
||||
char spawnpoint[MAX_QPATH];
|
||||
char *ext;
|
||||
|
||||
sv.loadgame = loadgame;
|
||||
sv.attractloop = attractloop;
|
||||
|
@ -549,7 +546,9 @@ SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame, qboolean isau
|
|||
--l;
|
||||
}
|
||||
|
||||
if ((l > 4) && !strcmp(level + l - 4, ".cin"))
|
||||
ext = (l <= 4) ? NULL : level + l - 4;
|
||||
|
||||
if (ext && !strcmp(ext, ".cin"))
|
||||
{
|
||||
#ifndef DEDICATED_ONLY
|
||||
SCR_BeginLoadingPlaque(); /* for local system */
|
||||
|
@ -557,7 +556,7 @@ SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame, qboolean isau
|
|||
SV_BroadcastCommand("changing\n");
|
||||
SV_SpawnServer(level, spawnpoint, ss_cinematic, attractloop, loadgame, isautosave);
|
||||
}
|
||||
else if ((l > 4) && !strcmp(level + l - 4, ".dm2"))
|
||||
else if (ext && !strcmp(ext, ".dm2"))
|
||||
{
|
||||
#ifndef DEDICATED_ONLY
|
||||
SCR_BeginLoadingPlaque(); /* for local system */
|
||||
|
@ -565,10 +564,10 @@ SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame, qboolean isau
|
|||
SV_BroadcastCommand("changing\n");
|
||||
SV_SpawnServer(level, spawnpoint, ss_demo, attractloop, loadgame, isautosave);
|
||||
}
|
||||
else if ((l > 4) && (!strcmp(level + l - 4, ".pcx") ||
|
||||
!strcmp(level + l - 4, ".tga") ||
|
||||
!strcmp(level + l - 4, ".jpg") ||
|
||||
!strcmp(level + l - 4, ".png")))
|
||||
else if (ext && (!strcmp(ext, ".pcx") ||
|
||||
!strcmp(ext, ".tga") ||
|
||||
!strcmp(ext, ".jpg") ||
|
||||
!strcmp(ext, ".png")))
|
||||
{
|
||||
#ifndef DEDICATED_ONLY
|
||||
SCR_BeginLoadingPlaque(); /* for local system */
|
||||
|
@ -582,7 +581,14 @@ SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame, qboolean isau
|
|||
SCR_BeginLoadingPlaque(); /* for local system */
|
||||
#endif
|
||||
SV_BroadcastCommand("changing\n");
|
||||
SV_SendClientMessages();
|
||||
|
||||
/* for some reason calling send messages here causes a lengthy reconnect delay */
|
||||
if (!(SV_Optimizations() & OPTIMIZE_RECONNECT))
|
||||
{
|
||||
SV_SendClientMessages();
|
||||
SV_SendPrepClientMessages();
|
||||
}
|
||||
|
||||
SV_SpawnServer(level, spawnpoint, ss_game, attractloop, loadgame, isautosave);
|
||||
Cbuf_CopyToDefer();
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@ netadr_t master_adr[MAX_MASTERS]; /* address of group servers */
|
|||
|
||||
client_t *sv_client; /* current client */
|
||||
|
||||
cvar_t *sv_optimize_sp_loadtime;
|
||||
cvar_t *sv_optimize_mp_loadtime;
|
||||
|
||||
cvar_t *sv_paused;
|
||||
cvar_t *sv_timedemo;
|
||||
cvar_t *sv_enforcetime;
|
||||
|
@ -373,9 +376,27 @@ SV_RunGameFrame(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
SV_Optimizations(void)
|
||||
{
|
||||
cvar_t *cv;
|
||||
|
||||
if (svs.gamemode <= 0 || svs.gamemode > 3)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
cv = (svs.gamemode == GAMEMODE_SP) ?
|
||||
sv_optimize_sp_loadtime : sv_optimize_mp_loadtime;
|
||||
|
||||
return cv ? cv->value : 0;
|
||||
}
|
||||
|
||||
void
|
||||
SV_Frame(int usec)
|
||||
{
|
||||
int opt_sendrate;
|
||||
|
||||
#ifndef DEDICATED_ONLY
|
||||
time_before_game = time_after_game = 0;
|
||||
#endif
|
||||
|
@ -397,6 +418,16 @@ SV_Frame(int usec)
|
|||
/* get packets from clients */
|
||||
SV_ReadPackets();
|
||||
|
||||
/* send messages more often to new clients getting ready for spawning in
|
||||
speeds up the process of sending configstrings, entty deltas, etc.
|
||||
*/
|
||||
opt_sendrate = SV_Optimizations() & OPTIMIZE_SENDRATE;
|
||||
|
||||
if (opt_sendrate)
|
||||
{
|
||||
SV_SendPrepClientMessages();
|
||||
}
|
||||
|
||||
/* move autonomous things around if enough time has passed */
|
||||
if (!sv_timedemo->value && (svs.realtime < sv.time))
|
||||
{
|
||||
|
@ -427,6 +458,12 @@ SV_Frame(int usec)
|
|||
/* send messages back to the clients that had packets read this frame */
|
||||
SV_SendClientMessages();
|
||||
|
||||
/* if not optimizing, send all messages here */
|
||||
if (!opt_sendrate)
|
||||
{
|
||||
SV_SendPrepClientMessages();
|
||||
}
|
||||
|
||||
/* save the entire world state if recording a serverdemo */
|
||||
SV_RecordDemoMessage();
|
||||
|
||||
|
@ -574,6 +611,9 @@ SV_Init(void)
|
|||
{
|
||||
SV_InitOperatorCommands();
|
||||
|
||||
sv_optimize_sp_loadtime = Cvar_Get("sv_optimize_sp_loadtime", "7", 0);
|
||||
sv_optimize_mp_loadtime = Cvar_Get("sv_optimize_mp_loadtime", "0", 0);
|
||||
|
||||
rcon_password = Cvar_Get("rcon_password", "", 0);
|
||||
Cvar_Get("skill", "1", 0);
|
||||
Cvar_Get("singleplayer", "0", CVAR_SERVERINFO | CVAR_LATCH);
|
||||
|
|
|
@ -136,15 +136,64 @@ SV_BroadcastCommand(const char *fmt, ...)
|
|||
* MULTICAST_PVS send to clients potentially visible from org
|
||||
* MULTICAST_PHS send to clients potentially hearable from org
|
||||
*/
|
||||
static qboolean
|
||||
SV_WereConnected(const vec3_t origin, const byte *mask, int area1)
|
||||
{
|
||||
vec3_t origin2;
|
||||
int leafnum;
|
||||
int cluster;
|
||||
|
||||
VectorCopy(origin, origin2);
|
||||
|
||||
leafnum = CM_PointLeafnum(origin2);
|
||||
cluster = CM_LeafCluster(leafnum);
|
||||
|
||||
// cluster can be -1 if we're in the void (or sometimes just at a wall)
|
||||
// and using a negative index into mask[] would be invalid
|
||||
if (cluster >= 0 && (mask[cluster >> 3] & (1 << (cluster & 7))))
|
||||
{
|
||||
if (CM_AreasConnected(area1, CM_LeafArea(leafnum)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// if the client is currently in water, do a second check
|
||||
if (CM_PointContents(origin2, 0) & MASK_WATER)
|
||||
{
|
||||
// if the client is half-submerged in opaque water so its origin
|
||||
// is below the water, but the head/camera is still above the water
|
||||
// and thus should be able to see/hear explosions or similar
|
||||
// that are above the water.
|
||||
// so try again at a slightly higher position
|
||||
// FIXME: OTOH, we have a similar problem if we're over water and shoot under water (near water level) => can't see explosion
|
||||
|
||||
origin2[2] += 32.0f;
|
||||
|
||||
leafnum = CM_PointLeafnum(origin2);
|
||||
cluster = CM_LeafCluster(leafnum);
|
||||
|
||||
if (cluster >= 0 && (mask[cluster >> 3] & (1 << (cluster & 7))))
|
||||
{
|
||||
if (CM_AreasConnected(area1, CM_LeafArea(leafnum)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
SV_Multicast(vec3_t origin, multicast_t to)
|
||||
{
|
||||
client_t *client;
|
||||
byte *mask;
|
||||
int leafnum = 0, cluster;
|
||||
int leafnum, cluster;
|
||||
int j;
|
||||
qboolean reliable;
|
||||
int area1, area2;
|
||||
int area1;
|
||||
|
||||
reliable = false;
|
||||
|
||||
|
@ -155,6 +204,7 @@ SV_Multicast(vec3_t origin, multicast_t to)
|
|||
}
|
||||
else
|
||||
{
|
||||
leafnum = 0;
|
||||
area1 = 0;
|
||||
}
|
||||
|
||||
|
@ -208,53 +258,14 @@ SV_Multicast(vec3_t origin, multicast_t to)
|
|||
|
||||
if (mask)
|
||||
{
|
||||
vec3_t origin;
|
||||
VectorCopy(client->edict->s.origin, origin);
|
||||
|
||||
qboolean wereConnected = false;
|
||||
for(int i=0; i<2; ++i)
|
||||
if (!SV_WereConnected(client->edict->s.origin, mask, area1))
|
||||
{
|
||||
leafnum = CM_PointLeafnum(origin);
|
||||
cluster = CM_LeafCluster(leafnum);
|
||||
area2 = CM_LeafArea(leafnum);
|
||||
|
||||
// cluster can be -1 if we're in the void (or sometimes just at a wall)
|
||||
// and using a negative index into mask[] would be invalid
|
||||
if (cluster >= 0 && CM_AreasConnected(area1, area2) && (mask[cluster >> 3] & (1 << (cluster & 7))))
|
||||
{
|
||||
wereConnected = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// if the client is currently *not* in water, do *not* do a second check
|
||||
if((CM_PointContents(origin, 0) & MASK_WATER) == 0)
|
||||
{
|
||||
break; // wereConnected remains false
|
||||
}
|
||||
|
||||
// if the client is half-submerged in opaque water so its origin
|
||||
// is below the water, but the head/camera is still above the water
|
||||
// and thus should be able to see/hear explosions or similar
|
||||
// that are above the water.
|
||||
// so try again at a slightly higher position
|
||||
origin[2] += 32.0f;
|
||||
// FIXME: OTOH, we have a similar problem if we're over water and shoot under water (near water level) => can't see explosion
|
||||
}
|
||||
if (!wereConnected)
|
||||
{
|
||||
continue; // don't send message to this client, continue with next client
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (reliable)
|
||||
{
|
||||
SZ_Write(&client->netchan.message, sv.multicast.data,
|
||||
sv.multicast.cursize);
|
||||
}
|
||||
else
|
||||
{
|
||||
SZ_Write(&client->datagram, sv.multicast.data, sv.multicast.cursize);
|
||||
}
|
||||
SZ_Write(reliable ? &client->netchan.message : &client->datagram,
|
||||
sv.multicast.data, sv.multicast.cursize);
|
||||
}
|
||||
|
||||
SZ_Clear(&sv.multicast);
|
||||
|
@ -519,6 +530,57 @@ SV_RateDrop(client_t *c)
|
|||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
SV_NextDemoChunk(byte *msgbuf)
|
||||
{
|
||||
size_t r;
|
||||
int n;
|
||||
|
||||
if (sv_paused->value)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = FS_FRead(&n, 4, 1, sv.demofile);
|
||||
|
||||
if (r != 4)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = LittleLong(n);
|
||||
|
||||
if (n == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (n > MAX_MSGLEN)
|
||||
{
|
||||
Com_Error(ERR_DROP,
|
||||
"SV_SendClientMessages: msglen > MAX_MSGLEN");
|
||||
}
|
||||
|
||||
r = FS_FRead(msgbuf, n, 1, sv.demofile);
|
||||
|
||||
return (r == n) ? n : -1;
|
||||
}
|
||||
|
||||
/* if the reliable message
|
||||
overflowed, drop the
|
||||
client */
|
||||
static void
|
||||
SV_SendDisconnect(client_t *c)
|
||||
{
|
||||
SZ_Clear(&c->netchan.message);
|
||||
SZ_Clear(&c->datagram);
|
||||
|
||||
SV_BroadcastPrintf(PRINT_HIGH, "%s overflowed\n", c->name);
|
||||
SV_DropClient(c);
|
||||
|
||||
Netchan_Transmit(&c->netchan, 0, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
SV_SendClientMessages(void)
|
||||
{
|
||||
|
@ -526,69 +588,35 @@ SV_SendClientMessages(void)
|
|||
client_t *c;
|
||||
int msglen;
|
||||
byte msgbuf[MAX_MSGLEN];
|
||||
size_t r;
|
||||
|
||||
msglen = 0;
|
||||
|
||||
/* read the next demo message if needed */
|
||||
if (sv.demofile && (sv.state == ss_demo))
|
||||
{
|
||||
if (sv_paused->value)
|
||||
msglen = SV_NextDemoChunk(msgbuf);
|
||||
|
||||
if (msglen < 0)
|
||||
{
|
||||
msglen = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* get the next message */
|
||||
r = FS_FRead(&msglen, 4, 1, sv.demofile);
|
||||
|
||||
if (r != 4)
|
||||
{
|
||||
SV_DemoCompleted();
|
||||
return;
|
||||
}
|
||||
|
||||
msglen = LittleLong(msglen);
|
||||
|
||||
if (msglen == -1)
|
||||
{
|
||||
SV_DemoCompleted();
|
||||
return;
|
||||
}
|
||||
|
||||
if (msglen > MAX_MSGLEN)
|
||||
{
|
||||
Com_Error(ERR_DROP,
|
||||
"SV_SendClientMessages: msglen > MAX_MSGLEN");
|
||||
}
|
||||
|
||||
r = FS_FRead(msgbuf, msglen, 1, sv.demofile);
|
||||
|
||||
if (r != msglen)
|
||||
{
|
||||
SV_DemoCompleted();
|
||||
return;
|
||||
}
|
||||
SV_DemoCompleted();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msglen = 0;
|
||||
}
|
||||
|
||||
/* send a message to each connected client */
|
||||
/* send a message to each spawned client */
|
||||
for (i = 0, c = svs.clients; i < maxclients->value; i++, c++)
|
||||
{
|
||||
if (!c->state)
|
||||
if (c->state == cs_free)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if the reliable message
|
||||
overflowed, drop the
|
||||
client */
|
||||
if (c->netchan.message.overflowed)
|
||||
{
|
||||
SZ_Clear(&c->netchan.message);
|
||||
SZ_Clear(&c->datagram);
|
||||
SV_BroadcastPrintf(PRINT_HIGH, "%s overflowed\n", c->name);
|
||||
SV_DropClient(c);
|
||||
SV_SendDisconnect(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((sv.state == ss_cinematic) ||
|
||||
|
@ -607,15 +635,43 @@ SV_SendClientMessages(void)
|
|||
|
||||
SV_SendClientDatagram(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* just update reliable if needed */
|
||||
if (c->netchan.message.cursize ||
|
||||
(curtime - c->netchan.last_sent > 1000))
|
||||
{
|
||||
Netchan_Transmit(&c->netchan, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* messages to non-spawned clients are sent by SendPrepClientMessages */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SV_SendPrepClientMessages(void)
|
||||
{
|
||||
client_t *c;
|
||||
int i;
|
||||
|
||||
if ((sv.state == ss_cinematic) ||
|
||||
(sv.state == ss_demo) ||
|
||||
(sv.state == ss_pic))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* send a message to each inactive client if needed */
|
||||
for (i = 0, c = svs.clients; i < maxclients->value; i++, c++)
|
||||
{
|
||||
if ((c->state == cs_free) || (c->state == cs_spawned))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c->netchan.message.overflowed)
|
||||
{
|
||||
SV_SendDisconnect(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* just update reliable if needed */
|
||||
if (c->netchan.message.cursize ||
|
||||
(curtime - c->netchan.last_sent > 1000))
|
||||
{
|
||||
Netchan_Transmit(&c->netchan, 0, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
|
||||
#define MAX_STRINGCMDS 8
|
||||
|
||||
#define CMD_MARGIN 40 /* space in message reserved for command */
|
||||
#define SAFE_MARGIN 24 /* space reserved for more data added elsewhere */
|
||||
|
||||
edict_t *sv_player;
|
||||
|
||||
void
|
||||
|
@ -115,6 +118,8 @@ void
|
|||
SV_Configstrings_f(void)
|
||||
{
|
||||
int start;
|
||||
char *cs;
|
||||
int max_msgutil;
|
||||
|
||||
Com_DPrintf("Configstrings() from %s\n", sv_client->name);
|
||||
|
||||
|
@ -134,16 +139,30 @@ SV_Configstrings_f(void)
|
|||
|
||||
start = (int)strtol(Cmd_Argv(2), (char **)NULL, 10);
|
||||
|
||||
/* write a packet full of data */
|
||||
while (sv_client->netchan.message.cursize < MAX_MSGLEN / 2 &&
|
||||
start < MAX_CONFIGSTRINGS)
|
||||
if (start < 0)
|
||||
{
|
||||
if (sv.configstrings[start][0])
|
||||
start = 0;
|
||||
}
|
||||
|
||||
/* 560 is roughly the legacy safety margin */
|
||||
max_msgutil = (SV_Optimizations() & OPTIMIZE_MSGUTIL) ?
|
||||
SAFE_MARGIN : 560;
|
||||
|
||||
/* write a packet full of data */
|
||||
while (start < MAX_CONFIGSTRINGS)
|
||||
{
|
||||
cs = sv.configstrings[start];
|
||||
|
||||
if (*cs != '\0')
|
||||
{
|
||||
if ((sv_client->netchan.message.cursize + MSG_ConfigString_Size(cs))
|
||||
> (MAX_MSGLEN - (CMD_MARGIN + max_msgutil)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
MSG_WriteByte(&sv_client->netchan.message, svc_configstring);
|
||||
MSG_WriteShort(&sv_client->netchan.message, start);
|
||||
MSG_WriteString(&sv_client->netchan.message,
|
||||
sv.configstrings[start]);
|
||||
MSG_WriteConfigString(&sv_client->netchan.message, start, cs);
|
||||
}
|
||||
|
||||
start++;
|
||||
|
@ -168,6 +187,7 @@ void
|
|||
SV_Baselines_f(void)
|
||||
{
|
||||
int start;
|
||||
int max_msgutil;
|
||||
entity_state_t nullstate;
|
||||
entity_state_t *base;
|
||||
|
||||
|
@ -188,16 +208,31 @@ SV_Baselines_f(void)
|
|||
}
|
||||
|
||||
start = (int)strtol(Cmd_Argv(2), (char **)NULL, 10);
|
||||
|
||||
if (start < 0)
|
||||
{
|
||||
start = 0;
|
||||
}
|
||||
|
||||
memset(&nullstate, 0, sizeof(nullstate));
|
||||
|
||||
/* 560 is roughly the legacy safety margin */
|
||||
max_msgutil = (SV_Optimizations() & OPTIMIZE_MSGUTIL) ?
|
||||
SAFE_MARGIN : 560;
|
||||
|
||||
/* write a packet full of data */
|
||||
while (sv_client->netchan.message.cursize < MAX_MSGLEN / 2 &&
|
||||
start < MAX_EDICTS)
|
||||
while (start < MAX_EDICTS)
|
||||
{
|
||||
base = &sv.baselines[start];
|
||||
|
||||
if (base->modelindex || base->sound || base->effects)
|
||||
{
|
||||
if ((sv_client->netchan.message.cursize + MSG_DeltaEntity_Size(&nullstate, base, true, true))
|
||||
> (MAX_MSGLEN - (CMD_MARGIN + max_msgutil)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
MSG_WriteByte(&sv_client->netchan.message, svc_spawnbaseline);
|
||||
MSG_WriteDeltaEntity(&nullstate, base,
|
||||
&sv_client->netchan.message,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue