r3668 - in trunk/data: . qcsrc/server

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Sun Jun 1 12:58:00 EDT 2008


Author: esteel
Date: 2008-06-01 12:57:58 -0400 (Sun, 01 Jun 2008)
New Revision: 3668

Added:
   trunk/data/qcsrc/server/vote.qc
   trunk/data/qcsrc/server/vote.qh
Modified:
   trunk/data/defaultNexuiz.cfg
   trunk/data/qcsrc/server/clientcommands.qc
   trunk/data/qcsrc/server/gamecommand.qc
   trunk/data/qcsrc/server/progs.src
Log:
- Moved all the voting stuff into a file of its own and made it accessible from the server console via sv_cmd vote.
  Now server admins can not only stop votes but use all the vote subcommands like calling a vote.  
  Can be used to act more 'democratic' or test vote settings
- Added a new alias vlogin and removed the kludge 'vote do login'


Modified: trunk/data/defaultNexuiz.cfg
===================================================================
--- trunk/data/defaultNexuiz.cfg	2008-06-01 13:23:44 UTC (rev 3667)
+++ trunk/data/defaultNexuiz.cfg	2008-06-01 16:57:58 UTC (rev 3668)
@@ -684,6 +684,7 @@
 	alias vmap "vcall gotomap $1"
 alias vstop "cmd vote stop"
 alias vmaster "cmd vote master"
+alias vlogin "cmd vote login $*"
 alias vdo "cmd vote do $*"
 	alias vdomap "vdo gotomap $1"
 alias vyes "cmd vote yes"

Modified: trunk/data/qcsrc/server/clientcommands.qc
===================================================================
--- trunk/data/qcsrc/server/clientcommands.qc	2008-06-01 13:23:44 UTC (rev 3667)
+++ trunk/data/qcsrc/server/clientcommands.qc	2008-06-01 16:57:58 UTC (rev 3668)
@@ -1,5 +1,4 @@
 void ReadyCount();
-string ValidateMap(string vote);
 void(entity e) DropFlag;
 string MapVote_Suggest(string m);
 
@@ -138,290 +137,14 @@
 	strunzone(msgstr);
 }
 
