From 0dbb57dd5c669baf1e9a4049158db4f072dc2750 Mon Sep 17 00:00:00 2001 From: Spoike Date: Thu, 16 Nov 2017 22:20:40 +0000 Subject: [PATCH] Fix some omissions with splitscreen+csqc: unicast svc_cgamepackets will now report which seat they were unicast to. sendevent will now reveal the seat that was active at the time of the call. csqc console commands will now reveal any p2 etc commands that were used. added 144hz refresh rate option to the built-in menu added sv_demo_write_csqc cvar to write csprogs.dat into mvds. I still need to read them... make irc client not create new accounts if there's already a connection registered for the given server. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5169 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_parse.c | 4 +- engine/client/client.h | 4 +- engine/client/console.c | 2 +- engine/client/m_items.c | 2 +- engine/client/m_options.c | 3 +- engine/client/menu.c | 2 +- engine/client/pr_csqc.c | 57 +++++++++++++----- engine/client/sbar.c | 46 +++++++------- engine/common/cmd.c | 4 +- engine/common/com_mesh.c | 1 - engine/qclib/qcc.h | 1 + engine/qclib/qcc_pr_comp.c | 2 - engine/qclib/qccmain.c | 4 +- engine/server/pr_cmds.c | 1 + engine/server/server.h | 7 ++- engine/server/sv_main.c | 2 +- engine/server/sv_mvd.c | 17 +++++- engine/server/sv_send.c | 42 +++++++++++-- engine/server/sv_user.c | 119 ++++++++++++++++++++++++++++++------- plugins/irc/ircclient.c | 17 ++++++ 20 files changed, 255 insertions(+), 82 deletions(-) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index e41a192e2..c1731836c 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -7073,7 +7073,7 @@ void CLQW_ParseServerMessage (void) break; #endif #ifdef CSQC_DAT - if (CSQC_ParseGamePacket()) + if (CSQC_ParseGamePacket(destsplit)) break; #endif Con_Printf("Unable to parse gamecode packet\n"); @@ -7721,7 +7721,7 @@ void CLNQ_ParseServerMessage (void) break; #endif #ifdef CSQC_DAT - if (CSQC_ParseGamePacket()) + if (CSQC_ParseGamePacket(destsplit)) break; #endif Con_Printf("Unable to parse gamecode packet\n"); diff --git a/engine/client/client.h b/engine/client/client.h index ffd94e84d..11af1814e 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -1384,14 +1384,14 @@ qboolean CSQC_StuffCmd(int lplayernum, char *cmd, char *cmdend); void CSQC_MapEntityEdited(int modelindex, int idx, const char *newe); qboolean CSQC_LoadResource(char *resname, char *restype); qboolean CSQC_ParsePrint(char *message, int printlevel); -qboolean CSQC_ParseGamePacket(void); +qboolean CSQC_ParseGamePacket(int seat); qboolean CSQC_CenterPrint(int seat, const char *cmd); qboolean CSQC_Parse_Damage(int seat, float save, float take, vec3_t source); qboolean CSQC_Parse_SetAngles(int seat, vec3_t newangles, qboolean wasdelta); void CSQC_Input_Frame(int seat, usercmd_t *cmd); void CSQC_WorldLoaded(void); qboolean CSQC_ParseTempEntity(void); -qboolean CSQC_ConsoleCommand(const char *cmd); +qboolean CSQC_ConsoleCommand(int seat, const char *cmd); qboolean CSQC_KeyPress(int key, int unicode, qboolean down, unsigned int devid); qboolean CSQC_MouseMove(float xdelta, float ydelta, unsigned int devid); qboolean CSQC_MousePosition(float xabs, float yabs, unsigned int devid); diff --git a/engine/client/console.c b/engine/client/console.c index e7442d279..018d73970 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -557,7 +557,7 @@ void Con_ToggleConsole_f (void) } #ifdef CSQC_DAT - if (!(key_dest_mask & kdm_editor) && CSQC_ConsoleCommand("toggleconsole")) + if (!(key_dest_mask & kdm_editor) && CSQC_ConsoleCommand(-1, "toggleconsole")) { Key_Dest_Remove(kdm_console); return; diff --git a/engine/client/m_items.c b/engine/client/m_items.c index 312cc9048..44033f3d9 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -1944,7 +1944,7 @@ void M_Menu_Main_f (void) static menuresel_t resel; #ifdef CSQC_DAT - if (CSQC_ConsoleCommand(va("%s %s", Cmd_Argv(0), Cmd_Args()))) + if (CSQC_ConsoleCommand(-1, va("%s %s", Cmd_Argv(0), Cmd_Args()))) return; #endif diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 63f801982..8103d4b34 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -2624,9 +2624,10 @@ void M_Menu_Video_f (void) "85Hz", "100Hz", "120Hz", + "144Hz", NULL }; - static const char *refreshvalues[] = {"", "59", "60", "70", "72", "75", "85", "100", "120", NULL}; + static const char *refreshvalues[] = {"", "59", "60", "70", "72", "75", "85", "100", "120", "144", NULL}; static const char *res2dmodeopts[] = { ASPECT_LIST diff --git a/engine/client/menu.c b/engine/client/menu.c index d7071350a..79f8ac8b2 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -331,7 +331,7 @@ void M_ToggleMenu_f (void) } #ifdef CSQC_DAT - if (CSQC_ConsoleCommand("togglemenu")) + if (CSQC_ConsoleCommand(-1, "togglemenu")) { Key_Dest_Remove(kdm_console|kdm_cwindows); return; diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 5a716bb91..a3fc292f1 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -1012,6 +1012,22 @@ static void QCBUILTIN PF_R_AddEntity(pubprogfuncs_t *prinst, struct globalvars_s V_AddAxisEntity(&ent); } } +static void QCBUILTIN PF_R_RemoveEntity(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + csqcedict_t *in = (void*)G_EDICT(prinst, OFS_PARM0); + entity_t ent; + if (ED_ISFREE(in) || in->entnum == 0) + { + csqc_deprecated("Tried drawing a free/removed/world entity\n"); + return; + } + + if (CopyCSQCEdictToEntity(in, &ent)) + { + CLQ1_AddShadow(&ent); + V_AddAxisEntity(&ent); + } +} void CL_AddDecal(shader_t *shader, vec3_t origin, vec3_t up, vec3_t side, vec3_t rgbvalue, float alphavalue); static void QCBUILTIN PF_R_AddDecal(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -3097,6 +3113,7 @@ static void QCBUILTIN PF_cs_sendevent (pubprogfuncs_t *prinst, struct globalvars return; MSG_WriteByte(&cls.netchan.message, clcfte_qcrequest); + for (i = 0; i < 6; i++) { if (argtypes[i] == 's') @@ -3130,6 +3147,8 @@ static void QCBUILTIN PF_cs_sendevent (pubprogfuncs_t *prinst, struct globalvars else break; } + if (csqc_playerseat > 0) + MSG_WriteByte(&cls.netchan.message, 200+csqc_playerseat); MSG_WriteByte(&cls.netchan.message, 0); MSG_WriteString(&cls.netchan.message, eventname); } @@ -3239,24 +3258,17 @@ static void QCBUILTIN PF_cs_getinputstate (pubprogfuncs_t *prinst, struct global /*outgoing_sequence says how many packets have actually been sent, but there's an extra pending packet which has not been sent yet - be warned though, its data will change in the coming frames*/ if (f == cl.movesequence) { -// int i; -// usercmd_t tmp; + int i; + usercmd_t tmp; cmd = &cl_pendingcmd[seat]; -/* + tmp = *cmd; cmd = &tmp; for (i=0 ; i<3 ; i++) cmd->angles[i] = ((int)(csqc_playerview->viewangles[i]*65536.0/360)&65535); if (!cmd->msec) - { -// *cmd = cl.outframes[(f-1)&UPDATE_MASK].cmd[seat]; - CL_BaseMove (cmd, seat, cmd->msec, newtime); - } -// if (cl.predservertimes) -// cmd->msec = (cl.time - cl.outframes[(f-1)&UPDATE_MASK].cmd[seat].fservertime)*1000; -// else - cmd->msec = (realtime - cl.outframes[(f-1)&UPDATE_MASK].senttime)*1000; -*/ + *cmd = cl.outframes[(f-1)&UPDATE_MASK].cmd[seat]; + cmd->msec = (realtime - cl.outframes[(f-1)&UPDATE_MASK].senttime)*1000; } else { @@ -3337,6 +3349,7 @@ static void QCBUILTIN PF_cs_runplayerphysics (pubprogfuncs_t *prinst, struct glo } pmove.jump_held = (int)ent->xv->pmove_flags & PMF_JUMP_HELD; pmove.waterjumptime = 0; + pmove.onground = (int)ent->v->flags & FL_ONGROUND; VectorCopy(ent->v->origin, pmove.origin); VectorCopy(ent->v->velocity, pmove.velocity); VectorCopy(ent->v->maxs, pmove.player_maxs); @@ -3357,6 +3370,10 @@ static void QCBUILTIN PF_cs_runplayerphysics (pubprogfuncs_t *prinst, struct glo ent->v->angles[0] *= r_meshpitch.value * 1/3.0f; //FIXME VectorCopy(pmove.origin, ent->v->origin); VectorCopy(pmove.velocity, ent->v->velocity); + if (pmove.onground) + ent->v->flags = (int)ent->v->flags | FL_ONGROUND; + else + ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND; ent->xv->pmove_flags = 0; ent->xv->pmove_flags += pmove.jump_held ? PMF_JUMP_HELD : 0; ent->xv->pmove_flags += pmove.onladder ? PMF_LADDER : 0; @@ -4633,8 +4650,9 @@ static void QCBUILTIN PF_cs_movetogoal (pubprogfuncs_t *prinst, struct globalvar static void CS_ConsoleCommand_f(void) { char cmd[2048]; + int seat = CL_TargettedSplit(false); Q_snprintfz(cmd, sizeof(cmd), "%s %s", Cmd_Argv(0), Cmd_Args()); - CSQC_ConsoleCommand(cmd); + CSQC_ConsoleCommand(seat, cmd); } static void QCBUILTIN PF_cs_registercommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -6033,7 +6051,8 @@ static struct { //300 {"clearscene", PF_R_ClearScene, 300}, // #300 void() clearscene (EXT_CSQC) {"addentities", PF_R_AddEntityMask, 301}, // #301 void(float mask) addentities (EXT_CSQC) - {"addentity", PF_R_AddEntity, 302}, // #302 void(entity ent) addentity (EXT_CSQC) + {"addentity", PF_R_AddEntity, 302}, // #302 void(entity ent) addentity (EXT_CSQC) +// {"removeentity", PF_R_RemoveEntity, 0}, {"setproperty", PF_R_SetViewFlag, 303}, // #303 float(float property, ...) setproperty (EXT_CSQC) {"renderscene", PF_R_RenderScene, 304}, // #304 void() renderscene (EXT_CSQC) @@ -7856,7 +7875,7 @@ qboolean CSQC_ConsoleLink(char *text, char *info) return G_FLOAT(OFS_RETURN); } -qboolean CSQC_ConsoleCommand(const char *cmd) +qboolean CSQC_ConsoleCommand(int seat, const char *cmd) { void *pr_globals; if (!csqcprogs || !csqcg.console_command) @@ -7866,6 +7885,10 @@ qboolean CSQC_ConsoleCommand(const char *cmd) return false; #endif + if (seat < 0) + seat = CL_TargettedSplit(false); + CSQC_ChangeLocalPlayer(seat); + pr_globals = PR_globals(csqcprogs, PR_CURRENT); (((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, cmd)); @@ -7904,7 +7927,7 @@ qboolean CSQC_ParseTempEntity(void) return false; } -qboolean CSQC_ParseGamePacket(void) +qboolean CSQC_ParseGamePacket(int seat) { int parsefnc = csqcg.parse_event?csqcg.parse_event:csqcg.parse_tempentity; @@ -7920,6 +7943,7 @@ qboolean CSQC_ParseGamePacket(void) } csqc_mayread = true; + CSQC_ChangeLocalPlayer(seat); PR_ExecuteProgram (csqcprogs, parsefnc); if (msg_readcount != start + len) @@ -7936,6 +7960,7 @@ qboolean CSQC_ParseGamePacket(void) return false; } csqc_mayread = true; + CSQC_ChangeLocalPlayer(seat); PR_ExecuteProgram (csqcprogs, parsefnc); } csqc_mayread = false; diff --git a/engine/client/sbar.c b/engine/client/sbar.c index ce6f23b3a..6aa409282 100644 --- a/engine/client/sbar.c +++ b/engine/client/sbar.c @@ -720,7 +720,7 @@ void Sbar_ShowTeamScores (void) return; #ifdef CSQC_DAT - if (CSQC_ConsoleCommand(Cmd_Argv(0))) + if (CSQC_ConsoleCommand(seat, Cmd_Argv(0))) return; #endif @@ -742,7 +742,7 @@ void Sbar_DontShowTeamScores (void) sb_updates = 0; #ifdef CSQC_DAT - if (CSQC_ConsoleCommand(Cmd_Argv(0))) + if (CSQC_ConsoleCommand(seat, Cmd_Argv(0))) return; #endif } @@ -767,7 +767,7 @@ void Sbar_ShowScores (void) return; #ifdef CSQC_DAT - if (CSQC_ConsoleCommand(Cmd_Argv(0))) + if (CSQC_ConsoleCommand(seat, Cmd_Argv(0))) return; #endif @@ -778,8 +778,9 @@ void Sbar_ShowScores (void) #ifdef HEXEN2 static void Sbar_Hexen2InvLeft_f(void) { + int seat = CL_TargettedSplit(false); #ifdef CSQC_DAT - if (CSQC_ConsoleCommand(Cmd_Argv(0))) + if (CSQC_ConsoleCommand(seat, Cmd_Argv(0))) return; #endif if (cls.protocol == CP_QUAKE2) @@ -789,8 +790,7 @@ static void Sbar_Hexen2InvLeft_f(void) else { int tries = 15; - int pnum = CL_TargettedSplit(false); - playerview_t *pv = &cl.playerview[pnum]; + playerview_t *pv = &cl.playerview[seat]; pv->sb_hexen2_item_time = realtime; while (tries-- > 0) { @@ -805,8 +805,9 @@ static void Sbar_Hexen2InvLeft_f(void) } static void Sbar_Hexen2InvRight_f(void) { + int seat = CL_TargettedSplit(false); #ifdef CSQC_DAT - if (CSQC_ConsoleCommand(Cmd_Argv(0))) + if (CSQC_ConsoleCommand(seat, Cmd_Argv(0))) return; #endif if (cls.protocol == CP_QUAKE2) @@ -816,8 +817,7 @@ static void Sbar_Hexen2InvRight_f(void) else { int tries = 15; - int pnum = CL_TargettedSplit(false); - playerview_t *pv = &cl.playerview[pnum]; + playerview_t *pv = &cl.playerview[seat]; pv->sb_hexen2_item_time = realtime; while (tries-- > 0) { @@ -832,8 +832,9 @@ static void Sbar_Hexen2InvRight_f(void) } static void Sbar_Hexen2InvUse_f(void) { + int seat = CL_TargettedSplit(false); #ifdef CSQC_DAT - if (CSQC_ConsoleCommand(Cmd_Argv(0))) + if (CSQC_ConsoleCommand(seat, Cmd_Argv(0))) return; #endif @@ -843,43 +844,46 @@ static void Sbar_Hexen2InvUse_f(void) } else { - int pnum = CL_TargettedSplit(false); - playerview_t *pv = &cl.playerview[pnum]; + playerview_t *pv = &cl.playerview[seat]; Cmd_ExecuteString(va("impulse %d\n", 100+pv->sb_hexen2_cur_item), Cmd_ExecLevel); } } static void Sbar_Hexen2ShowInfo_f(void) { - playerview_t *pv = &cl.playerview[CL_TargettedSplit(false)]; + int seat = CL_TargettedSplit(false); + playerview_t *pv = &cl.playerview[seat]; #ifdef CSQC_DAT - if (CSQC_ConsoleCommand(Cmd_Argv(0))) + if (CSQC_ConsoleCommand(seat, Cmd_Argv(0))) return; #endif pv->sb_hexen2_extra_info = true; } static void Sbar_Hexen2DontShowInfo_f(void) { - playerview_t *pv = &cl.playerview[CL_TargettedSplit(false)]; + int seat = CL_TargettedSplit(false); + playerview_t *pv = &cl.playerview[seat]; #ifdef CSQC_DAT - if (CSQC_ConsoleCommand(Cmd_Argv(0))) + if (CSQC_ConsoleCommand(seat, Cmd_Argv(0))) return; #endif pv->sb_hexen2_extra_info = false; } static void Sbar_Hexen2PInfoPlaque_f(void) { - playerview_t *pv = &cl.playerview[CL_TargettedSplit(false)]; + int seat = CL_TargettedSplit(false); + playerview_t *pv = &cl.playerview[seat]; #ifdef CSQC_DAT - if (CSQC_ConsoleCommand(Cmd_Argv(0))) + if (CSQC_ConsoleCommand(seat, Cmd_Argv(0))) return; #endif pv->sb_hexen2_infoplaque = true; } static void Sbar_Hexen2MInfoPlaque_f(void) { - playerview_t *pv = &cl.playerview[CL_TargettedSplit(false)]; + int seat = CL_TargettedSplit(false); + playerview_t *pv = &cl.playerview[seat]; #ifdef CSQC_DAT - if (CSQC_ConsoleCommand(Cmd_Argv(0))) + if (CSQC_ConsoleCommand(seat, Cmd_Argv(0))) return; #endif pv->sb_hexen2_infoplaque = false; @@ -906,7 +910,7 @@ void Sbar_DontShowScores (void) sb_updates = 0; #ifdef CSQC_DAT - if (CSQC_ConsoleCommand(Cmd_Argv(0))) + if (CSQC_ConsoleCommand(seat, Cmd_Argv(0))) return; #endif } diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 80e2c3702..411cd4c72 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -2518,7 +2518,7 @@ void Cmd_ExecuteString (const char *text, int level) if (Cmd_AliasExist(cmd_argv[0], level)) break; //server stuffed an alias for a command that it would already have received. use that instead. #if defined(CSQC_DAT) && !defined(SERVERONLY) - if (CSQC_ConsoleCommand(text)) + if (CSQC_ConsoleCommand(-1, text)) return; //let the csqc handle it if it wants. #endif #if defined(MENU_DAT) && !defined(SERVERONLY) @@ -2679,7 +2679,7 @@ void Cmd_ExecuteString (const char *text, int level) } #if defined(CSQC_DAT) && !defined(SERVERONLY) - if (CSQC_ConsoleCommand(text)) + if (CSQC_ConsoleCommand(-1, text)) return; #endif #if defined(MENU_DAT) && !defined(SERVERONLY) diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 9a26e6879..c0311ac10 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -2868,7 +2868,6 @@ void Mod_LoadAliasShaders(model_t *mod) } for (i = 0; i < numskins; i++) { - shader_t *result = NULL; skinid_t skinid; skinfile_t *skinfile; char *filedata; diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index 7fd30a42c..d5bb64371 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -1001,6 +1001,7 @@ void QCC_PR_EmitArrayGetFunction(QCC_def_t *defscope, QCC_def_t *thearray, char void QCC_PR_EmitArraySetFunction(QCC_def_t *defscope, QCC_def_t *thearray, char *arrayname); void QCC_PR_EmitClassFromFunction(QCC_def_t *defscope, QCC_type_t *basetype); +void QCC_PR_ParseDefs (char *classname, pbool fatal); QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, const char *name, QCC_function_t *scope, int arraysize, QCC_def_t *rootsymbol, unsigned int ofs, int referable, unsigned int flags); void QCC_PR_ParseInitializerDef(QCC_def_t *def, unsigned int flags); void QCC_PR_FinaliseFunctions(void); diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 66a044e1d..93375e126 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -269,8 +269,6 @@ unsigned int locals_marshalled; // largest local block size that needs to be a jmp_buf pr_parse_abort; // longjump with this on parse error -void QCC_PR_ParseDefs (char *classname, pbool fatal); - pbool qcc_usefulstatement; pbool debug_armour_defined; diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index d5f6d4331..22ecb7475 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -5282,8 +5282,6 @@ void QCC_FinishCompile(void) extern char *pr_file_p; extern int pr_source_line; -void QCC_PR_ParseDefs (char *classname); - @@ -5386,7 +5384,7 @@ void new_QCC_ContinueCompile(void) pr_scope = NULL; // outside all functions - QCC_PR_ParseDefs (NULL); + QCC_PR_ParseDefs (NULL, false); } /*void new_QCC_ContinueCompile(void) diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 7b9b1c63d..42168976a 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -10387,6 +10387,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"clearscene", PF_Fixme, 0, 0, 0, 300, D("void()", "Forgets all rentities, polygons, and temporary dlights. Resets all view properties to their default values.")},// (EXT_CSQC) {"addentities", PF_Fixme, 0, 0, 0, 301, D("void(float mask)", "Walks through all entities effectively doing this:\n if (ent.drawmask&mask){ if (!ent.predaw()) addentity(ent); }\nIf mask&MASK_DELTA, non-csqc entities, particles, and related effects will also be added to the rentity list.\n If mask&MASK_STDVIEWMODEL then the default view model will also be added.")},// (EXT_CSQC) {"addentity", PF_Fixme, 0, 0, 0, 302, D("void(entity ent)", "Copies the entity fields into a new rentity for later rendering via addscene.")},// (EXT_CSQC) + {"removeentity", PF_Fixme, 0, 0, 0, 0, D("void(entity ent)", "Undoes all addentities with that entity, without removing ALL entities (useful for splitscreen).")},// (EXT_CSQC) {"addtrisoup_simple",PF_Fixme, 0, 0, 0, 0, D("typedef float vec2[2];\ntypedef float vec3[3];\ntypedef float vec4[4];\ntypedef struct trisoup_simple_vert_s {vec3 xyz;vec2 st;vec4 rgba;} trisoup_simple_vert_t;\nvoid(string texturename, int flags, struct trisoup_simple_vert_s *verts, int *indexes, int numindexes)", "Adds the specified trisoup into the scene as additional geometry. This permits caching geometry to reduce builtin spam. Indexes are a triangle list (so eg quads will need 6 indicies to form two triangles). NOTE: this is not going to be a speedup over polygons if you're still generating lots of new data every frame.")}, {"setproperty", PF_Fixme, 0, 0, 0, 303, D("#define setviewprop setproperty\nfloat(float property, ...)", "Allows you to override default view properties like viewport, fov, and whether the engine hud will be drawn. Different VF_ values have slightly different arguments, some are vectors, some floats.")},// (EXT_CSQC) {"renderscene", PF_Fixme, 0, 0, 0, 304, D("void()", "Draws all entities, polygons, and particles on the rentity list (which were added via addentities or addentity), using the various view properties set via setproperty. There is no ordering dependancy.\nThe scene must generally be cleared again before more entities are added, as entities will persist even over to the next frame.\nYou may call this builtin multiple times per frame, but should only be called from CSQC_UpdateView.")},// (EXT_CSQC) diff --git a/engine/server/server.h b/engine/server/server.h index 059ada127..88d0c95cb 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -403,10 +403,11 @@ enum PRESPAWN_INVALID=0, PRESPAWN_PROTOCOLSWITCH, //nq drops unreliables until reliables are acked. this gives us a chance to drop any clc_move packets with formats from the previous map PRESPAWN_SERVERINFO, - PRESPAWN_SOUNDLIST, //nq skips these - PRESPAWN_VWEPMODELLIST, //qw ugly extension. + PRESPAWN_CSPROGS, //demos contain a copy of the csprogs. + PRESPAWN_SOUNDLIST, //nq skips these + PRESPAWN_VWEPMODELLIST, //qw ugly extension. PRESPAWN_MODELLIST, - PRESPAWN_MAPCHECK, //wait for old prespawn command + PRESPAWN_MAPCHECK, //wait for old prespawn command PRESPAWN_PARTICLES, PRESPAWN_CUSTOMTENTS, PRESPAWN_SIGNON_BUF, diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 2801948a7..ee15cb81c 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -3567,7 +3567,7 @@ qboolean SVC_ThrottleInfo (void) { #define THROTTLE_PPS 20 static unsigned int blockuntil; - unsigned int curtime, i, inc = 1000/THROTTLE_PPS; + unsigned int curtime, inc = 1000/THROTTLE_PPS; if (Net_AddressIsMaster(&net_from)) return true; //allow it without contributing to any throttling. diff --git a/engine/server/sv_mvd.c b/engine/server/sv_mvd.c index 5ed440107..26094246d 100644 --- a/engine/server/sv_mvd.c +++ b/engine/server/sv_mvd.c @@ -43,6 +43,7 @@ cvar_t sv_demoMaxSize = CVARD("sv_demoMaxSize", "", "Demos will be truncated to cvar_t sv_demoExtraNames = CVAR("sv_demoExtraNames", ""); cvar_t sv_demoExtensions = CVARD("sv_demoExtensions", "", "Enables protocol extensions within MVDs. This will cause older/non-fte clients to error upon playback.\n0: off.\n1: all extensions.\n2: extensions also supported by a certain other engine."); cvar_t sv_demoAutoCompress = CVARD("sv_demoAutoCompress", "", "Specifies whether to compress demos as they're recorded.\n0 = no compression.\n1 = gzip compression."); +cvar_t sv_demo_write_csqc = CVARD("sv_demo_write_csqc", "", "Writes a copy of the csprogs into recorded demos. This ensures that the demo can be played back despite future gamecode changes."); cvar_t qtv_password = CVAR( "qtv_password", ""); cvar_t qtv_maxstreams = CVARAFD( "qtv_maxstreams", "0", @@ -1181,6 +1182,7 @@ void MVD_Init (void) Cvar_Register (&sv_demoExtraNames, MVDVARGROUP); Cvar_Register (&sv_demoExtensions, MVDVARGROUP); Cvar_Register (&sv_demoAutoCompress,MVDVARGROUP); + Cvar_Register (&sv_demo_write_csqc,MVDVARGROUP); } static char *SV_PrintTeams(void) @@ -1646,7 +1648,7 @@ void SV_MVD_SendInitialGamestate(mvddest_t *dest) demo.resetdeltas = true; host_client = &demo.recorder; - if (host_client->fteprotocolextensions & PEXT_CSQC) + if (demo.recorder.fteprotocolextensions & PEXT_CSQC) SV_EnableClientsCSQC(); @@ -1670,6 +1672,19 @@ void SV_MVD_SendInitialGamestate(mvddest_t *dest) if (!gamedir[0]) gamedir = FS_GetGamedir(true); + //generate some meta info so the file can be identified later + { + char timestr[64]; + time_t t; + MSG_WriteByte (&buf, svc_stufftext); + MSG_WriteString(&buf, va("//protocolname %s\n", com_protocolname.string)); //so that the game is known when playing back, to deal with games that conventionally have entirely separate installations. + + MSG_WriteByte (&buf, svc_stufftext); + t = time(NULL); + strftime(timestr, sizeof(timestr), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t)); + MSG_WriteString(&buf, va("//recorddate %s\n", timestr)); //in order to avoid needing to depend upon file times that get destroyed in many different ways. + } + MSG_WriteByte (&buf, svc_serverdata); //fix up extensions to match sv_bigcoords correctly. sorry for old clients not working. diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index c4b9acb4c..f1cda9c4c 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -705,6 +705,10 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int int j; qboolean reliable; client_t *oneclient = NULL, *split; + int seat; + + if (!sv.multicast.cursize) + return; if (to == MULTICAST_INIT) { @@ -807,7 +811,7 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int if (client->controller) continue; //FIXME: send if at least one of the players is near enough. - for (split = client; split; split = split->controlled) + for (split = client, seat = 0; split; split = split->controlled, seat++) { if (client->protocol == SCP_QUAKEWORLD) { @@ -902,11 +906,26 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int case SCP_QUAKEWORLD: if (reliable) { - ClientReliableCheckBlock(client, sv.multicast.cursize); + if (oneclient && seat) + { + ClientReliableCheckBlock(client, 2+sv.multicast.cursize); + ClientReliableWrite_Byte(client, svcfte_choosesplitclient); + ClientReliableWrite_Byte(client, seat); + } + else + ClientReliableCheckBlock(client, sv.multicast.cursize); + ClientReliableWrite_SZ(client, sv.multicast.data, sv.multicast.cursize); } else + { + if (oneclient && seat) + { + MSG_WriteByte (&client->datagram, svcfte_choosesplitclient); + MSG_WriteByte (&client->datagram, seat); + } SZ_Write (&client->datagram, sv.multicast.data, sv.multicast.cursize); + } break; } } @@ -976,7 +995,7 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int if (client->controller) continue; - for (split = client; split; split = split->controlled) + for (split = client, seat = 0; split; split = split->controlled, seat++) { if (split->protocol == SCP_QUAKEWORLD) { @@ -1078,11 +1097,26 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int case SCP_QUAKEWORLD: if (reliable) { - ClientReliableCheckBlock(client, sv.multicast.cursize); + if (oneclient && seat) + { + ClientReliableCheckBlock(client, 2+sv.multicast.cursize); + ClientReliableWrite_Byte(client, svcfte_choosesplitclient); + ClientReliableWrite_Byte(client, seat); + } + else + ClientReliableCheckBlock(client, sv.multicast.cursize); + ClientReliableWrite_SZ(client, sv.multicast.data, sv.multicast.cursize); } else + { + if (oneclient && seat) + { + MSG_WriteByte (&client->datagram, svcfte_choosesplitclient); + MSG_WriteByte (&client->datagram, seat); + } SZ_Write (&client->datagram, sv.multicast.data, sv.multicast.cursize); + } break; } } diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 87f0de51b..159389b54 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -1056,6 +1056,71 @@ void SV_SendClientPrespawnInfo(client_t *client) } } + if (client->prespawn_stage == PRESPAWN_CSPROGS) + { + extern cvar_t sv_demo_write_csqc; + if (client == &demo.recorder && sv_demo_write_csqc.ival) //we only really want to do this for demos. actual clients can make the request themselves. + if (client->fteprotocolextensions & PEXT_CHUNKEDDOWNLOADS) //there's many different download mechanisms... + { + if (!client->prespawn_idx && !client->download) + { + extern cvar_t sv_csqc_progname; + Q_strncpyz(client->downloadfn, sv_csqc_progname.string, sizeof(client->downloadfn)); + client->download = FS_OpenVFS(sv_csqc_progname.string, "rb", FS_GAME); + client->downloadcount = 0; + client->prespawn_idx = 1; + if (client->download) + { + client->downloadsize = VFS_GETLEN(client->download); + + //send the size+filename + ClientReliableWrite_Begin (client, svc_download, 18+strlen(client->downloadfn)); + ClientReliableWrite_Long (client, -1); //offset + if (client->downloadsize >= 0x7fffffff) + { //avoid unsigned values. + ClientReliableWrite_Long (client, 0x80000000); //signal that its 64bit + ClientReliableWrite_Long (client, qofs_Low(client->downloadsize)); + ClientReliableWrite_Long (client, qofs_High(client->downloadsize)); + } + else + ClientReliableWrite_Long (client, client->downloadsize); + ClientReliableWrite_String (client, client->downloadfn); + } + } + //send the data while possible+needed + if (client->prespawn_idx && client->download) + { + while (client->downloadcount < client->downloadsize) + { + qbyte chunk[DLBLOCKSIZE]; + int sz; + if (client->netchan.message.maxsize - client->netchan.message.cursize < 1100) + return; //don't flood... + sz = VFS_READ(client->download, chunk, DLBLOCKSIZE); + if (sz <= 0) + break; + if (sz < DLBLOCKSIZE) + { + memset(chunk+sz, 0, DLBLOCKSIZE-sz); //zero-fill if the chunk is at the end. + sz = DLBLOCKSIZE; + } + + ClientReliableWrite_Begin (client, svc_download, 5+sz); + ClientReliableWrite_Long(client, client->downloadcount/DLBLOCKSIZE); + ClientReliableWrite_SZ(client, chunk, sz); + client->downloadcount += sz; + } + + //don't need to write completion. the client should be tracking that itself with chunks. + VFS_CLOSE(client->download); + client->download = NULL; + } + } + + client->prespawn_stage++; + client->prespawn_idx = 0; + } + if (client->prespawn_stage == PRESPAWN_SOUNDLIST) { if (!ISQWCLIENT(client)) @@ -2239,9 +2304,8 @@ void SV_DarkPlacesDownloadAck(client_t *cl) static void SV_NextChunkedDownload(unsigned int chunknum, int ezpercent, int ezfilenum, int chunks) { -#define CHUNKSIZE 1024 - char buffer[CHUNKSIZE]; - qbyte oobdata[1+ (sizeof("\\chunk")-1) + 4 + 1 + 4 + CHUNKSIZE]; + char buffer[DLBLOCKSIZE]; + qbyte oobdata[1+ (sizeof("\\chunk")-1) + 4 + 1 + 4 + DLBLOCKSIZE]; sizebuf_t *msg, msg_oob; int i; int error = false; @@ -2252,24 +2316,24 @@ static void SV_NextChunkedDownload(unsigned int chunknum, int ezpercent, int ezf if (chunknum == -1) error = 2; //silent, don't report it - else if (chunknum*CHUNKSIZE > host_client->downloadsize) + else if (chunknum*DLBLOCKSIZE > host_client->downloadsize) { - SV_ClientTPrintf (host_client, PRINT_HIGH, "Warning: Invalid file chunk requested %u to %u of %u.\n", chunknum*CHUNKSIZE, (chunknum+1)*CHUNKSIZE, host_client->downloadsize); + SV_ClientTPrintf (host_client, PRINT_HIGH, "Warning: Invalid file chunk requested %u to %u of %u.\n", chunknum*DLBLOCKSIZE, (chunknum+1)*DLBLOCKSIZE, host_client->downloadsize); error = 2; } - if (!error && VFS_SEEK (host_client->download, (qofs_t)chunknum*CHUNKSIZE) == false) + if (!error && VFS_SEEK (host_client->download, (qofs_t)chunknum*DLBLOCKSIZE) == false) error = true; else { - if (host_client->downloadcount < chunknum*CHUNKSIZE) - host_client->downloadcount = chunknum*CHUNKSIZE; + if (host_client->downloadcount < chunknum*DLBLOCKSIZE) + host_client->downloadcount = chunknum*DLBLOCKSIZE; } while (!error && chunks > 0) { - if ((host_client->datagram.cursize + CHUNKSIZE+5+50 > host_client->datagram.maxsize) || (host_client->datagram.cursize + CHUNKSIZE+5 > 1400)) + if ((host_client->datagram.cursize + DLBLOCKSIZE+5+50 > host_client->datagram.maxsize) || (host_client->datagram.cursize + DLBLOCKSIZE+5 > 1400)) { //would overflow the packet, or result in (ethernet) fragmentation and high packet loss. msg = &msg_oob; @@ -2284,7 +2348,7 @@ static void SV_NextChunkedDownload(unsigned int chunknum, int ezpercent, int ezf return; } - i = VFS_READ (host_client->download, buffer, CHUNKSIZE); + i = VFS_READ (host_client->download, buffer, DLBLOCKSIZE); if (i > 0) { @@ -2303,12 +2367,12 @@ static void SV_NextChunkedDownload(unsigned int chunknum, int ezpercent, int ezf MSG_WriteLong(msg, ezfilenum); //echoing the file num is used so the packets don't go out of sync. } - if (i != CHUNKSIZE) - memset(buffer+i, 0, CHUNKSIZE-i); + if (i != DLBLOCKSIZE) + memset(buffer+i, 0, DLBLOCKSIZE-i); MSG_WriteByte(msg, svc_download); MSG_WriteLong(msg, chunknum); - SZ_Write(msg, buffer, CHUNKSIZE); + SZ_Write(msg, buffer, DLBLOCKSIZE); if (msg == &msg_oob) { @@ -3422,8 +3486,7 @@ void SV_BeginDownload_f(void) } else #endif - - if (ISNQCLIENT(host_client)) + if (ISNQCLIENT(host_client)) { //FIXME support 64bit files char *s = va("\ncl_downloadbegin %u %s\n", (unsigned int)host_client->downloadsize, host_client->downloadfn); @@ -7363,6 +7426,7 @@ void SV_ReadQCRequest(void) func_t f; int i; globalvars_t *pr_globals; + client_t *cl = host_client; if (!svprogfuncs) { @@ -7372,19 +7436,31 @@ void SV_ReadQCRequest(void) pr_globals = PR_globals(svprogfuncs, PR_CURRENT); - for (i = 0; ; i++) + for (i = 0; ; ) { + qbyte ev = MSG_ReadByte(); + if (ev >= 200 && ev < 200+MAX_SPLITS) + { + ev -= 200; + while (ev-- && cl) + cl = cl->controlled; + continue; + } if (i >= sizeof(args)-1) { - if (MSG_ReadByte() != ev_void) + if (ev != ev_void) { msg_badread = true; return; } goto done; } - switch(MSG_ReadByte()) + switch(ev) { + default: + args[i] = '?'; + G_INT(OFS_PARM0+i*3) = MSG_ReadLong(); + break; case ev_void: goto done; case ev_float: @@ -7413,6 +7489,7 @@ void SV_ReadQCRequest(void) G_INT(OFS_PARM0+i*3) = EDICT_TO_PROG(svprogfuncs, EDICT_NUM(svprogfuncs, e)); break; } + i++; } done: @@ -7431,9 +7508,11 @@ done: rname = va("Cmd_%s", rname); f = PR_FindFunction(svprogfuncs, rname, PR_ANY); } - if (f) + if (!cl) + ; //bad seat! not going to warn as they might have been removed recently + else if (f) { - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict); PR_ExecuteProgram(svprogfuncs, f); } else diff --git a/plugins/irc/ircclient.c b/plugins/irc/ircclient.c index 99a2b1d7d..88badb6b6 100644 --- a/plugins/irc/ircclient.c +++ b/plugins/irc/ircclient.c @@ -475,6 +475,17 @@ void IRC_AddClientMessage(ircclient_t *irc, char *msg) if (irc_debug.value == 1) { IRC_Printf(irc, DEFAULTCONSOLE,COLOURYELLOW "<< %s \n",msg); } } +ircclient_t *IRC_FindAccount(const char *server) +{ + ircclient_t *irc; + for (irc = ircclients; irc; irc = irc->next) + { + if (!strcmp(irc->server, server)) + return irc; + } + return NULL; //no match +} + ircclient_t *IRC_Create(const char *server, const char *nick, const char *realname, const char *hostname, const char *password, const char *channels) { ircclient_t *irc; @@ -2328,6 +2339,12 @@ void IRC_Command(ircclient_t *ircclient, char *dest) if (!*nick) pCvar_GetString("name", nick, sizeof(nick)); + if (IRC_FindAccount(server)) + { + IRC_Printf(ircclient, dest, "IRC connection to %s already registered\n"); + return; //silently ignore it if the account already exists + } + ircclient = IRC_Create(server, nick, defaultuser, irc_hostname.string, password, channels); if (ircclient) {