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