Q3TA: Added snd_ignorecueloops cvar to ignore auto-looping sounds.

Q3TA: Fixed script parsing, so the menus are not so broken.
Q3TA: added the UI_CIN_* and CG_CIN_* builtins for 2d cinematic playbacks.
Q3TA: backspace should work for gamecode text entry now.
Q3TA: map_restart now directly restarts without needing loading screens.
Fixed multiple envmap generation bugs.
Fixed envmaps getting flushed with r_keepimages 0.
Console image previews can now display cubemaps (although their orientation is probably a little off).
Made a 'remapshader' csqc builtin. Depending on the r_remapshader console command was stupid.
Fixed packet command to create a udp socket, if needed.
sv_public can now be set to a non-numeric name for custom names (instead of needing to poke sv_port too).
Support extended data on the end of miptex entries in bsps. Requires a qbsp (like vanilla) that doesn't rewrite the miptex entries.
Updated imgtool to generate/read our new extended miptex data.



git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5644 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2020-03-07 09:00:40 +00:00
parent 2b01dbea52
commit 062cdf6b21
53 changed files with 2231 additions and 972 deletions

View file

@ -10624,7 +10624,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"changelevel", PF_changelevel, 70, 70, 70, 0, D("void(string mapname, optional string newmapstartspot)", "Attempts to change the map to the named map. If 'newmapstartspot' is specified, the state of the current map will be preserved, and the argument will be passed to the next map in the 'startspot' global, and the next map will be loaded from archived state if it was previously visited. If not specified, all archived map states will be purged.")}, //70
{"cvar_set", PF_cvar_set, 72, 72, 72, 0, D("void(string cvarname, string valuetoset)", "Instantly sets a cvar to the given string value.")}, //72
{"cvar_set", PF_cvar_set, 72, 72, 72, 0, D("void(string cvarname, string valuetoset)", "Instantly sets a cvar to the given string value. Warning: the resulting string includes apostrophies surrounding the result. You may wish to use sprintf instead.")}, //72
{"centerprint", PF_centerprint, 73, 73, 73, 0, "void(entity ent, string text, optional string text2, optional string text3, optional string text4, optional string text5, optional string text6, optional string text7)"}, //73
{"ambientsound", PF_ambientsound, 74, 74, 74, 0, "void (vector pos, string samp, float vol, float atten)"}, //74
@ -10866,12 +10866,13 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"rotatevectorsbyvectors",PF_rotatevectorsbymatrix,0,0, 0, 236, "void(vector fwd, vector right, vector up)"}, // #236
{"skinforname", PF_skinforname, 0, 0, 0, 237, "float(float mdlindex, string skinname)"}, // #237
{"shaderforname", PF_Fixme, 0, 0, 0, 238, D("float(string shadername, optional string defaultshader, ...)", "Caches the named shader and returns a handle to it.\nIf the shader could not be loaded from disk (missing file or ruleset_allow_shaders 0), it will be created from the 'defaultshader' string if specified, or a 'skin shader' default will be used.\ndefaultshader if not empty should include the outer {} that you would ordinarily find in a shader.")},
{"remapshader", PF_Fixme, 0, 0, 0, 0, D("void(string shadername, string replacement, float timeoffset)", "All surfaces drawn with the specified shader will instead be drawn using the specified replacement shader. Shaders can be remapped to something else later by using the same source shadername. This is mostly useful for worldmodel surfaces (eg showing which team is currently winning). Entities should generally use setcustomskin or forceshader instead. Remaps will be forgotten on vid_reload, but can be reapplied via CSQC_RendererRestarted.")},
{"te_bloodqw", PF_te_bloodqw, 0, 0, 0, 239, "void(vector org, optional float count)"},
{"te_muzzleflash", PF_te_muzzleflash, 0, 0, 0, 0, "void(entity ent)"},
{"checkpvs", PF_checkpvs, 0, 0, 0, 240, "float(vector viewpos, entity entity)"},
{"matchclientname", PF_matchclient, 0, 0, 0, 241, "entity(string match, optional float matchnum)"},
{"sendpacket", PF_SendPacket, 0, 0, 0, 242, D("void(string destaddress, string content)", "Sends a UDP packet to the specified destination. Note that the payload will be prefixed with four 255 bytes as a sort of security feature.")},// (FTE_QC_SENDPACKET)
{"sendpacket", PF_SendPacket, 0, 0, 0, 242, D("float(string destaddress, string content)", "Sends a UDP packet to the specified destination. Note that the payload will be prefixed with four 255 bytes as a sort of security feature.")},// (FTE_QC_SENDPACKET)
// {"bulleten", PF_bulleten, 0, 0, 0, 243}, (removed builtin)
@ -12349,7 +12350,7 @@ void PR_DumpPlatform_f(void)
{"CONTENTBIT_BODY", "const int", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_BODY)"i"},
{"CONTENTBIT_CORPSE", "const int", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_CORPSE)"i"},
{"CONTENTBIT_Q2LADDER", "const int", QW|NQ|CS, D("Content bit specific to q2bsp"), 0,STRINGIFY(Q2CONTENTS_LADDER)"i"},
{"CONTENTBIT_SKY", "const int", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_SKY)"i"},
{"CONTENTBIT_SKY", "const int", QW|NQ|CS, D("Content bit somewhat specific to q1bsp (aliases to NODROP in q3bsp), but you should probably check surfaceflags&SURF_SKY as well for q2+q3bsp too."), 0,STRINGIFY(FTECONTENTS_SKY)"i"},
{"CONTENTBITS_POINTSOLID", "const int", QW|NQ|CS, D("Bits that traceline would normally consider solid"), 0,"CONTENTBIT_SOLID|"STRINGIFY(Q2CONTENTS_WINDOW)"i|CONTENTBIT_BODY"},
{"CONTENTBITS_BOXSOLID", "const int", QW|NQ|CS, D("Bits that tracebox would normally consider solid"), 0,"CONTENTBIT_SOLID|"STRINGIFY(Q2CONTENTS_WINDOW)"i|CONTENTBIT_BODY|CONTENTBIT_PLAYERCLIP"},
{"CONTENTBITS_FLUID", "const int", QW|NQ|CS, NULL, 0,"CONTENTBIT_WATER|CONTENTBIT_SLIME|CONTENTBIT_LAVA|CONTENTBIT_SKY"},

