diff --git a/docs/commandline.html b/docs/commandline.html
index 9240cdae..2494c845 100644
--- a/docs/commandline.html
+++ b/docs/commandline.html
@@ -553,11 +553,13 @@
vote for coopedit mode |
| p |
vote for a gamemode that is not supported by the map (default: no one can) |
+ | w |
+ vote to kick/ban for "weak" reasons, i.e, the other player is not obviously lagging, nor teamkilling, nor spamming, nor cheating (as far as the server can detect it) |
For example: -PbmA enables ban & mastermode to be voted by normal players but
only admins can change autoteam. The '-P' parameter can be used several times on the commandline.
For example, '-Pb -Pm -PA' is equivalent to '-PbmA'.
- The default settings are: "fkBMasRCDE".
+ The default settings are: "fKBMasRCDEW".
diff --git a/source/src/protocol.cpp b/source/src/protocol.cpp
index e9b38511..388b6dc3 100644
--- a/source/src/protocol.cpp
+++ b/source/src/protocol.cpp
@@ -307,7 +307,7 @@ const char *modeacronymnames[] =
"OSOK", "TOSOK", "BOSOK", "HTF", "TKTF", "KTF"
};
-const char *voteerrors[] = { "voting is currently disabled", "there is already a vote pending", "already voted", "can't vote that often", "this vote is not allowed in the current environment (singleplayer/multiplayer)", "no permission", "invalid vote" };
+const char *voteerrors[] = { "voting is currently disabled", "there is already a vote pending", "already voted", "can't vote that often", "this vote is not allowed in the current environment (singleplayer/multiplayer)", "no permission", "invalid vote", "server denied your call" };
const char *mmfullnames[] = { "open", "private", "match" };
const char *fullmodestr(int n) { return (n>=-1 && size_t(n+1) < sizeof(modefullnames)/sizeof(modefullnames[0])) ? modefullnames[n+1] : "unknown"; }
diff --git a/source/src/protocol.h b/source/src/protocol.h
index f9cc685d..32665ca4 100644
--- a/source/src/protocol.h
+++ b/source/src/protocol.h
@@ -53,7 +53,7 @@ extern const char *messagenames[SV_NUM];
enum { SA_KICK = 0, SA_BAN, SA_REMBANS, SA_MASTERMODE, SA_AUTOTEAM, SA_FORCETEAM, SA_GIVEADMIN, SA_MAP, SA_RECORDDEMO, SA_STOPDEMO, SA_CLEARDEMOS, SA_SERVERDESC, SA_SHUFFLETEAMS, SA_NUM};
enum { VOTE_NEUTRAL = 0, VOTE_YES, VOTE_NO, VOTE_NUM };
-enum { VOTEE_DISABLED = 0, VOTEE_CUR, VOTEE_MUL, VOTEE_MAX, VOTEE_AREA, VOTEE_PERMISSION, VOTEE_INVALID, VOTEE_NUM };
+enum { VOTEE_DISABLED = 0, VOTEE_CUR, VOTEE_MUL, VOTEE_MAX, VOTEE_AREA, VOTEE_PERMISSION, VOTEE_INVALID, VOTEE_WEAK, VOTEE_NUM };
enum { MM_OPEN = 0, MM_PRIVATE, MM_MATCH, MM_NUM }; enum { MM_MASK = 0x03 };
enum { AT_DISABLED = 0, AT_ENABLED = 1, AT_SHUFFLE = 2, AT_NUM };
enum { FA_PICKUP = 0, FA_STEAL, FA_DROP, FA_LOST, FA_RETURN, FA_SCORE, FA_NUM, FA_RESET };
diff --git a/source/src/server.cpp b/source/src/server.cpp
index 293a9cb8..e3a43dbd 100644
--- a/source/src/server.cpp
+++ b/source/src/server.cpp
@@ -1846,10 +1846,11 @@ void sendserveropinfo(int receiver)
struct voteinfo
{
+ bool boot;
int owner, callmillis, result;
serveraction *action;
- voteinfo() : owner(0), callmillis(0), result(VOTE_NEUTRAL), action(NULL) {}
+ voteinfo() : boot(false), owner(0), callmillis(0), result(VOTE_NEUTRAL), action(NULL) {}
~voteinfo() { delete action; }
void end(int result)
@@ -1866,7 +1867,7 @@ struct voteinfo
}
bool isvalid() { return valid_client(owner) && action != NULL && action->isvalid(); }
- bool isalive() { return servmillis - callmillis < 40*1000; }
+ bool isalive() { return servmillis - callmillis < 30*1000; }
void evaluate(bool forceend = false)
{
@@ -1921,7 +1922,7 @@ void scallvotesuc(voteinfo *v)
DELETEP(curvote);
curvote = v;
clients[v->owner]->lastvotecall = servmillis;
-
+ clients[v->owner]->nvotes--; // successful votes do not count as abuse
sendf(v->owner, 1, "ri", SV_CALLVOTESUC);
logline(ACLOG_INFO, "[%s] client %s called a vote: %s", clients[v->owner]->hostname, clients[v->owner]->name, v->action->desc ? v->action->desc : "[unknown]");
}
@@ -1937,14 +1938,25 @@ bool scallvote(voteinfo *v, ENetPacket *msg) // true if a regular vote was calle
{
int area = isdedicated ? EE_DED_SERV : EE_LOCAL_SERV;
int error = -1;
+ client *c = clients[v->owner], *b = v->boot ? clients[cn2boot] : NULL;
- if(!v || !v->isvalid()) error = VOTEE_INVALID;
- else if(v->action->role > clients[v->owner]->role) error = VOTEE_PERMISSION;
- else if(!(area & v->action->area)) error = VOTEE_AREA;
- else if(curvote && curvote->result==VOTE_NEUTRAL) error = VOTEE_CUR;
- else if(clients[v->owner]->role == CR_DEFAULT && v->action->isdisabled()) error = VOTEE_DISABLED;
- else if(clients[v->owner]->lastvotecall && servmillis - clients[v->owner]->lastvotecall < 60*1000 && clients[v->owner]->role != CR_ADMIN && numclients()>1)
- error = VOTEE_MAX;
+ int time = servmillis - c->lastvotecall;
+ if ( c->nvotes > 0 && time > 4*60*1000 ) c->nvotes -= time/(4*60*1000);
+ if ( c->nvotes < 0 ) c->nvotes = 0;
+ c->nvotes++;
+
+ if( !v || !v->isvalid() || (v->boot && !b) ) error = VOTEE_INVALID;
+ else if( v->action->role > c->role ) error = VOTEE_PERMISSION;
+ else if( !(area & v->action->area) ) error = VOTEE_AREA;
+ else if( curvote && curvote->result==VOTE_NEUTRAL ) error = VOTEE_CUR;
+ else if( c->role == CR_DEFAULT && v->action->isdisabled() ) error = VOTEE_DISABLED;
+ else if( (c->lastvotecall && servmillis - c->lastvotecall < 60*1000 && c->role != CR_ADMIN && numclients()>1) || c->nvotes > 3 ) error = VOTEE_MAX;
+ else if( c->role < roleconf('W') && v->boot == true && !is_lagging(b) && !b->mute && b->spamcount < 2 ) {
+ /** not same team, with low ratio, not lagging, and not spamming... so, why to kick? */
+ if ( !isteam(c->team, b->team) && b->state.frags < ( b->state.deaths ? b->state.deaths : 1 ) * 5 ) error = VOTEE_WEAK;
+ /** same team, with low tk, not lagging, and not spamming... so, why to kick? */
+ else if ( isteam(c->team, b->team) && b->state.teamkills <= c->state.teamkills ) error = VOTEE_WEAK;
+ }
if(error>=0)
{
@@ -2756,7 +2768,7 @@ void process(ENetPacket *packet, int sender, int chan)
while(curmsgposition.add(p.buf[curmsg++]);
}
if(maplayout && !m_demo && !m_coop) checkclientpos(cl);
- checkmove(cn, val);
+ checkmove(cl, val);
break;
}
@@ -2801,7 +2813,7 @@ void process(ENetPacket *packet, int sender, int chan)
while(curmsgposition.add(p.buf[curmsg++]);
}
if(maplayout && !m_demo && !m_coop) checkclientpos(cl);
- checkmove(cn, val);
+ checkmove(cl, val);
break;
}
@@ -2931,6 +2943,7 @@ void process(ENetPacket *packet, int sender, int chan)
case SV_CALLVOTE:
{
voteinfo *vi = new voteinfo;
+ vi->boot = false;
int type = getint(p);
switch(type)
{
@@ -2949,6 +2962,7 @@ void process(ENetPacket *packet, int sender, int chan)
getstring(text, p);
filtertext(text, text);
vi->action = new kickaction(cn2boot, newstring(text));
+ vi->boot = true;
break;
}
case SA_BAN:
@@ -2957,6 +2971,7 @@ void process(ENetPacket *packet, int sender, int chan)
getstring(text, p);
filtertext(text, text);
vi->action = new banaction(cn2boot, newstring(text));
+ vi->boot = true;
break;
}
case SA_REMBANS:
diff --git a/source/src/server.h b/source/src/server.h
index e978cbf7..52f87eea 100644
--- a/source/src/server.h
+++ b/source/src/server.h
@@ -226,7 +226,7 @@ struct client // server side version of "dynent" type
int skin[2];
int vote;
int role;
- int connectmillis;
+ int connectmillis, lmillis, ldt, spj;
int mute, spam, lastvc; // server side voice comm spam control
int acversion, acbuildtype;
bool isauthed; // for passworded servers
@@ -253,6 +253,7 @@ struct client // server side version of "dynent" type
medals md;
bool upspawnp;
vec spawnp;
+ int nvotes;
gameevent &addevent()
{
@@ -281,6 +282,7 @@ struct client // server side version of "dynent" type
md.reset();
upspawnp = false;
spawnp = vec(-1e10f, -1e10f, -1e10f);
+ lmillis = ldt = spj = 0;
}
void reset()
@@ -301,7 +303,7 @@ struct client // server side version of "dynent" type
spectcn = FPCN_VOID;
mapchange();
freshgame = false; // don't spawn into running games
- mute = spam = lastvc = badspeech = badmillis = 0;
+ mute = spam = lastvc = badspeech = badmillis = nvotes = 0;
}
void zap()
diff --git a/source/src/serveractions.h b/source/src/serveractions.h
index 07a37cd6..4343c2d6 100644
--- a/source/src/serveractions.h
+++ b/source/src/serveractions.h
@@ -3,7 +3,7 @@
enum { EE_LOCAL_SERV = 1, EE_DED_SERV = 1<<1 }; // execution environment
int roleconf(int key)
-{ // current defaults: "fGkBMasRCDE"
+{ // current defaults: "fGkBMasRCDEW"
if(strchr(scl.voteperm, tolower(key))) return CR_DEFAULT;
if(strchr(scl.voteperm, toupper(key))) return CR_ADMIN;
return (key) == tolower(key) ? CR_DEFAULT : CR_ADMIN;
diff --git a/source/src/serverchecks.h b/source/src/serverchecks.h
index cb00f28f..a20ad2af 100644
--- a/source/src/serverchecks.h
+++ b/source/src/serverchecks.h
@@ -452,6 +452,10 @@ void checkfrag (client *target, client *actor, int gun, bool gib)
}
}
+inline bool is_lagging(client *cl)
+{
+ return ( cl->spj > 80 || gamemillis - cl->lmillis > 400 );
+}
/**
If you read README.txt you must know that AC does not have cheat protection implemented.
@@ -469,9 +473,12 @@ This part is here for compatibility purposes.
If you know nothing about these detections, please, just ignore it.
*/
-inline void checkmove (int cn, int *v)
+inline void checkmove (client *cl, int *v)
{
- client *cl = clients[cn];
+ cl->ldt = gamemillis - cl->lmillis;
+ cl->lmillis = gamemillis;
+ if ( cl->ldt < 30 ) cl->ldt = 30;
+ cl->spj = (( 7 * cl->spj + cl->ldt ) >> 3);
if ( !cl->upspawnp ) {
cl->spawnp = cl->state.o;
cl->upspawnp = true;