diff --git a/config/admin.cfg b/config/admin.cfg index aaa24894..15214b14 100644 --- a/config/admin.cfg +++ b/config/admin.cfg @@ -5,7 +5,7 @@ ban = [ callvote 1 $arg1 $arg2 ] removebans = [ callvote 2 $arg1 ] mastermode = [ callvote 3 $arg1 ] autoteam = [ callvote 4 $arg1 ] -forceteam = [ callvote 5 $arg1 ] +forceteam = [ callvote 5 $arg1 $arg2 ] forceme = [ forceteam (findcn $curname) ] ignoreall = [ if $connected [ ingall = 1; loop ia 21 [ ignore $ia ]; echo (c 1)NOTE: (c 5)All clients currently on the server were ignored. ] ] unignoreall = [ if $connected [ ingall = 0; clearignored ]; echo (c 1)NOTE: (c 5)All clients currently on the server were unignored. ] @@ -17,6 +17,7 @@ cleardemo = [ callvote 10 $arg1 ] cleardemos = [ callvote 10 0 ] serverdesc = [ callvote 11 $arg1 ] shuffleteams = [ callvote 12 ] +lockteams = [ callvote 13 $arg1 ] demo = [ _demofile = $arg1 diff --git a/source/src/client.cpp b/source/src/client.cpp index 7b1b9d11..48e78cf6 100644 --- a/source/src/client.cpp +++ b/source/src/client.cpp @@ -243,7 +243,7 @@ void _toserver(char *text, int msg, int msgt) if(toteam) text++; filtertext(text, text); trimtrailingwhitespace(text); - if(servstate.mastermode == MM_MATCH && servstate.matchteamsize && !team_isactive(player1->team) && !(player1->team == TEAM_SPECT && player1->clientrole == CR_ADMIN)) toteam = true; // spect chat + if(servstate.mastermode == MM_MATCH && !team_isactive(player1->team) && !(player1->team == TEAM_SPECT && player1->clientrole == CR_ADMIN)) toteam = true; // spect chat if(*text) { if(msg == SV_TEXTME) conoutf("\f%d%s %s", toteam ? 1 : 0, colorname(player1), highlight(text)); diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index 8b7f8ade..8bf7e434 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -1334,7 +1334,7 @@ COMMAND(dropflag, ARG_NONE); char *votestring(int type, const char *arg1, const char *arg2) { - 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 the enemy team", "give admin to player %s", "load map %s in mode %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 %s", "give admin to player %s", "load map %s in mode %s%s", "%s demo recording for the next match", "stop demo recording", "clear all demos", "set server description to '%s'", "shuffle teams", "%s teams" }; const char *msg = msgs[type]; char *out = newstring(MAXSTRLEN); out[MAXSTRLEN] = '\0'; @@ -1342,8 +1342,8 @@ char *votestring(int type, const char *arg1, const char *arg2) { case SA_KICK: case SA_BAN: - case SA_FORCETEAM: case SA_GIVEADMIN: + case SA_FORCETEAM: { int cn = atoi(arg1); playerent *p = (cn == getclientnum() ? player1 : getclient(cn)); @@ -1355,6 +1355,7 @@ char *votestring(int type, const char *arg1, const char *arg2) else formatstring(reason)("%s (ping %d)", arg2, p->ping); formatstring(out)(msg, colorname(p), reason); } + else if(type == SA_FORCETEAM) formatstring(out)(msg, colorname(p), arg2 && *arg2 ? arg2 : team_string(team_opposite(p->team))); else formatstring(out)(msg, colorname(p)); break; } @@ -1365,6 +1366,9 @@ char *votestring(int type, const char *arg1, const char *arg2) case SA_RECORDDEMO: formatstring(out)(msg, atoi(arg1) == 0 ? "disable" : "enable"); break; + case SA_LOCKTEAMS: + formatstring(out)(msg, atoi(arg1) == 0 ? "unlock" : "lock"); + break; case SA_MAP: { int n = atoi(arg2); @@ -1417,6 +1421,7 @@ void callvote(int type, const char *arg1, const char *arg2) { case SA_KICK: case SA_BAN: + case SA_FORCETEAM: putint(p, atoi(arg1)); sendstring(arg2, p); break; diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index ec7529e3..29a919cd 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -1183,9 +1183,9 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) case SV_SERVERMODE: { int sm = getint(p); - servstate.autoteam = sm & 1; + servstate.autoteam = sm & 0x1; servstate.mastermode = (sm >> 2) & MM_MASK; - servstate.matchteamsize = sm >> 4; + servstate.teamslocked = sm & 0xF; //if(sm & AT_SHUFFLE) playsound(TEAMSHUFFLE); // TODO break; } @@ -1222,6 +1222,12 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) v = newvotedisplayinfo(d, type, a, text); break; } + case SA_FORCETEAM: + { + itoa(a, getint(p)); + v = newvotedisplayinfo(d, type, a, team_string(getint(p))); + break; + } case SA_SERVERDESC: getstring(text, p); filtertext(text, text); diff --git a/source/src/protocol.h b/source/src/protocol.h index 9f3eeb71..dc45d335 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -51,7 +51,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_LOCKTEAMS, 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/protos.h b/source/src/protos.h index 467ceb33..fed9d13f 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -376,7 +376,7 @@ extern int gametimecurrent; extern int gametimemaximum; extern int lastgametimeupdate; extern bool watchingdemo; -struct serverstate { int autoteam; int mastermode; int matchteamsize; void reset() { autoteam = mastermode = matchteamsize = 0; }}; +struct serverstate { int mastermode; bool autoteam, teamslocked; void reset() { mastermode = 0; autoteam = teamslocked = false; }}; extern struct serverstate servstate; extern void updateworld(int curtime, int lastmillis); extern void resetmap(bool mrproper = true); diff --git a/source/src/scoreboard.cpp b/source/src/scoreboard.cpp index 4c571916..bf7f62be 100644 --- a/source/src/scoreboard.cpp +++ b/source/src/scoreboard.cpp @@ -349,7 +349,7 @@ void renderscores(void *menu, bool init) serverinfo *s = getconnectedserverinfo(); if(s) { - if(servstate.mastermode > MM_OPEN) concatformatstring(serverline, servstate.mastermode == MM_MATCH ? "M%d " : "P ", servstate.matchteamsize); + if(servstate.mastermode > MM_OPEN) concatformatstring(serverline, servstate.mastermode == MM_MATCH ? "M " : "P "); // ft: 2010jun12: this can write over the menu boundary //concatformatstring(serverline, "%s:%d %s", s->name, s->port, s->sdesc); // for now we'll just cut it off, same as the serverbrowser diff --git a/source/src/server.cpp b/source/src/server.cpp index e38cbdb5..f22690f6 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -35,7 +35,7 @@ vector demofiles; int mastermode = MM_OPEN; static bool autoteam = true; -int matchteamsize = 0; +static bool teamslocked = true; long int incoming_size = 0; @@ -227,32 +227,11 @@ int *numteamclients(int exclude = -1) int sendservermode(bool send = true) { - int sm = (autoteam ? AT_ENABLED : AT_DISABLED) | ((mastermode & MM_MASK) << 2) | (matchteamsize << 4); + int sm = (autoteam ? AT_ENABLED : AT_DISABLED) | ((mastermode & MM_MASK) << 2) | (teamslocked << 4); if(send) sendf(-1, 1, "ri2", SV_SERVERMODE, sm); return sm; } -void changematchteamsize(int newteamsize) -{ - if(newteamsize < 0) return; - if(matchteamsize != newteamsize) - { - matchteamsize = newteamsize; - sendservermode(); - } - if(mastermode == MM_MATCH && matchteamsize && m_teammode) - { - int size[2] = { 0 }; - loopv(clients) if(clients[i]->type!=ST_EMPTY && clients[i]->isauthed && clients[i]->isonrightmap) - { - if(team_isactive(clients[i]->team)) - { - if(++size[clients[i]->team] > matchteamsize) updateclientteam(i, team_tospec(clients[i]->team), FTR_SILENTFORCE); - } - } - } -} - void changemastermode(int newmode) { if(mastermode != newmode) @@ -266,8 +245,7 @@ void changemastermode(int newmode) if(clients[i]->team == TEAM_CLA_SPECT || clients[i]->team == TEAM_RVSF_SPECT) updateclientteam(i, TEAM_SPECT, FTR_SILENTFORCE); } } - else if(matchteamsize) changematchteamsize(matchteamsize); - sendservermode(); + sendservermode(); } } @@ -1544,15 +1522,6 @@ int canspawn(client *c) // beware: canspawn() doesn't check m_arena! (servmillis - c->connectmillis < 1000 + c->state.reconnections * 2000 && gamemillis > 10000 && totalclients > 3 && !team_isspect(c->team)) ) return SP_OK_NUM; // equivalent to SP_DENY if(!c->isonrightmap) return SP_WRONGMAP; - if(mastermode == MM_MATCH && matchteamsize) - { - if(c->team == TEAM_SPECT || (team_isspect(c->team) && !m_teammode)) return SP_SPECT; - if(c->team == TEAM_CLA_SPECT || c->team == TEAM_RVSF_SPECT) - { - if(numteamclients()[team_base(c->team)] >= matchteamsize) return SP_SPECT; - else return SP_REFILLMATCH; - } - } return SP_OK; } @@ -1564,6 +1533,7 @@ bool updateclientteam(int cln, int newteam, int ftr) client &cl = *clients[cln]; if(cl.team == newteam && ftr != FTR_AUTOTEAM) return true; // no change int *teamsizes = numteamclients(cln); + if(teamslocked && ftr != FTR_SILENTFORCE && ftr != FTR_INFO) return false; if( mastermode == MM_OPEN && cl.state.forced && ftr == FTR_PLAYERWISH && newteam < TEAM_SPECT && team_base(cl.team) != team_base(newteam) ) return false; // no free will changes to forced people if(newteam == TEAM_ANYACTIVE) // when spawning from spect @@ -1588,21 +1558,10 @@ bool updateclientteam(int cln, int newteam, int ftr) } if(ftr == FTR_PLAYERWISH) { - if(mastermode == MM_MATCH && matchteamsize && m_teammode) - { - if(newteam != TEAM_SPECT && (team_base(newteam) != team_base(cl.team) || !m_teammode)) return false; // no switching sides in match mode when teamsize is set - } if(team_isactive(newteam)) { if(!m_teammode && cl.state.state == CS_ALIVE) return false; // no comments - if(mastermode == MM_MATCH) - { - if(m_teammode && matchteamsize && teamsizes[newteam] >= matchteamsize) return false; // ensure maximum team size - } - else - { - if(m_teammode && autoteam && teamsizes[newteam] > teamsizes[team_opposite(newteam)]) return false; // don't switch to an already bigger team - } + if(m_teammode && autoteam && teamsizes[newteam] > teamsizes[team_opposite(newteam)]) return false; // don't switch to an already bigger team } else { @@ -1671,7 +1630,7 @@ void shuffleteams(int ftr = FTR_AUTOTEAM) bool balanceteams(int ftr) // pro vs noobs never more { - if(mastermode != MM_OPEN || totalclients < 3 ) return true; + if(teamslocked || mastermode != MM_OPEN || totalclients < 3 ) return true; int tsize[2] = {0, 0}, tscore[2] = {0, 0}; int totalscore = 0, nplayers = 0; int flagmult = (m_ctf ? 50 : (m_htf ? 25 : 12)); @@ -1963,7 +1922,6 @@ void startgame(const char *newname, int newmode, int newtime, bool notify) send_item_list(q); // always send the item list when a game starts sendpacket(-1, 1, q.finalize()); defformatstring(gsmsg)("Game start: %s on %s, %d players, %d minutes, mastermode %d, ", modestr(smode), smapname, numclients(), minremain, mastermode); - if(mastermode == MM_MATCH) concatformatstring(gsmsg, "teamsize %d, ", matchteamsize); if(ms) concatformatstring(gsmsg, "(map rev %d/%d, %s, 'getmap' %sprepared)", smapstats.hdr.maprevision, smapstats.cgzsize, maplocstr[maploc], mapbuffer.available() ? "" : "not "); else concatformatstring(gsmsg, "error: failed to preload map (map: %s)", maplocstr[maploc]); logline(ACLOG_INFO, "\n%s", gsmsg); @@ -2191,6 +2149,7 @@ bool scallvote(voteinfo *v, ENetPacket *msg) // true if a regular vote was calle /** same team, with low tk, not lagging, and not spamming... so, why to kick? */ else if ( isteam(c->team, b->team) && b->state.teamkills < c->state.teamkills ) error = VOTEE_WEAK; } + else if( teamslocked && v->type == SA_FORCETEAM && c->role != CR_ADMIN) error = VOTEE_WEAK; if(error>=0) { @@ -2241,6 +2200,11 @@ void callvotepacket (int cn, voteinfo *v = curvote) case SA_REMBANS: case SA_SHUFFLETEAMS: break; + case SA_FORCETEAM: + putint(q, v->num); + logline(ACLOG_INFO, "team sent %d", ((forceteamaction *)v)->team); + putint(q, ((forceteamaction *)v)->team); + break; default: putint(q, v->num); break; @@ -2788,7 +2752,7 @@ void process(ENetPacket *packet, int sender, int chan) bool canspeech = forbiddenlist.canspeech(text); if(!spamdetect(cl, text) && canspeech) { - if(mastermode != MM_MATCH || !matchteamsize || team_isactive(cl->team) || (cl->team == TEAM_SPECT && cl->role == CR_ADMIN)) // common chat + if(mastermode != MM_MATCH || team_isactive(cl->team) || (cl->team == TEAM_SPECT && cl->role == CR_ADMIN)) // common chat { logline(ACLOG_INFO, "[%s] %s%s says: '%s'", cl->hostname, type == SV_TEXTME ? "(me) " : "", cl->name, text); if(cl->type==ST_TCPIP) while(mid1messages.add(p.buf[mid1++]); @@ -3428,8 +3392,15 @@ void process(ENetPacket *packet, int sender, int chan) case SA_SHUFFLETEAMS: vi->action = new shuffleteamaction(); break; + case SA_LOCKTEAMS: + vi->action = new lockteamsaction((vi->num = getint(p)) > 0); + break; case SA_FORCETEAM: - vi->action = new forceteamaction((vi->num = getint(p)), sender); + vi->num = getint(p); + getstring(text, p); + filtertext(text, text); + trimtrailingwhitespace(text); + vi->action = new forceteamaction(vi->num, sender, text); break; case SA_GIVEADMIN: vi->action = new giveadminaction(vi->num = getint(p)); @@ -3516,18 +3487,6 @@ void process(ENetPacket *packet, int sender, int chan) sendservmsg("your message has been logged", sender); } } - else if(!strcmp(ext, "set::teamsize")) - { - // intermediate solution to set the teamsize (will be voteable) - - getstring(text, p, n); - if(valid_client(sender) && clients[sender]->role==CR_ADMIN && mastermode == MM_MATCH) - { - changematchteamsize(atoi(text)); - defformatstring(msg)("match team size set to %d", matchteamsize); - sendservmsg(msg, -1); - } - } // else if() // add other extensions here @@ -3597,8 +3556,8 @@ void resetserverifempty() { loopv(clients) if(clients[i]->type!=ST_EMPTY) return; resetserver("", 0, 10); - matchteamsize = 0; autoteam = true; + teamslocked = false; changemastermode(MM_OPEN); nextmapname[0] = '\0'; } diff --git a/source/src/serveractions.h b/source/src/serveractions.h index c18060a1..61c10826 100644 --- a/source/src/serveractions.h +++ b/source/src/serveractions.h @@ -158,13 +158,26 @@ struct playeraction : serveraction struct forceteamaction : playeraction { - void perform() { updateclientteam(cn, team_opposite(clients[cn]->team), FTR_SILENTFORCE); } - virtual bool isvalid() { return m_teammode && valid_client(cn); } - forceteamaction(int cn, int caller) : playeraction(cn) + char *teamname; + int team; + + void perform() { updateclientteam(cn, team, FTR_SILENTFORCE); } + virtual bool isvalid() { - if(cn != caller) role = roleconf('f'); - if(isvalid() && !(clients[cn]->state.forced && clients[caller]->role != CR_ADMIN)) formatstring(desc)("force player %s to the enemy team", clients[cn]->name); + return (team == TEAM_CLA || team == TEAM_RVSF || team == TEAM_SPECT) && m_teammode && valid_client(cn); } + forceteamaction(int cn, int caller, char *teamstring) : playeraction(cn) + { + team = -1; teamname = newstring(teamstring); + if(cn != caller) role = roleconf('f'); + + if(teamname && *teamname) { loopi(TEAM_NUM) { if(!stricmp(teamnames_s[i], teamname)) { team = i; break; } } } // FIXME ? maybe it should be done clientside ? + else if(valid_client(cn)) team = team_opposite(clients[cn]->team); + + if(isvalid() && !(clients[cn]->state.forced && clients[caller]->role != CR_ADMIN)) formatstring(desc)("force player %s to %s", clients[cn]->name, team_string(team)); + } + + ~forceteamaction() { DELETEA(teamname); } }; struct giveadminaction : playeraction @@ -274,6 +287,21 @@ struct shuffleteamaction : serveraction } }; +struct lockteamsaction : enableaction +{ + void perform() + { + teamslocked = enable; + sendservermode(); + if(m_teammode && enable && mastermode != MM_MATCH) refillteams(true); + } + lockteamsaction(bool enable) : enableaction(enable) + { + role = roleconf('L'); + if(isvalid()) formatstring(desc)("%s teams", enable ? "lock" : "unlock"); + } +}; + struct recorddemoaction : enableaction // TODO: remove completely { void perform() { }