1 // available server actions
2 
3 enum { EE_LOCAL_SERV = 1, EE_DED_SERV = 1<<1 }; // execution environment
4 
roleconf(int key)5 int roleconf(int key)
6 { // current defaults: "fkbMASRCDEPtw"
7     if(strchr(scl.voteperm, tolower(key))) return CR_DEFAULT;
8     if(strchr(scl.voteperm, toupper(key))) return CR_ADMIN;
9     return (key) == tolower(key) ? CR_DEFAULT : CR_ADMIN;
10 }
11 
12 struct serveraction
13 {
14     int role; // required client role
15     int area; // only on ded servers
16     string desc;
17 
18     virtual void perform() = 0;
isvalidserveraction19     virtual bool isvalid() { return true; }
isdisabledserveraction20     virtual bool isdisabled() { return false; }
serveractionserveraction21     serveraction() : role(CR_DEFAULT), area(EE_DED_SERV) { desc[0] = '\0'; }
~serveractionserveraction22     virtual ~serveraction() { }
23 };
24 
kick_abuser(int cn,int & cmillis,int & count,int limit)25 void kick_abuser(int cn, int &cmillis, int &count, int limit)
26 {
27     if ( cmillis + 30000 > servmillis ) count++;
28     else {
29         count -= count > 0 ? (servmillis - cmillis)/30000 : 0;
30         if ( count <= 0 ) count = 1;
31     }
32     cmillis = servmillis;
33     if( count >= limit ) disconnect_client(cn, DISC_ABUSE);
34 }
35 
36 struct mapaction : serveraction
37 {
38     char *map;
39     int mode, time;
40     bool mapok, queue;
performmapaction41     void perform()
42     {
43         if(queue)
44         {
45             nextgamemode = mode;
46             copystring(nextmapname, map);
47         }
48         else if(isdedicated && numclients() > 2 && smode >= 0 && smode != 1 && ( gamemillis > gamelimit/4 || scl.demo_interm ))
49         {
50             forceintermission = true;
51             nextgamemode = mode;
52             copystring(nextmapname, map);
53         }
54         else
55         {
56             startgame(map, mode, time);
57         }
58     }
isvalidmapaction59     bool isvalid() { return serveraction::isvalid() && mode != GMODE_DEMO && map[0] && mapok && !(isdedicated && !m_mp(mode)); }
isdisabledmapaction60     bool isdisabled() { return maprot.current() && !maprot.current()->vote; }
mapactionmapaction61     mapaction(char *map, int mode, int time, int caller, bool q) : map(map), mode(mode), time(time), queue(q)
62     {
63         if(isdedicated)
64         {
65             bool notify = valid_client(caller);
66             int maploc = MAP_NOTFOUND;
67             mapstats *ms = map[0] ? getservermapstats(map, false, &maploc) : NULL;
68             bool validname = validmapname(map);
69             mapok = (ms != NULL) && validname && ( (mode != GMODE_COOPEDIT && mapisok(ms)) || (mode == GMODE_COOPEDIT && !readonlymap(maploc)) );
70             if(!mapok)
71             {
72                 if(notify)
73                 {
74                     if(!validname)
75                         sendservmsg("invalid map name", caller);
76                     else
77                     {
78                         sendservmsg(ms ?
79                             ( mode == GMODE_COOPEDIT ? "this map cannot be coopedited in this server" : "sorry, but this map does not satisfy some quality requisites to be played in MultiPlayer Mode" ) :
80                             "the server does not have this map",
81                             caller);
82                     }
83                 }
84             }
85             else
86             { // check, if map supports mode
87                 if(mode == GMODE_COOPEDIT && !strchr(scl.voteperm, 'e')) role = CR_ADMIN;
88                 bool romap = mode == GMODE_COOPEDIT && readonlymap(maploc);
89                 int smode = mode;  // 'borrow' the mode macros by replacing a global by a local var
90                 bool spawns = mode == GMODE_COOPEDIT || (m_teammode && !m_ktf ? ms->hasteamspawns : ms->hasffaspawns);
91                 bool flags = mode != GMODE_COOPEDIT && m_flags && !m_htf ? ms->hasflags : true;
92                 if(!spawns || !flags || romap)
93                 { // unsupported mode
94                     if(strchr(scl.voteperm, 'P')) role = CR_ADMIN;
95                     else if(!strchr(scl.voteperm, 'p')) mapok = false; // default: no one can vote for unsupported mode/map combinations
96                     defformatstring(msg)("\f3map \"%s\" does not support \"%s\": ", behindpath(map), modestr(mode, false));
97                     if(romap) concatstring(msg, "map is readonly");
98                     else
99                     {
100                         if(!spawns) concatstring(msg, "player spawns");
101                         if(!spawns && !flags) concatstring(msg, " and ");
102                         if(!flags) concatstring(msg, "flag bases");
103                         concatstring(msg, " missing");
104                     }
105                     if(notify) sendservmsg(msg, caller);
106                     logline(ACLOG_INFO, "%s", msg);
107                 }
108             }
109             loopv(scl.adminonlymaps)
110             {
111                 const char *s = scl.adminonlymaps[i], *h = strchr(s, '#'), *m = behindpath(map);
112                 size_t sl = strlen(s);
113                 if(h)
114                 {
115                     if(h != s)
116                     {
117                         sl = h - s;
118                         if(mode != atoi(h + 1)) continue;
119                     }
120                     else
121                     {
122                         if(mode == atoi(h+1))
123                         {
124                             role = CR_ADMIN;
125                             break;
126                         }
127                     }
128                 }
129                 if(sl == strlen(m) && !strncmp(m, scl.adminonlymaps[i], sl)) role = CR_ADMIN;
130             }
131         }
132         else mapok = true;
133         area |= EE_LOCAL_SERV; // local too
134         formatstring(desc)("load map '%s' in mode '%s'", map, modestr(mode));
135         if(q) concatstring(desc, " (in the next game)");
136     }
~mapactionmapaction137     ~mapaction() { DELETEA(map); }
138 };
139 
140 struct demoplayaction : serveraction
141 {
142     char *demofilename;
performdemoplayaction143     void perform() { startdemoplayback(demofilename); }
demoplayactiondemoplayaction144     demoplayaction(char *demofilename) : demofilename(demofilename)
145     {
146         area = EE_LOCAL_SERV; // only local
147     }
148 
~demoplayactiondemoplayaction149     ~demoplayaction() { DELETEA(demofilename); }
150 };
151 
152 struct playeraction : serveraction
153 {
154     int cn;
155     ENetAddress address;
disconnectplayeraction156     void disconnect(int reason)
157     {
158         int i = findcnbyaddress(&address);
159         if(i >= 0) disconnect_client(i, reason);
160     }
isvalidplayeraction161     virtual bool isvalid() { return valid_client(cn) && clients[cn]->role != CR_ADMIN; } // actions can't be done on admins
playeractionplayeraction162     playeraction(int cn) : cn(cn)
163     {
164         if(isvalid()) address = clients[cn]->peer->address;
165     };
166 };
167 
168 struct forceteamaction : playeraction
169 {
170     int team;
performforceteamaction171     void perform() { updateclientteam(cn, team, FTR_SILENTFORCE); }
isvalidforceteamaction172     virtual bool isvalid() { return valid_client(cn) && team_isvalid(team) && team != clients[cn]->team; }
forceteamactionforceteamaction173     forceteamaction(int cn, int caller, int team) : playeraction(cn), team(team)
174     {
175         if(cn != caller) role = roleconf('f');
176         if(isvalid() && !(clients[cn]->state.forced && clients[caller]->role != CR_ADMIN)) formatstring(desc)("force player %s to team %s", clients[cn]->name, teamnames[team]);
177     }
178 };
179 
180 struct giveadminaction : playeraction
181 {
performgiveadminaction182     void perform() { changeclientrole(cn, CR_ADMIN, NULL, true); }
giveadminactiongiveadminaction183     giveadminaction(int cn) : playeraction(cn)
184     {
185         role = CR_ADMIN;
186 //        role = roleconf('G');
187     }
188 };
189 
190 struct kickaction : playeraction
191 {
192     bool wasvalid;
performkickaction193     void perform()  { disconnect(DISC_MKICK); }
isvalidkickaction194     virtual bool isvalid() { return wasvalid || playeraction::isvalid(); }
kickactionkickaction195     kickaction(int cn, char *reason) : playeraction(cn)
196     {
197         wasvalid = false;
198         role = roleconf('k');
199         if(isvalid() && strlen(reason) > 3 && valid_client(cn))
200         {
201             wasvalid = true;
202             formatstring(desc)("kick player %s, reason: %s", clients[cn]->name, reason);
203         }
204     }
205 };
206 
207 struct banaction : playeraction
208 {
209     bool wasvalid;
performbanaction210     void perform()
211     {
212         int i = findcnbyaddress(&address);
213         if(i >= 0) addban(clients[i], DISC_MBAN, BAN_VOTE);
214     }
isvalidbanaction215     virtual bool isvalid() { return wasvalid || playeraction::isvalid(); }
banactionbanaction216     banaction(int cn, char *reason) : playeraction(cn)
217     {
218         wasvalid = false;
219         role = roleconf('b');
220         if(isvalid() && strlen(reason) > 3)
221         {
222             wasvalid = true;
223             formatstring(desc)("ban player %s, reason: %s", clients[cn]->name, reason);
224         }
225     }
226 };
227 
228 struct removebansaction : serveraction
229 {
performremovebansaction230     void perform() { bans.shrink(0); }
removebansactionremovebansaction231     removebansaction()
232     {
233         role = roleconf('b');
234         copystring(desc, "remove all bans");
235     }
236 };
237 
238 struct mastermodeaction : serveraction
239 {
240     int mode;
performmastermodeaction241     void perform() { changemastermode(mode); }
isvalidmastermodeaction242     bool isvalid() { return mode >= 0 && mode < MM_NUM; }
mastermodeactionmastermodeaction243     mastermodeaction(int mode) : mode(mode)
244     {
245         role = roleconf('M');
246         if(isvalid()) formatstring(desc)("change mastermode to '%s'", mmfullname(mode));
247     }
248 };
249 
250 struct enableaction : serveraction
251 {
252     bool enable;
enableactionenableaction253     enableaction(bool enable) : enable(enable) {}
254 };
255 
256 struct autoteamaction : enableaction
257 {
performautoteamaction258     void perform()
259     {
260         autoteam = enable;
261         sendservermode();
262         if(m_teammode && enable) refillteams(true);
263     }
autoteamactionautoteamaction264     autoteamaction(bool enable) : enableaction(enable)
265     {
266         role = roleconf('A');
267         if(isvalid()) formatstring(desc)("%s autoteam", enable ? "enable" : "disable");
268     }
269 };
270 
271 struct shuffleteamaction : serveraction
272 {
performshuffleteamaction273     void perform()
274     {
275         sendf(-1, 1, "ri2", SV_SERVERMODE, sendservermode(false) | AT_SHUFFLE);
276         shuffleteams();
277     }
isvalidshuffleteamaction278     bool isvalid() { return serveraction::isvalid() && m_teammode; }
shuffleteamactionshuffleteamaction279     shuffleteamaction()
280     {
281         role = roleconf('S');
282         if(isvalid()) copystring(desc, "shuffle teams");
283     }
284 };
285 
286 struct recorddemoaction : enableaction            // TODO: remove completely
287 {
performrecorddemoaction288     void perform() { }
isvalidrecorddemoaction289     bool isvalid() { return serveraction::isvalid(); }
recorddemoactionrecorddemoaction290     recorddemoaction(bool enable) : enableaction(enable)
291     {
292         role = roleconf('R');
293         if(isvalid()) formatstring(desc)("%s demorecord", enable ? "enable" : "disable");
294     }
295 };
296 
297 struct cleardemosaction : serveraction
298 {
299     int demo;
performcleardemosaction300     void perform() { cleardemos(demo); }
cleardemosactioncleardemosaction301     cleardemosaction(int demo) : demo(demo)
302     {
303         role = roleconf('C');
304         if(isvalid()) formatstring(desc)("clear demo %d", demo);
305     }
306 };
307 
308 struct serverdescaction : serveraction
309 {
310     char *sdesc;
311     int cn;
312     ENetAddress address;
performserverdescaction313     void perform() { updatesdesc(sdesc, &address); }
isvalidserverdescaction314     bool isvalid() { return serveraction::isvalid() && updatedescallowed() && valid_client(cn); }
serverdescactionserverdescaction315     serverdescaction(char *sdesc, int cn) : sdesc(sdesc), cn(cn)
316     {
317         role = roleconf('D');
318         formatstring(desc)("set server description to '%s'", sdesc);
319         if(isvalid()) address = clients[cn]->peer->address;
320     }
~serverdescactionserverdescaction321     ~serverdescaction() { DELETEA(sdesc); }
322 };
323