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