]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/command/vote.qc
Merge branch 'master' into martin-t/mg-solidpen
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / command / vote.qc
1 #include "vote.qh"
2
3 #include <server/defs.qh>
4 #include <server/miscfunctions.qh>
5
6 #include <common/command/_mod.qh>
7 #include "vote.qh"
8
9 #include "common.qh"
10
11 #include "../g_damage.qh"
12 #include "../g_world.qh"
13 #include "../teamplay.qh"
14 #include "../race.qh"
15 #include "../round_handler.qh"
16 #include "../scores.qh"
17
18 #include <server/mutators/_mod.qh>
19 #include <common/gamemodes/_mod.qh>
20
21 #include <common/constants.qh>
22 #include <common/net_linked.qh>
23 #include <common/mapinfo.qh>
24 #include <common/notifications/all.qh>
25 #include <common/playerstats.qh>
26 #include <common/util.qh>
27
28 // =============================================
29 //  Server side voting code, reworked by Samual
30 //  Last updated: December 27th, 2011
31 // =============================================
32
33 //  Nagger for players to know status of voting
34 bool Nagger_SendEntity(entity this, entity to, float sendflags)
35 {
36         int nags, i, f, b;
37         entity e;
38         WriteHeader(MSG_ENTITY, ENT_CLIENT_NAGGER);
39
40         // bits:
41         //   1 = ready
42         //   2 = player needs to ready up
43         //   4 = vote
44         //   8 = player needs to vote
45         //  16 = warmup
46         // sendflags:
47         //  64 = vote counts
48         // 128 = vote string
49
50         nags = 0;
51         if (readycount)
52         {
53                 nags |= BIT(0);
54                 if (to.ready == 0) nags |= BIT(1);
55         }
56         if (vote_called)
57         {
58                 nags |= BIT(2);
59                 if (to.vote_selection == 0) nags |= BIT(3);
60         }
61         if (warmup_stage) nags |= BIT(4);
62
63         if (sendflags & BIT(6)) nags |= BIT(6);
64
65         if (sendflags & BIT(7)) nags |= BIT(7);
66
67         if (!(nags & 4))  // no vote called? send no string
68                 nags &= ~(BIT(6) | BIT(7));
69
70         WriteByte(MSG_ENTITY, nags);
71
72         if (nags & BIT(6))
73         {
74                 WriteByte(MSG_ENTITY, vote_accept_count);
75                 WriteByte(MSG_ENTITY, vote_reject_count);
76                 WriteByte(MSG_ENTITY, vote_needed_overall);
77                 WriteChar(MSG_ENTITY, to.vote_selection);
78         }
79
80         if (nags & BIT(7)) WriteString(MSG_ENTITY, vote_called_display);
81
82         if (nags & 1)
83         {
84                 for (i = 1; i <= maxclients; i += 8)
85                 {
86                         for (f = 0, e = edict_num(i), b = BIT(0); b < BIT(8); b <<= 1, e = nextent(e))
87                                 if (!IS_REAL_CLIENT(e) || e.ready)
88                                         f |= b;
89                         WriteByte(MSG_ENTITY, f);
90                 }
91         }
92
93         return true;
94 }
95
96 void Nagger_Init()
97 {
98         Net_LinkEntity(nagger = new_pure(nagger), false, 0, Nagger_SendEntity);
99 }
100
101 void Nagger_VoteChanged()
102 {
103         if (nagger) nagger.SendFlags |= BIT(7);
104 }
105
106 void Nagger_VoteCountChanged()
107 {
108         if (nagger) nagger.SendFlags |= BIT(6);
109 }
110
111 void Nagger_ReadyCounted()
112 {
113         if (nagger) nagger.SendFlags |= BIT(0);
114 }
115
116 // If the vote_caller is still here, return their name, otherwise vote_caller_name
117 string OriginalCallerName()
118 {
119         if (IS_REAL_CLIENT(vote_caller)) return playername(vote_caller, false);
120         return vote_caller_name;
121 }
122
123 // =======================
124 //  Game logic for voting
125 // =======================
126
127 void VoteReset()
128 {
129         FOREACH_CLIENT(true, { it.vote_selection = 0; });
130
131         if (vote_called)
132         {
133                 strfree(vote_called_command);
134                 strfree(vote_called_display);
135                 strfree(vote_caller_name);
136         }
137
138         vote_called = VOTE_NULL;
139         vote_caller = NULL;
140         vote_endtime = 0;
141
142         vote_parsed_command = string_null;
143         vote_parsed_display = string_null;
144
145         Nagger_VoteChanged();
146 }
147
148 void VoteStop(entity stopper)
149 {
150         bprint("\{1}^2* ^3", GetCallerName(stopper), "^2 stopped ^3", OriginalCallerName(), "^2's vote\n");
151         if (autocvar_sv_eventlog)   GameLogEcho(strcat(":vote:vstop:", ftos(stopper.playerid)));
152         // Don't force them to wait for next vote, this way they can e.g. correct their vote.
153         if ((vote_caller) && (stopper == vote_caller))   vote_caller.vote_waittime = time + autocvar_sv_vote_stop;
154         VoteReset();
155 }
156
157 void VoteAccept()
158 {
159         bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ^1", vote_called_display, "^2 was accepted\n");
160
161         if ((vote_called == VOTE_MASTER) && vote_caller) vote_caller.vote_master = 1;
162         else localcmd(strcat(vote_called_command, "\n"));
163
164         if (vote_caller)   vote_caller.vote_waittime = 0;  // people like your votes, you don't need to wait to vote again
165
166         VoteReset();
167         Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_ACCEPT);
168 }
169
170 void VoteReject()
171 {
172         bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ", vote_called_display, "^2 was rejected\n");
173         VoteReset();
174         Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_FAIL);
175 }
176
177 void VoteTimeout()
178 {
179         bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ", vote_called_display, "^2 timed out\n");
180         VoteReset();
181         Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_FAIL);
182 }
183
184 void VoteSpam(float notvoters, float mincount, string result)
185 {
186         bprint(strcat(
187                 strcat("\{1}^2* vote results: ^1", ftos(vote_accept_count)),
188                 strcat("^2:^1", ftos(vote_reject_count)),
189                 ((mincount >= 0) ? strcat("^2 (^1", ftos(mincount), "^2 needed)") : "^2"),
190                 strcat(", ^1", ftos(vote_abstain_count), "^2 didn't care"),
191                 strcat(", ^1", ftos(notvoters), strcat("^2 didn't ", ((mincount >= 0) ? "" : "have to "), "vote\n"))));
192
193         if (autocvar_sv_eventlog)
194         {
195                 GameLogEcho(strcat(
196                         strcat(":vote:v", result, ":", ftos(vote_accept_count)),
197                         strcat(":", ftos(vote_reject_count)),
198                         strcat(":", ftos(vote_abstain_count)),
199                         strcat(":", ftos(notvoters)),
200                         strcat(":", ftos(mincount))));
201         }
202 }
203
204 #define spectators_allowed (!autocvar_sv_vote_nospectators || (autocvar_sv_vote_nospectators == 1 && (warmup_stage || intermission_running)))
205
206 void VoteCount(float first_count)
207 {
208         // declarations
209         vote_accept_count = vote_reject_count = vote_abstain_count = 0;
210
211         float vote_player_count = 0, notvoters = 0;
212         float vote_real_player_count = 0, vote_real_accept_count = 0;
213         float vote_real_reject_count = 0, vote_real_abstain_count = 0;
214         float vote_needed_of_voted, final_needed_votes;
215         float vote_factor_overall, vote_factor_of_voted;
216
217         Nagger_VoteCountChanged();
218
219         // add up all the votes from each connected client
220         FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_CLIENT(it), {
221                 ++vote_player_count;
222                 if (IS_PLAYER(it))   ++vote_real_player_count;
223                 switch (it.vote_selection)
224                 {
225                         case VOTE_SELECT_REJECT:
226                         { ++vote_reject_count;
227                           { if (IS_PLAYER(it)) ++vote_real_reject_count; } break;
228                         }
229                         case VOTE_SELECT_ACCEPT:
230                         { ++vote_accept_count;
231                           { if (IS_PLAYER(it)) ++vote_real_accept_count; } break;
232                         }
233                         case VOTE_SELECT_ABSTAIN:
234                         { ++vote_abstain_count;
235                           { if (IS_PLAYER(it)) ++vote_real_abstain_count; } break;
236                         }
237                         default: break;
238                 }
239         });
240
241         // Check to see if there are enough players on the server to allow master voting... otherwise, vote master could be used for evil.
242         if ((vote_called == VOTE_MASTER) && autocvar_sv_vote_master_playerlimit > vote_player_count)
243         {
244                 if (vote_caller)   vote_caller.vote_waittime = 0;
245                 print_to(vote_caller, "^1There are not enough players on this server to allow you to become vote master.");
246                 VoteReset();
247                 return;
248         }
249
250         // if spectators aren't allowed to vote and there are players in a match, then only count the players in the vote and ignore spectators.
251         if (!spectators_allowed && (vote_real_player_count > 0))
252         {
253                 vote_accept_count = vote_real_accept_count;
254                 vote_reject_count = vote_real_reject_count;
255                 vote_abstain_count = vote_real_abstain_count;
256                 vote_player_count = vote_real_player_count;
257         }
258
259         // people who have no opinion in any way :D
260         notvoters = (vote_player_count - vote_accept_count - vote_reject_count - vote_abstain_count);
261
262         // determine the goal for the vote to be passed or rejected normally
263         vote_factor_overall = bound(0.5, autocvar_sv_vote_majority_factor, 0.999);
264         vote_needed_overall = floor((vote_player_count - vote_abstain_count) * vote_factor_overall) + 1;
265
266         // if the vote times out, determine the amount of votes needed of the people who actually already voted
267         vote_factor_of_voted = bound(0.5, autocvar_sv_vote_majority_factor_of_voted, 0.999);
268         vote_needed_of_voted = floor((vote_accept_count + vote_reject_count) * vote_factor_of_voted) + 1;
269
270         // are there any players at all on the server? it could be an admin vote
271         if (vote_player_count == 0 && first_count)
272         {
273                 VoteSpam(0, -1, "yes");  // no players at all, just accept it
274                 VoteAccept();
275                 return;
276         }
277
278         // since there ARE players, finally calculate the result of the vote
279         if (vote_accept_count >= vote_needed_overall)
280         {
281                 VoteSpam(notvoters, -1, "yes");  // there is enough acceptions to pass the vote
282                 VoteAccept();
283                 return;
284         }
285
286         if (vote_reject_count > vote_player_count - vote_abstain_count - vote_needed_overall)
287         {
288                 VoteSpam(notvoters, -1, "no");  // there is enough rejections to deny the vote
289                 VoteReject();
290                 return;
291         }
292
293         // there is not enough votes in either direction, now lets just calculate what the voters have said
294         if (time > vote_endtime)
295         {
296                 final_needed_votes = vote_needed_overall;
297
298                 if (autocvar_sv_vote_majority_factor_of_voted)
299                 {
300                         if (vote_accept_count >= vote_needed_of_voted)
301                         {
302                                 VoteSpam(notvoters, min(vote_needed_overall, vote_needed_of_voted), "yes");
303                                 VoteAccept();
304                                 return;
305                         }
306
307                         if (vote_accept_count + vote_reject_count > 0)
308                         {
309                                 VoteSpam(notvoters, min(vote_needed_overall, vote_needed_of_voted), "no");
310                                 VoteReject();
311                                 return;
312                         }
313
314                         final_needed_votes = min(vote_needed_overall, vote_needed_of_voted);
315                 }
316
317                 // it didn't pass or fail, so not enough votes to even make a decision.
318                 VoteSpam(notvoters, final_needed_votes, "timeout");
319                 VoteTimeout();
320         }
321 }
322
323 void VoteThink()
324 {
325         if (vote_endtime > 0)        // a vote was called
326         {
327                 if (time > vote_endtime) // time is up
328                         VoteCount(false);
329         }
330 }
331
332
333 // =======================
334 //  Game logic for warmup
335 // =======================
336
337 // Resets the state of all clients, items, weapons, waypoints, ... of the map.
338 void reset_map(bool dorespawn)
339 {
340         if (time <= game_starttime)
341         {
342                 if (game_stopped)
343                         return;
344                 if (round_handler_IsActive())
345                         round_handler_Reset(game_starttime);
346         }
347
348         if (shuffleteams_on_reset_map)
349         {
350                 shuffleteams();
351                 shuffleteams_on_reset_map = false;
352         }
353         MUTATOR_CALLHOOK(reset_map_global);
354
355         FOREACH_ENTITY_FLOAT_ORDERED(pure_data, false,
356         {
357                 if(IS_CLIENT(it))
358                         continue;
359                 if (it.reset)
360                 {
361                         it.reset(it);
362                         continue;
363                 }
364                 if (it.team_saved) it.team = it.team_saved;
365                 if (it.flags & FL_PROJECTILE) delete(it);  // remove any projectiles left
366         });
367
368         // Waypoints and assault start come LAST
369         FOREACH_ENTITY_ORDERED(IS_NOT_A_CLIENT(it), {
370                 if (it.reset2) it.reset2(it);
371         });
372
373         FOREACH_CLIENT(IS_PLAYER(it) && STAT(FROZEN, it), { Unfreeze(it, false); });
374
375         // Moving the player reset code here since the player-reset depends
376         // on spawnpoint entities which have to be reset first --blub
377         if (dorespawn)
378         {
379                 if (!MUTATOR_CALLHOOK(reset_map_players))
380                 {
381                         if (restart_mapalreadyrestarted || (time < game_starttime))
382                         {
383                                 FOREACH_CLIENT(IS_PLAYER(it),
384                                 {
385                                         /*
386                                         only reset players if a restart countdown is active
387                                         this can either be due to cvar sv_ready_restart_after_countdown having set
388                                         restart_mapalreadyrestarted to 1 after the countdown ended or when
389                                         sv_ready_restart_after_countdown is not used and countdown is still running
390                                         */
391                                         // NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players
392                                         // PlayerScore_Clear(it);
393                                         CS(it).killcount = 0;
394                                         // stop the player from moving so that he stands still once he gets respawned
395                                         it.velocity = '0 0 0';
396                                         it.avelocity = '0 0 0';
397                                         CS(it).movement = '0 0 0';
398                                         PutClientInServer(it);
399                                 });
400                         }
401                 }
402         }
403 }
404
405 // Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set)
406 void ReadyRestart_think(entity this)
407 {
408         restart_mapalreadyrestarted = true;
409         reset_map(true);
410         Score_ClearAll();
411         delete(this);
412 }
413
414 // Forces a restart of the game without actually reloading the map // this is a mess...
415 void ReadyRestart_force()
416 {
417         if (time <= game_starttime && game_stopped)
418                 return;
419
420         bprint("^1Server is restarting...\n");
421
422         VoteReset();
423
424         // clear overtime, we have to decrease timelimit to its original value again.
425         if (checkrules_overtimesadded > 0 && g_race_qualifying != 2)
426                 cvar_set("timelimit", ftos(autocvar_timelimit - (checkrules_overtimesadded * autocvar_timelimit_overtime)));
427         checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0;
428
429         readyrestart_happened = true;
430         game_starttime = time + RESTART_COUNTDOWN;
431
432         // clear player attributes
433         FOREACH_CLIENT(IS_PLAYER(it), {
434                 it.alivetime = 0;
435                 CS(it).killcount = 0;
436                 float val = PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, 0);
437                 PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, -val);
438         });
439
440         restart_mapalreadyrestarted = false; // reset this var, needed when cvar sv_ready_restart_repeatable is in use
441
442         // disable the warmup global for the server
443         warmup_stage = 0;                // once the game is restarted the game is in match stage
444
445         // reset the .ready status of all players (also spectators)
446         FOREACH_CLIENT(IS_REAL_CLIENT(it), { it.ready = false; });
447         readycount = 0;
448         Nagger_ReadyCounted();  // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
449
450         // lock teams with lockonrestart
451         if (autocvar_teamplay_lockonrestart && teamplay)
452         {
453                 lockteams = true;
454                 bprint("^1The teams are now locked.\n");
455         }
456
457         // initiate the restart-countdown-announcer entity
458         if (sv_ready_restart_after_countdown)
459         {
460                 entity restart_timer = new_pure(restart_timer);
461                 setthink(restart_timer, ReadyRestart_think);
462                 restart_timer.nextthink = game_starttime;
463         }
464
465         // after a restart every players number of allowed timeouts gets reset, too
466         if (autocvar_sv_timeout)
467         {
468                 FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { CS(it).allowed_timeouts = autocvar_sv_timeout_number; });
469         }
470
471         if (!sv_ready_restart_after_countdown) reset_map(true);
472         if (autocvar_sv_eventlog) GameLogEcho(":restart");
473 }
474
475 void ReadyRestart()
476 {
477         if (MUTATOR_CALLHOOK(ReadyRestart_Deny) || game_stopped || race_completing) localcmd("restart\n");
478         else localcmd("\nsv_hook_gamerestart\n");
479
480         // Reset ALL scores, but only do that at the beginning of the countdown if sv_ready_restart_after_countdown is off!
481         // Otherwise scores could be manipulated during the countdown.
482         if (!sv_ready_restart_after_countdown) Score_ClearAll();
483         ReadyRestart_force();
484 }
485
486 // Count the players who are ready and determine whether or not to restart the match
487 void ReadyCount()
488 {
489         float ready_needed_factor, ready_needed_count;
490         float t_ready = 0, t_players = 0;
491
492         FOREACH_CLIENT(IS_REAL_CLIENT(it) && (IS_PLAYER(it) || it.caplayer == 1), {
493                 ++t_players;
494                 if (it.ready) ++t_ready;
495         });
496
497         readycount = t_ready;
498
499         Nagger_ReadyCounted();
500
501         ready_needed_factor = bound(0.5, cvar("g_warmup_majority_factor"), 0.999);
502         ready_needed_count = floor(t_players * ready_needed_factor) + 1;
503
504         if (readycount >= ready_needed_count) ReadyRestart();
505 }
506
507
508 // ======================================
509 //  Supporting functions for VoteCommand
510 // ======================================
511
512 float Votecommand_check_assignment(entity caller, float assignment)
513 {
514         float from_server = (!caller);
515
516         if ((assignment == VC_ASGNMNT_BOTH)
517             || ((!from_server && assignment == VC_ASGNMNT_CLIENTONLY)
518             || (from_server && assignment == VC_ASGNMNT_SERVERONLY))) return true;
519
520         return false;
521 }
522
523 string VoteCommand_extractcommand(string input, float startpos, int argc)
524 {
525         string output;
526
527         if ((argc - 1) < startpos) output = "";
528         else output = substring(input, argv_start_index(startpos), argv_end_index(-1) - argv_start_index(startpos));
529
530         return output;
531 }
532
533 float VoteCommand_checknasty(string vote_command)
534 {
535         if ((strstrofs(vote_command, ";", 0) >= 0)
536             || (strstrofs(vote_command, "\n", 0) >= 0)
537             || (strstrofs(vote_command, "\r", 0) >= 0)
538             || (strstrofs(vote_command, "$", 0) >= 0)) return false;
539
540         return true;
541 }
542
543 float VoteCommand_checkinlist(string vote_command, string list)
544 {
545         string l = strcat(" ", list, " ");
546
547         if (strstrofs(l, strcat(" ", vote_command, " "), 0) >= 0) return true;
548
549         return false;
550 }
551
552 string ValidateMap(string validated_map, entity caller)
553 {
554         validated_map = MapInfo_FixName(validated_map);
555
556         if (!validated_map)
557         {
558                 print_to(caller, "This map is not available on this server.");
559                 return string_null;
560         }
561
562         if (!autocvar_sv_vote_override_mostrecent && caller)
563         {
564                 if (Map_IsRecent(validated_map))
565                 {
566                         print_to(caller, "This server does not allow for recent maps to be played again. Please be patient for some rounds.");
567                         return string_null;
568                 }
569         }
570
571         if (!MapInfo_CheckMap(validated_map))
572         {
573                 print_to(caller, strcat("^1Invalid mapname, \"^3", validated_map, "^1\" does not support the current game mode."));
574                 return string_null;
575         }
576
577         return validated_map;
578 }
579
580 float VoteCommand_checkargs(float startpos, int argc)
581 {
582         float p, q, check, minargs;
583         string cvarname = strcat("sv_vote_command_restriction_", argv(startpos));
584         string cmdrestriction = "";  // No we don't.
585         string charlist, arg;
586         float checkmate;
587
588         if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
589                 cmdrestriction = cvar_string(cvarname);
590         else
591                 LOG_INFO("NOTE: ", cvarname, " does not exist, no restrictions will be applied.");
592
593         if (cmdrestriction == "") return true;
594
595         ++startpos;  // skip command name
596
597         // check minimum arg count
598
599         // 0 args: argc == startpos
600         // 1 args: argc == startpos + 1
601         // ...
602
603         minargs = stof(cmdrestriction);
604         if (argc - startpos < minargs) return false;
605
606         p = strstrofs(cmdrestriction, ";", 0);  // find first semicolon
607
608         for ( ; ; )
609         {
610                 // we know that at any time, startpos <= argc - minargs
611                 // so this means: argc-minargs >= startpos >= argc, thus
612                 // argc-minargs >= argc, thus minargs <= 0, thus all minargs
613                 // have been seen already
614
615                 if (startpos >= argc) // all args checked? GOOD
616                         break;
617
618                 if (p < 0)            // no more args? FAIL
619                 {
620                         // exception: exactly minargs left, this one included
621                         if (argc - startpos == minargs) break;
622
623                         // otherwise fail
624                         return false;
625                 }
626
627                 // cut to next semicolon
628                 q = strstrofs(cmdrestriction, ";", p + 1);  // find next semicolon
629                 if (q < 0) charlist = substring(cmdrestriction, p + 1, -1);
630                 else charlist = substring(cmdrestriction, p + 1, q - (p + 1));
631
632                 // in case we ever want to allow semicolons in VoteCommand_checknasty
633                 // charlist = strreplace("^^", ";", charlist);
634
635                 if (charlist != "")
636                 {
637                         // verify the arg only contains allowed chars
638                         arg = argv(startpos);
639                         checkmate = strlen(arg);
640                         for (check = 0; check < checkmate; ++check)
641                                 if (strstrofs(charlist, substring(arg, check, 1), 0) < 0) return false;
642                         // not allowed character
643                         // all characters are allowed. FINE.
644                 }
645
646                 ++startpos;
647                 --minargs;
648                 p = q;
649         }
650
651         return true;
652 }
653
654 int VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, int argc)
655 {
656         string first_command = argv(startpos);
657         int missing_chars = argv_start_index(startpos);
658
659         if (autocvar_sv_vote_limit > 0 && strlen(vote_command) > autocvar_sv_vote_limit)
660                 return 0;
661
662         if (!VoteCommand_checkinlist(first_command, vote_list)) return 0;
663
664         if (!VoteCommand_checkargs(startpos, argc)) return 0;
665
666         switch (MUTATOR_CALLHOOK(VoteCommand_Parse, caller, first_command, vote_command, startpos, argc))
667         {
668                 case MUT_VOTEPARSE_CONTINUE: { break; }
669                 case MUT_VOTEPARSE_SUCCESS: { return 1; }
670                 case MUT_VOTEPARSE_INVALID: { return -1; }
671                 case MUT_VOTEPARSE_UNACCEPTABLE: { return 0; }
672         }
673
674         switch (first_command) // now go through and parse the proper commands to adjust as needed.
675         {
676                 case "kick":
677                 case "kickban":    // catch all kick/kickban commands
678                 {
679                         entity victim = GetIndexedEntity(argc, (startpos + 1));
680                         float accepted = VerifyClientEntity(victim, true, false);
681
682                         if (accepted > 0)
683                         {
684                                 string reason = "No reason provided";
685                                 if(argc > next_token)
686                                         reason = substring(vote_command, argv_start_index(next_token) - missing_chars, -1);
687
688                                 string command_arguments = reason;
689                                 if (first_command == "kickban")
690                                         command_arguments = strcat(ftos(autocvar_g_ban_default_bantime), " ", ftos(autocvar_g_ban_default_masksize), " ~");
691
692                                 vote_parsed_command = strcat(first_command, " # ", ftos(etof(victim)), " ", command_arguments);
693                                 vote_parsed_display = sprintf("^1%s #%d ^7%s^1 %s", first_command, etof(victim), victim.netname, reason);
694                         }
695                         else { print_to(caller, strcat("vcall: ", GetClientErrorString(accepted, argv(startpos + 1)), ".\n")); return 0; }
696
697                         break;
698                 }
699
700                 case "map":
701                 case "chmap":
702                 case "gotomap":  // re-direct all map selection commands to gotomap
703                 {
704                         vote_command = ValidateMap(argv(startpos + 1), caller);
705                         if (!vote_command)  return -1;
706                         vote_parsed_command = strcat("gotomap ", vote_command);
707                         vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
708
709                         break;
710                 }
711
712                 case "nextmap": // TODO: replicate the old behaviour of being able to vote for maps from different modes on multimode servers (possibly support it in gotomap too), maybe fallback instead of aborting if map name is invalid?
713                 {
714                         vote_command = ValidateMap(argv(startpos + 1), caller);
715                         if (!vote_command)  return -1;
716                         vote_parsed_command = strcat("nextmap ", vote_command);
717                         vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
718
719                         break;
720                 }
721
722                 case "restart":
723                 {
724                         // add a delay so that vote result can be seen and announcer can be heard
725                         // if the vote is accepted
726                         vote_parsed_command = strcat("defer 1 ", vote_command);
727                         vote_parsed_display = strzone(strcat("^1", vote_command));
728
729                         break;
730                 }
731
732                 default:
733                 {
734                         vote_parsed_command = vote_command;
735                         vote_parsed_display = strzone(strcat("^1", vote_command));
736
737                         break;
738                 }
739         }
740
741         return 1;
742 }
743
744
745 // =======================
746 //  Command Sub-Functions
747 // =======================
748
749 void VoteCommand_abstain(int request, entity caller)  // CLIENT ONLY
750 {
751         switch (request)
752         {
753                 case CMD_REQUEST_COMMAND:
754                 {
755                         if (!vote_called) { print_to(caller, "^1No vote called."); }
756                         else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
757                         {
758                                 print_to(caller, "^1You have already voted.");
759                         }
760
761                         else  // everything went okay, continue changing vote
762                         {
763                                 print_to(caller, "^1You abstained from your vote.");
764                                 caller.vote_selection = VOTE_SELECT_ABSTAIN;
765                                 msg_entity = caller;
766                                 if (!autocvar_sv_vote_singlecount)   VoteCount(false); }
767
768                         return;
769                 }
770
771                 default:
772                 case CMD_REQUEST_USAGE:
773                 {
774                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote abstain"));
775                         print_to(caller, "  No arguments required.");
776                         return;
777                 }
778         }
779 }
780
781 void VoteCommand_call(int request, entity caller, int argc, string vote_command)  // BOTH
782 {
783         switch (request)
784         {
785                 case CMD_REQUEST_COMMAND:
786                 {
787                         float tmp_playercount = 0;
788                         int parse_error;
789
790                         vote_command = VoteCommand_extractcommand(vote_command, 2, argc);
791
792                         if (!autocvar_sv_vote_call && caller) { print_to(caller, "^1Vote calling is not allowed."); }
793                         else if (!autocvar_sv_vote_gamestart && time < game_starttime)
794                         {
795                                 print_to(caller, "^1Vote calling is not allowed before the match has started.");
796                         }
797                         else if (vote_called)
798                         {
799                                 print_to(caller, "^1There is already a vote called.");
800                         }
801                         else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
802                         {
803                                 print_to(caller, "^1Only players can call a vote.");
804                         }
805                         else if (caller && !IS_CLIENT(caller))
806                         {
807                                 print_to(caller, "^1Only connected clients can vote.");
808                         }
809                         else if (timeout_status)
810                         {
811                                 print_to(caller, "^1You can not call a vote while a timeout is active.");
812                         }
813                         else if (caller && (time < caller.vote_waittime))
814                         {
815                                 print_to(caller, strcat("^1You have to wait ^2", ftos(ceil(caller.vote_waittime - time)), "^1 seconds before you can again call a vote."));
816                         }
817                         else if (!VoteCommand_checknasty(vote_command))
818                         {
819                                 print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
820                         }
821                         else if ((parse_error = VoteCommand_parse(caller, vote_command, autocvar_sv_vote_commands, 2, argc)) <= 0)
822                         {
823                                 if(parse_error == 0)
824                                         print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
825                         }
826                         else  // everything went okay, continue with calling the vote
827                         {
828                                 vote_caller = caller;  // remember who called the vote
829                                 vote_caller_name = strzone(GetCallerName(vote_caller));
830                                 vote_called = VOTE_NORMAL;
831                                 vote_called_command = strzone(vote_parsed_command);
832                                 vote_called_display = strzone(vote_parsed_display);
833                                 vote_endtime = time + autocvar_sv_vote_timeout;
834
835                                 if (caller)
836                                 {
837                                         caller.vote_selection = VOTE_SELECT_ACCEPT;
838                                         caller.vote_waittime = time + autocvar_sv_vote_wait;
839                                         msg_entity = caller;
840                                 }
841
842                                 FOREACH_CLIENT(IS_REAL_CLIENT(it), { ++tmp_playercount; });
843
844                                 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote for ", vote_called_display, "\n");
845                                 if (autocvar_sv_eventlog)
846                                         GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
847                                 Nagger_VoteChanged();
848                                 VoteCount(true);  // needed if you are the only one
849
850                                 if (tmp_playercount > 1 && vote_called != VOTE_NULL)
851                                         Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_CALL);
852                         }
853
854                         return;
855                 }
856
857                 default:
858                 case CMD_REQUEST_USAGE:
859                 {
860                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote call command"));
861                         print_to(caller, "  Where 'command' is the command to request a vote upon.");
862                         print_to(caller, strcat("Examples: ", GetCommandPrefix(caller), " vote call gotomap dance"));
863                         print_to(caller, strcat("          ", GetCommandPrefix(caller), " vote call endmatch"));
864                         return;
865                 }
866         }
867 }
868
869 void VoteCommand_master(int request, entity caller, int argc, string vote_command)  // CLIENT ONLY
870 {
871         switch (request)
872         {
873                 case CMD_REQUEST_COMMAND:
874                 {
875                         if (autocvar_sv_vote_master)
876                         {
877                                 switch (strtolower(argv(2)))
878                                 {
879                                         case "do":
880                                         {
881                                                 int parse_error;
882                                                 vote_command = VoteCommand_extractcommand(vote_command, 3, argc);
883
884                                                 if (!caller.vote_master)
885                                                         print_to(caller, "^1You do not have vote master privileges.");
886                                                 else if (!VoteCommand_checknasty(vote_command))
887                                                 {
888                                                         print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
889                                                 }
890                                                 else if ((parse_error = VoteCommand_parse(caller, vote_command, strcat(autocvar_sv_vote_commands, " ", autocvar_sv_vote_master_commands), 3, argc)) <= 0)
891                                                 {
892                                                         if(parse_error == 0)
893                                                                 print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
894                                                 }
895                                                 else  // everything went okay, proceed with command
896                                                 {
897                                                         localcmd(strcat(vote_parsed_command, "\n"));
898                                                         print_to(caller, strcat("Executing command '", vote_parsed_display, "' on server."));
899                                                         bprint("\{1}^2* ^3", GetCallerName(caller), "^2 used their ^3master^2 status to do \"^2", vote_parsed_display, "^2\".\n");
900                                                         if (autocvar_sv_eventlog)
901                                                                 GameLogEcho(strcat(":vote:vdo:", ftos(caller.playerid), ":", vote_parsed_display));
902                                                 }
903
904                                                 return;
905                                         }
906
907                                         case "login":
908                                         {
909                                                 if (autocvar_sv_vote_master_password == "") { print_to(caller, "^1Login to vote master is not allowed."); }
910                                                 else if (caller.vote_master)
911                                                 {
912                                                         print_to(caller, "^1You are already logged in as vote master.");
913                                                 }
914                                                 else if (autocvar_sv_vote_master_password != argv(3))
915                                                 {
916                                                         print_to(caller, strcat("Rejected vote master login from ", GetCallerName(caller)));
917                                                 }
918                                                 else  // everything went okay, proceed with giving this player master privilages
919                                                 {
920                                                         caller.vote_master = true;
921                                                         print_to(caller, strcat("Accepted vote master login from ", GetCallerName(caller)));
922                                                         bprint("\{1}^2* ^3", GetCallerName(caller), "^2 logged in as ^3master^2\n");
923                                                         if (autocvar_sv_eventlog)
924                                                                 GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid)));
925                                                 }
926
927                                                 return;
928                                         }
929
930                                         default:  // calling a vote for master
931                                         {
932                                                 if (!autocvar_sv_vote_master_callable) { print_to(caller, "^1Vote to become vote master is not allowed."); }
933                                                 else if (vote_called)
934                                                 {
935                                                         print_to(caller, "^1There is already a vote called.");
936                                                 }
937                                                 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
938                                                 {
939                                                         print_to(caller, "^1Only players can call a vote.");
940                                                 }
941                                                 else if (timeout_status)
942                                                 {
943                                                         print_to(caller, "^1You can not call a vote while a timeout is active.");
944                                                 }
945                                                 else  // everything went okay, continue with creating vote
946                                                 {
947                                                         vote_caller = caller;
948                                                         vote_caller_name = strzone(GetCallerName(vote_caller));
949                                                         vote_called = VOTE_MASTER;
950                                                         vote_called_command = strzone("XXX");
951                                                         vote_called_display = strzone("^3master");
952                                                         vote_endtime = time + autocvar_sv_vote_timeout;
953
954                                                         caller.vote_selection = VOTE_SELECT_ACCEPT;
955                                                         caller.vote_waittime = time + autocvar_sv_vote_wait;
956
957                                                         bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote to become ^3master^2.\n");
958                                                         if (autocvar_sv_eventlog)
959                                                                 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
960                                                         Nagger_VoteChanged();
961                                                         VoteCount(true);  // needed if you are the only one
962                                                 }
963
964                                                 return;
965                                         }
966                                 }
967                         }
968                         else { print_to(caller, "^1Master control of voting is not allowed."); }
969
970                         return;
971                 }
972
973                 default:
974                 case CMD_REQUEST_USAGE:
975                 {
976                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote master [action [command | password]]"));
977                         print_to(caller, "  If action is left blank, it calls a vote for you to become master.");
978                         print_to(caller, "  Otherwise the actions are either 'do' a command or 'login' as master.");
979                         return;
980                 }
981         }
982 }
983
984 void VoteCommand_no(int request, entity caller)  // CLIENT ONLY
985 {
986         switch (request)
987         {
988                 case CMD_REQUEST_COMMAND:
989                 {
990                         if (!vote_called) { print_to(caller, "^1No vote called."); }
991                         else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
992                         {
993                                 print_to(caller, "^1You have already voted.");
994                         }
995                         else if (((caller == vote_caller) || caller.vote_master) && autocvar_sv_vote_no_stops_vote)
996                         {
997                                 VoteStop(caller);
998                         }
999
1000                         else  // everything went okay, continue changing vote
1001                         {
1002                                 print_to(caller, "^1You rejected the vote.");
1003                                 caller.vote_selection = VOTE_SELECT_REJECT;
1004                                 msg_entity = caller;
1005                                 if (!autocvar_sv_vote_singlecount)
1006                                         VoteCount(false);
1007                         }
1008
1009                         return;
1010                 }
1011
1012                 default:
1013                 case CMD_REQUEST_USAGE:
1014                 {
1015                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote no"));
1016                         print_to(caller, "  No arguments required.");
1017                         return;
1018                 }
1019         }
1020 }
1021
1022 void VoteCommand_status(int request, entity caller)  // BOTH
1023 {
1024         switch (request)
1025         {
1026                 case CMD_REQUEST_COMMAND:
1027                 {
1028                         if (vote_called) print_to(caller, strcat("^7Vote for ", vote_called_display, "^7 called by ^7", OriginalCallerName(), "^7."));
1029                         else print_to(caller, "^1No vote called.");
1030
1031                         return;
1032                 }
1033
1034                 default:
1035                 case CMD_REQUEST_USAGE:
1036                 {
1037                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote status"));
1038                         print_to(caller, "  No arguments required.");
1039                         return;
1040                 }
1041         }
1042 }
1043
1044 void VoteCommand_stop(int request, entity caller)  // BOTH
1045 {
1046         switch (request)
1047         {
1048                 case CMD_REQUEST_COMMAND:
1049                 {
1050                         if (!vote_called)   print_to(caller, "^1No vote called.");
1051                         else if ((caller == vote_caller) || !caller || caller.vote_master)   VoteStop(caller);
1052                         else   print_to(caller, "^1You are not allowed to stop that vote.");
1053                         return;
1054                 }
1055
1056                 default:
1057                 case CMD_REQUEST_USAGE:
1058                 {
1059                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote stop"));
1060                         print_to(caller, "  No arguments required.");
1061                         return;
1062                 }
1063         }
1064 }
1065
1066 void VoteCommand_yes(int request, entity caller)  // CLIENT ONLY
1067 {
1068         switch (request)
1069         {
1070                 case CMD_REQUEST_COMMAND:
1071                 {
1072                         if (!vote_called) { print_to(caller, "^1No vote called."); }
1073                         else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
1074                         {
1075                                 print_to(caller, "^1You have already voted.");
1076                         }
1077                         else  // everything went okay, continue changing vote
1078                         {
1079                                 print_to(caller, "^1You accepted the vote.");
1080                                 caller.vote_selection = VOTE_SELECT_ACCEPT;
1081                                 msg_entity = caller;
1082                                 if (!autocvar_sv_vote_singlecount)
1083                                         VoteCount(false);
1084                         }
1085
1086                         return;
1087                 }
1088
1089                 default:
1090                 case CMD_REQUEST_USAGE:
1091                 {
1092                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote yes"));
1093                         print_to(caller, "  No arguments required.");
1094                         return;
1095                 }
1096         }
1097 }
1098
1099 /* use this when creating a new command, making sure to place it in alphabetical order... also,
1100 ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
1101 void VoteCommand_(int request)
1102 {
1103     switch(request)
1104     {
1105         case CMD_REQUEST_COMMAND:
1106         {
1107
1108             return;
1109         }
1110
1111         default:
1112         case CMD_REQUEST_USAGE:
1113         {
1114             print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote ");
1115             print_to(caller, "  No arguments required.");
1116             return;
1117         }
1118     }
1119 }
1120 */
1121
1122
1123 // ================================
1124 //  Macro system for vote commands
1125 // ================================
1126
1127 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
1128 #define VOTE_COMMANDS(request, caller, arguments, command) \
1129         VOTE_COMMAND("abstain", VoteCommand_abstain(request, caller), "Abstain your vote in current vote", VC_ASGNMNT_CLIENTONLY) \
1130         VOTE_COMMAND("call", VoteCommand_call(request, caller, arguments, command), "Create a new vote for players to decide on", VC_ASGNMNT_BOTH) \
1131         VOTE_COMMAND("help", VoteCommand_macro_help(caller, arguments), "Shows this information", VC_ASGNMNT_BOTH) \
1132         VOTE_COMMAND("master", VoteCommand_master(request, caller, arguments, command), "Full control over all voting and vote commands", VC_ASGNMNT_CLIENTONLY) \
1133         VOTE_COMMAND("no", VoteCommand_no(request, caller), "Select no in current vote", VC_ASGNMNT_CLIENTONLY) \
1134         VOTE_COMMAND("status", VoteCommand_status(request, caller), "Prints information about current vote", VC_ASGNMNT_BOTH) \
1135         VOTE_COMMAND("stop", VoteCommand_stop(request, caller), "Immediately end a vote", VC_ASGNMNT_BOTH) \
1136         VOTE_COMMAND("yes", VoteCommand_yes(request, caller), "Select yes in current vote", VC_ASGNMNT_CLIENTONLY) \
1137         /* nothing */
1138
1139 void VoteCommand_macro_help(entity caller, int argc)
1140 {
1141         string command_origin = GetCommandPrefix(caller);
1142
1143         if (argc == 2 || argv(2) == "help")  // help display listing all commands
1144         {
1145                 print_to(caller, "\nVoting commands:\n");
1146                 #define VOTE_COMMAND(name, function, description, assignment) \
1147                         { if (Votecommand_check_assignment(caller, assignment)) { print_to(caller, strcat("  ^2", name, "^7: ", description)); } }
1148
1149                 VOTE_COMMANDS(0, caller, 0, "");
1150 #undef VOTE_COMMAND
1151
1152                 print_to(caller, strcat("\nUsage:^3 ", command_origin, " vote COMMAND...^7, where possible commands are listed above.\n"));
1153                 print_to(caller, strcat("For help about a specific command, type ", command_origin, " vote help COMMAND"));
1154                 print_to(caller, strcat("\n^7You can call a vote for or execute these commands: ^3", autocvar_sv_vote_commands, "^7 and maybe further ^3arguments^7"));
1155         }
1156         else  // usage for individual command
1157         {
1158                 #define VOTE_COMMAND(name, function, description, assignment) \
1159                         { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(2))) { function; return; } } }
1160
1161                 VOTE_COMMANDS(CMD_REQUEST_USAGE, caller, argc, "");
1162 #undef VOTE_COMMAND
1163
1164                 string cvarname = strcat("sv_vote_command_help_", argv(2));
1165                 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
1166                         wordwrap_sprint(caller, cvar_string(cvarname), 1000);
1167                 else
1168                         print_to(caller, "No documentation exists for this vote");
1169         }
1170 }
1171
1172 float VoteCommand_macro_command(entity caller, int argc, string vote_command)
1173 {
1174         #define VOTE_COMMAND(name, function, description, assignment) \
1175                 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(1))) { function; return true; } } }
1176
1177         VOTE_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, vote_command);
1178 #undef VOTE_COMMAND
1179
1180         return false;
1181 }
1182
1183
1184 // ======================================
1185 //  Main function handling vote commands
1186 // ======================================
1187
1188 void VoteCommand(int request, entity caller, int argc, string vote_command)
1189 {
1190         // Guide for working with argc arguments by example:
1191         // argc:   1    - 2      - 3     - 4
1192         // argv:   0    - 1      - 2     - 3
1193         // cmd     vote - master - login - password
1194
1195         switch (request)
1196         {
1197                 case CMD_REQUEST_COMMAND:
1198                 {
1199                         if (VoteCommand_macro_command(caller, argc, vote_command)) return;
1200                 }
1201
1202                 default:
1203                         print_to(caller, strcat(((argv(1) != "") ? strcat("Unknown vote command \"", argv(1), "\"") : "No command provided"), ". For a list of supported commands, try ", GetCommandPrefix(caller), " vote help.\n"));
1204                 case CMD_REQUEST_USAGE:
1205                 {
1206                         VoteCommand_macro_help(caller, argc);
1207                         return;
1208                 }
1209         }
1210 }