From 7d0e84fe951ad0533c50aeaed07f17c70a061629 Mon Sep 17 00:00:00 2001 From: drian Date: Wed, 14 Apr 2021 09:19:22 +0200 Subject: [PATCH] #108 merged 'Pause' PR - work in progress --- source/src/clientgame.cpp | 48 +++++++++++++++++++++++++++++++------- source/src/clients2c.cpp | 12 ++++++++++ source/src/cube.h | 1 + source/src/main.cpp | 6 ++++- source/src/physics.cpp | 9 ++++--- source/src/protocol.cpp | 1 + source/src/protocol.h | 3 ++- source/src/renderhud.cpp | 2 +- source/src/server.cpp | 32 +++++++++++++++++++++++-- source/src/server.h | 4 ++++ source/src/serveractions.h | 11 +++++++++ source/src/weapon.cpp | 7 +++--- 12 files changed, 114 insertions(+), 22 deletions(-) diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index c6f859b4..0f6a9572 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -29,6 +29,7 @@ int lasthit = 0; int curtime = 0; string clientmap = ""; int spawnpermission = SP_WRONGMAP; +int ispaused = 0; char *getclientmap() { return clientmap; } @@ -218,6 +219,7 @@ COMMAND(curmodeattr, "s"); COMMANDF(isclient, "i", (int *cn) { intret(getclient(*cn) != NULL ? 1 : 0); } ); COMMANDF(curmastermode, "", (void) { intret(servstate.mastermode); }); COMMANDF(curautoteam, "", (void) { intret(servstate.autoteam); }); +COMMANDF(curpausemode, "", (void) { intret(ispaused); }); COMMANDF(curmap, "", (void) { result(behindpath(getclientmap())); }); COMMANDF(curplayers, "", (void) { intret(players.length() + 1); }); VARP(showscoresondeath, 0, 1, 1); @@ -517,6 +519,7 @@ void zapplayer(playerent *&d) void movelocalplayer() { + if(ispaused && player1->state == CS_ALIVE) return; if(!player1->allowmove()) { if(lastmillis-player1->lastpain<2000) @@ -565,7 +568,7 @@ void moveotherplayers() { playerent *d = players[i]; const int lagtime = totalmillis-d->lastupdate; - if(!lagtime || intermission) continue; + if(!lagtime || intermission || ispaused) continue; else if(lagtime>1000 && d->state==CS_ALIVE) { d->state = CS_LAGGED; @@ -613,7 +616,7 @@ int lastspawnattempt = 0; void showrespawntimer() { - if(intermission || spawnpermission > SP_OK_NUM) return; + if(intermission || ispaused || spawnpermission > SP_OK_NUM) return; if(m_arena) { if(!arenaintermission) return; @@ -902,7 +905,7 @@ COMMANDF(burstshots, "si", (char *gun, int *shots) void dodamage(int damage, playerent *pl, playerent *actor, int gun, bool gib, bool local) { - if(pl->state != CS_ALIVE || intermission) return; + if(pl->state != CS_ALIVE || intermission || ispaused) return; pl->lastpain = lastmillis; playerent *h = player1->isspectating() && player1->followplayercn >= 0 && (player1->spectatemode == SM_FOLLOW1ST || player1->spectatemode == SM_FOLLOW3RD || player1->spectatemode == SM_FOLLOW3RD_TRANSPARENT) ? getclient(player1->followplayercn) : NULL; if(!h) h = player1; @@ -1004,6 +1007,7 @@ void silenttimeupdate(int milliscur, int millismax) void timeupdate(int milliscur, int millismax) { + if (ispaused) return; static int lastgametimedisplay = 0; silenttimeupdate(milliscur, millismax); @@ -1183,6 +1187,7 @@ void startmap(const char *name, bool reset, bool norespawn) // called just aft minutesremaining = -1; lastgametimeupdate = 0; arenaintermission = 0; + ispaused = 0; bool noflags = (m_ctf || m_ktf) && !clentstats.hasflags; if(*clientmap) conoutf("game mode is \"%s\"%s", modestr(gamemode, modeacronyms > 0), noflags ? " - \f2but there are no flag bases on this map" : ""); @@ -1313,7 +1318,7 @@ COMMAND(dropflag, ""); char *votestring(int type, const char *arg1, const char *arg2, const char *arg3) { - const char *msgs[] = { "kick player %s, reason: %s", "ban player %s, reason: %s", "remove all bans", "set mastermode to %s", "%s autoteam", "force player %s to team %s", "give admin to player %s", "load map %s in mode %s%s%s", "%s demo recording for the next match", "stop demo recording", "clear all demos", "set server description to '%s'", "shuffle teams"}; + const char *msgs[] = { "kick player %s, reason: %s", "ban player %s, reason: %s", "remove all bans", "set mastermode to %s", "%s autoteam", "force player %s to team %s", "give admin to player %s", "load map %s in mode %s%s%s", "%s demo recording for the next match", "stop demo recording", "clear all demos", "set server description to '%s'", "shuffle teams", "%s the game" }; const char *msg = msgs[type]; char *out = newstring(MAXSTRLEN); out[MAXSTRLEN] = '\0'; @@ -1372,6 +1377,9 @@ char *votestring(int type, const char *arg1, const char *arg2, const char *arg3) case SA_SERVERDESC: formatstring(out)(msg, arg1); break; + case SA_PAUSE: + formatstring(out)(msg, atoi(arg1) == 1 ? "pause" : "resume"); + break; default: formatstring(out)(msg, arg1, arg2); break; @@ -1429,11 +1437,10 @@ void callvote(int type, const char *arg1, const char *arg2, const char *arg3) putint(p, atoi(arg1)); putint(p, atoi(arg2)); break; + case SA_PAUSE: + putint(p, atoi(arg1)); + break; default: - - - - putint(p, atoi(arg1)); break; } @@ -1458,6 +1465,18 @@ void scallvote(int *type, const char *arg1, const char *arg2) callvote(t, arg1, m, arg2); break; } + case SA_PAUSE: + { + if (!arg1 || !isdigit(arg1[0]) || !multiplayer(false)) + { + if(!multiplayer(false)) + conoutf("\f3pause is not available in singleplayer."); + else if (arg1 && !isdigit(arg1[0])) conoutf("\f3invalid vote"); + break; + } + callvote(t, arg1, arg2); + break; + } case SA_KICK: case SA_BAN: { @@ -1593,6 +1612,19 @@ COMMAND(gonext, "i"); COMMANDN(callvote, scallvote, "iss"); //fixme,ah COMMANDF(vote, "i", (int *v) { vote(*v); }); +void cmd_pause(int* arg1) +{ + if (*arg1 != 0 && *arg1 != 1) return; + if (servstate.mastermode != MM_MATCH) + { + conoutf("You may only pause the game in mastermode match."); + return; + } + defformatstring(m)("%d", *arg1); + callvote(SA_PAUSE, m, "-1", "0"); +} +COMMANDN(pause, cmd_pause, "i"); + void cleanplayervotes(playerent *p) { if(calledvote && calledvote->owner==p) calledvote->owner = NULL; diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index 4d5eb65f..be66ba72 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -8,6 +8,7 @@ VARP(networkdebug, 0, 0, 1); extern bool watchingdemo; extern string clientpassword; +extern int ispaused; void *downloaddemomenu = NULL; static vector demo_mlines; @@ -1135,6 +1136,13 @@ void parsemessages(int cn, playerent *d, ucharbuf &p, bool demo = false) break; } + case SV_PAUSEMODE: + { + ispaused = getint(p); + if (player1->state == CS_ALIVE) player1->attacking = false; + break; + } + case SV_CALLVOTE: { int type = getint(p); @@ -1189,6 +1197,10 @@ void parsemessages(int cn, playerent *d, ucharbuf &p, bool demo = false) itoa(a2, getint(p)); v = newvotedisplayinfo(d, type, a1, a2); break; + case SA_PAUSE: + itoa(a1, getint(p)); + v = newvotedisplayinfo(d, type, a1, NULL); + break; default: itoa(a1, getint(p)); v = newvotedisplayinfo(d, type, a1, NULL); diff --git a/source/src/cube.h b/source/src/cube.h index 2f41fd9e..d30b214f 100644 --- a/source/src/cube.h +++ b/source/src/cube.h @@ -51,6 +51,7 @@ extern int farplane; extern bool minimap, reflecting, refracting; extern int stenciling, stencilshadow, effective_stencilshadow; extern bool intermission; +extern int ispaused; extern int arenaintermission; extern hashtable mapinfo; extern int hwtexsize, hwmaxaniso; diff --git a/source/src/main.cpp b/source/src/main.cpp index d7b7d86a..2895b693 100644 --- a/source/src/main.cpp +++ b/source/src/main.cpp @@ -1498,7 +1498,11 @@ int main(int argc, char **argv) limitfps(millis, totalmillis); int elapsed = millis-totalmillis; entropy_add_byte(elapsed); - if(multiplayer(NULL)) curtime = elapsed; + if(multiplayer(false)) + { + if(ispaused) curtime = 0; + else curtime = elapsed; + } else { static int timeerr = 0; diff --git a/source/src/physics.cpp b/source/src/physics.cpp index fe9c057c..4fbe343f 100644 --- a/source/src/physics.cpp +++ b/source/src/physics.cpp @@ -767,8 +767,7 @@ void moveplayer(physent *pl, int moveres, bool local, int curtime) // apply volume-resize when crouching if(pl->type==ENT_PLAYER || pl->type==ENT_BOT) { -// if(pl==player1 && !(intermission || player1->onladder || (pl->trycrouch && !player1->onfloor && player1->timeinair > 50))) updatecrouch(player1, player1->trycrouch); - if(!intermission && (pl == player1 || pl->type == ENT_BOT)) updatecrouch((playerent *)pl, pl->trycrouch); + if(!intermission && !ispaused && (pl == player1 || pl->type == ENT_BOT)) updatecrouch((playerent *)pl, pl->trycrouch); const float croucheyeheight = pl->maxeyeheight*3.0f/4.0f; resizephysent(pl, moveres, curtime, croucheyeheight, pl->maxeyeheight); } @@ -848,7 +847,7 @@ dir(right, strafe, -1, k_right, k_left) void attack(bool on) { - if(intermission) return; + if(intermission || (ispaused && player1->state == CS_ALIVE)) return; if(editmode) editdrag(on); else if(player1->state==CS_DEAD || player1->state==CS_SPECTATE) { @@ -860,7 +859,7 @@ void attack(bool on) void jumpn(bool on) { static bool wason = false; - player1->jumpnext = on && !wason && !player1->crouching && !intermission && !player1->isspectating(); + player1->jumpnext = on && !wason && !player1->crouching && !intermission && !player1->isspectating() && !ispaused; wason = on; if(player1->isspectating()) { @@ -914,7 +913,7 @@ FVARP(mfilter, 0.0f, 0.0f, 6.0f); // simple lowpass filtering (zer void mousemove(int idx, int idy) { - if(intermission || (player1->isspectating() && player1->spectatemode==SM_FOLLOW1ST)) return; + if(intermission || (ispaused && player1->state == CS_ALIVE) || (player1->isspectating() && player1->spectatemode==SM_FOLLOW1ST)) return; bool zooming = player1->weaponsel->type == GUN_SNIPER && ((sniperrifle *)player1->weaponsel)->scoped; // check if player uses scope float dx = idx, dy = idy; if(mfilter > 0.0001f) diff --git a/source/src/protocol.cpp b/source/src/protocol.cpp index a919f1ca..cc3a6b9d 100644 --- a/source/src/protocol.cpp +++ b/source/src/protocol.cpp @@ -604,6 +604,7 @@ static const int msgsizes[] = // size inclusive message token, 0 f SV_CLIENT, 0, SV_EXTENSION, 0, SV_MAPIDENT, 3, SV_DEMOCHECKSUM, 0, SV_DEMOSIGNATURE, 0, + SV_PAUSEMODE, 1, -1 }; diff --git a/source/src/protocol.h b/source/src/protocol.h index ae70d067..9c27ce46 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -49,6 +49,7 @@ enum SV_CLIENT, SV_EXTENSION, SV_MAPIDENT, SV_DEMOCHECKSUM, SV_DEMOSIGNATURE, + SV_PAUSEMODE, SV_NUM }; @@ -60,7 +61,7 @@ extern void protocoldebug(bool enable); extern const char *messagenames[SV_NUM]; #endif -enum { SA_KICK = 0, SA_BAN, SA_REMBANS, SA_MASTERMODE, SA_AUTOTEAM, SA_FORCETEAM, SA_GIVEADMIN, SA_MAP, SA_RECORDDEMO, SA_STOPDEMO, SA_CLEARDEMOS, SA_SERVERDESC, SA_SHUFFLETEAMS, SA_NUM}; +enum { SA_KICK = 0, SA_BAN, SA_REMBANS, SA_MASTERMODE, SA_AUTOTEAM, SA_FORCETEAM, SA_GIVEADMIN, SA_MAP, SA_RECORDDEMO, SA_STOPDEMO, SA_CLEARDEMOS, SA_SERVERDESC, SA_SHUFFLETEAMS, SA_PAUSE, SA_NUM}; enum { VOTE_NEUTRAL = 0, VOTE_YES, VOTE_NO, VOTE_NUM }; enum { VOTEE_DISABLED = 0, VOTEE_CUR, VOTEE_MUL, VOTEE_MAX, VOTEE_AREA, VOTEE_PERMISSION, VOTEE_INVALID, VOTEE_WEAK, VOTEE_NEXT, VOTEE_NUM }; enum { MM_OPEN = 0, MM_PRIVATE, MM_MATCH, MM_NUM }; enum { MM_MASK = 0x03 }; diff --git a/source/src/renderhud.cpp b/source/src/renderhud.cpp index 084089ab..8bd3646f 100644 --- a/source/src/renderhud.cpp +++ b/source/src/renderhud.cpp @@ -902,7 +902,7 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat if(dimeditinfopanel > 255) dimeditinfopanel = 255; } - if(!intermission && lastgametimeupdate!=0) + if((!intermission && lastgametimeupdate!=0) || !ispaused) { int cssec = (gametimecurrent+(lastmillis-lastgametimeupdate))/1000; int gtsec = cssec%60; diff --git a/source/src/server.cpp b/source/src/server.cpp index 745c193e..dc797060 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -25,6 +25,7 @@ struct servergame string servdesc_current; ENetAddress servdesc_caller; bool custom_servdesc; + int sispaused = 0; // current game string smapname, nextmapname; @@ -592,6 +593,21 @@ void changemastermode(int newmode) } } +void setpausemode(int newmode) +{ + if (sg->sispaused != newmode) { + sg->sispaused = newmode; + } + + loopv(clients) if (clients[i]->type != ST_EMPTY) + { + if (!valid_client(i)) return; + clients[i]->ispaused = newmode; + } + + sendf(-1, 1, "ri2", SV_PAUSEMODE, newmode); +} + int findcnbyaddress(ENetAddress *address) { loopv(clients) @@ -2207,6 +2223,7 @@ void resetserver(const char *newname, int newmode, int newtime) else savedscores.shrink(0); ctfreset(); + sg->sispaused = false; sg->nextmapname[0] = '\0'; sg->forceintermission = false; } @@ -2596,6 +2613,9 @@ void callvotepacket (int cn, voteinfo *v = curvote) putint(q, v->num1); putint(q, v->num2); break; + case SA_PAUSE: + putint(q, v->num1); + break; default: putint(q, v->num1); break; @@ -2834,6 +2854,8 @@ void welcomepacket(packetbuf &p, int n) } putint(p, SV_SERVERMODE); putint(p, sendservermode(false)); + putint(p, SV_PAUSEMODE); + putint(p, sg->sispaused); const char *motd = scl.motd[0] ? scl.motd : serverinfomotd.getmsg(); if(*motd) { @@ -2872,7 +2894,7 @@ int checktype(int type, client *cl) SV_CALLVOTESUC, SV_CALLVOTEERR, SV_VOTERESULT, SV_SETTEAM, SV_TEAMDENY, SV_SERVERMODE, SV_IPLIST, SV_SENDDEMOLIST, SV_SENDDEMO, SV_DEMOPLAYBACK, - SV_CLIENT, SV_DEMOCHECKSUM }; + SV_CLIENT, SV_DEMOCHECKSUM, SV_PAUSEMODE }; // only allow edit messages in coop-edit mode static int edittypes[] = { SV_EDITENT, SV_EDITXY, SV_EDITARCH, SV_EDITBLOCK, SV_EDITD, SV_EDITE, SV_NEWMAP }; if(cl) @@ -3870,6 +3892,9 @@ void process(ENetPacket *packet, int sender, int chan) case SA_REMBANS: vi->action = new removebansaction(); break; + case SA_PAUSE: + vi->action = new pauseaction(vi->num1 = getint(p)); + break; case SA_MASTERMODE: vi->action = new mastermodeaction(vi->num1 = getint(p)); break; @@ -3935,6 +3960,9 @@ void process(ENetPacket *packet, int sender, int chan) QUEUE_MSG; break; + case SV_PAUSEMODE: + break; + case SV_EXTENSION: { // AC server extensions @@ -4119,7 +4147,7 @@ client &addclient() void checkintermission() { - if(sg->minremain>0) + if(sg->minremain>0 && !sg->sispaused) { sg->minremain = (sg->gamemillis >= sg->gamelimit || sg->forceintermission) ? 0 : (sg->gamelimit - sg->gamemillis + 60000 - 1) / 60000; sendf(-1, 1, "ri3", SV_TIMEUP, (sg->gamemillis >= sg->gamelimit || sg->forceintermission) ? sg->gamelimit : sg->gamemillis, sg->gamelimit); diff --git a/source/src/server.h b/source/src/server.h index e24d5d3f..9cb9fcb1 100644 --- a/source/src/server.h +++ b/source/src/server.h @@ -253,6 +253,7 @@ struct client // server side version of "dynent" type bool haswelcome; bool isonrightmap, loggedwrongmap, freshgame; bool timesync; + bool ispaused; int overflow; int gameoffset, lastevent, lastvotecall; int lastprofileupdate, fastprofileupdates; @@ -310,6 +311,7 @@ struct client // server side version of "dynent" type ldt = spj = 0; ffire = 0; f = yaw = pitch = t = 0; + ispaused = 0; } void reset() @@ -337,6 +339,7 @@ struct client // server side version of "dynent" type freshgame = false; // don't spawn into running games mute = spam = lastvc = badspeech = badmillis = nvotes = 0; input = inputmillis = 0; + ispaused = 0; } void zap() @@ -438,6 +441,7 @@ const char *messagenames[SV_NUM] = "SV_CLIENT", "SV_EXTENSION", "SV_MAPIDENT", "SV_DEMOCHECKSUM", "SV_DEMOSIGNATURE", + "SV_PAUSEMODE" }; const char *entnames[] = diff --git a/source/src/serveractions.h b/source/src/serveractions.h index c8367b3b..b0a86303 100644 --- a/source/src/serveractions.h +++ b/source/src/serveractions.h @@ -233,6 +233,17 @@ struct removebansaction : serveraction } }; +struct pauseaction : serveraction +{ + int mode; + void perform() { setpausemode(mode); } + pauseaction(int mode) : mode(mode) + { + role = roleconf('p'); + copystring(desc, (mode == 1) ? "pause the game" : "resume the game"); + } +}; + struct mastermodeaction : serveraction { int mode; diff --git a/source/src/weapon.cpp b/source/src/weapon.cpp index c9982d4b..bc513222 100644 --- a/source/src/weapon.cpp +++ b/source/src/weapon.cpp @@ -156,7 +156,7 @@ COMMANDF(magreserve, "s", (char *ws) void tryreload(playerent *p) { - if(!p || p->state!=CS_ALIVE || p->weaponsel->reloading || p->weaponchanging) return; + if(!p || p->state!=CS_ALIVE || ispaused || p->weaponsel->reloading || p->weaponchanging) return; p->weaponsel->reload(false); } @@ -659,6 +659,7 @@ void removebounceents(playerent *owner) void movebounceents() { + if(ispaused) return; loopv(bounceents) if(bounceents[i]) { bounceent *p = bounceents[i]; @@ -1038,11 +1039,9 @@ void weapon::renderhudmodel(int lastaction, int index) unitv.div(dist); weaponmove wm; - if(!intermission) wm.calcmove(unitv, lastaction, p); -// if(!intermission) wm.calcmove(unitv, p->lastaction, p); + if(!intermission || !ispaused) wm.calcmove(unitv, lastaction, p); defformatstring(path)("weapons/%s", info.modelname); bool emit = (wm.anim&ANIM_INDEX)==ANIM_GUN_SHOOT && (lastmillis - lastaction) < flashtime(); -// bool emit = (wm.anim&ANIM_INDEX)==ANIM_GUN_SHOOT && (lastmillis - p->lastaction) < flashtime(); rendermodel(path, wm.anim|ANIM_DYNALLOC|(righthanded==index ? ANIM_MIRROR : 0)|(emit ? ANIM_PARTICLE : 0), 0, -1, wm.pos, 0, p->yaw+90, p->pitch+wm.k_rot, 40.0f, wm.basetime, NULL, NULL, 1.28f); }