]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/command/vote.qc
Merge branch 'master' into Lyberta/TeamplayOverhaul2
[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         MUTATOR_CALLHOOK(reset_map_global);
349
350         FOREACH_ENTITY_FLOAT_ORDERED(pure_data, false,
351         {
352                 if(IS_CLIENT(it))
353                         continue;
354                 if (it.reset)
355                 {
356                         it.reset(it);
357                         continue;
358                 }
359                 if (it.team_saved) it.team = it.team_saved;
360                 if (it.flags & FL_PROJECTILE) delete(it);  // remove any projectiles left
361         });
362
363         // Waypoints and assault start come LAST
364         FOREACH_ENTITY_ORDERED(IS_NOT_A_CLIENT(it), {
365                 if (it.reset2) it.reset2(it);
366         });
367
368         FOREACH_CLIENT(IS_PLAYER(it) && STAT(FROZEN, it), { Unfreeze(it); });
369
370         // Moving the player reset code here since the player-reset depends
371         // on spawnpoint entities which have to be reset first --blub
372         if (dorespawn)
373         {
374                 if (!MUTATOR_CALLHOOK(reset_map_players))
375                 {
376                         if (restart_mapalreadyrestarted || (time < game_starttime))
377                         {
378                                 FOREACH_CLIENT(IS_PLAYER(it),
379                                 {
380                                         /*
381                                         only reset players if a restart countdown is active
382                                         this can either be due to cvar sv_ready_restart_after_countdown having set
383                                         restart_mapalreadyrestarted to 1 after the countdown ended or when
384                                         sv_ready_restart_after_countdown is not used and countdown is still running
385                                         */
386                                         // NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players
387                                         // PlayerScore_Clear(it);
388                                         CS(it).killcount = 0;
389                                         // stop the player from moving so that he stands still once he gets respawned
390                                         it.velocity = '0 0 0';
391                                         it.avelocity = '0 0 0';
392                                         CS(it).movement = '0 0 0';
393                                         PutClientInServer(it);
394                                 });
395                         }
396                 }
397         }
398 }
399
400 // Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set)
401 void ReadyRestart_think(entity this)
402 {
403         restart_mapalreadyrestarted = true;
404         reset_map(true);
405         Score_ClearAll();
406         delete(this);
407 }
408
409 // Forces a restart of the game without actually reloading the map // this is a mess...
410 void ReadyRestart_force()
411 {
412         if (time <= game_starttime && game_stopped)
413                 return;
414
415         bprint("^1Server is restarting...\n");
416
417         VoteReset();
418
419         // clear overtime, we have to decrease timelimit to its original value again.
420         if (checkrules_overtimesadded > 0 && g_race_qualifying != 2)
421                 cvar_set("timelimit", ftos(autocvar_timelimit - (checkrules_overtimesadded * autocvar_timelimit_overtime)));
422         checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0;
423
424         readyrestart_happened = true;
425         game_starttime = time + RESTART_COUNTDOWN;
426
427         // clear player attributes
428         FOREACH_CLIENT(IS_PLAYER(it), {
429                 it.alivetime = 0;
430                 CS(it).killcount = 0;
431                 float val = PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, 0);
432                 PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, -val);
433         });
434
435         restart_mapalreadyrestarted = false; // reset this var, needed when cvar sv_ready_restart_repeatable is in use
436
437         // disable the warmup global for the server
438         warmup_stage = 0;                // once the game is restarted the game is in match stage
439
440         // reset the .ready status of all players (also spectators)
441         FOREACH_CLIENT(IS_REAL_CLIENT(it), { it.ready = false; });
442         readycount = 0;
443         Nagger_ReadyCounted();  // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
444
445         // lock teams with lockonrestart
446         if (autocvar_teamplay_lockonrestart && teamplay)
447         {
448                 lockteams = true;
449                 bprint("^1The teams are now locked.\n");
450         }
451
452         // initiate the restart-countdown-announcer entity
453         if (sv_ready_restart_after_countdown)
454         {
455                 entity restart_timer = new_pure(restart_timer);
456                 setthink(restart_timer, ReadyRestart_think);
457                 restart_timer.nextthink = game_starttime;
458         }
459
460         // after a restart every players number of allowed timeouts gets reset, too
461         if (autocvar_sv_timeout)
462         {
463                 FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { CS(it).allowed_timeouts = autocvar_sv_timeout_number; });
464         }
465
466         if (!sv_ready_restart_after_countdown) reset_map(true);
467         if (autocvar_sv_eventlog) GameLogEcho(":restart");
468 }
469
470 void ReadyRestart()
471 {
472         if (MUTATOR_CALLHOOK(ReadyRestart_Deny) || game_stopped || race_completing) localcmd("restart\n");
473         else localcmd("\nsv_hook_gamerestart\n");
474
475         // Reset ALL scores, but only do that at the beginning of the countdown if sv_ready_restart_after_countdown is off!
476         // Otherwise scores could be manipulated during the countdown.
477         if (!sv_ready_restart_after_countdown) Score_ClearAll();
478         ReadyRestart_force();
479 }
480
481 // Count the players who are ready and determine whether or not to restart the match
482 void ReadyCount()
483 {
484         float ready_needed_factor, ready_needed_count;
485         float t_ready = 0, t_players = 0;
486
487         FOREACH_CLIENT(IS_REAL_CLIENT(it) && (IS_PLAYER(it) || it.caplayer == 1), {
488                 ++t_players;
489                 if (it.ready) ++t_ready;
490         });
491
492         readycount = t_ready;
493
494         Nagger_ReadyCounted();
495
496         ready_needed_factor = bound(0.5, cvar("g_warmup_majority_factor"), 0.999);
497         ready_needed_count = floor(t_players * ready_needed_factor) + 1;
498
499         if (readycount >= ready_needed_count) ReadyRestart();
500 }
501
502
503 // ======================================
504 //  Supporting functions for VoteCommand
505 // ======================================
506
507 float Votecommand_check_assignment(entity caller, float assignment)
508 {
509         float from_server = (!caller);
510
511         if ((assignment == VC_ASGNMNT_BOTH)
512             || ((!from_server && assignment == VC_ASGNMNT_CLIENTONLY)
513             || (from_server && assignment == VC_ASGNMNT_SERVERONLY))) return true;
514
515         return false;
516 }
517
518 string VoteCommand_extractcommand(string input, float startpos, int argc)
519 {
520         string output;
521
522         if ((argc - 1) < startpos) output = "";
523         else output = substring(input, argv_start_index(startpos), argv_end_index(-1) - argv_start_index(startpos));
524
525         return output;
526 }
527
528 float VoteCommand_checknasty(string vote_command)
529 {
530         if ((strstrofs(vote_command, ";", 0) >= 0)
531             || (strstrofs(vote_command, "\n", 0) >= 0)
532             || (strstrofs(vote_command, "\r", 0) >= 0)
533             || (strstrofs(vote_command, "$", 0) >= 0)) return false;
534
535         return true;
536 }
537
538 float VoteCommand_checkinlist(string vote_command, string list)
539 {
540         string l = strcat(" ", list, " ");
541
542         if (strstrofs(l, strcat(" ", vote_command, " "), 0) >= 0) return true;
543
544         return false;
545 }
546
547 string ValidateMap(string validated_map, entity caller)
548 {
549         validated_map = MapInfo_FixName(validated_map);
550
551         if (!validated_map)
552         {
553                 print_to(caller, "This map is not available on this server.");
554                 return string_null;
555         }
556
557         if (!autocvar_sv_vote_override_mostrecent && caller)
558         {
559                 if (Map_IsRecent(validated_map))
560                 {
561                         print_to(caller, "This server does not allow for recent maps to be played again. Please be patient for some rounds.");
562                         return string_null;
563                 }
564         }
565
566         if (!MapInfo_CheckMap(validated_map))
567         {
568                 print_to(caller, strcat("^1Invalid mapname, \"^3", validated_map, "^1\" does not support the current game mode."));
569                 return string_null;
570         }
571
572         return validated_map;
573 }
574
575 float VoteCommand_checkargs(float startpos, int argc)
576 {
577         float p, q, check, minargs;
578         string cvarname = strcat("sv_vote_command_restriction_", argv(startpos));
579         string cmdrestriction = "";  // No we don't.
580         string charlist, arg;
581         float checkmate;
582
583         if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
584                 cmdrestriction = cvar_string(cvarname);
585         else
586                 LOG_INFO("NOTE: ", cvarname, " does not exist, no restrictions will be applied.");
587
588         if (cmdrestriction == "") return true;
589
590         ++startpos;  // skip command name
591
592         // check minimum arg count
593
594         // 0 args: argc == startpos
595         // 1 args: argc == startpos + 1
596         // ...
597
598         minargs = stof(cmdrestriction);
599         if (argc - startpos < minargs) return false;
600
601         p = strstrofs(cmdrestriction, ";", 0);  // find first semicolon
602
603         for ( ; ; )
604         {
605                 // we know that at any time, startpos <= argc - minargs
606                 // so this means: argc-minargs >= startpos >= argc, thus
607                 // argc-minargs >= argc, thus minargs <= 0, thus all minargs
608                 // have been seen already
609
610                 if (startpos >= argc) // all args checked? GOOD
611                         break;
612
613                 if (p < 0)            // no more args? FAIL
614                 {
615                         // exception: exactly minargs left, this one included
616                         if (argc - startpos == minargs) break;
617
618                         // otherwise fail
619                         return false;
620                 }
621
622                 // cut to next semicolon
623                 q = strstrofs(cmdrestriction, ";", p + 1);  // find next semicolon
624                 if (q < 0) charlist = substring(cmdrestriction, p + 1, -1);
625                 else charlist = substring(cmdrestriction, p + 1, q - (p + 1));
626
627                 // in case we ever want to allow semicolons in VoteCommand_checknasty
628                 // charlist = strreplace("^^", ";", charlist);
629
630                 if (charlist != "")
631                 {
632                         // verify the arg only contains allowed chars
633                         arg = argv(startpos);
634                         checkmate = strlen(arg);
635                         for (check = 0; check < checkmate; ++check)
636                                 if (strstrofs(charlist, substring(arg, check, 1), 0) < 0) return false;
637                         // not allowed character
638                         // all characters are allowed. FINE.
639                 }
640
641                 ++startpos;
642                 --minargs;
643                 p = q;
644         }
645
646         return true;
647 }
648
649 int VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, int argc)
650 {
651         string first_command = argv(startpos);
652         int missing_chars = argv_start_index(startpos);
653
654         if (autocvar_sv_vote_limit > 0 && strlen(vote_command) > autocvar_sv_vote_limit)
655                 return 0;
656
657         if (!VoteCommand_checkinlist(first_command, vote_list)) return 0;
658
659         if (!VoteCommand_checkargs(startpos, argc)) return 0;
660
661         switch (MUTATOR_CALLHOOK(VoteCommand_Parse, caller, first_command, vote_command, startpos, argc))
662         {
663                 case MUT_VOTEPARSE_CONTINUE: { break; }
664                 case MUT_VOTEPARSE_SUCCESS: { return 1; }
665                 case MUT_VOTEPARSE_INVALID: { return -1; }
666                 case MUT_VOTEPARSE_UNACCEPTABLE: { return 0; }
667         }
668
669         switch (first_command) // now go through and parse the proper commands to adjust as needed.
670         {
671                 case "kick":
672                 case "kickban":    // catch all kick/kickban commands
673                 {
674                         entity victim = GetIndexedEntity(argc, (startpos + 1));
675                         float accepted = VerifyClientEntity(victim, true, false);
676
677                         if (accepted > 0)
678                         {
679                                 string reason = "No reason provided";
680                                 if(argc > next_token)
681                                         reason = substring(vote_command, argv_start_index(next_token) - missing_chars, -1);
682
683                                 string command_arguments = reason;
684                                 if (first_command == "kickban")
685                                         command_arguments = strcat(ftos(autocvar_g_ban_default_bantime), " ", ftos(autocvar_g_ban_default_masksize), " ~");
686
687                                 vote_parsed_command = strcat(first_command, " # ", ftos(etof(victim)), " ", command_arguments);
688                                 vote_parsed_display = sprintf("^1%s #%d ^7%s^1 %s", first_command, etof(victim), victim.netname, reason);
689                         }
690                         else { print_to(caller, strcat("vcall: ", GetClientErrorString(accepted, argv(startpos + 1)), ".\n")); return 0; }
691
692                         break;
693                 }
694
695                 case "map":
696                 case "chmap":
697                 case "gotomap":  // re-direct all map selection commands to gotomap
698                 {
699                         vote_command = ValidateMap(argv(startpos + 1), caller);
700                         if (!vote_command)  return -1;
701                         vote_parsed_command = strcat("gotomap ", vote_command);
702                         vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
703
704                         break;
705                 }
706
707                 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?
708                 {
709                         vote_command = ValidateMap(argv(startpos + 1), caller);
710                         if (!vote_command)  return -1;
711                         vote_parsed_command = strcat("nextmap ", vote_command);
712                         vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
713
714                         break;
715                 }
716
717                 default:
718                 {
719                         vote_parsed_command = vote_command;
720                         vote_parsed_display = strzone(strcat("^1", vote_command));
721
722                         break;
723                 }
724         }
725
726         return 1;
727 }
728
729
730 // =======================
731 //  Command Sub-Functions
732 // =======================
733
734 void VoteCommand_abstain(int request, entity caller)  // CLIENT ONLY
735 {
736         switch (request)
737         {
738                 case CMD_REQUEST_COMMAND:
739                 {
740                         if (!vote_called) { print_to(caller, "^1No vote called."); }
741                         else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
742                         {
743                                 print_to(caller, "^1You have already voted.");
744                         }
745
746                         else  // everything went okay, continue changing vote
747                         {
748                                 print_to(caller, "^1You abstained from your vote.");
749                                 caller.vote_selection = VOTE_SELECT_ABSTAIN;
750                                 msg_entity = caller;
751                                 if (!autocvar_sv_vote_singlecount)   VoteCount(false); }
752
753                         return;
754                 }
755
756                 default:
757                 case CMD_REQUEST_USAGE:
758                 {
759                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote abstain"));
760                         print_to(caller, "  No arguments required.");
761                         return;
762                 }
763         }
764 }
765
766 void VoteCommand_call(int request, entity caller, int argc, string vote_command)  // BOTH
767 {
768         switch (request)
769         {
770                 case CMD_REQUEST_COMMAND:
771                 {
772                         float tmp_playercount = 0;
773                         int parse_error;
774
775                         vote_command = VoteCommand_extractcommand(vote_command, 2, argc);
776
777                         if (!autocvar_sv_vote_call && caller) { print_to(caller, "^1Vote calling is not allowed."); }
778                         else if (!autocvar_sv_vote_gamestart && time < game_starttime)
779                         {
780                                 print_to(caller, "^1Vote calling is not allowed before the match has started.");
781                         }
782                         else if (vote_called)
783                         {
784                                 print_to(caller, "^1There is already a vote called.");
785                         }
786                         else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
787                         {
788                                 print_to(caller, "^1Only players can call a vote.");
789                         }
790                         else if (caller && !IS_CLIENT(caller))
791                         {
792                                 print_to(caller, "^1Only connected clients can vote.");
793                         }
794                         else if (timeout_status)
795                         {
796                                 print_to(caller, "^1You can not call a vote while a timeout is active.");
797                         }
798                         else if (caller && (time < caller.vote_waittime))
799                         {
800                                 print_to(caller, strcat("^1You have to wait ^2", ftos(ceil(caller.vote_waittime - time)), "^1 seconds before you can again call a vote."));
801                         }
802                         else if (!VoteCommand_checknasty(vote_command))
803                         {
804                                 print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
805                         }
806                         else if ((parse_error = VoteCommand_parse(caller, vote_command, autocvar_sv_vote_commands, 2, argc)) <= 0)
807                         {
808                                 if(parse_error == 0)
809                                         print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
810                         }
811                         else  // everything went okay, continue with calling the vote
812                         {
813                                 vote_caller = caller;  // remember who called the vote
814                                 vote_caller_name = strzone(GetCallerName(vote_caller));
815                                 vote_called = VOTE_NORMAL;
816                                 vote_called_command = strzone(vote_parsed_command);
817                                 vote_called_display = strzone(vote_parsed_display);
818                                 vote_endtime = time + autocvar_sv_vote_timeout;
819
820                                 if (caller)
821                                 {
822                                         caller.vote_selection = VOTE_SELECT_ACCEPT;
823                                         caller.vote_waittime = time + autocvar_sv_vote_wait;
824                                         msg_entity = caller;
825                                 }
826
827                                 FOREACH_CLIENT(IS_REAL_CLIENT(it), { ++tmp_playercount; });
828                                 if (tmp_playercount > 1)
829                                         Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_CALL);
830
831                                 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote for ", vote_called_display, "\n");
832                                 if (autocvar_sv_eventlog)
833                                         GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
834                                 Nagger_VoteChanged();
835                                 VoteCount(true);  // needed if you are the only one
836                         }
837
838                         return;
839                 }
840
841                 default:
842                 case CMD_REQUEST_USAGE:
843                 {
844                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote call command"));
845                         print_to(caller, "  Where 'command' is the command to request a vote upon.");
846                         print_to(caller, strcat("Examples: ", GetCommandPrefix(caller), " vote call gotomap dance"));
847                         print_to(caller, strcat("          ", GetCommandPrefix(caller), " vote call endmatch"));
848                         return;
849                 }
850         }
851 }
852
853 void VoteCommand_master(int request, entity caller, int argc, string vote_command)  // CLIENT ONLY
854 {
855         switch (request)
856         {
857                 case CMD_REQUEST_COMMAND:
858                 {
859                         if (autocvar_sv_vote_master)
860                         {
861                                 switch (strtolower(argv(2)))
862                                 {
863                                         case "do":
864                                         {
865                                                 int parse_error;
866                                                 vote_command = VoteCommand_extractcommand(vote_command, 3, argc);
867
868                                                 if (!caller.vote_master)
869                                                         print_to(caller, "^1You do not have vote master privileges.");
870                                                 else if (!VoteCommand_checknasty(vote_command))
871                                                 {
872                                                         print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
873                                                 }
874                                                 else if ((parse_error = VoteCommand_parse(caller, vote_command, strcat(autocvar_sv_vote_commands, " ", autocvar_sv_vote_master_commands), 3, argc)) <= 0)
875                                                 {
876                                                         if(parse_error == 0)
877                                                                 print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
878                                                 }
879                                                 else  // everything went okay, proceed with command
880                                                 {
881                                                         localcmd(strcat(vote_parsed_command, "\n"));
882                                                         print_to(caller, strcat("Executing command '", vote_parsed_display, "' on server."));
883                                                         bprint("\{1}^2* ^3", GetCallerName(caller), "^2 used their ^3master^2 status to do \"^2", vote_parsed_display, "^2\".\n");
884                                                         if (autocvar_sv_eventlog)
885                                                                 GameLogEcho(strcat(":vote:vdo:", ftos(caller.playerid), ":", vote_parsed_display));
886                                                 }
887
888                                                 return;
889                                         }
890
891                                         case "login":
892                                         {
893                                                 if (autocvar_sv_vote_master_password == "") { print_to(caller, "^1Login to vote master is not allowed."); }
894                                                 else if (caller.vote_master)
895                                                 {
896                                                         print_to(caller, "^1You are already logged in as vote master.");
897                                                 }
898                                                 else if (autocvar_sv_vote_master_password != argv(3))
899                                                 {
900                                                         print_to(caller, strcat("Rejected vote master login from ", GetCallerName(caller)));
901                                                 }
902                                                 else  // everything went okay, proceed with giving this player master privilages
903                                                 {
904                                                         caller.vote_master = true;
905                                                         print_to(caller, strcat("Accepted vote master login from ", GetCallerName(caller)));
906                                                         bprint("\{1}^2* ^3", GetCallerName(caller), "^2 logged in as ^3master^2\n");
907                                                         if (autocvar_sv_eventlog)
908                                                                 GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid)));
909                                                 }
910
911                                                 return;
912                                         }
913
914                                         default:  // calling a vote for master
915                                         {
916                                                 if (!autocvar_sv_vote_master_callable) { print_to(caller, "^1Vote to become vote master is not allowed."); }
917                                                 else if (vote_called)
918                                                 {
919                                                         print_to(caller, "^1There is already a vote called.");
920                                                 }
921                                                 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
922                                                 {
923                                                         print_to(caller, "^1Only players can call a vote.");
924                                                 }
925                                                 else if (timeout_status)
926                                                 {
927                                                         print_to(caller, "^1You can not call a vote while a timeout is active.");
928                                                 }
929                                                 else  // everything went okay, continue with creating vote
930                                                 {
931                                                         vote_caller = caller;
932                                                         vote_caller_name = strzone(GetCallerName(vote_caller));
933                                                         vote_called = VOTE_MASTER;
934                                                         vote_called_command = strzone("XXX");
935                                                         vote_called_display = strzone("^3master");
936                                                         vote_endtime = time + autocvar_sv_vote_timeout;
937
938                                                         caller.vote_selection = VOTE_SELECT_ACCEPT;
939                                                         caller.vote_waittime = time + autocvar_sv_vote_wait;
940
941                                                         bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote to become ^3master^2.\n");
942                                                         if (autocvar_sv_eventlog)
943                                                                 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
944                                                         Nagger_VoteChanged();
945                                                         VoteCount(true);  // needed if you are the only one
946                                                 }
947
948                                                 return;
949                                         }
950                                 }
951                         }
952                         else { print_to(caller, "^1Master control of voting is not allowed."); }
953
954                         return;
955                 }
956
957                 default:
958                 case CMD_REQUEST_USAGE:
959                 {
960                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote master [action [command | password]]"));
961                         print_to(caller, "  If action is left blank, it calls a vote for you to become master.");
962                         print_to(caller, "  Otherwise the actions are either 'do' a command or 'login' as master.");
963                         return;
964                 }
965         }
966 }
967
968 void VoteCommand_no(int request, entity caller)  // CLIENT ONLY
969 {
970         switch (request)
971         {
972                 case CMD_REQUEST_COMMAND:
973                 {
974                         if (!vote_called) { print_to(caller, "^1No vote called."); }
975                         else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
976                         {
977                                 print_to(caller, "^1You have already voted.");
978                         }
979                         else if (((caller == vote_caller) || caller.vote_master) && autocvar_sv_vote_no_stops_vote)
980                         {
981                                 VoteStop(caller);
982                         }
983
984                         else  // everything went okay, continue changing vote
985                         {
986                                 print_to(caller, "^1You rejected the vote.");
987                                 caller.vote_selection = VOTE_SELECT_REJECT;
988                                 msg_entity = caller;
989                                 if (!autocvar_sv_vote_singlecount)
990                                         VoteCount(false);
991                         }
992
993                         return;
994                 }
995
996                 default:
997                 case CMD_REQUEST_USAGE:
998                 {
999                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote no"));
1000                         print_to(caller, "  No arguments required.");
1001                         return;
1002                 }
1003         }
1004 }
1005
1006 void VoteCommand_status(int request, entity caller)  // BOTH
1007 {
1008         switch (request)
1009         {
1010                 case CMD_REQUEST_COMMAND:
1011                 {
1012                         if (vote_called) print_to(caller, strcat("^7Vote for ", vote_called_display, "^7 called by ^7", OriginalCallerName(), "^7."));
1013                         else print_to(caller, "^1No vote called.");
1014
1015                         return;
1016                 }
1017
1018                 default:
1019                 case CMD_REQUEST_USAGE:
1020                 {
1021                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote status"));
1022                         print_to(caller, "  No arguments required.");
1023                         return;
1024                 }
1025         }
1026 }
1027
1028 void VoteCommand_stop(int request, entity caller)  // BOTH
1029 {
1030         switch (request)
1031         {
1032                 case CMD_REQUEST_COMMAND:
1033                 {
1034                         if (!vote_called)   print_to(caller, "^1No vote called.");
1035                         else if ((caller == vote_caller) || !caller || caller.vote_master)   VoteStop(caller);
1036                         else   print_to(caller, "^1You are not allowed to stop that vote.");
1037                         return;
1038                 }
1039
1040                 default:
1041                 case CMD_REQUEST_USAGE:
1042                 {
1043                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote stop"));
1044                         print_to(caller, "  No arguments required.");
1045                         return;
1046                 }
1047         }
1048 }
1049
1050 void VoteCommand_yes(int request, entity caller)  // CLIENT ONLY
1051 {
1052         switch (request)
1053         {
1054                 case CMD_REQUEST_COMMAND:
1055                 {
1056                         if (!vote_called) { print_to(caller, "^1No vote called."); }
1057                         else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
1058                         {
1059                                 print_to(caller, "^1You have already voted.");
1060                         }
1061                         else  // everything went okay, continue changing vote
1062                         {
1063                                 print_to(caller, "^1You accepted the vote.");
1064                                 caller.vote_selection = VOTE_SELECT_ACCEPT;
1065                                 msg_entity = caller;
1066                                 if (!autocvar_sv_vote_singlecount)
1067                                         VoteCount(false);
1068                         }
1069
1070                         return;
1071                 }
1072
1073                 default:
1074                 case CMD_REQUEST_USAGE:
1075                 {
1076                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote yes"));
1077                         print_to(caller, "  No arguments required.");
1078                         return;
1079                 }
1080         }
1081 }
1082
1083 /* use this when creating a new command, making sure to place it in alphabetical order... also,
1084 ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
1085 void VoteCommand_(int request)
1086 {
1087     switch(request)
1088     {
1089         case CMD_REQUEST_COMMAND:
1090         {
1091
1092             return;
1093         }
1094
1095         default:
1096         case CMD_REQUEST_USAGE:
1097         {
1098             print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote ");
1099             print_to(caller, "  No arguments required.");
1100             return;
1101         }
1102     }
1103 }
1104 */
1105
1106
1107 // ================================
1108 //  Macro system for vote commands
1109 // ================================
1110
1111 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
1112 #define VOTE_COMMANDS(request, caller, arguments, command) \
1113         VOTE_COMMAND("abstain", VoteCommand_abstain(request, caller), "Abstain your vote in current vote", VC_ASGNMNT_CLIENTONLY) \
1114         VOTE_COMMAND("call", VoteCommand_call(request, caller, arguments, command), "Create a new vote for players to decide on", VC_ASGNMNT_BOTH) \
1115         VOTE_COMMAND("help", VoteCommand_macro_help(caller, arguments), "Shows this information", VC_ASGNMNT_BOTH) \
1116         VOTE_COMMAND("master", VoteCommand_master(request, caller, arguments, command), "Full control over all voting and vote commands", VC_ASGNMNT_CLIENTONLY) \
1117         VOTE_COMMAND("no", VoteCommand_no(request, caller), "Select no in current vote", VC_ASGNMNT_CLIENTONLY) \
1118         VOTE_COMMAND("status", VoteCommand_status(request, caller), "Prints information about current vote", VC_ASGNMNT_BOTH) \
1119         VOTE_COMMAND("stop", VoteCommand_stop(request, caller), "Immediately end a vote", VC_ASGNMNT_BOTH) \
1120         VOTE_COMMAND("yes", VoteCommand_yes(request, caller), "Select yes in current vote", VC_ASGNMNT_CLIENTONLY) \
1121         /* nothing */
1122
1123 void VoteCommand_macro_help(entity caller, int argc)
1124 {
1125         string command_origin = GetCommandPrefix(caller);
1126
1127         if (argc == 2 || argv(2) == "help")  // help display listing all commands
1128         {
1129                 print_to(caller, "\nVoting commands:\n");
1130                 #define VOTE_COMMAND(name, function, description, assignment) \
1131                         { if (Votecommand_check_assignment(caller, assignment)) { print_to(caller, strcat("  ^2", name, "^7: ", description)); } }
1132
1133                 VOTE_COMMANDS(0, caller, 0, "");
1134 #undef VOTE_COMMAND
1135
1136                 print_to(caller, strcat("\nUsage:^3 ", command_origin, " vote COMMAND...^7, where possible commands are listed above.\n"));
1137                 print_to(caller, strcat("For help about a specific command, type ", command_origin, " vote help COMMAND"));
1138                 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"));
1139         }
1140         else  // usage for individual command
1141         {
1142                 #define VOTE_COMMAND(name, function, description, assignment) \
1143                         { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(2))) { function; return; } } }
1144
1145                 VOTE_COMMANDS(CMD_REQUEST_USAGE, caller, argc, "");
1146 #undef VOTE_COMMAND
1147
1148                 string cvarname = strcat("sv_vote_command_help_", argv(2));
1149                 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
1150                         wordwrap_sprint(caller, cvar_string(cvarname), 1000);
1151                 else
1152                         print_to(caller, "No documentation exists for this vote");
1153         }
1154 }
1155
1156 float VoteCommand_macro_command(entity caller, int argc, string vote_command)
1157 {
1158         #define VOTE_COMMAND(name, function, description, assignment) \
1159                 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(1))) { function; return true; } } }
1160
1161         VOTE_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, vote_command);
1162 #undef VOTE_COMMAND
1163
1164         return false;
1165 }
1166
1167
1168 // ======================================
1169 //  Main function handling vote commands
1170 // ======================================
1171
1172 void VoteCommand(int request, entity caller, int argc, string vote_command)
1173 {
1174         // Guide for working with argc arguments by example:
1175         // argc:   1    - 2      - 3     - 4
1176         // argv:   0    - 1      - 2     - 3
1177         // cmd     vote - master - login - password
1178
1179         switch (request)
1180         {
1181                 case CMD_REQUEST_COMMAND:
1182                 {
1183                         if (VoteCommand_macro_command(caller, argc, vote_command)) return;
1184                 }
1185
1186                 default:
1187                         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"));
1188                 case CMD_REQUEST_USAGE:
1189                 {
1190                         VoteCommand_macro_help(caller, argc);
1191                         return;
1192                 }
1193         }
1194 }