From: Samual Date: Mon, 5 Dec 2011 02:34:01 +0000 (-0500) Subject: Lots and lots more work on votecommand stuff, many commands have been added back... X-Git-Tag: xonotic-v0.6.0~188^2~28^2~188 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=8c6b3635250d3460d43b8014b656240abd085a24;p=xonotic%2Fxonotic-data.pk3dir.git Lots and lots more work on votecommand stuff, many commands have been added back... but still missing the main one (vote call) --- diff --git a/qcsrc/client/gamecommand.qc b/qcsrc/client/gamecommand.qc index ab6aa359f..e9dfb496f 100644 --- a/qcsrc/client/gamecommand.qc +++ b/qcsrc/client/gamecommand.qc @@ -266,16 +266,16 @@ void GameCommand_settemp(float request, float argc) float i = cvar_clientsettemp_restore(); if(i) - print("Restored ", ftos(i), " temporary cvar settings to their original values.\n"); + dprint("Restored ", ftos(i), " temporary cvar settings to their original values.\n"); else - print("Nothing to restore.\n"); + dprint("Nothing to restore.\n"); } else { if(cvar_clientsettemp(argv(1), argv(2))) - print("Creating new settemp tracker for ", argv(1), " and setting it to \"", argv(2), "\" temporarily.\n"); + dprint("Creating new settemp tracker for ", argv(1), " and setting it to \"", argv(2), "\" temporarily.\n"); else - print("Already had a tracker for ", argv(1), ", updating it to \"", argv(2), "\".\n"); + dprint("Already had a tracker for ", argv(1), ", updating it to \"", argv(2), "\".\n"); } return; diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index 175d6dcf1..8a8c0ee74 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -1162,6 +1162,7 @@ float autocvar_sv_vote_change; string autocvar_sv_vote_commands; float autocvar_sv_vote_majority_factor; float autocvar_sv_vote_master; +float autocvar_sv_vote_master_callable; string autocvar_sv_vote_master_commands; string autocvar_sv_vote_master_password; float autocvar_sv_vote_nospectators; diff --git a/qcsrc/server/clientcommands.qc b/qcsrc/server/clientcommands.qc index cfe266bb3..19921aae4 100644 --- a/qcsrc/server/clientcommands.qc +++ b/qcsrc/server/clientcommands.qc @@ -894,7 +894,7 @@ void ClientCommand_(float request) CLIENT_COMMAND("timein", ClientCommand_timein(request), "Resume the game from being paused with a timeout") \ CLIENT_COMMAND("timeout", ClientCommand_timeout(request), "Call a timeout which pauses the game for certain amount of time unless unpaused") \ CLIENT_COMMAND("voice", ClientCommand_voice(request, arguments, command), "Send voice message via sound") \ - CLIENT_COMMAND("vote", VoteCommand(request, self, arguments), "Request an action to be voted upon by players") /* handled in server/vote.qc */ \ + CLIENT_COMMAND("vote", VoteCommand(request, self, arguments, command), "Request an action to be voted upon by players") /* handled in server/vote.qc */ \ /* nothing */ void ClientCommand_macro_help() diff --git a/qcsrc/server/gamecommand.qc b/qcsrc/server/gamecommand.qc index cd8c623f3..0a28ddbf5 100644 --- a/qcsrc/server/gamecommand.qc +++ b/qcsrc/server/gamecommand.qc @@ -1886,7 +1886,7 @@ void GameCommand_(float request) // ================================== // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;) -#define SERVER_COMMANDS(request,arguments) \ +#define SERVER_COMMANDS(request,arguments,command) \ SERVER_COMMAND("adminmsg", GameCommand_adminmsg(request, arguments), "Send an admin message to a client directly") \ SERVER_COMMAND("allready", GameCommand_allready(request), "Restart the server and reset the players") \ SERVER_COMMAND("allspec", GameCommand_allspec(request, arguments), "Force all players to spectate") \ @@ -1927,7 +1927,7 @@ void GameCommand_(float request) SERVER_COMMAND("trace", GameCommand_trace(request, arguments), "Various debugging tools with tracing") \ SERVER_COMMAND("unlockteams", GameCommand_unlockteams(request), "Enable the ability for players to switch or enter teams") \ SERVER_COMMAND("warp", GameCommand_warp(request, arguments), "Choose different level in campaign") \ - SERVER_COMMAND("vote", VoteCommand(request, world, arguments), "Server side control of voting") /* handled in server/vote.qc */ \ + SERVER_COMMAND("vote", VoteCommand(request, world, arguments, command), "Server side control of voting") /* handled in server/vote.qc */ \ /* nothing */ void GameCommand_macro_help() @@ -1935,18 +1935,18 @@ void GameCommand_macro_help() #define SERVER_COMMAND(name,function,description) \ { print(" ^2", name, "^7: ", description, "\n"); } - SERVER_COMMANDS(0, 0) + SERVER_COMMANDS(0, 0, "") #undef SERVER_COMMAND return; } -float GameCommand_macro_command(float argc) +float GameCommand_macro_command(float argc, string command) { #define SERVER_COMMAND(name,function,description) \ { if(name == strtolower(argv(0))) { function; return TRUE; } } - SERVER_COMMANDS(GC_REQUEST_COMMAND, argc) + SERVER_COMMANDS(GC_REQUEST_COMMAND, argc, command) #undef SERVER_COMMAND return FALSE; @@ -1957,7 +1957,7 @@ float GameCommand_macro_usage(float argc) #define SERVER_COMMAND(name,function,description) \ { if(name == strtolower(argv(1))) { function; return TRUE; } } - SERVER_COMMANDS(GC_REQUEST_USAGE, argc) + SERVER_COMMANDS(GC_REQUEST_USAGE, argc, "") #undef SERVER_COMMAND return FALSE; @@ -1998,7 +1998,7 @@ void GameCommand(string command) { return; // handled by common/gamecommand.qc } - else if(GameCommand_macro_command(argc)) // continue as usual and scan for normal commands + else if(GameCommand_macro_command(argc, command)) // continue as usual and scan for normal commands { return; // handled by one of the above GameCommand_* functions } diff --git a/qcsrc/server/vote.qc b/qcsrc/server/vote.qc index 1e6a33549..0f48ea1db 100644 --- a/qcsrc/server/vote.qc +++ b/qcsrc/server/vote.qc @@ -15,31 +15,16 @@ #define VOTE_SELECT_NULL 0 #define VOTE_SELECT_ACCEPT 1 -// ============================ -// Misc. Supporting Functions -// ============================ +string GetKickVoteVictim_newcommand; +string GetKickVoteVictim_reason; -float Votecommand_check_assignment(entity caller, float assignment) -{ - float from_server = (!caller); - - if((assignment == VC_ASGNMNT_BOTH) - || ((!from_server && assignment == VC_ASGNMNT_CLIENTONLY) - || (from_server && assignment == VC_ASGNMNT_SERVERONLY))) - { - return TRUE; - } +string RemapVote_display; +string RemapVote_vote; - return FALSE; -} -string VoteCommand_getprefix(entity caller) -{ - if(caller) - return "cmd"; - else - return "sv_cmd"; -} +// ============================================= +// Nagger for players to know status of voting +// ============================================= float Nagger_SendEntity(entity to, float sendflags) { @@ -132,144 +117,84 @@ void Nagger_ReadyCounted() nagger.SendFlags |= 1; } -void ReadyRestartForce() -{ - local entity e; - - bprint("^1Server is restarting...\n"); - - VoteReset(); - - // clear overtime - if (checkrules_overtimesadded > 0 && g_race_qualifying != 2) { - //we have to decrease timelimit to its original value again!! - float newTL; - newTL = autocvar_timelimit; - newTL -= checkrules_overtimesadded * autocvar_timelimit_overtime; - cvar_set("timelimit", ftos(newTL)); - } - checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0; - - - readyrestart_happened = 1; - game_starttime = time; - if(!g_ca && !g_arena) - game_starttime += RESTART_COUNTDOWN; - restart_mapalreadyrestarted = 0; //reset this var, needed when cvar sv_ready_restart_repeatable is in use +// ======================= +// Game logic for voting +// ======================= - inWarmupStage = 0; //once the game is restarted the game is in match stage +void VoteStop(entity stopper) +{ + bprint("\{1}^2* ^3", VoteCommand_getname(stopper), "^2 stopped ^3", VoteCommand_getname(votecaller), "^2's vote\n"); + if(autocvar_sv_eventlog) { GameLogEcho(strcat(":vote:vstop:", ftos(stopper.playerid))); } + + // Don't force them to wait for next vote, this way they can e.g. correct their vote. + if(votecaller) + if(stopper == votecaller) + votecaller.vote_next = time + autocvar_sv_vote_stop; - //reset the .ready status of all players (also spectators) - FOR_EACH_CLIENTSLOT(e) - e.ready = 0; - readycount = 0; - Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client + VoteReset(); +} - if(autocvar_teamplay_lockonrestart && teamplay) { - lockteams = 1; - bprint("^1The teams are now locked.\n"); - } - //initiate the restart-countdown-announcer entity - if(autocvar_sv_ready_restart_after_countdown && !g_ca && !g_arena) - { - restartTimer = spawn(); - restartTimer.think = restartTimer_Think; - restartTimer.nextthink = game_starttime; - } +// ============================================ +// Misc. supporting functions for votecommand +// ============================================ - //after a restart every players number of allowed timeouts gets reset, too - if(autocvar_sv_timeout) +float Votecommand_check_assignment(entity caller, float assignment) +{ + float from_server = (!caller); + + if((assignment == VC_ASGNMNT_BOTH) + || ((!from_server && assignment == VC_ASGNMNT_CLIENTONLY) + || (from_server && assignment == VC_ASGNMNT_SERVERONLY))) { - FOR_EACH_REALPLAYER(e) - e.allowedTimeouts = autocvar_sv_timeout_number; + return TRUE; } - //reset map immediately if this cvar is not set - if (!autocvar_sv_ready_restart_after_countdown) - reset_map(TRUE); - - if(autocvar_sv_eventlog) - GameLogEcho(":restart"); + return FALSE; } -void ReadyRestart() +string VoteCommand_getprefix(entity caller) { - // no arena, assault support yet... - if(g_arena | g_assault | gameover | intermission_running | race_completing) - localcmd("restart\n"); + if(caller) + return "cmd"; else - localcmd("\nsv_hook_gamerestart\n"); - - ReadyRestartForce(); - - // reset ALL scores, but only do that at the beginning - //of the countdown if sv_ready_restart_after_countdown is off! - //Otherwise scores could be manipulated during the countdown! - if (!autocvar_sv_ready_restart_after_countdown) - Score_ClearAll(); + return "sv_cmd"; } -/** - * Counts how many players are ready. If not enough players are ready, the function - * does nothing. If all players are ready, the timelimit will be extended and the - * restart_countdown variable is set to allow other functions like PlayerPostThink - * to detect that the countdown is now active. If the cvar sv_ready_restart_after_countdown - * is not set the map will be resetted. - * - * Function is called after the server receives a 'ready' sign from a player. - */ -void ReadyCount() +string VoteCommand_getname(entity caller) { - entity tmp_player; - float t_ready, t_players; - - FOR_EACH_REALPLAYER(tmp_player) - { - ++t_players; - if(tmp_player.ready) { ++t_ready; } - } - - readycount = t_ready; - - Nagger_ReadyCounted(); - - // TODO: Add ability to - if(t_ready) // at least one is ready - if(t_ready == t_players) // and, everyone is ready - ReadyRestart(); + if(caller) + return caller.netname; + else + return ((autocvar_sv_adminnick != "") ? autocvar_sv_adminnick : autocvar_hostname); } - -// Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set) -void restartTimer_Think() +string VoteCommand_extractcommand(string input, float startpos, float argc) { - restart_mapalreadyrestarted = 1; - reset_map(TRUE); - Score_ClearAll(); - remove(self); - return; + string output; + + if((argc - 1) < startpos) + output = ""; + else + output = substring(input, argv_start_index(startpos), argv_end_index(-1) - argv_start_index(startpos)); + + print("VoteCommand_parse: '", output, "'. \n"); + return output; } -float VoteCheckNasty(string cmd) +float VoteCommand_checknasty(string vote_command) { - if(strstrofs(cmd, ";", 0) >= 0) - return TRUE; - if(strstrofs(cmd, "\n", 0) >= 0) - return TRUE; - if(strstrofs(cmd, "\r", 0) >= 0) - return TRUE; - if(strstrofs(cmd, "$", 0) >= 0) + if((strstrofs(vote_command, ";", 0) >= 0) + || (strstrofs(vote_command, "\n", 0) >= 0) + || (strstrofs(vote_command, "\r", 0) >= 0) + || (strstrofs(vote_command, "$", 0) >= 0)) return TRUE; return FALSE; } -string GetKickVoteVictim_newcommand; -string GetKickVoteVictim_reason; - -entity GetKickVoteVictim(string vote, string cmd, entity caller) +entity GetKickVoteVictim(string vote, string cmd, entity caller) // todo re-write this { float tokens; string ns; @@ -323,8 +248,6 @@ entity GetKickVoteVictim(string vote, string cmd, entity caller) return world; } -string RemapVote_display; -string Remapvote_selection; float RemapVote(string vote, string cmd, entity e) { float vote_argc; @@ -359,12 +282,12 @@ float RemapVote(string vote, string cmd, entity e) { if(!(victim = GetKickVoteVictim(vote, cmd, e))) return FALSE; - Remapvote_selection = GetKickVoteVictim_newcommand; + RemapVote_vote = GetKickVoteVictim_newcommand; RemapVote_display = strcat("^1", vote, " (^7", victim.netname, "^1): ", GetKickVoteVictim_reason); } else { - Remapvote_selection = vote; + RemapVote_vote = vote; RemapVote_display = strzone(strcat("^1", vote)); } @@ -376,24 +299,22 @@ float RemapVote(string vote, string cmd, entity e) // Command Sub-Functions // ======================= -void VoteCommand_abstain(float request, entity caller) +void VoteCommand_abstain(float request, entity caller) // CLIENT ONLY { switch(request) { case VC_REQUEST_COMMAND: { - if(votecalled) + if not(votecalled) { print_to(caller, "^1No vote called."); } + else if not(caller.vote_selection == VOTE_SELECT_NULL || autocvar_sv_vote_change) { print_to(caller, "^1You have already voted."); } + + else // everything went okay, continue changing vote { - if(caller.vote_selection == VOTE_SELECT_NULL || autocvar_sv_vote_change) - { - print_to(caller, "^1You abstained from your vote."); - caller.vote_selection = VOTE_SELECT_ABSTAIN; - msg_entity = caller; - if(!autocvar_sv_vote_singlecount) { VoteCount(); } - } - else { print_to(caller, "^1You have already voted."); } - } - else { print_to(caller, "^1No vote called."); } + print_to(caller, "^1You abstained from your vote."); + caller.vote_selection = VOTE_SELECT_ABSTAIN; + msg_entity = caller; + if(!autocvar_sv_vote_singlecount) { VoteCount(); } + } return; } @@ -408,7 +329,7 @@ void VoteCommand_abstain(float request, entity caller) } } -void VoteCommand_call(float request, entity caller, float argc) +void VoteCommand_call(float request, entity caller, float argc, string vote_command) // BOTH { switch(request) { @@ -428,7 +349,7 @@ void VoteCommand_call(float request, entity caller, float argc) } } -void VoteCommand_force(float request, float argc) +void VoteCommand_force(float request, float argc, string vote_command) // SERVER ONLY { switch(request) { @@ -441,19 +362,87 @@ void VoteCommand_force(float request, float argc) default: case VC_REQUEST_USAGE: { - print("\nUsage:^3 vote \n"); + print("\nUsage:^3 vote force result\n"); print(" No arguments required.\n"); return; } } } -void VoteCommand_master(float request, entity caller, float argc) +void VoteCommand_master(float request, entity caller, float argc, string vote_command) // CLIENT ONLY { switch(request) { case VC_REQUEST_COMMAND: { + if(autocvar_sv_vote_master) + { + switch(strtolower(argv(2))) + { + case "do": + { + vote_command = VoteCommand_extractcommand(vote_command, 3, argc); + + if not(caller.vote_master) { print_to(caller, "^1You do not have vote master privelages."); } + else if not(VoteCommand_checknasty(vote_command)) { print_to(caller, "^1Syntax error in command, see 'vhelp' for more info."); } + else if not(RemapVote(vote_command, "vdo", caller)) { print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info."); } + + else // everything went okay, proceed with command + { + localcmd(strcat(RemapVote_vote, "\n")); + print_to(caller, strcat("Executing command '", RemapVote_display, "' on server.")); + bprint("\{1}^2* ^3", VoteCommand_getname(caller), "^2 used their ^3master^2 status to do \"^2", RemapVote_display, "^2\".\n"); + if(autocvar_sv_eventlog) { GameLogEcho(strcat(":vote:vdo:", ftos(caller.playerid), ":", RemapVote_display)); } + } + + return; + } + + case "login": + { + if not(autocvar_sv_vote_master_password != "") { print_to(caller, "^1Login to vote master is not allowed."); } + else if(caller.vote_master) { print_to(caller, "^1You are already logged in as vote master."); } + else if not(autocvar_sv_vote_master_password == argv(3)) { print_to(caller, strcat("Rejected vote master login from ", VoteCommand_getname(caller))); } + + else // everything went okay, proceed with giving this player master privilages + { + caller.vote_master = TRUE; + print_to(caller, strcat("Accepted vote master login from ", VoteCommand_getname(caller))); + bprint("\{1}^2* ^3", VoteCommand_getname(caller), "^2 logged in as ^3master^2\n"); + if(autocvar_sv_eventlog) { GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid))); } + } + + return; + } + + default: // calling a vote for master + { + if not(autocvar_sv_vote_master_callable) { print_to(caller, "^1Vote to become vote master is not allowed."); } // todo update this description in defaultXonotic.cfg + else if(votecalled) { print_to(caller, "^1There is already a vote called."); } + + else // everything went okay, continue with creating vote + { + votecalled = TRUE; + votecalledmaster = TRUE; + votecalledvote = strzone("XXX"); + votecalledvote_display = strzone("^3master"); + votefinished = time + autocvar_sv_vote_timeout; + votecaller = caller; + + caller.vote_selection = VOTE_SELECT_ACCEPT; + caller.vote_next = time + autocvar_sv_vote_wait; + + bprint("\{1}^2* ^3", VoteCommand_getname(votecaller), "^2 calls a vote to become ^3master^2.\n"); + if(autocvar_sv_eventlog) { GameLogEcho(strcat(":vote:vcall:", ftos(votecaller.playerid), ":", votecalledvote_display)); } + Nagger_VoteChanged(); + VoteCount(); // needed if you are the only one + } + + return; + } + } + } + else { print_to(caller, "^1Master control of voting is not allowed."); } return; } @@ -462,30 +451,28 @@ void VoteCommand_master(float request, entity caller, float argc) case VC_REQUEST_USAGE: { print("\nUsage:^3 vote master action [arguments]\n"); - print(" No arguments required.\n"); + print(" TODO.\n"); return; } } } -void VoteCommand_no(float request, entity caller) +void VoteCommand_no(float request, entity caller) // CLIENT ONLY { switch(request) { case VC_REQUEST_COMMAND: { - if(votecalled) + if not(votecalled) { print_to(caller, "^1No vote called."); } + else if not(caller.vote_selection == VOTE_SELECT_NULL || autocvar_sv_vote_change) { print_to(caller, "^1You have already voted."); } + + else // everything went okay, continue changing vote { - if(caller.vote_selection == VOTE_SELECT_NULL || autocvar_sv_vote_change) - { - print_to(caller, "^1You rejected the vote."); - caller.vote_selection = VOTE_SELECT_REJECT; - msg_entity = caller; - if(!autocvar_sv_vote_singlecount) { VoteCount(); } - } - else { print_to(caller, "^1You have already voted."); } - } - else { print_to(caller, "^1No vote called."); } + print_to(caller, "^1You rejected the vote."); + caller.vote_selection = VOTE_SELECT_REJECT; + msg_entity = caller; + if(!autocvar_sv_vote_singlecount) { VoteCount(); } + } return; } @@ -500,7 +487,7 @@ void VoteCommand_no(float request, entity caller) } } -void VoteCommand_status(float request, entity caller) +void VoteCommand_status(float request, entity caller) // BOTH { switch(request) { @@ -520,12 +507,15 @@ void VoteCommand_status(float request, entity caller) } } -void VoteCommand_stop(float request, entity caller) +void VoteCommand_stop(float request, entity caller) // BOTH { switch(request) { case VC_REQUEST_COMMAND: { + if not(votecalled) { print_to(caller, "^1No vote called."); } + else if((caller == votecaller) || !caller || caller.vote_master) { VoteStop(caller); } + else { print_to(caller, "^1You are not allowed to stop that Vote."); } return; } @@ -540,24 +530,22 @@ void VoteCommand_stop(float request, entity caller) } } -void VoteCommand_yes(float request, entity caller) +void VoteCommand_yes(float request, entity caller) // CLIENT ONLY { switch(request) { case VC_REQUEST_COMMAND: { - if(votecalled) + if not(votecalled) { print_to(caller, "^1No vote called."); } + if not(caller.vote_selection == VOTE_SELECT_NULL || autocvar_sv_vote_change) { print_to(caller, "^1You have already voted."); } + + else // everything went okay, continue changing vote { - if(caller.vote_selection == VOTE_SELECT_NULL || autocvar_sv_vote_change) - { - print_to(caller, "^1You accepted the vote."); - caller.vote_selection = VOTE_SELECT_ACCEPT; - msg_entity = caller; - if(!autocvar_sv_vote_singlecount) { VoteCount(); } - } - else { print_to(caller, "^1You have already voted."); } - } - else { print_to(caller, "^1No vote called."); } + print_to(caller, "^1You accepted the vote."); + caller.vote_selection = VOTE_SELECT_ACCEPT; + msg_entity = caller; + if(!autocvar_sv_vote_singlecount) { VoteCount(); } + } return; } @@ -600,12 +588,12 @@ void VoteCommand_(float request) // ================================ // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;) -#define VOTE_COMMANDS(request,caller,arguments) \ +#define VOTE_COMMANDS(request,caller,arguments,command) \ VOTE_COMMAND("abstain", VoteCommand_abstain(request, caller), "Abstain your vote in current vote", VC_ASGNMNT_CLIENTONLY) \ - VOTE_COMMAND("call", VoteCommand_call(request, caller, arguments), "Create a new vote for players to decide on", VC_ASGNMNT_BOTH) \ - VOTE_COMMAND("force", VoteCommand_force(request, arguments), "Force the result of a vote", VC_ASGNMNT_SERVERONLY) \ + VOTE_COMMAND("call", VoteCommand_call(request, caller, arguments, command), "Create a new vote for players to decide on", VC_ASGNMNT_BOTH) \ + VOTE_COMMAND("force", VoteCommand_force(request, arguments, command), "Force the result of a vote", VC_ASGNMNT_SERVERONLY) \ VOTE_COMMAND("help", VoteCommand_macro_help(caller, arguments), "Shows this information", VC_ASGNMNT_BOTH) \ - VOTE_COMMAND("master", VoteCommand_master(request, caller, arguments), "", VC_ASGNMNT_CLIENTONLY) \ + VOTE_COMMAND("master", VoteCommand_master(request, caller, arguments, command), "", VC_ASGNMNT_CLIENTONLY) \ VOTE_COMMAND("no", VoteCommand_no(request, caller), "Select no in current vote", VC_ASGNMNT_CLIENTONLY) \ VOTE_COMMAND("status", VoteCommand_status(request, caller), "Prints information about current vote", VC_ASGNMNT_BOTH) \ VOTE_COMMAND("stop", VoteCommand_stop(request, caller), "Immediately end a vote", VC_ASGNMNT_BOTH) \ @@ -623,7 +611,7 @@ void VoteCommand_macro_help(entity caller, float argc) #define VOTE_COMMAND(name,function,description,assignment) \ { if(Votecommand_check_assignment(caller, assignment)) { print(" ^2", name, "^7: ", description, "\n"); } } - VOTE_COMMANDS(0, caller, 0) + VOTE_COMMANDS(0, caller, 0, "") #undef VOTE_COMMAND print("For help about specific commands, type ", command_origin, " vote help COMMAND\n"); @@ -633,19 +621,19 @@ void VoteCommand_macro_help(entity caller, float argc) #define VOTE_COMMAND(name,function,description,assignment) \ { if(Votecommand_check_assignment(caller, assignment)) { if(name == strtolower(argv(2))) { function; return; } } } - VOTE_COMMANDS(VC_REQUEST_USAGE, caller, argc) + VOTE_COMMANDS(VC_REQUEST_USAGE, caller, argc, "") #undef VOTE_COMMAND } return; } -float VoteCommand_macro_command(entity caller, float argc) +float VoteCommand_macro_command(entity caller, float argc, string vote_command) { #define VOTE_COMMAND(name,function,description,assignment) \ { if(Votecommand_check_assignment(caller, assignment)) { if(name == strtolower(argv(1))) { function; return TRUE; } } } - VOTE_COMMANDS(VC_REQUEST_COMMAND, caller, argc) + VOTE_COMMANDS(VC_REQUEST_COMMAND, caller, argc, vote_command) #undef VOTE_COMMAND return FALSE; @@ -656,13 +644,18 @@ float VoteCommand_macro_command(entity caller, float argc) // Main function handling vote commands // ====================================== -void VoteCommand(float request, entity caller, float argc) +void VoteCommand(float request, entity caller, float argc, string vote_command) { + // Guide for working with argc arguments by example: + // argc: 1 - 2 - 3 - 4 + // argv: 0 - 1 - 2 - 3 + // cmd vote - master - login - password + switch(request) { case VC_REQUEST_COMMAND: { - if(VoteCommand_macro_command(caller, argc)) + if(VoteCommand_macro_command(caller, argc, vote_command)) return; } @@ -676,6 +669,130 @@ void VoteCommand(float request, entity caller, float argc) } } +// ======================= +// Game logic for voting +// ======================= + +void ReadyRestartForce() +{ + local entity e; + + bprint("^1Server is restarting...\n"); + + VoteReset(); + + // clear overtime + if (checkrules_overtimesadded > 0 && g_race_qualifying != 2) { + //we have to decrease timelimit to its original value again!! + float newTL; + newTL = autocvar_timelimit; + newTL -= checkrules_overtimesadded * autocvar_timelimit_overtime; + cvar_set("timelimit", ftos(newTL)); + } + + checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0; + + + readyrestart_happened = 1; + game_starttime = time; + if(!g_ca && !g_arena) + game_starttime += RESTART_COUNTDOWN; + restart_mapalreadyrestarted = 0; //reset this var, needed when cvar sv_ready_restart_repeatable is in use + + inWarmupStage = 0; //once the game is restarted the game is in match stage + + //reset the .ready status of all players (also spectators) + FOR_EACH_CLIENTSLOT(e) + e.ready = 0; + readycount = 0; + Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client + + if(autocvar_teamplay_lockonrestart && teamplay) { + lockteams = 1; + bprint("^1The teams are now locked.\n"); + } + + //initiate the restart-countdown-announcer entity + if(autocvar_sv_ready_restart_after_countdown && !g_ca && !g_arena) + { + restartTimer = spawn(); + restartTimer.think = restartTimer_Think; + restartTimer.nextthink = game_starttime; + } + + //after a restart every players number of allowed timeouts gets reset, too + if(autocvar_sv_timeout) + { + FOR_EACH_REALPLAYER(e) + e.allowedTimeouts = autocvar_sv_timeout_number; + } + + //reset map immediately if this cvar is not set + if (!autocvar_sv_ready_restart_after_countdown) + reset_map(TRUE); + + if(autocvar_sv_eventlog) + GameLogEcho(":restart"); +} + +void ReadyRestart() +{ + // no arena, assault support yet... + if(g_arena | g_assault | gameover | intermission_running | race_completing) + localcmd("restart\n"); + else + localcmd("\nsv_hook_gamerestart\n"); + + ReadyRestartForce(); + + // reset ALL scores, but only do that at the beginning + //of the countdown if sv_ready_restart_after_countdown is off! + //Otherwise scores could be manipulated during the countdown! + if (!autocvar_sv_ready_restart_after_countdown) + Score_ClearAll(); +} + +/** + * Counts how many players are ready. If not enough players are ready, the function + * does nothing. If all players are ready, the timelimit will be extended and the + * restart_countdown variable is set to allow other functions like PlayerPostThink + * to detect that the countdown is now active. If the cvar sv_ready_restart_after_countdown + * is not set the map will be resetted. + * + * Function is called after the server receives a 'ready' sign from a player. + */ +void ReadyCount() +{ + entity tmp_player; + float t_ready, t_players; + + FOR_EACH_REALPLAYER(tmp_player) + { + ++t_players; + if(tmp_player.ready) { ++t_ready; } + } + + readycount = t_ready; + + Nagger_ReadyCounted(); + + // TODO: Add ability to + if(t_ready) // at least one is ready + if(t_ready == t_players) // and, everyone is ready + ReadyRestart(); +} + + +// Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set) +void restartTimer_Think() +{ + restart_mapalreadyrestarted = 1; + reset_map(TRUE); + Score_ClearAll(); + remove(self); + return; +} + void VoteHelp(entity e) { string vmasterdis; if(!autocvar_sv_vote_master) { @@ -709,19 +826,6 @@ void VoteHelp(entity e) { print_to(e, strcat("^3", autocvar_sv_vote_commands, "^7 and maybe further ^3arguments^7")); } -string VoteNetname(entity e) -{ - if(e) { - return e.netname; - } else { - if(autocvar_sv_adminnick != "") { - return autocvar_sv_adminnick; - } else { - return autocvar_hostname; - } - } -} - string ValidateMap(string m, entity e) { m = MapInfo_FixName(m); @@ -754,12 +858,6 @@ void VoteThink() { } } -string VoteParse(string all, float argc) { - if(argc < 3) - return ""; - return substring(all, argv_start_index(2), argv_end_index(-1) - argv_start_index(2)); -} - float VoteCommandInList(string votecommand, string list) { string l; @@ -821,7 +919,7 @@ void VoteReset() { } void VoteAccept() { - bprint("\{1}^2* ^3", VoteNetname(votecaller), "^2's vote for ^1", votecalledvote_display, "^2 was accepted\n"); + bprint("\{1}^2* ^3", VoteCommand_getname(votecaller), "^2's vote for ^1", votecalledvote_display, "^2 was accepted\n"); if(votecalledmaster) { if(votecaller) { @@ -839,30 +937,17 @@ void VoteAccept() { } void VoteReject() { - bprint("\{1}^2* ^3", VoteNetname(votecaller), "^2's vote for ", votecalledvote_display, "^2 was rejected\n"); + bprint("\{1}^2* ^3", VoteCommand_getname(votecaller), "^2's vote for ", votecalledvote_display, "^2 was rejected\n"); VoteReset(); Announce("votefail"); } void VoteTimeout() { - bprint("\{1}^2* ^3", VoteNetname(votecaller), "^2's vote for ", votecalledvote_display, "^2 timed out\n"); + bprint("\{1}^2* ^3", VoteCommand_getname(votecaller), "^2's vote for ", votecalledvote_display, "^2 timed out\n"); VoteReset(); Announce("votefail"); } -void VoteStop(entity stopper) { - bprint("\{1}^2* ^3", VoteNetname(stopper), "^2 stopped ^3", VoteNetname(votecaller), "^2's vote\n"); - if(autocvar_sv_eventlog) - GameLogEcho(strcat(":vote:vstop:", ftos(stopper.playerid))); - if(stopper == votecaller) { - // no wait for next vote so you can correct your vote - if(votecaller) { - votecaller.vote_next = time + autocvar_sv_vote_stop; - } - } - VoteReset(); -} - void VoteSpam(float notvoters, float mincount, string result) { string s; diff --git a/qcsrc/server/vote.qh b/qcsrc/server/vote.qh index 7a25bda64..a5b25584f 100644 --- a/qcsrc/server/vote.qh +++ b/qcsrc/server/vote.qh @@ -13,14 +13,12 @@ float vote_abstaincount; float vote_needed_absolute; float vote_needed_simple; -float VoteCheckNasty(string cmd); +string VoteCommand_getname(entity caller); entity GetKickVoteVictim(string vote, string cmd, entity caller); -void VoteCommand(float request, entity caller, float argc); +void VoteCommand(float request, entity caller, float argc, string vote_command); void VoteHelp(entity e); -string VoteNetname(entity e); string ValidateMap(string m, entity e); void VoteThink(); -string VoteParse(string s, float tokens); float VoteAllowed(string vote, string cmd); void VoteReset(); void VoteAccept();