// available server actions enum { EE_LOCAL_SERV = 1, EE_DED_SERV = 1<<1 }; // execution environment int roleconf(int key) { // current defaults: "fGkBMasRCDxP" if(strchr(voteperm, tolower(key))) return CR_DEFAULT; if(strchr(voteperm, toupper(key))) return CR_ADMIN; return (key) == tolower(key) ? CR_DEFAULT : CR_ADMIN; } struct serveraction { int role; // required client role int area; // only on ded servers string desc; virtual void perform() = 0; virtual bool isvalid() { return true; } virtual bool isdisabled() { return false; } serveraction() : role(CR_DEFAULT), area(EE_DED_SERV) { desc[0] = '\0'; } virtual ~serveraction() { } }; struct mapaction : serveraction { char *map; int mode; void perform() { if(isdedicated && numclients() > 2 && smode >= 0 && smode != 1 && gamemillis > gamelimit/4) { forceintermission = true; nextgamemode = mode; s_strcpy(nextmapname, map); } else { resetmap(map, mode); } } bool isvalid() { return serveraction::isvalid() && mode != GMODE_DEMO && map[0]; } bool isdisabled() { return configsets.inrange(curcfgset) && !configsets[curcfgset].vote; } mapaction(char *map, int mode, int caller) : map(map), mode(mode) { if(isdedicated) { bool notify = valid_client(caller) && clients[caller]->role == CR_DEFAULT; mapstats *ms = getservermapstats(map); if(strchr(voteperm, 'X') && !ms) // admin needed for unknown maps { role = CR_ADMIN; if(notify) sendservmsg("the server does not have this map", caller); } if(ms && !strchr(voteperm, 'p')) // admin needed for mismatched modes { int smode = mode; // 'borrow' the mode macros by replacing a global by a local var bool spawns = (m_teammode && !m_ktf) ? ms->hasteamspawns : ms->hasffaspawns; bool flags = m_flags && !m_htf ? ms->hasflags : true; if(!spawns || !flags) { role = CR_ADMIN; s_sprintfd(msg)("\f3map \"%s\" does not support \"%s\": ", behindpath(map), modestr(mode, false)); if(!spawns) s_strcat(msg, "player spawns"); if(!spawns && !flags) s_strcat(msg, " and "); if(!flags) s_strcat(msg, "flag bases"); s_strcat(msg, " missing"); if(notify) sendservmsg(msg, caller); logger->writeline(log::info, "%s", msg); } } } area |= EE_LOCAL_SERV; // local too s_sprintf(desc)("load map '%s' in mode '%s'", map, modestr(mode)); } ~mapaction() { DELETEA(map); } }; struct demoplayaction : serveraction { char *map; void perform() { resetmap(map, GMODE_DEMO); } demoplayaction(char *map) : map(map) { area = EE_LOCAL_SERV; // only local } ~demoplayaction() { DELETEA(map); } }; struct playeraction : serveraction { int cn; ENetAddress address; void disconnect(int reason) { int i = findcnbyaddress(&address); if(i >= 0) disconnect_client(i, reason); } virtual bool isvalid() { return valid_client(cn) && clients[cn]->role != CR_ADMIN; } // actions can't be done on admins playeraction(int cn) : cn(cn) { if(isvalid()) address = clients[cn]->peer->address; }; }; struct forceteamaction : playeraction { void perform() { forceteam(cn, team_opposite(team_int(clients[cn]->team)), true); } virtual bool isvalid() { return m_teammode && valid_client(cn); } forceteamaction(int cn, int caller) : playeraction(cn) { if(cn != caller) role = roleconf('f'); if(isvalid()) s_sprintf(desc)("force player %s to the enemy team", clients[cn]->name); } }; struct giveadminaction : playeraction { void perform() { changeclientrole(cn, CR_ADMIN, NULL, true); } giveadminaction(int cn) : playeraction(cn) { role = CR_ADMIN; // role = roleconf('G'); } }; struct kickaction : playeraction { bool wasvalid; void perform() { disconnect(DISC_MKICK); } virtual bool isvalid() { return wasvalid || playeraction::isvalid(); } kickaction(int cn) : playeraction(cn) { wasvalid = false; role = roleconf('k'); if(isvalid()) { wasvalid = true; s_sprintf(desc)("kick player %s", clients[cn]->name); } } }; struct banaction : playeraction { bool wasvalid; void perform() { ban b = { address, servmillis+20*60*1000 }; bans.add(b); disconnect(DISC_MBAN); } virtual bool isvalid() { return wasvalid || playeraction::isvalid(); } banaction(int cn) : playeraction(cn) { role = roleconf('B'); if(isvalid()) { wasvalid = true; s_sprintf(desc)("ban player %s", clients[cn]->name); } } }; struct removebansaction : serveraction { void perform() { bans.setsize(0); } removebansaction() { role = roleconf('B'); s_strcpy(desc, "remove all bans"); } }; struct mastermodeaction : serveraction { int mode; void perform() { mastermode = mode; } bool isvalid() { return mode >= 0 && mode < MM_NUM; } mastermodeaction(int mode) : mode(mode) { role = roleconf('M'); if(isvalid()) s_sprintf(desc)("change mastermode to '%s'", mmfullname(mode)); } }; struct enableaction : serveraction { bool enable; enableaction(bool enable) : enable(enable) {} }; struct autoteamaction : enableaction { void perform() { sendf(-1, 1, "ri2", SV_AUTOTEAM, (autoteam = enable) == 1 ? AT_ENABLED : AT_DISABLED); if(m_teammode && enable) refillteams(true); } autoteamaction(bool enable) : enableaction(enable) { role = roleconf('a'); if(isvalid()) s_sprintf(desc)("%s autoteam", enable ? "enable" : "disable"); } }; struct shuffleteamaction : serveraction { void perform() { sendf(-1, 1, "ri2", SV_AUTOTEAM, AT_SHUFFLE); shuffleteams(); } bool isvalid() { return serveraction::isvalid() && m_teammode; } shuffleteamaction() { role = roleconf('s'); if(isvalid()) s_strcpy(desc, "shuffle teams"); } }; struct recorddemoaction : enableaction { void perform() { demonextmatch = enable; } recorddemoaction(bool enable) : enableaction(enable) { role = roleconf('R'); if(isvalid()) s_sprintf(desc)("%s demorecord", enable ? "enable" : "disable"); } }; struct stopdemoaction : serveraction { void perform() { if(m_demo) enddemoplayback(); else enddemorecord(); } stopdemoaction() { role = CR_ADMIN; area |= EE_LOCAL_SERV; s_strcpy(desc, "stop demo"); } }; struct cleardemosaction : serveraction { int demo; void perform() { cleardemos(demo); } cleardemosaction(int demo) : demo(demo) { role = roleconf('C'); if(isvalid()) s_sprintf(desc)("clear demo %d", demo); } }; struct serverdescaction : serveraction { char *sdesc; int cn; ENetAddress address; void perform() { updatesdesc(sdesc, &address); } bool isvalid() { return serveraction::isvalid() && updatedescallowed() && valid_client(cn); } serverdescaction(char *sdesc, int cn) : sdesc(sdesc), cn(cn) { role = roleconf('D'); s_sprintf(desc)("set server description to '%s'", sdesc); if(isvalid()) address = clients[cn]->peer->address; } ~serverdescaction() { DELETEA(sdesc); } };