diff --git a/config/admin.cfg b/config/admin.cfg index 7803ff62..aaa24894 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 $arg2 ] +forceteam = [ callvote 5 $arg1 ] 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,8 +17,6 @@ cleardemo = [ callvote 10 $arg1 ] cleardemos = [ callvote 10 0 ] serverdesc = [ callvote 11 $arg1 ] shuffleteams = [ callvote 12 ] -lockteams = [ callvote 13 1 ] -unlockteams = [ callvote 13 0 ] demo = [ _demofile = $arg1 diff --git a/source/src/client.cpp b/source/src/client.cpp index 48e78cf6..7b1b9d11 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 && !team_isactive(player1->team) && !(player1->team == TEAM_SPECT && player1->clientrole == CR_ADMIN)) toteam = true; // spect chat + if(servstate.mastermode == MM_MATCH && servstate.matchteamsize && !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 8bf7e434..8b7f8ade 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 %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 *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 *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_GIVEADMIN: case SA_FORCETEAM: + case SA_GIVEADMIN: { int cn = atoi(arg1); playerent *p = (cn == getclientnum() ? player1 : getclient(cn)); @@ -1355,7 +1355,6 @@ 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; } @@ -1366,9 +1365,6 @@ 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); @@ -1421,7 +1417,6 @@ 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 29a919cd..ec7529e3 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 & 0x1; + servstate.autoteam = sm & 1; servstate.mastermode = (sm >> 2) & MM_MASK; - servstate.teamslocked = sm & 0xF; + servstate.matchteamsize = sm >> 4; //if(sm & AT_SHUFFLE) playsound(TEAMSHUFFLE); // TODO break; } @@ -1222,12 +1222,6 @@ 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 dc45d335..9f3eeb71 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_LOCKTEAMS, 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_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 fed9d13f..467ceb33 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 mastermode; bool autoteam, teamslocked; void reset() { mastermode = 0; autoteam = teamslocked = false; }}; +struct serverstate { int autoteam; int mastermode; int matchteamsize; void reset() { autoteam = mastermode = matchteamsize = 0; }}; 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 bf7f62be..4c571916 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 " : "P "); + if(servstate.mastermode > MM_OPEN) concatformatstring(serverline, servstate.mastermode == MM_MATCH ? "M%d " : "P ", servstate.matchteamsize); // 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 f22690f6..e38cbdb5 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; -static bool teamslocked = true; +int matchteamsize = 0; long int incoming_size = 0; @@ -227,11 +227,32 @@ int *numteamclients(int exclude = -1) int sendservermode(bool send = true) { - int sm = (autoteam ? AT_ENABLED : AT_DISABLED) | ((mastermode & MM_MASK) << 2) | (teamslocked << 4); + int sm = (autoteam ? AT_ENABLED : AT_DISABLED) | ((mastermode & MM_MASK) << 2) | (matchteamsize << 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) @@ -245,7 +266,8 @@ void changemastermode(int newmode) if(clients[i]->team == TEAM_CLA_SPECT || clients[i]->team == TEAM_RVSF_SPECT) updateclientteam(i, TEAM_SPECT, FTR_SILENTFORCE); } } - sendservermode(); + else if(matchteamsize) changematchteamsize(matchteamsize); + sendservermode(); } } @@ -1522,6 +1544,15 @@ 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; } @@ -1533,7 +1564,6 @@ 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 @@ -1558,10 +1588,21 @@ 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(m_teammode && autoteam && teamsizes[newteam] > teamsizes[team_opposite(newteam)]) return false; // don't switch to an already bigger team + 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 + } } else { @@ -1630,7 +1671,7 @@ void shuffleteams(int ftr = FTR_AUTOTEAM) bool balanceteams(int ftr) // pro vs noobs never more { - if(teamslocked || mastermode != MM_OPEN || totalclients < 3 ) return true; + if(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)); @@ -1922,6 +1963,7 @@ 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); @@ -2149,7 +2191,6 @@ 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) { @@ -2200,11 +2241,6 @@ 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; @@ -2752,7 +2788,7 @@ void process(ENetPacket *packet, int sender, int chan) bool canspeech = forbiddenlist.canspeech(text); if(!spamdetect(cl, text) && canspeech) { - if(mastermode != MM_MATCH || team_isactive(cl->team) || (cl->team == TEAM_SPECT && cl->role == CR_ADMIN)) // common chat + if(mastermode != MM_MATCH || !matchteamsize || 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++]); @@ -3392,15 +3428,8 @@ 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->num = getint(p); - getstring(text, p); - filtertext(text, text); - trimtrailingwhitespace(text); - vi->action = new forceteamaction(vi->num, sender, text); + vi->action = new forceteamaction((vi->num = getint(p)), sender); break; case SA_GIVEADMIN: vi->action = new giveadminaction(vi->num = getint(p)); @@ -3487,6 +3516,18 @@ 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 @@ -3556,8 +3597,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 61c10826..c18060a1 100644 --- a/source/src/serveractions.h +++ b/source/src/serveractions.h @@ -158,26 +158,13 @@ struct playeraction : serveraction struct forceteamaction : playeraction { - char *teamname; - int team; - - void perform() { updateclientteam(cn, team, FTR_SILENTFORCE); } - virtual bool isvalid() + 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) { - 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)); + if(isvalid() && !(clients[cn]->state.forced && clients[caller]->role != CR_ADMIN)) formatstring(desc)("force player %s to the enemy team", clients[cn]->name); } - - ~forceteamaction() { DELETEA(teamname); } }; struct giveadminaction : playeraction @@ -287,21 +274,6 @@ 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() { }