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