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