-float VoteCheckNasty(string cmd)
-{
-	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)
-		return TRUE;
-	return FALSE;
-}
-
-string GetKickVoteVictim_newcommand;
-string GetKickVoteVictim_reason;
-entity GetKickVoteVictim(string vote, string cmd)
-{
-	float tokens;
-	float i, n, t;
-	string ns;
-	entity e;
-
-	tokens = tokenize(vote);
-	ns = "";
-
-	if(tokens >= 2)
-		if(substring(argv(1), 0, 1) == "#")
-		{
-			ns = substring(argv(1), 1, 999);
-			t = 2;
-		}
-
-	if(tokens >= 3)
-		if(argv(1) == "#")
-		{
-			ns = argv(2);
-			t = 3;
-		}
-
-	if(ns != "")
-	{
-		GetKickVoteVictim_reason = "";
-		for(i = t; i < tokens; ++i)
-			GetKickVoteVictim_reason = strcat(GetKickVoteVictim_reason, argv(i), " ");
-		GetKickVoteVictim_reason = substring(GetKickVoteVictim_reason, 0, strlen(GetKickVoteVictim_reason) - 1);
-
-		n = stof(ns);
-		if(ns == ftos(n)) if(n >= 1) if(n <= maxclients)
-		{
-			e = edict_num(n);
-			if(clienttype(e) == CLIENTTYPE_REAL)
-			{
-				GetKickVoteVictim_newcommand = strcat(argv(0), " # ", ns);
-				return e;
-			}
-		}
-	}
-
-	sprint(self, strcat("Usage: ", cmd, " ", argv(0), " #playernumber (as in \"status\")\n"));
-	return world;
-}
-
 void SV_ParseClientCommand(string s) {
 	local string cmd;
-	local entity e;
 	local float i;
 
 	tokenize(s);
 
-	if(argv(0) == "vote") {
-		if(argv(1) == "help") {
-			local string vmasterdis;
-			if(!cvar("sv_vote_master")) {
-				vmasterdis = " ^1(disabled)";
-			}
-			local string vcalldis;
-			if(!cvar("sv_vote_call")) {
-				vcalldis = " ^1(disabled)";
-			}
-			sprint(self, "^7You can use voting with \"^2cmd vote help^7\" \"^2cmd vote status^7\" \"^2cmd vote call ^3COMMAND ARGUMENTS^7\" \"^2cmd vote stop^7\" \"^2cmd vote master^7\" \"^2cmd vote do ^3COMMAND ARGUMENTS^7\" \"^2cmd vote yes^7\" \"^2cmd vote no^7\" \"^2cmd vote dontcare^7\".\n");
-			sprint(self, "^7Or if your version is up to date you can use these aliases \"^2vhelp^7\" \"^2vstatus^7\" \"^2vcall ^3COMMAND ARGUMENTS^7\" \"^2vstop^7\" \"^2vmaster^7\" \"^2vdo ^3COMMAND ARGUMENTS^7\" \"^2vyes^7\" \"^2vno^7\" \"^2vdontcare^7\".\n");
-			sprint(self, "^7\"^2help^7\" shows this info.\n");
-			sprint(self, "^7\"^2status^7\" shows if there is a vote called and who called it.\n");
-			sprint(self, strcat("^7\"^2call^7\" is used to call a vote. See the list of allowed commands.", vcalldis, "^7\n"));
-			sprint(self, "^7\"^2stop^7\" can be used by the vote caller or an admin to stop a vote and maybe correct it.\n");
-			sprint(self, strcat("^7\"^2master^7\" is used to call a vote to become a master.", vmasterdis, "^7\n"));
-			sprint(self, "^7\"^2do^7\" If you are a master you can execute a command without a vote. See the list of allowed commands.\n");
-			sprint(self, "^7\"^2yes^7\", \"^2no^7\" and \"^2dontcare^7\" to make your vote.\n");
-			sprint(self, "^7If enough of the players vote yes the vote is accepted.\n");
-			sprint(self, "^7If enough of the players vote no the vote is rejected.\n");
-			sprint(self, strcat("^7The vote will end after ", cvar_string("sv_vote_timeout"), "^7 seconds.\n"));
-			sprint(self, "^7You can call a vote for or execute these commands:\n");
-			sprint(self, strcat("^3", cvar_string("sv_vote_commands"), "^7 and maybe further ^3arguments^7\n"));
-		} else if(argv(1) == "status") {
-			if(votecalled) {
-				sprint(self, strcat("^7Vote for ", votecalledvote_display, "^7 called by ^7", votecaller.netname, "^7.\n"));
-			} else {
-				sprint(self, "^1No vote called.\n");
-			}
-		} else if(argv(1) == "call") {
-			if(cvar("sv_vote_call")) {
-				if(tourneyInMatchStage && cvar("g_tourney_disable_spec_vote") && self.classname != "player") {
-					sprint(self, "^1Error: Only players can call a vote during the match-stage.\n");
-				}
-				else if(timeoutStatus) { //don't allow a vote call during a timeout
-					sprint(self, "^1Error: You can not call a vote while a timeout is active.\n");
-				}
-				else if(votecalled) {
-					sprint(self, "^1There is already a vote called.\n");
-				} else {
-					local string vote;
-					vote = VoteParse();
-					if(vote == "") {
-						sprint(self, "^1Your vote is empty. See help for more info.\n");
-					} else if(time < self.vote_next) {
-						sprint(self, strcat("^1You have to wait ^2", ftos(self.vote_next - time), "^1 seconds before you can again call a vote.\n"));
-					} else if(VoteCheckNasty(vote)) {
-						sprint(self, "Syntax error in command.\n");
-					} else if(VoteAllowed(strcat1(argv(2)))) { // strcat seems to be necessary
-						// remap chmap to gotomap (forces intermission)
-						if(vote == "chmap" || vote == "gotomap") // won't work without arguments
-							return;
-						if(substring(vote, 0, 6) == "chmap ")
-							vote = strcat("gotomap ", substring(vote, 6, strlen(vote) - 6));
-						if(substring(vote, 0, 8) == "gotomap ")
-						{
-							if(!(vote = ValidateMap(substring(vote, 8, strlen(vote) - 8))))
-								return;
-							vote = strcat("gotomap ", vote);
-						}
-
-						// make kick and kickban votes a bit nicer (and reject them if formatted badly)
-						if(substring(vote, 0, 5) == "kick " || substring(vote, 0, 8) == "kickban ")
-						{
-							if(!(e = GetKickVoteVictim(vote, "vcall")))
-								return;
-							vote = GetKickVoteVictim_newcommand;
-							votecalledvote_display = strzone(strcat("^1", vote, " (^7", e.netname, "^1): ", GetKickVoteVictim_reason));
-						}
-						else
-						{
-							votecalledvote_display = strzone(strcat("^1", vote));
-						}
-						votecalledvote = strzone(vote);
-						votecalled = TRUE;
-						votecalledmaster = FALSE;
-						votecaller = self; // remember who called the vote
-						votefinished = time + cvar("sv_vote_timeout");
-						votecaller.vote_vote = 1; // of course you vote yes
-						votecaller.vote_next = time + cvar("sv_vote_wait");
-						bprint("\{1}^2* ^3", votecaller.netname, "^2 calls a vote for ", votecalledvote_display, "\n");
-						VoteCount(); // needed if you are the only one
-					} else {
-						sprint(self, "^1This vote is not ok. See help for more info.\n");
-					}
-				}
-			} else {
-				sprint(self, "^1Vote calling is NOT allowed.\n");
-			}
-		} else if(argv(1) == "stop") {
-			if(!votecalled) {
-				sprint(self, "^1No vote called.\n");
-			} else if(self == votecaller) { // the votecaller can stop a vote
-				VoteStop(self);
-			} else if(self.vote_master) { // masters can, too
-				VoteStop(self);
-			} else {
-				sprint(self, "^1You are not allowed to stop that Vote.\n");
-			}
-		} else if(argv(1) == "master") {
-			if(cvar("sv_vote_master")) {
-				if(votecalled) {
-					sprint(self, "^1There is already a vote called.\n");
-				} else {
-					votecalled = TRUE;
-					votecalledmaster = TRUE;
-					votecalledvote = strzone("XXX");
-					votecalledvote_display = strzone("^3master");
-					votecaller = self; // remember who called the vote
-					votefinished = time + cvar("sv_vote_timeout");
-					votecaller.vote_vote = 1; // of course you vote yes
-					votecaller.vote_next = time + cvar("sv_vote_wait");
-					bprint("\{1}^2* ^3", votecaller.netname, "^2 calls a vote to become ^3master^2.\n");
-					VoteCount(); // needed if you are the only one
-				}
-			} else {
-				sprint(self, "^1Vote to become master is NOT allowed.\n");
-			}
-		} else if(argv(1) == "do") {
-			if(argv(2) == "login") {
-				local string masterpwd;
-				masterpwd = cvar_string("sv_vote_master_password");
-				if(masterpwd != "") {
-					self.vote_master = (masterpwd == argv(3));
-					if(self.vote_master) {
-						ServerConsoleEcho(strcat("Accepted master login from ", self.netname), TRUE);
-						bprint("\{1}^2* ^3", self.netname, "^2 logged in as ^3master^2\n");
-					}
-					else
-						ServerConsoleEcho(strcat("REJECTED master login from ", self.netname), TRUE);
-				}
-				else
-					sprint(self, "^1You are NOT a master.\n");
-			} else if(self.vote_master) {
-				local string dovote, dovote_display;
-				dovote = VoteParse();
-				if(dovote == "") {
-					sprint(self, "^1Your command was empty. See help for more info.\n");
-				} else if(VoteCheckNasty(dovote)) {
-					sprint(self, "Syntax error in command.\n");
-				} else if(VoteAllowed(strcat1(argv(2)))) { // strcat seems to be necessary
-					if(dovote == "chmap" || dovote == "gotomap") // won't work without arguments
-						return;
-					if(substring(dovote, 0, 6) == "chmap ")
-						dovote = strcat("gotomap ", substring(dovote, 6, strlen(dovote) - 6));
-					if(substring(dovote, 0, 8) == "gotomap ")
-					{
-						if(!(dovote = ValidateMap(substring(dovote, 8, strlen(dovote) - 8))))
-							return;
-						dovote = strcat("gotomap ", dovote);
-					}
-
-					dovote_display = dovote;
-					if(substring(dovote, 0, 5) == "kick " || substring(dovote, 0, 8) == "kickban ")
-					{
-						if(!(e = GetKickVoteVictim(dovote, "vdo")))
-							return;
-						dovote = GetKickVoteVictim_newcommand;
-						dovote_display = strcat("^1", dovote, " (^7", e.netname, "^1): ", GetKickVoteVictim_reason);
-					}
-					bprint("\{1}^2* ^3", self.netname, "^2 used his ^3master^2 status to do \"^2", dovote_display, "^2\".\n");
-					localcmd(strcat(dovote, "\n"));
-				} else {
-					sprint(self, "^1This command is not ok. See help for more info.\n");
-				}
-			} else {
-				sprint(self, "^1You are NOT a master.\n");
-			}
-		} else if(argv(1) == "yes") {
-			if(!votecalled) {
-				sprint(self, "^1No vote called.\n");
-			} else if(self.vote_vote == 0
-				  || cvar("sv_vote_change")) {
-				sprint(self, "^1You accepted the vote.\n");
-				self.vote_vote = 1;
-				centerprint_expire(self, CENTERPRIO_VOTE);
-				if(!cvar("sv_vote_singlecount")) {
-					VoteCount();
-				}
-			} else {
-				sprint(self, "^1You have already voted.\n");
-			}
-		} else if(argv(1) == "no") {
-			if(!votecalled) {
-				sprint(self, "^1No vote called.\n");
-			} else if(self.vote_vote == 0
-				  || cvar("sv_vote_change")) {
-				sprint(self, "^1You rejected the vote.\n");
-				self.vote_vote = -1;
-				centerprint_expire(self, CENTERPRIO_VOTE);
-				if(!cvar("sv_vote_singlecount")) {
-					VoteCount();
-				}
-			} else {
-				sprint(self, "^1You have already voted.\n");
-			}
-		} else if(argv(1) == "abstain" || argv(1) == "dontcare") {
-			if(!votecalled) {
-				sprint(self, "^1No vote called.\n");
-			} else if(self.vote_vote == 0
-				  || cvar("sv_vote_change")) {
-				sprint(self, "^1You abstained from your vote.\n");
-				self.vote_vote = -2;
-				centerprint_expire(self, CENTERPRIO_VOTE);
-				if(!cvar("sv_vote_singlecount")) {
-					VoteCount();
-				}
-			} else {
-				sprint(self, "^1You have already voted.\n");
-			}
-		} else {
-			// ignore this?
-			sprint(self, "^1Unknown vote command.\n");
-		}
+	if(GameCommand_Vote(s, self)) {
+		return;
 	} else if(argv(0) == "autoswitch") {
 		// be backwards compatible with older clients (enabled)
 		self.autoswitch = ("0" != argv(1));
@@ -609,286 +332,6 @@
 	}
 }
 
-string ValidateMap(string m)
-{
-#ifdef MAPINFO
-	m = MapInfo_FixName(m);
-	if(!m)
-	{
-		sprint(self, "This map is not available on this server.\n");
-		return string_null;
-	}
-#else
-	if(!cvar("sv_vote_change_gametype"))
-		if(!IsSameGametype(m))
-		{
-			sprint(self, "This server does not allow changing the game type by map votes.\n");
-			return string_null;
-		}
-#endif
-	if(!cvar("sv_vote_override_mostrecent"))
-		if(Map_IsRecent(m))
-		{
-			sprint(self, "This server does not allow for recent maps to be played again. Please be patient for some rounds.\n");
-			return string_null;
-		}
-#ifdef MAPINFO
-	if(!MapInfo_CheckMap(m))
-	{
-		sprint(self, strcat("^1Invalid mapname, \"^3", m, "^1\" does not support the current game mode.\n"));
-		return string_null;
-	}
-#else
-	if(!TryFile(strcat("maps/", m, ".mapcfg")))
-	{
-		sprint(self, strcat("^1Invalid mapname, \"^3", m, "^1\" does not exist on this server.\n"));
-		return string_null;
-	}
-#endif
-
-	return m;
-}
-
-
-void VoteThink() {
-	if(votefinished > 0 // a vote was called
-	    && time > votefinished) // time is up
-	{
-		VoteCount();
-	}
-}
-
-string VoteParse() {
-	local float index;
-	index = 3;
-	local string vote;
-	vote = argv(2);
-	while(argv(index) != "") {
-		vote = strcat(vote, " ", argv(index));
-		index++;
-	}
-
-	// necessary for some of the string operations
-	vote = strzone(vote);
-
-	// now we remove some things that could be misused
-	index = 0;
-	local float found;
-	found = FALSE;
-	local float votelength;
-	votelength = strlen(vote);
-	while(!found && index < votelength)
-	{
-		local string badchar;
-		badchar = substring(vote, index, 1);
-		if(badchar == ";"
-		   || badchar == "\r"
-		   || badchar == "\n")
-		{
-			found = TRUE;
-		} else {
-			index++;
-		}
-	}
-	return substring(vote, 0, index);
-}
-
-float VoteAllowed(string votecommand) {
-	tokenize(cvar_string("sv_vote_commands"));
-	local float index;
-	index = 0;
-	while(argv(index) != "") {
-		local string allowed;
-		allowed = argv(index);
-		if(votecommand == allowed) {
-			return TRUE;
-		}
-		index++;
-	}
-	return FALSE;
-}
-
-void VoteReset() {
-	local entity player;
-
-	FOR_EACH_CLIENT(player)
-	{
-		player.vote_vote = 0;
-		centerprint_expire(player, CENTERPRIO_VOTE);
-	}
-
-	if(votecalled)
-	{
-		strunzone(votecalledvote);
-		strunzone(votecalledvote_display);
-	}
-
-	votecalled = FALSE;
-	votecalledmaster = FALSE;
-	votefinished = 0;
-}
-
-void VoteAccept() {
-	bprint("\{1}^2* ^3", votecaller.netname, "^2's vote for ^1", votecalledvote_display, "^2 was accepted\n");
-	if(votecalledmaster)
-	{
-		votecaller.vote_master = 1;
-	} else {
-		//in g_tourney mode and if the vote is a timelimit-change, don't change it immediately but after restart
-		if(cvar("g_tourney") && substring(votecalledvote, 0, 10) == "timelimit ") {
-			if( stof(substring(votecalledvote, 10, strlen(votecalledvote) - 10)) > 0 ) {
-				timelimit_orig = stof(substring(votecalledvote, 10, strlen(votecalledvote) - 10));
-				bprint(strcat("The timelimit will be set to ", ftos(timelimit_orig), " minutes after the next restart!\n"));
-			}
-			else //calls like "timelimit -1" can pass immediately
-				localcmd(strcat(votecalledvote, "\n"));
-		}
-		else
-			localcmd(strcat(votecalledvote, "\n"));
-	}
-	votecaller.vote_next = 0; // people like your votes, no wait for next vote
-	VoteReset();
-}
-
-void VoteReject() {
-	bprint("\{1}^2* ^3", votecaller.netname, "^2's vote for ", votecalledvote_display, "^2 was rejected\n");
-	VoteReset();
-}
-
-void VoteTimeout() {
-	bprint("\{1}^2* ^3", votecaller.netname, "^2's vote for ", votecalledvote_display, "^2 timed out\n");
-	VoteReset();
-}
-
-void VoteStop(entity stopper) {
-	bprint("\{1}^2* ^3", stopper.netname, "^2 stopped ^3", votecaller.netname, "^2's vote\n");
-	if(stopper == votecaller) {
-		// no wait for next vote so you can correct your vote
-		votecaller.vote_next = 0;
-	}
-	VoteReset();
-}
-
-void VoteNag() {
-	if(votecalled)
-		if(self.vote_vote == 0)
-			centerprint_atprio(self, CENTERPRIO_VOTE, strcat("^7^3", votecaller.netname, "^2 called a vote for ", votecalledvote_display, "\n\n^2You have not voted yet!\n^2HINT: By default, F1 is yes and F2 is no."));
-}
-
-void VoteSpam(float yescount, float nocount, float abstaincount, float notvoters, float mincount)
-{
-	string s;
-	if(mincount >= 0)
-	{
-		s = strcat("\{1}^2* vote results: ^1", ftos(yescount), "^2:^1");
-		s = strcat(s, ftos(nocount), "^2 (^1");
-		s = strcat(s, ftos(mincount), "^2 needed), ^1");
-		s = strcat(s, ftos(abstaincount), "^2 didn't care, ^1");
-		s = strcat(s, ftos(notvoters), "^2 didn't vote\n");
-	}
-	else
-	{
-		s = strcat("\{1}^2* vote results: ^1", ftos(yescount), "^2:^1");
-		s = strcat(s, ftos(nocount), "^2 (^1");
-		s = strcat(s, ftos(abstaincount), "^2 didn't care, ^1");
-		s = strcat(s, ftos(notvoters), "^2 didn't have to vote\n");
-	}
-	bprint(s);
-}
-
-void VoteCount() {
-	local float playercount;
-	playercount = 0;
-	local float yescount;
-	yescount = 0;
-	local float nocount;
-	nocount = 0;
-	local float abstaincount;
-	abstaincount = 0;
-	local entity player;
-	//same for real players
-	local float realplayercount;
-	local float realplayeryescount;
-	local float realplayernocount;
-	local float realplayerabstaincount;
-	realplayercount = realplayernocount = realplayerabstaincount = realplayeryescount = 0;
-
-	FOR_EACH_REALCLIENT(player)
-	{
-		if(player.vote_vote == -1) {
-			nocount++;
-		} else if(player.vote_vote == 1) {
-			yescount++;
-		} else if(player.vote_vote == -2) {
-			abstaincount++;
-		}
-		playercount++;
-		//do the same for real players
-		if(player.classname == "player") {
-			if(player.vote_vote == -1) {
-				realplayernocount++;
-			} else if(player.vote_vote == 1) {
-				realplayeryescount++;
-			} else if(player.vote_vote == -2) {
-				realplayerabstaincount++;
-			}
-			realplayercount++;
-		}
-	}
-
-	//in tournament mode, if we have at least one player then don't make the vote dependent on spectators (so specs don't have to press F1)
-	if( cvar("g_tourney") && (realplayercount > 0) ) {
-		yescount = realplayeryescount;
-		nocount = realplayernocount;
-		abstaincount = realplayerabstaincount;
-		playercount = realplayercount;
-	}
-
-
-	if((playercount == 1) && votecalledmaster) {
-		// if only one player is on the server becoming vote
-		// master is not allowed.  This could be used for
-		// trolling or worse. 'self' is the user who has
-		// called the vote because this function is called
-		// by SV_ParseClientCommand. Maybe all voting should
-		// be disabled for a single player?
-		sprint(self, "^1You are the only player on this server so you can not become vote master.\n");
-		votecaller.vote_next = 0;
-		VoteReset();
-	} else {
-		float votefactor;
-		votefactor = bound(0.5, cvar("sv_vote_majority_factor"), 0.999);
-		if(yescount > (playercount - abstaincount) * votefactor)
-		{
-			VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, -1);
-			VoteAccept();
-		}
-		else if(nocount >= (playercount - abstaincount) * (1 - votefactor)) // that means, yescount cannot reach minyes any more
-		{
-			VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, -1);
-			VoteReject();
-		}
-		else if(time > votefinished)
-		{
-			if(cvar("sv_vote_simple_majority"))
-			{
-				VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, floor((yescount + nocount) * votefactor) + 1);
-				if(yescount > (yescount + nocount) * votefactor)
-					VoteAccept();
-				else if(yescount + nocount > 0)
-					VoteReject();
-				else
-					VoteTimeout();
-			}
-			else
-			{
-				VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, floor((playercount - abstaincount) * votefactor) + 1);
-				VoteTimeout();
-			}
-		}
-	}
-}
-
 /**
  * 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

Modified: trunk/data/qcsrc/server/gamecommand.qc
===================================================================
--- trunk/data/qcsrc/server/gamecommand.qc	2008-06-01 13:23:44 UTC (rev 3667)
+++ trunk/data/qcsrc/server/gamecommand.qc	2008-06-01 16:57:58 UTC (rev 3668)
@@ -31,12 +31,15 @@
 		print("  savedb filename\n");
 		print("  dumpdb filename\n");
 		print("  loaddb filename\n");
-		print("  vstop\n");
+		GameCommand_Vote("help", world);
 		GameCommand_Ban("help");
 		GameCommand_Generic("help");
 		return;
 	}
 
+	if(GameCommand_Vote(command, world))
+		return;
+
 	if(GameCommand_Ban(command))
 		return;
 
@@ -184,18 +187,6 @@
 			bprint("That command can only be used in a team-based gamemode.\n");
 		return;
 	}
-	if(argv(0) == "vstop")
-	{
-		local entity temp;
-		temp = spawn();
-		if(cvar_string("sv_adminnick") == "")
-			temp.netname = cvar_string("hostname");
-		else
-			temp.netname = cvar_string("sv_adminnick");
-		VoteStop(temp);
-		remove(temp);
-		return;
-	}
 
 	print("Invalid command. For a list of supported commands, try sv_cmd help.\n");
 }

Modified: trunk/data/qcsrc/server/progs.src
===================================================================
--- trunk/data/qcsrc/server/progs.src	2008-06-01 13:23:44 UTC (rev 3667)
+++ trunk/data/qcsrc/server/progs.src	2008-06-01 16:57:58 UTC (rev 3668)
@@ -21,6 +21,8 @@
 
 antilag.qh
 
+vote.qh
+
 miscfunctions.qc
 
 waypointsprites.qc
@@ -87,6 +89,8 @@
 
 clientcommands.qc
 
+vote.qc
+
 campaign.qc
 ../common/campaign_file.qc
 ../common/campaign_setup.qc

Added: trunk/data/qcsrc/server/vote.qc
===================================================================
--- trunk/data/qcsrc/server/vote.qc	                        (rev 0)
+++ trunk/data/qcsrc/server/vote.qc	2008-06-01 16:57:58 UTC (rev 3668)
@@ -0,0 +1,599 @@
+float VoteCheckNasty(string cmd)
+{
+	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)
+		return TRUE;
+	return FALSE;
+}
+
+entity GetKickVoteVictim(string vote, string cmd, entity caller)
+{
+	float tokens;
+	float i, n, t;
+	string ns;
+	entity e;
+
+	tokens = tokenize(vote);
+	ns = "";
+
+	if(tokens >= 2)
+		if(substring(argv(1), 0, 1) == "#")
+		{
+			ns = substring(argv(1), 1, 999);
+			t = 2;
+		}
+
+	if(tokens >= 3)
+		if(argv(1) == "#")
+		{
+			ns = argv(2);
+			t = 3;
+		}
+
+	if(ns != "")
+	{
+		GetKickVoteVictim_reason = "";
+		for(i = t; i < tokens; ++i)
+			GetKickVoteVictim_reason = strcat(GetKickVoteVictim_reason, argv(i), " ");
+		GetKickVoteVictim_reason = substring(GetKickVoteVictim_reason, 0, strlen(GetKickVoteVictim_reason) - 1);
+
+		n = stof(ns);
+		if(ns == ftos(n)) if(n >= 1) if(n <= maxclients)
+		{
+			e = edict_num(n);
+			if(clienttype(e) == CLIENTTYPE_REAL)
+			{
+				GetKickVoteVictim_newcommand = strcat(argv(0), " # ", ns);
+				return e;
+			}
+		}
+	}
+
+	print_to(caller, strcat("Usage: ", cmd, " ", argv(0), " #playernumber (as in \"status\")\n"));
+	return world;
+}
+
+float GameCommand_Vote(string s, entity e) {
+	if(argv(0) == "help") {
+		print_to(e, "  vote COMMANDS ARGUMENTS. See 'vote help' for more info.");
+		return TRUE;
+	} else if(argv(0) == "vote") {
+		if(argv(1) == "") {
+			print_to(e, "^1You have to supply a vote command. See help for more info.");
+		} else if(argv(1) == "help") {
+			VoteHelp(e);
+		} else if(argv(1) == "status") {
+			if(votecalled) {
+				print_to(e, strcat("^7Vote for ", votecalledvote_display, "^7 called by ^7", VoteNetname(votecaller), "^7."));
+			} else {
+				print_to(e, "^1No vote called.");
+			}
+		} else if(argv(1) == "call") {
+			if(cvar("sv_vote_call")) {
+				if(tourneyInMatchStage
+				   && cvar("g_tourney_disable_spec_vote")
+				   && e.classname != "player") {
+					print_to(e, "^1Error: Only players can call a vote during the match-stage.");
+				}
+				else if(timeoutStatus) { //don't allow a vote call during a timeout
+					print_to(e, "^1Error: You can not call a vote while a timeout is active.");
+				}
+				else if(votecalled) {
+					print_to(e, "^1There is already a vote called.");
+				} else {
+					local string vote;
+					vote = VoteParse();
+					if(vote == "") {
+						print_to(e, "^1Your vote is empty. See help for more info.");
+					} else if(e
+						&& time < e.vote_next) {
+							print_to(e, strcat("^1You have to wait ^2", ftos(e.vote_next - time), "^1 seconds before you can again call a vote."));
+					} else if(VoteCheckNasty(vote)) {
+						print_to(e, "Syntax error in command. See help for more info.");
+					} else if(VoteAllowed(strcat1(argv(2)))) { // strcat seems to be necessary
+						// remap chmap to gotomap (forces intermission)
+						if(vote == "chmap" || vote == "gotomap") // won't work without arguments
+							return TRUE;
+						if(substring(vote, 0, 6) == "chmap ")
+							vote = strcat("gotomap ", substring(vote, 6, strlen(vote) - 6));
+						if(substring(vote, 0, 8) == "gotomap ")
+						{
+							if(!(vote = ValidateMap(substring(vote, 8, strlen(vote) - 8), e)))
+								return TRUE;
+							vote = strcat("gotomap ", vote);
+						}
+
+						// make kick and kickban votes a bit nicer (and reject them if formatted badly)
+						if(substring(vote, 0, 5) == "kick " || substring(vote, 0, 8) == "kickban ")
+						{
+							if(!(e = GetKickVoteVictim(vote, "vcall", e)))
+								return TRUE;
+							vote = GetKickVoteVictim_newcommand;
+							votecalledvote_display = strzone(strcat("^1", vote, " (^7", VoteNetname(e), "^1): ", GetKickVoteVictim_reason));
+						}
+						else
+						{
+							votecalledvote_display = strzone(strcat("^1", vote));
+						}
+						votecalledvote = strzone(vote);
+						votecalled = TRUE;
+						votecalledmaster = FALSE;
+						votefinished = time + cvar("sv_vote_timeout");
+						votecaller = e; // remember who called the vote
+						if(e) {
+							e.vote_vote = 1; // of course you vote yes
+							e.vote_next = time + cvar("sv_vote_wait");
+						}
+						bprint("\{1}^2* ^3", VoteNetname(votecaller), "^2 calls a vote for ", votecalledvote_display, "\n");
+						VoteCount(); // needed if you are the only one
+					} else {
+						print_to(e, "^1This vote is not ok. See help for more info.");
+					}
+				}
+			} else {
+				print_to(e, "^1Vote calling is NOT allowed.");
+			}
+		} else if(argv(1) == "stop") {
+			if(!votecalled) {
+				print_to(e, "^1No vote called.");
+			} else if(e == votecaller) { // the votecaller can stop a vote
+				VoteStop(e);
+			} else if(!e) { // server admin / console can too
+				VoteStop(e);
+			} else if(e.vote_master) { // masters can too
+				VoteStop(e);
+			} else {
+				print_to(e, "^1You are not allowed to stop that Vote.");
+			}
+		} else if(argv(1) == "master") {
+			if(cvar("sv_vote_master")) {
+				if(votecalled) {
+					print_to(e, "^1There is already a vote called.");
+				} else {
+					votecalled = TRUE;
+					votecalledmaster = TRUE;
+					votecalledvote = strzone("XXX");
+					votecalledvote_display = strzone("^3master");
+					votefinished = time + cvar("sv_vote_timeout");
+					votecaller = e; // remember who called the vote
+					if(e) {
+						e.vote_vote = 1; // of course you vote yes
+						e.vote_next = time + cvar("sv_vote_wait");
+					}
+					bprint("\{1}^2* ^3", VoteNetname(votecaller), "^2 calls a vote to become ^3master^2.\n");
+					VoteCount(); // needed if you are the only one
+				}
+			} else {
+				print_to(e, "^1Vote to become master is NOT allowed.");
+			}
+		} else if(argv(1) == "do") {
+			if(!e || e.vote_master) {
+				local string dovote, dovote_display;
+				dovote = VoteParse();
+				if(dovote == "") {
+					print_to(e, "^1Your command was empty. See help for more info.");
+				} else if(VoteCheckNasty(dovote)) {
+					print_to(e, "Syntax error in command. See help for more info.");
+				} else if(VoteAllowed(strcat1(argv(2)))) { // strcat seems to be necessary
+					if(dovote == "chmap" || dovote == "gotomap") // won't work without arguments
+						return TRUE;
+					if(substring(dovote, 0, 6) == "chmap ")
+						dovote = strcat("gotomap ", substring(dovote, 6, strlen(dovote) - 6));
+					if(substring(dovote, 0, 8) == "gotomap ")
+					{
+						if(!(dovote = ValidateMap(substring(dovote, 8, strlen(dovote) - 8), e)))
+							return TRUE;
+						dovote = strcat("gotomap ", dovote);
+					}
+
+					dovote_display = dovote;
+					if(substring(dovote, 0, 5) == "kick " || substring(dovote, 0, 8) == "kickban ")
+					{
+						if(!(e = GetKickVoteVictim(dovote, "vdo", e)))
+							return TRUE;
+						dovote = GetKickVoteVictim_newcommand;
+						dovote_display = strcat("^1", dovote, " (^7", VoteNetname(e), "^1): ", GetKickVoteVictim_reason);
+					}
+					bprint("\{1}^2* ^3", VoteNetname(e), "^2 used his ^3master^2 status to do \"^2", dovote_display, "^2\".\n");
+					localcmd(strcat(dovote, "\n"));
+				} else {
+					print_to(e, "^1This command is not ok. See help for more info.");
+				}
+			} else {
+				print_to(e, "^1You are NOT a master.  You might need to login or vote to become master first. See help for more info.");
+			}
+		} else if(argv(1) == "login") {
+			local string masterpwd;
+			masterpwd = cvar_string("sv_vote_master_password");
+			if(masterpwd != "") {
+				local float granted;
+				granted = (masterpwd == argv(2));
+				if (e)
+					e.vote_master = granted;
+				if(granted) {
+					ServerConsoleEcho(strcat("Accepted master login from ", VoteNetname(e)), TRUE);
+					bprint("\{1}^2* ^3", VoteNetname(e), "^2 logged in as ^3master^2\n");
+				}
+				else
+					ServerConsoleEcho(strcat("REJECTED master login from ", VoteNetname(e)), TRUE);
+			}
+			else
+				print_to(e, "^1Login to become master is NOT allowed.");
+		} else if(argv(1) == "yes") {
+			if(!votecalled) {
+				print_to(e, "^1No vote called.");
+			} else if (!e) {
+				print_to(e, "^1You can't vote from the server console.");
+			} else if(e.vote_vote == 0
+				  || cvar("sv_vote_change")) {
+				print_to(e, "^1You accepted the vote.");
+				e.vote_vote = 1;
+				centerprint_expire(e, CENTERPRIO_VOTE);
+				if(!cvar("sv_vote_singlecount")) {
+					VoteCount();
+				}
+			} else {
+				print_to(e, "^1You have already voted.");
+			}
+		} else if(argv(1) == "no") {
+			if(!votecalled) {
+				print_to(e, "^1No vote called.");
+			} else if (!e) {
+				print_to(e, "^1You can't vote from the server console.");
+			} else if(e.vote_vote == 0
+				  || cvar("sv_vote_change")) {
+				print_to(e, "^1You rejected the vote.");
+				e.vote_vote = -1;
+				centerprint_expire(e, CENTERPRIO_VOTE);
+				if(!cvar("sv_vote_singlecount")) {
+					VoteCount();
+				}
+			} else {
+				print_to(e, "^1You have already voted.");
+			}
+		} else if(argv(1) == "abstain" || argv(1) == "dontcare") {
+			if(!votecalled) {
+				print_to(e, "^1No vote called.");
+			} else if (!e) {
+				print_to(e, "^1You can't vote from the server console.");
+			} else if(e.vote_vote == 0
+				  || cvar("sv_vote_change")) {
+				print_to(e, "^1You abstained from your vote.");
+				e.vote_vote = -2;
+				centerprint_expire(e, CENTERPRIO_VOTE);
+				if(!cvar("sv_vote_singlecount")) {
+					VoteCount();
+				}
+			} else {
+				print_to(e, "^1You have already voted.");
+			}
+		} else {
+			// ignore this?
+			print_to(e, "^1Unknown vote command.");
+		}
+		return TRUE;
+	}
+	return FALSE;
+}
+
+void VoteHelp(entity e) {
+	local string vmasterdis;
+	if(!cvar("sv_vote_master")) {
+		vmasterdis = " ^1(disabled)";
+	}
+
+	local string vlogindis;
+	if("" == cvar_string("sv_vote_master_password")) {
+		vlogindis = " ^1(disabled)";
+	}
+
+	local string vcalldis;
+	if(!cvar("sv_vote_call")) {
+		vcalldis = " ^1(disabled)";
+	}
+
+	print_to(e, "^7You can use voting with \"^2cmd vote help^7\" \"^2cmd vote status^7\" \"^2cmd vote call ^3COMMAND ARGUMENTS^7\" \"^2cmd vote stop^7\" \"^2cmd vote master^7\" \"^2cmd vote login^7\" \"^2cmd vote do ^3COMMAND ARGUMENTS^7\" \"^2cmd vote yes^7\" \"^2cmd vote no^7\" \"^2cmd vote abstain^7\" \"^2cmd vote dontcare^7\".");
+	print_to(e, "^7Or if your version is up to date you can use these aliases \"^2vhelp^7\" \"^2vstatus^7\" \"^2vcall ^3COMMAND ARGUMENTS^7\" \"^2vstop^7\" \"^2vmaster^7\" \"^2vlogin^7\" \"^2vdo ^3COMMAND ARGUMENTS^7\" \"^2vyes^7\" \"^2vno^7\" \"^2abstain^7\" \"^2vdontcare^7\".");
+	print_to(e, "^7\"^2help^7\" shows this info.");
+	print_to(e, "^7\"^2status^7\" shows if there is a vote called and who called it.");
+	print_to(e, strcat("^7\"^2call^7\" is used to call a vote. See the list of allowed commands.", vcalldis, "^7"));
+	print_to(e, "^7\"^2stop^7\" can be used by the vote caller or an admin to stop a vote and maybe correct it.");
+	print_to(e, strcat("^7\"^2master^7\" call a vote to become master who can execute commands without a vote", vmasterdis, "^7"));
+	print_to(e, strcat("^7\"^2login^7\" login to become master who can execute commands without a vote.", vlogindis, "^7"));
+	print_to(e, "^7\"^2do^7\" executes a command if you are a master. See the list of allowed commands.");
+	print_to(e, "^7\"^2yes^7\", \"^2no^7\", \"^2abstain^7\" and \"^2dontcare^7\" to make your vote.");
+	print_to(e, "^7If enough of the players vote yes the vote is accepted.");
+	print_to(e, "^7If enough of the players vote no the vote is rejected.");
+	print_to(e, strcat("^7If neither the vote will timeout after ", cvar_string("sv_vote_timeout"), "^7 seconds."));
+	print_to(e, "^7You can call a vote for or execute these commands:");
+	print_to(e, strcat("^3", cvar_string("sv_vote_commands"), "^7 and maybe further ^3arguments^7"));
+}
+
+string VoteNetname(entity e)
+{
+	if(e) {
+		return e.netname;
+	} else {
+		if(cvar_string("sv_adminnick") != "") {
+			return cvar_string("sv_adminnick");
+		} else {
+			return cvar_string("hostname");
+		}
+	}
+}
+
+string ValidateMap(string m, entity e)
+{
+#ifdef MAPINFO
+	m = MapInfo_FixName(m);
+	if(!m)
+	{
+		print_to(e, "This map is not available on this server.");
+		return string_null;
+	}
+#else
+	if(!cvar("sv_vote_change_gametype"))
+		if(!IsSameGametype(m))
+		{
+			print_to(e, "This server does not allow changing the game type by map votes.");
+			return string_null;
+		}
+#endif
+	if(!cvar("sv_vote_override_mostrecent"))
+		if(Map_IsRecent(m))
+		{
+			print_to(e, "This server does not allow for recent maps to be played again. Please be patient for some rounds.");
+			return string_null;
+		}
+#ifdef MAPINFO
+	if(!MapInfo_CheckMap(m))
+	{
+		print_to(e, strcat("^1Invalid mapname, \"^3", m, "^1\" does not support the current game mode."));
+		return string_null;
+	}
+#else
+	if(!TryFile(strcat("maps/", m, ".mapcfg")))
+	{
+		print_to(e, strcat("^1Invalid mapname, \"^3", m, "^1\" does not exist on this server."));
+		return string_null;
+	}
+#endif
+
+	return m;
+}
+
+
+void VoteThink() {
+	if(votefinished > 0) // a vote was called
+	if(time > votefinished) // time is up
+	{
+		VoteCount();
+	}
+}
+
+string VoteParse() {
+	local float index;
+	index = 3;
+	local string vote;
+	vote = argv(2);
+	while(argv(index) != "") {
+		vote = strcat(vote, " ", argv(index));
+		index++;
+	}
+
+	// necessary for some of the string operations
+	vote = strzone(vote);
+
+	return vote;
+}
+
+float VoteAllowed(string votecommand) {
+	tokenize(cvar_string("sv_vote_commands"));
+	local float index;
+	index = 0;
+	while(argv(index) != "") {
+		if(votecommand == argv(index)) {
+			return TRUE;
+		}
+		index++;
+	}
+	return FALSE;
+}
+
+void VoteReset() {
+	local entity player;
+
+	FOR_EACH_CLIENT(player)
+	{
+		player.vote_vote = 0;
+		centerprint_expire(player, CENTERPRIO_VOTE);
+	}
+
+	if(votecalled)
+	{
+		strunzone(votecalledvote);
+		strunzone(votecalledvote_display);
+	}
+
+	votecalled = FALSE;
+	votecalledmaster = FALSE;
+	votefinished = 0;
+}
+
+void VoteAccept() {
+	bprint("\{1}^2* ^3", VoteNetname(votecaller), "^2's vote for ^1", votecalledvote_display, "^2 was accepted\n");
+	if(votecalledmaster)
+	{
+		if(votecaller) {
+			votecaller.vote_master = 1;
+		}
+	} else {
+		//in g_tourney mode and if the vote is a timelimit-change, don't change it immediately but after restart
+		if(cvar("g_tourney")
+		   && substring(votecalledvote, 0, 10) == "timelimit ") {
+			if( stof(substring(votecalledvote, 10, strlen(votecalledvote) - 10)) > 0 ) {
+				timelimit_orig = stof(substring(votecalledvote, 10, strlen(votecalledvote) - 10));
+				bprint(strcat("The timelimit will be set to ", ftos(timelimit_orig), " minutes after the next restart!\n"));
+			}
+			else //calls like "timelimit -1" can pass immediately
+				localcmd(strcat(votecalledvote, "\n"));
+		}
+		else
+			localcmd(strcat(votecalledvote, "\n"));
+	}
+	if(votecaller) {
+		votecaller.vote_next = 0; // people like your votes,
+					  // no wait for next vote
+	}
+	VoteReset();
+}
+
+void VoteReject() {
+	bprint("\{1}^2* ^3", VoteNetname(votecaller), "^2's vote for ", votecalledvote_display, "^2 was rejected\n");
+	VoteReset();
+}
+
+void VoteTimeout() {
+	bprint("\{1}^2* ^3", VoteNetname(votecaller), "^2's vote for ", votecalledvote_display, "^2 timed out\n");
+	VoteReset();
+}
+
+void VoteStop(entity stopper) {
+	bprint("\{1}^2* ^3", VoteNetname(stopper), "^2 stopped ^3", VoteNetname(votecaller), "^2's vote\n");
+	if(stopper == votecaller) {
+		// no wait for next vote so you can correct your vote
+		if(votecaller) {
+			votecaller.vote_next = 0;
+		}
+	}
+	VoteReset();
+}
+
+void VoteNag() {
+	if(votecalled)
+	if(self.vote_vote == 0)
+		centerprint_atprio(self, CENTERPRIO_VOTE, strcat("^7^3", VoteNetname(votecaller), "^2 called a vote for ", votecalledvote_display, "\n\n^2You have not voted yet!\n^2HINT: By default, F1 is yes and F2 is no."));
+}
+
+void VoteSpam(float yescount, float nocount, float abstaincount, float notvoters, float mincount)
+{
+	string s;
+	if(mincount >= 0)
+	{
+		s = strcat("\{1}^2* vote results: ^1", ftos(yescount), "^2:^1");
+		s = strcat(s, ftos(nocount), "^2 (^1");
+		s = strcat(s, ftos(mincount), "^2 needed), ^1");
+		s = strcat(s, ftos(abstaincount), "^2 didn't care, ^1");
+		s = strcat(s, ftos(notvoters), "^2 didn't vote\n");
+	}
+	else
+	{
+		s = strcat("\{1}^2* vote results: ^1", ftos(yescount), "^2:^1");
+		s = strcat(s, ftos(nocount), "^2 (^1");
+		s = strcat(s, ftos(abstaincount), "^2 didn't care, ^1");
+		s = strcat(s, ftos(notvoters), "^2 didn't have to vote\n");
+	}
+	bprint(s);
+}
+
+void VoteCount() {
+	local float playercount;
+	playercount = 0;
+	local float yescount;
+	yescount = 0;
+	local float nocount;
+	nocount = 0;
+	local float abstaincount;
+	abstaincount = 0;
+	local entity player;
+	//same for real players
+	local float realplayercount;
+	local float realplayeryescount;
+	local float realplayernocount;
+	local float realplayerabstaincount;
+	realplayercount = realplayernocount = realplayerabstaincount = realplayeryescount = 0;
+
+	FOR_EACH_REALCLIENT(player)
+	{
+		if(player.vote_vote == -1) {
+			nocount++;
+		} else if(player.vote_vote == 1) {
+			yescount++;
+		} else if(player.vote_vote == -2) {
+			abstaincount++;
+		}
+		playercount++;
+		//do the same for real players
+		if(player.classname == "player") {
+			if(player.vote_vote == -1) {
+				realplayernocount++;
+			} else if(player.vote_vote == 1) {
+				realplayeryescount++;
+			} else if(player.vote_vote == -2) {
+				realplayerabstaincount++;
+			}
+			realplayercount++;
+		}
+	}
+
+	//in tournament mode, if we have at least one player then don't make the vote dependent on spectators (so specs don't have to press F1)
+	if(cvar("g_tourney"))
+	if(realplayercount > 0) {
+		yescount = realplayeryescount;
+		nocount = realplayernocount;
+		abstaincount = realplayerabstaincount;
+		playercount = realplayercount;
+	}
+
+
+	if(votecalledmaster
+	   && playercount == 1) {
+		// if only one player is on the server becoming vote
+		// master is not allowed.  This could be used for
+		// trolling or worse. 'self' is the user who has
+		// called the vote because this function is called
+		// by SV_ParseClientCommand. Maybe all voting should
+		// be disabled for a single player?
+		print_to(votecaller, "^1You are the only player on this server so you can not become vote master.");
+		if(votecaller) {
+			votecaller.vote_next = 0;
+		}
+		VoteReset();
+	} else {
+		float votefactor;
+		votefactor = bound(0.5, cvar("sv_vote_majority_factor"), 0.999);
+		if(yescount > (playercount - abstaincount) * votefactor)
+		{
+			VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, -1);
+			VoteAccept();
+		}
+		else if(nocount >= (playercount - abstaincount) * (1 - votefactor)) // that means, yescount cannot reach minyes any more
+		{
+			VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, -1);
+			VoteReject();
+		}
+		else if(time > votefinished)
+		{
+			if(cvar("sv_vote_simple_majority"))
+			{
+				VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, floor((yescount + nocount) * votefactor) + 1);
+				if(yescount > (yescount + nocount) * votefactor)
+					VoteAccept();
+				else if(yescount + nocount > 0)
+					VoteReject();
+				else
+					VoteTimeout();
+			}
+			else
+			{
+				VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, floor((playercount - abstaincount) * votefactor) + 1);
+				VoteTimeout();
+			}
+		}
+	}
+}

Added: trunk/data/qcsrc/server/vote.qh
===================================================================
--- trunk/data/qcsrc/server/vote.qh	                        (rev 0)
+++ trunk/data/qcsrc/server/vote.qh	2008-06-01 16:57:58 UTC (rev 3668)
@@ -0,0 +1,20 @@
+string GetKickVoteVictim_newcommand;
+string GetKickVoteVictim_reason;
+
+float VoteCheckNasty(string cmd);
+entity GetKickVoteVictim(string vote, string cmd, entity caller);
+float GameCommand_Vote(string s, entity e);
+void VoteHelp(entity e);
+string VoteNetname(entity e);
+string ValidateMap(string m, entity e);
+void VoteThink();
+string VoteParse();
+float VoteAllowed(string votecommand);
+void VoteReset();
+void VoteAccept();
+void VoteReject();
+void VoteTimeout();
+void VoteStop(entity stopper);
+void VoteNag();
+void VoteSpam(float yescount, float nocount, float abstaincount, float notvoters, float mincount);
+void VoteCount();




More information about the nexuiz-commits mailing list