]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/command/vote.qc
83c62cd2b004a60fd641f27f401b4fa5ab426c2e
[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                 case "restart":
718                 {
719                         // add a delay so that vote result can be seen and announcer can be heard
720                         // if the vote is accepted
721                         vote_parsed_command = strcat("defer 1 ", vote_command);
722                         vote_parsed_display = strzone(strcat("^1", vote_command));
723
724                         break;
725                 }
726
727                 default:
728                 {
729                         vote_parsed_command = vote_command;
730                         vote_parsed_display = strzone(strcat("^1", vote_command));
731
732                         break;
733                 }
734         }
735
736         return 1;
737 }
738
739
740 // =======================
741 //  Command Sub-Functions
742 // =======================
743
744 void VoteCommand_abstain(int request, entity caller)  // CLIENT ONLY
745 {
746         switch (request)
747         {
748                 case CMD_REQUEST_COMMAND:
749                 {
750                         if (!vote_called) { print_to(caller, "^1No vote called."); }
751                         else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
752                         {
753                                 print_to(caller, "^1You have already voted.");
754                         }
755
756                         else  // everything went okay, continue changing vote
757                         {
758                                 print_to(caller, "^1You abstained from your vote.");
759                                 caller.vote_selection = VOTE_SELECT_ABSTAIN;
760                                 msg_entity = caller;
761                                 if (!autocvar_sv_vote_singlecount)   VoteCount(false); }
762
763                         return;
764                 }
765
766                 default:
767                 case CMD_REQUEST_USAGE:
768                 {
769                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote abstain"));
770                         print_to(caller, "  No arguments required.");
771                         return;
772                 }
773         }
774 }
775
776 void VoteCommand_call(int request, entity caller, int argc, string vote_command)  // BOTH
777 {
778         switch (request)
779         {
780                 case CMD_REQUEST_COMMAND:
781                 {
782                         float tmp_playercount = 0;
783                         int parse_error;
784
785                         vote_command = VoteCommand_extractcommand(vote_command, 2, argc);
786
787                         if (!autocvar_sv_vote_call && caller) { print_to(caller, "^1Vote calling is not allowed."); }
788                         else if (!autocvar_sv_vote_gamestart && time < game_starttime)
789                         {
790                                 print_to(caller, "^1Vote calling is not allowed before the match has started.");
791                         }
792                         else if (vote_called)
793                         {
794                                 print_to(caller, "^1There is already a vote called.");
795                         }
796                         else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
797                         {
798                                 print_to(caller, "^1Only players can call a vote.");
799                         }
800                         else if (caller && !IS_CLIENT(caller))
801                         {
802                                 print_to(caller, "^1Only connected clients can vote.");
803                         }
804                         else if (timeout_status)
805                         {
806                                 print_to(caller, "^1You can not call a vote while a timeout is active.");
807                         }
808                         else if (caller && (time < caller.vote_waittime))
809                         {
810                                 print_to(caller, strcat("^1You have to wait ^2", ftos(ceil(caller.vote_waittime - time)), "^1 seconds before you can again call a vote."));
811                         }
812                         else if (!VoteCommand_checknasty(vote_command))
813                         {
814                                 print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
815                         }
816                         else if ((parse_error = VoteCommand_parse(caller, vote_command, autocvar_sv_vote_commands, 2, argc)) <= 0)
817                         {
818                                 if(parse_error == 0)
819                                         print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
820                         }
821                         else  // everything went okay, continue with calling the vote
822                         {
823                                 vote_caller = caller;  // remember who called the vote
824                                 vote_caller_name = strzone(GetCallerName(vote_caller));
825                                 vote_called = VOTE_NORMAL;
826                                 vote_called_command = strzone(vote_parsed_command);
827                                 vote_called_display = strzone(vote_parsed_display);
828                                 vote_endtime = time + autocvar_sv_vote_timeout;
829
830                                 if (caller)
831                                 {
832                                         caller.vote_selection = VOTE_SELECT_ACCEPT;
833                                         caller.vote_waittime = time + autocvar_sv_vote_wait;
834                                         msg_entity = caller;
835                                 }
836
837                                 FOREACH_CLIENT(IS_REAL_CLIENT(it), { ++tmp_playercount; });
838                                 if (tmp_playercount > 1)
839                                         Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_CALL);
840
841                                 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote for ", vote_called_display, "\n");
842                                 if (autocvar_sv_eventlog)
843                                         GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
844                                 Nagger_VoteChanged();
845                                 VoteCount(true);  // needed if you are the only one
846                         }
847
848                         return;
849                 }
850
851                 default:
852                 case CMD_REQUEST_USAGE:
853                 {
854                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote call command"));
855                         print_to(caller, "  Where 'command' is the command to request a vote upon.");
856                         print_to(caller, strcat("Examples: ", GetCommandPrefix(caller), " vote call gotomap dance"));
857                         print_to(caller, strcat("          ", GetCommandPrefix(caller), " vote call endmatch"));
858                         return;
859                 }
860         }
861 }
862
863 void VoteCommand_master(int request, entity caller, int argc, string vote_command)  // CLIENT ONLY
864 {
865         switch (request)
866         {
867                 case CMD_REQUEST_COMMAND:
868                 {
869                         if (autocvar_sv_vote_master)
870                         {
871                                 switch (strtolower(argv(2)))
872                                 {
873                                         case "do":
874                                         {
875                                                 int parse_error;
876                                                 vote_command = VoteCommand_extractcommand(vote_command, 3, argc);
877
878                                                 if (!caller.vote_master)
879                                                         print_to(caller, "^1You do not have vote master privileges.");
880                                                 else if (!VoteCommand_checknasty(vote_command))
881                                                 {
882                                                         print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
883                                                 }
884                                                 else if ((parse_error = VoteCommand_parse(caller, vote_command, strcat(autocvar_sv_vote_commands, " ", autocvar_sv_vote_master_commands), 3, argc)) <= 0)
885                                                 {
886                                                         if(parse_error == 0)
887                                                                 print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
888                                                 }
889                                                 else  // everything went okay, proceed with command
890                                                 {
891                                                         localcmd(strcat(vote_parsed_command, "\n"));
892                                                         print_to(caller, strcat("Executing command '", vote_parsed_display, "' on server."));
893                                                         bprint("\{1}^2* ^3", GetCallerName(caller), "^2 used their ^3master^2 status to do \"^2", vote_parsed_display, "^2\".\n");
894                                                         if (autocvar_sv_eventlog)
895                                                                 GameLogEcho(strcat(":vote:vdo:", ftos(caller.playerid), ":", vote_parsed_display));
896                                                 }
897
898                                                 return;
899                                         }
900
901                                         case "login":
902                                         {
903                                                 if (autocvar_sv_vote_master_password == "") { print_to(caller, "^1Login to vote master is not allowed."); }
904                                                 else if (caller.vote_master)
905                                                 {
906                                                         print_to(caller, "^1You are already logged in as vote master.");
907                                                 }
908                                                 else if (autocvar_sv_vote_master_password != argv(3))
909                                                 {
910                                                         print_to(caller, strcat("Rejected vote master login from ", GetCallerName(caller)));
911                                                 }
912                                                 else  // everything went okay, proceed with giving this player master privilages
913                                                 {
914                                                         caller.vote_master = true;
915                                                         print_to(caller, strcat("Accepted vote master login from ", GetCallerName(caller)));
916                                                         bprint("\{1}^2* ^3", GetCallerName(caller), "^2 logged in as ^3master^2\n");
917                                                         if (autocvar_sv_eventlog)
918                                                                 GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid)));
919                                                 }
920
921                                                 return;
922                                         }
923
924                                         default:  // calling a vote for master
925                                         {
926                                                 if (!autocvar_sv_vote_master_callable) { print_to(caller, "^1Vote to become vote master is not allowed."); }
927                                                 else if (vote_called)
928                                                 {
929                                                         print_to(caller, "^1There is already a vote called.");
930                                                 }
931                                                 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
932                                                 {
933                                                         print_to(caller, "^1Only players can call a vote.");
934                                                 }
935                                                 else if (timeout_status)
936                                                 {
937                                                         print_to(caller, "^1You can not call a vote while a timeout is active.");
938                                                 }
939                                                 else  // everything went okay, continue with creating vote
940                                                 {
941                                                         vote_caller = caller;
942                                                         vote_caller_name = strzone(GetCallerName(vote_caller));
943                                                         vote_called = VOTE_MASTER;
944                                                         vote_called_command = strzone("XXX");
945                                                         vote_called_display = strzone("^3master");
946                                                         vote_endtime = time + autocvar_sv_vote_timeout;
947
948                                                         caller.vote_selection = VOTE_SELECT_ACCEPT;
949                                                         caller.vote_waittime = time + autocvar_sv_vote_wait;
950
951                                                         bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote to become ^3master^2.\n");
952                                                         if (autocvar_sv_eventlog)
953                                                                 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
954                                                         Nagger_VoteChanged();
955                                                         VoteCount(true);  // needed if you are the only one
956                                                 }
957
958                                                 return;
959                                         }
960                                 }
961                         }
962                         else { print_to(caller, "^1Master control of voting is not allowed."); }
963
964                         return;
965                 }
966
967                 default:
968                 case CMD_REQUEST_USAGE:
969                 {
970                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote master [action [command | password]]"));
971                         print_to(caller, "  If action is left blank, it calls a vote for you to become master.");
972                         print_to(caller, "  Otherwise the actions are either 'do' a command or 'login' as master.");
973                         return;
974                 }
975         }
976 }
977
978 void VoteCommand_no(int request, entity caller)  // CLIENT ONLY
979 {
980         switch (request)
981         {
982                 case CMD_REQUEST_COMMAND:
983                 {
984                         if (!vote_called) { print_to(caller, "^1No vote called."); }
985                         else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
986                         {
987                                 print_to(caller, "^1You have already voted.");
988                         }
989                         else if (((caller == vote_caller) || caller.vote_master) && autocvar_sv_vote_no_stops_vote)
990                         {
991                                 VoteStop(caller);
992                         }
993
994                         else  // everything went okay, continue changing vote
995                         {
996                                 print_to(caller, "^1You rejected the vote.");
997                                 caller.vote_selection = VOTE_SELECT_REJECT;
998                                 msg_entity = caller;
999                                 if (!autocvar_sv_vote_singlecount)
1000                                         VoteCount(false);
1001                         }
1002
1003                         return;
1004                 }
1005
1006                 default:
1007                 case CMD_REQUEST_USAGE:
1008                 {
1009                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote no"));
1010                         print_to(caller, "  No arguments required.");
1011                         return;
1012                 }
1013         }
1014 }
1015
1016 void VoteCommand_status(int request, entity caller)  // BOTH
1017 {
1018         switch (request)
1019         {
1020                 case CMD_REQUEST_COMMAND:
1021                 {
1022                         if (vote_called) print_to(caller, strcat("^7Vote for ", vote_called_display, "^7 called by ^7", OriginalCallerName(), "^7."));
1023                         else print_to(caller, "^1No vote called.");
1024
1025                         return;
1026                 }
1027
1028                 default:
1029                 case CMD_REQUEST_USAGE:
1030                 {
1031                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote status"));
1032                         print_to(caller, "  No arguments required.");
1033                         return;
1034                 }
1035         }
1036 }
1037
1038 void VoteCommand_stop(int request, entity caller)  // BOTH
1039 {
1040         switch (request)
1041         {
1042                 case CMD_REQUEST_COMMAND:
1043                 {
1044                         if (!vote_called)   print_to(caller, "^1No vote called.");
1045                         else if ((caller == vote_caller) || !caller || caller.vote_master)   VoteStop(caller);
1046                         else   print_to(caller, "^1You are not allowed to stop that vote.");
1047                         return;
1048                 }
1049
1050                 default:
1051                 case CMD_REQUEST_USAGE:
1052                 {
1053                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote stop"));
1054                         print_to(caller, "  No arguments required.");
1055                         return;
1056                 }
1057         }
1058 }
1059
1060 void VoteCommand_yes(int request, entity caller)  // CLIENT ONLY
1061 {
1062         switch (request)
1063         {
1064                 case CMD_REQUEST_COMMAND:
1065                 {
1066                         if (!vote_called) { print_to(caller, "^1No vote called."); }
1067                         else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
1068                         {
1069                                 print_to(caller, "^1You have already voted.");
1070                         }
1071                         else  // everything went okay, continue changing vote
1072                         {
1073                                 print_to(caller, "^1You accepted the vote.");
1074                                 caller.vote_selection = VOTE_SELECT_ACCEPT;
1075                                 msg_entity = caller;
1076                                 if (!autocvar_sv_vote_singlecount)
1077                                         VoteCount(false);
1078                         }
1079
1080                         return;
1081                 }
1082
1083                 default:
1084                 case CMD_REQUEST_USAGE:
1085                 {
1086                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote yes"));
1087                         print_to(caller, "  No arguments required.");
1088                         return;
1089                 }
1090         }
1091 }
1092
1093 /* use this when creating a new command, making sure to place it in alphabetical order... also,
1094 ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
1095 void VoteCommand_(int request)
1096 {
1097     switch(request)
1098     {
1099         case CMD_REQUEST_COMMAND:
1100         {
1101
1102             return;
1103         }
1104
1105         default:
1106         case CMD_REQUEST_USAGE:
1107         {
1108             print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote ");
1109             print_to(caller, "  No arguments required.");
1110             return;
1111         }
1112     }
1113 }
1114 */
1115
1116
1117 // ================================
1118 //  Macro system for vote commands
1119 // ================================
1120
1121 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
1122 #define VOTE_COMMANDS(request, caller, arguments, command) \
1123         VOTE_COMMAND("abstain", VoteCommand_abstain(request, caller), "Abstain your vote in current vote", VC_ASGNMNT_CLIENTONLY) \
1124         VOTE_COMMAND("call", VoteCommand_call(request, caller, arguments, command), "Create a new vote for players to decide on", VC_ASGNMNT_BOTH) \
1125         VOTE_COMMAND("help", VoteCommand_macro_help(caller, arguments), "Shows this information", VC_ASGNMNT_BOTH) \
1126         VOTE_COMMAND("master", VoteCommand_master(request, caller, arguments, command), "Full control over all voting and vote commands", VC_ASGNMNT_CLIENTONLY) \
1127         VOTE_COMMAND("no", VoteCommand_no(request, caller), "Select no in current vote", VC_ASGNMNT_CLIENTONLY) \
1128         VOTE_COMMAND("status", VoteCommand_status(request, caller), "Prints information about current vote", VC_ASGNMNT_BOTH) \
1129         VOTE_COMMAND("stop", VoteCommand_stop(request, caller), "Immediately end a vote", VC_ASGNMNT_BOTH) \
1130         VOTE_COMMAND("yes", VoteCommand_yes(request, caller), "Select yes in current vote", VC_ASGNMNT_CLIENTONLY) \
1131         /* nothing */
1132
1133 void VoteCommand_macro_help(entity caller, int argc)
1134 {
1135         string command_origin = GetCommandPrefix(caller);
1136
1137         if (argc == 2 || argv(2) == "help")  // help display listing all commands
1138         {
1139                 print_to(caller, "\nVoting commands:\n");
1140                 #define VOTE_COMMAND(name, function, description, assignment) \
1141                         { if (Votecommand_check_assignment(caller, assignment)) { print_to(caller, strcat("  ^2", name, "^7: ", description)); } }
1142
1143                 VOTE_COMMANDS(0, caller, 0, "");
1144 #undef VOTE_COMMAND
1145
1146                 print_to(caller, strcat("\nUsage:^3 ", command_origin, " vote COMMAND...^7, where possible commands are listed above.\n"));
1147                 print_to(caller, strcat("For help about a specific command, type ", command_origin, " vote help COMMAND"));
1148                 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"));
1149         }
1150         else  // usage for individual command
1151         {
1152                 #define VOTE_COMMAND(name, function, description, assignment) \
1153                         { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(2))) { function; return; } } }
1154
1155                 VOTE_COMMANDS(CMD_REQUEST_USAGE, caller, argc, "");
1156 #undef VOTE_COMMAND
1157
1158                 string cvarname = strcat("sv_vote_command_help_", argv(2));
1159                 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
1160                         wordwrap_sprint(caller, cvar_string(cvarname), 1000);
1161                 else
1162                         print_to(caller, "No documentation exists for this vote");
1163         }
1164 }
1165
1166 float VoteCommand_macro_command(entity caller, int argc, string vote_command)
1167 {
1168         #define VOTE_COMMAND(name, function, description, assignment) \
1169                 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(1))) { function; return true; } } }
1170
1171         VOTE_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, vote_command);
1172 #undef VOTE_COMMAND
1173
1174         return false;
1175 }
1176
1177
1178 // ======================================
1179 //  Main function handling vote commands
1180 // ======================================
1181
1182 void VoteCommand(int request, entity caller, int argc, string vote_command)
1183 {
1184         // Guide for working with argc arguments by example:
1185         // argc:   1    - 2      - 3     - 4
1186         // argv:   0    - 1      - 2     - 3
1187         // cmd     vote - master - login - password
1188
1189         switch (request)
1190         {
1191                 case CMD_REQUEST_COMMAND:
1192                 {
1193                         if (VoteCommand_macro_command(caller, argc, vote_command)) return;
1194                 }
1195
1196                 default:
1197                         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"));
1198                 case CMD_REQUEST_USAGE:
1199                 {
1200                         VoteCommand_macro_help(caller, argc);
1201                         return;
1202                 }
1203         }
1204 }