View file

@ -113,13 +113,15 @@ typedef struct
qboolean csqcdebug;
unsigned int csqcchecksum;
qboolean mapchangelocked;
qboolean restarting;
#ifdef SAVEDGAMES
char loadgame_on_restart[MAX_QPATH]; //saved game to load on map_restart
double autosave_time;
#endif
double time;
double starttime;
double time; //current map time
double restartedtime; //sv.time from last map restart
double starttime; //system time we changed map.
int framenum;
int logindatabase;
@ -1283,8 +1285,10 @@ void SVQ2_BuildBaselines(void);
//q3 stuff
#ifdef Q3SERVER
void SVQ3_ShutdownGame(void);
qboolean SVQ3_InitGame(void);
void SVQ3_ShutdownGame(qboolean restarting);
qboolean SVQ3_InitGame(qboolean restarting);
void SVQ3_ServerinfoChanged(const char *key);
qboolean SVQ3_RestartGamecode(void);
qboolean SVQ3_ConsoleCommand(void);
qboolean SVQ3_HandleClient(void);
void SVQ3_DirectConnect(void);

View file

@ -556,6 +556,13 @@ void SV_Map_f (void)
if (!Q_strcasecmp(Cmd_Argv(0), "map_restart"))
{
const char *arg = Cmd_Argv(1);
#ifdef Q3SERVER
if (sv.state==ss_active && svs.gametype==GT_QUAKE3)
if (SVQ3_RestartGamecode())
return;
#endif
#ifdef SAVEDGAMES
if (!strcmp(arg, "restore")) //hexen2 reload-saved-game
;
@ -800,6 +807,8 @@ void SV_Map_f (void)
{
cvar_t *gametype;
Cvar_ApplyLatches(CVAR_LATCH);
gametype = Cvar_Get("mapname", "", CVAR_LATCH|CVAR_SERVERINFO, "Q3 compatability");
gametype->flags |= CVAR_SERVERINFO;
Cvar_ForceSet(gametype, level);

View file

@ -625,7 +625,7 @@ void SV_UnspawnServer (void) //terminate the running server.
}
PR_Deinit();
#ifdef Q3SERVER
SVQ3_ShutdownGame();
SVQ3_ShutdownGame(false);
#endif
#ifdef Q2SERVER
SVQ2_ShutdownGameProgs();
@ -921,7 +921,7 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents,
#ifdef Q3SERVER
if (svs.gametype == GT_QUAKE3)
SVQ3_ShutdownGame(); //botlib kinda mandates this. :(
SVQ3_ShutdownGame(false); //botlib kinda mandates this. :(
#endif
Mod_ClearAll ();
@ -981,7 +981,7 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents,
CL_CheckServerInfo();
#endif
sv.restarting = false;
sv.state = ss_loading;
#if defined(Q2BSPS)
if (usecinematic)
@ -1132,7 +1132,7 @@ MSV_OpenUserDatabase();
else
#endif
#ifdef Q3SERVER
if (SVQ3_InitGame())
if (SVQ3_InitGame(false))
newgametype = GT_QUAKE3;
else
#endif
@ -1167,7 +1167,7 @@ MSV_OpenUserDatabase();
#endif
#ifdef Q3SERVER
if (newgametype != GT_QUAKE3)
SVQ3_ShutdownGame();
SVQ3_ShutdownGame(false);
#endif
#ifdef Q2SERVER
if (newgametype != GT_QUAKE2) //we don't want the q2 stuff anymore.
@ -1380,7 +1380,7 @@ MSV_OpenUserDatabase();
#endif
#ifdef Q3SERVER
case GT_QUAKE3:
SV_UpdateMaxPlayers(32);
SV_UpdateMaxPlayers(max(8,maxclients.ival));
break;
#endif
#ifdef HLSERVER

View file

@ -111,7 +111,15 @@ extern cvar_t sv_allow_splitscreen;
#ifdef SUPPORT_ICE
static void QDECL SV_Public_Callback(struct cvar_s *var, char *oldvalue)
{
if (var->ival == 2)
char name[64], *e;
COM_ParseOut(var->string, name, sizeof(name));
strtol(name, &e, 0);
if (*name&&e==name) //failed to read any number out of it.
{
FTENET_AddToCollection(svs.sockets, var->name, va("/%s", name), NA_INVALID, NP_RTC_TLS);
var->value = var->ival = 2; //so other stuff sees us as holepunched.
}
else if (var->ival == 2)
FTENET_AddToCollection(svs.sockets, var->name, "/", NA_INVALID, NP_RTC_TLS);
else
FTENET_AddToCollection(svs.sockets, var->name, "", NA_INVALID, NP_INVALID);
@ -5281,6 +5289,11 @@ float SV_Frame (void)
static void SV_InfoChanged(void *context, const char *key)
{
size_t i;
#ifdef Q3SERVER
SVQ3_ServerinfoChanged(key);
#endif
if (context != &svs.info && *key == '_')
return; //these keys are considered private to originating client/server, and are not broadcast to anyone else

View file

@ -1941,6 +1941,18 @@ void SVQW_Spawn_f (void)
// when that is completed, a begin command will be issued
ClientReliableWrite_Begin (host_client, svc_stufftext, 8);
ClientReliableWrite_String (host_client, "skins\n" );
if (sv.allocated_client_slots > 1)
{ //okay, so nq player physics don't suppot prediction.
//if we use qw physics in nq mods then we risk breaking things.
//the only progs many players will have is the vanilla nq one.
//so prediction is broken on most people's quicky servers.
//which really sucks.
//so let multiplayer people know what's going on so that they don't think its an actual bug, and can harass the admin to get it fixed in mods that allow for it.
if (!strcmp(sv_nqplayerphysics.string, "auto") || !strcmp(sv_nqplayerphysics.string, ""))
if (sv_nqplayerphysics.ival)
SV_PrintToClient(host_client, PRINT_HIGH, CON_WARNING"Movement prediction is disabled in favour of non-quakeworld mod compatibilty\n");
}
}
/*

View file

@ -61,6 +61,7 @@ static vm_t *q3gamevm;
#define MAX_CONFIGSTRINGS 1024
static char *svq3_configstrings[MAX_CONFIGSTRINGS];
static qboolean q3_serverinfo_dirty;
static q3sharedEntity_t *q3_entarray;
static int numq3entities;
static int sizeofq3gentity;
@ -799,14 +800,21 @@ void SVQ3_SendConfigString(client_t *dest, int num, char *string)
void SVQ3_SetConfigString(int num, char *string)
{
int len;
if (num < 0 || num >= MAX_Q3_CONFIGSTRINGS)
return; //no exploits please
if (!string)
string = "";
if (!strcmp(svq3_configstrings[num]?svq3_configstrings[num]:"", string))
return;
len = strlen(string);
if (svq3_configstrings[num])
Z_Free(svq3_configstrings[num]);
svq3_configstrings[num] = Z_Malloc(len+1);
strcpy(svq3_configstrings[num], string);
if (sv.state == ss_loading && !sv.restarting)
return; //don't spam these, the svcq3_gamestate will have a copy anyway and the gamecode can get confused.
SVQ3_SendConfigString(NULL, num, string);
}
@ -1676,33 +1684,37 @@ static qintptr_t EXPORT_FN Q3G_SystemCallsNative(qintptr_t arg, ...)
return Q3G_SystemCalls(NULL, ~(quintptr_t)0, arg, args);
}
void SVQ3_ShutdownGame(void)
void SVQ3_ShutdownGame(qboolean restarting)
{
int i;
if (!q3gamevm)
return;
if (!restarting)
{
#ifdef USEBOTLIB
if (botlib)
{ //it crashes otherwise, probably due to our huck clearage
botlib->BotLibShutdown();
Z_FreeTags(Z_TAG_BOTLIB);
}
if (botlib)
{ //it crashes otherwise, probably due to our huck clearage
botlib->BotLibShutdown();
Z_FreeTags(Z_TAG_BOTLIB);
}
#endif
for (i = 0; i < MAX_CONFIGSTRINGS; i++)
{
if (svq3_configstrings[i])
for (i = 0; i < MAX_CONFIGSTRINGS; i++)
{
Z_Free(svq3_configstrings[i]);
svq3_configstrings[i] = NULL;
if (svq3_configstrings[i])
{
Z_Free(svq3_configstrings[i]);
svq3_configstrings[i] = NULL;
}
}
Z_Free(q3_sentities);
q3_sentities = NULL;
BZ_Free(q3_snapshot_entities);
q3_snapshot_entities = NULL;
}
Z_Free(q3_sentities);
q3_sentities = NULL;
BZ_Free(q3_snapshot_entities);
q3_snapshot_entities = NULL;
VM_Destroy(q3gamevm);
q3gamevm = NULL;
@ -1926,12 +1938,27 @@ static void SV_InitBotLib(void)
#endif
}
qboolean SVQ3_InitGame(void)
void SVQ3_ServerinfoChanged(const char *key)
{ //roll up multiple updates into a single splurge. SVQ3_UpdateServerinfo will be called after the next frame.
q3_serverinfo_dirty = true;
}
static void SVQ3_UpdateServerinfo(void)
{
char buffer[8192];
/*update serverinfo - qw serverinfo settings are not normally visible in the q3 serverinfo, so strip them from the configstring*/
static const char *ignores[] = {"maxclients", "map", "sv_maxclients", "*z_ext", "*bspversion", "*gamedir", NULL};
extern cvar_t maxclients;
InfoBuf_ToString(&svs.info, buffer, sizeof(buffer), NULL, ignores, NULL, NULL, NULL);
//add in maxclients.. the q3 version
Q_strncatz(buffer, va("\\sv_maxclients\\%s", maxclients.string), sizeof(buffer));
SVQ3_SetConfigString(0, buffer);
q3_serverinfo_dirty = false;
}
qboolean SVQ3_InitGame(qboolean restart)
{
int i;
char buffer[8192];
char *str;
char sysinfo[8192];
if (sv.world.worldmodel->type == mod_heightmap)
{
@ -1946,7 +1973,7 @@ qboolean SVQ3_InitGame(void)
return false;
SVQ3_ShutdownGame();
SVQ3_ShutdownGame(restart);
q3gamevm = VM_Create("qagame", com_nogamedirnativecode.ival?NULL:Q3G_SystemCallsNative, "vm/qagame", Q3G_SystemCallsVM);
@ -1965,68 +1992,67 @@ qboolean SVQ3_InitGame(void)
q3_sentities = Z_Malloc(sizeof(q3serverEntity_t)*MAX_GENTITIES);
{ /*qw serverinfo settings are not normally visible in the q3 serverinfo, so strip them from the configstring*/
static const char *ignores[] = {"maxclients", "map", "sv_maxclients", "*z_ext", "*bspversion", "*gamedir", NULL};
extern cvar_t maxclients;
InfoBuf_ToString(&svs.info, buffer, sizeof(buffer), NULL, ignores, NULL, NULL, NULL);
//add in maxclients.. the q3 version
Q_strncatz(buffer, va("\\sv_maxclients\\%s", maxclients.string), sizeof(buffer));
}
SVQ3_SetConfigString(0, buffer);
Cvar_Set(Cvar_Get("sv_running", "0", 0, "Q3 compatability"), "1");
sysinfo[0] = '\0';
Info_SetValueForKey(sysinfo, "sv_serverid", va("%i", svs.spawncount), MAX_SERVERINFO_STRING);
if (!restart)
{
char buffer[8192];
char sysinfo[8192];
/*update the system info*/
sysinfo[0] = '\0';
Info_SetValueForKey(sysinfo, "sv_serverid", va("%i", svs.spawncount), MAX_SERVERINFO_STRING);
str = FS_GetPackHashes(buffer, sizeof(buffer), false);
Info_SetValueForKey(sysinfo, "sv_paks", str, MAX_SERVERINFO_STRING);
str = FS_GetPackNames(buffer, sizeof(buffer), false, false);
Info_SetValueForKey(sysinfo, "sv_pakNames", str, MAX_SERVERINFO_STRING);
str = FS_GetPackHashes(buffer, sizeof(buffer), true);
Info_SetValueForKey(sysinfo, "sv_referencedPaks", str, MAX_SERVERINFO_STRING);
str = FS_GetPackNames(buffer, sizeof(buffer), true, false);
Info_SetValueForKey(sysinfo, "sv_referencedPakNames", str, MAX_SERVERINFO_STRING);
Info_SetValueForKey(sysinfo, "sv_pure", sv_pure.string, MAX_SERVERINFO_STRING);
SVQ3_SetConfigString(1, sysinfo);
Info_SetValueForKey(sysinfo, "sv_paks", FS_GetPackHashes(buffer, sizeof(buffer), false ), MAX_SERVERINFO_STRING);
Info_SetValueForKey(sysinfo, "sv_pakNames", FS_GetPackNames (buffer, sizeof(buffer), false, false), MAX_SERVERINFO_STRING);
Info_SetValueForKey(sysinfo, "sv_referencedPaks", FS_GetPackHashes(buffer, sizeof(buffer), true ), MAX_SERVERINFO_STRING);
Info_SetValueForKey(sysinfo, "sv_referencedPakNames", FS_GetPackNames (buffer, sizeof(buffer), true, false ), MAX_SERVERINFO_STRING);
Info_SetValueForKey(sysinfo, "sv_pure", sv_pure.string, MAX_SERVERINFO_STRING);
SVQ3_SetConfigString(1, sysinfo);
q3_serverinfo_dirty = true;
}
mapentspointer = Mod_GetEntitiesString(sv.world.worldmodel);
VM_Call(q3gamevm, GAME_INIT, 0, (int)rand(), false);
VM_Call(q3gamevm, GAME_INIT, (intptr_t)(sv.time*1000), (int)rand(), restart);
CM_InitBoxHull();
SVQ3_CreateBaseline();
if (!restart)
{
SVQ3_CreateBaseline();
q3_num_snapshot_entities = 32 * Q3UPDATE_BACKUP * 32;
if (q3_snapshot_entities)
BZ_Free(q3_snapshot_entities);
q3_next_snapshot_entities = 0;
q3_snapshot_entities = BZ_Malloc(sizeof( q3entityState_t ) * q3_num_snapshot_entities);
q3_num_snapshot_entities = 32 * Q3UPDATE_BACKUP * 32;
if (q3_snapshot_entities)
BZ_Free(q3_snapshot_entities);
q3_next_snapshot_entities = 0;
q3_snapshot_entities = Z_Malloc(sizeof( q3entityState_t ) * q3_num_snapshot_entities);
}
// run a few frames to allow everything to settle
// run a few frames to allow everything to settle
for (i = 0; i < 3; i++)
{
SVQ3_RunFrame();
sv.time += 0.1;
}
#ifdef HAVE_CLIENT
//there's a whole load of ugly debug crap there. make sure it stays hidden.
Con_ClearNotify();
#endif
return true;
}
void SVQ3_RunFrame(void)
{
VM_Call(q3gamevm, GAME_RUN_FRAME, (int)(sv.time*1000));
#ifdef USEBOTLIB
if (botlib)
VM_Call(q3gamevm, BOTAI_START_FRAME, (int)(sv.time*1000));
#endif
VM_Call(q3gamevm, GAME_RUN_FRAME, (int)(sv.time*1000));
if (q3_serverinfo_dirty)
SVQ3_UpdateServerinfo();
}
void SVQ3_ClientCommand(client_t *cl)
@ -2038,6 +2064,7 @@ void SVQ3_ClientBegin(client_t *cl)
{
VM_Call(q3gamevm, GAME_CLIENT_BEGIN, (int)(cl-svs.clients));
sv.spawned_client_slots++;
cl->spawned = true;
}
void SVQ3_ClientThink(client_t *cl)
@ -2464,7 +2491,7 @@ void SVQ3_BuildClientSnapshot( client_t *client )
{
q3_num_snapshot_entities = 32 * Q3UPDATE_BACKUP * 32;
q3_next_snapshot_entities = 0;
q3_snapshot_entities = BZ_Malloc(sizeof( q3entityState_t ) * q3_num_snapshot_entities);
q3_snapshot_entities = Z_Malloc(sizeof( q3entityState_t ) * q3_num_snapshot_entities);
}
clientNum = client - svs.clients;
@ -2484,7 +2511,7 @@ void SVQ3_BuildClientSnapshot( client_t *client )
// this is the frame we are creating
snap = &client->frameunion.q3frames[client->netchan.outgoing_sequence & Q3UPDATE_MASK];
snap->serverTime = sv.time*1000;//svs.levelTime; // save it for ping calc later
snap->serverTime = sv.restartedtime*1000 + sv.time*1000;//svs.levelTime; // save it for ping calc later
snap->flags = 0;
if( client->state < cs_spawned )
@ -2818,8 +2845,8 @@ void SVQ3_SendGameState(client_t *client)
// write baselines
for( i=0; i<MAX_GENTITIES; i++ )
{
if (!q3_baselines[i].number)
continue;
if (!q3_baselines[i].number)
continue;
MSG_WriteBits(&msg, svcq3_baseline, 8);
MSGQ3_WriteDeltaEntity( &msg, NULL, &q3_baselines[i], true );
@ -2932,6 +2959,7 @@ client_t *SVQ3_FindExistingPlayerByIP(netadr_t *na, int qport)
qboolean Netchan_ProcessQ3 (netchan_t *chan);
static qboolean SVQ3_Netchan_Process(client_t *client)
{
#ifndef Q3_NOENCRYPT
int serverid;
int lastSequence;
int lastServerCommandNum;
@ -2941,12 +2969,14 @@ static qboolean SVQ3_Netchan_Process(client_t *client)
char *string;
int bit;
int readcount;
#endif
if (!Netchan_ProcessQ3(&client->netchan))
{
return false;
}
#ifndef Q3_NOENCRYPT
// archive buffer state
bit = net_message.currentbit;
readcount = msg_readcount;
@ -2965,7 +2995,6 @@ static qboolean SVQ3_Netchan_Process(client_t *client)
bitmask = (serverid ^ lastSequence ^ client->challenge) & 0xff;
string = client->server_commands[lastServerCommandNum & Q3TEXTCMD_MASK];
#ifndef Q3_NOENCRYPT
// decrypt the packet
for(i=msg_readcount+12,j=0; i<net_message.cursize; i++,j++)
{
@ -2989,16 +3018,17 @@ static qboolean SVQ3_Netchan_Process(client_t *client)
void SVQ3_Netchan_Transmit(client_t *client, int length, qbyte *data)
{
qbyte buffer[MAX_OVERALLMSGLEN];
int i;
#ifndef Q3_NOENCRYPT
qbyte bitmask;
qbyte c;
int i, j;
int j;
char *string;
// calculate bitmask
bitmask = (client->netchan.outgoing_sequence ^ client->challenge) & 0xff;
string = client->last_client_command;
#ifndef Q3_NOENCRYPT
//first four bytes are not encrypted.
for(i=0; i<4 ; i++)
buffer[i] = data[i];
@ -3145,7 +3175,7 @@ void SVQ3_UpdateUserinfo_f(client_t *cl)
InfoBuf_FromString(&cl->userinfo, Cmd_Argv(1), false);
SV_ExtractFromUserinfo (cl, true);
if (svs.gametype == GT_QUAKE3)
if (svs.gametype == GT_QUAKE3 && cl->spawned)
VM_Call(q3gamevm, GAME_CLIENT_USERINFO_CHANGED, (int)(cl-svs.clients));
}
@ -3385,6 +3415,69 @@ qboolean SVQ3_HandleClient(void)
SVQ3_ParseClientMessage(&svs.clients[i]);
return true;
}
//Q3 gamecode does map_restart weirdly.
//it simply reloads the gamecode without changing any maps/models/sounds
//this won't work for q1/q2, but q3 expects it.
//note that time continues from its prior value without any kind of reset.
qboolean SVQ3_RestartGamecode(void)
{
int i;
extern cvar_t maxclients;
int newmaxclients = max(8,maxclients.ival);
if (sv.allocated_client_slots != newmaxclients)
return false; //can't do it if maxclients needs to change.
Cvar_ApplyLatches(CVAR_LATCH);
//reload the gamecode
sv.state = ss_loading;
sv.restarting = true;
if (!SVQ3_InitGame(true))
return false;
// svs.spawncount++; //so new snapshots get sent
sv.restartedtime = 0;
//and then reconnect the players as appropriate
// SVQ3_NewMapConnects();
for (i = 0; i < sv.allocated_client_slots; i++)
{
if (svs.clients[i].state < cs_connected)
continue;
if (VM_Call(q3gamevm, GAME_CLIENT_CONNECT, i, false, svs.clients[i].protocol == SCP_BAD))
{
SV_DropClient(&svs.clients[i]);
continue;
}
if (svs.clients[i].spawned)
{
sv.spawned_client_slots--;
SVQ3_ClientBegin(&svs.clients[i]);
}
}
sv.starttime = Sys_DoubleTime() - sv.time;
#ifdef SAVEDGAMES
sv.autosave_time = sv.time + sv_autosave.value*60;
#endif
//basically done
sv.state = ss_active;
sv.restarting = false;
//and an extra physics frame for luck
sv.time+=0.1;
sv.world.physicstime=sv.time;
SVQ3_RunFrame();
SVQ3_SendServerCommand(NULL, "map_restart");
return true; //yup, we did it.
}
void SVQ3_NewMapConnects(void)
{
int i;
@ -3467,10 +3560,13 @@ void SVQ3_DirectConnect(void) //Actually connect the client, use up a slot, and
else
{
InfoBuf_FromString(&cl->userinfo, userinfo, false);
reason = NET_AdrToString(adr, sizeof(adr), &net_from);
if (net_from.type == NA_LOOPBACK)
reason = "localhost"; //Q3 uses this specific string for listen servers.
else
reason = NET_AdrToString(adr, sizeof(adr), &net_from);
InfoBuf_SetKey(&cl->userinfo, "ip", reason); //q3 gamecode needs to know the client's ip (server's perception of the client, NOT QW client's perception of the server/proxy)
ret = VM_Call(q3gamevm, GAME_CLIENT_CONNECT, (int)(cl-svs.clients), false, false);
ret = VM_Call(q3gamevm, GAME_CLIENT_CONNECT, (int)(cl-svs.clients), true, false);
if (!ret)
reason = NULL;
else