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