diff --git a/source/src/protocol.h b/source/src/protocol.h index ce7053c2..83276e85 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -42,6 +42,16 @@ enum { MM_OPEN, MM_PRIVATE, MM_NUM }; enum { DISC_NONE = 0, DISC_EOP, DISC_CN, DISC_MKICK, DISC_MBAN, DISC_TAGT, DISC_BANREFUSE, DISC_WRONGPW, DISC_SOPLOGINFAIL, DISC_MAXCLIENTS, DISC_MASTERMODE, DISC_AUTOKICK, DISC_NUM }; +#define EXT_ACK -1 +#define EXT_VERSION 102 +#define EXT_ERROR_NONE 0 +#define EXT_ERROR 1 +#define EXT_PLAYERSTATS_RESP_IDS -10 +#define EXT_UPTIME 0 +#define EXT_PLAYERSTATS 1 +#define EXT_TEAMSCORE 2 +#define EXT_PLAYERSTATS_RESP_STATS -11 + /* Gamemodes 0 tdm 1 coop edit diff --git a/source/src/protos.h b/source/src/protos.h index ffe526aa..38759d96 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -441,6 +441,10 @@ extern char msgsizelookup(int msg); extern void serverms(int mode, int numplayers, int minremain, char *smapname, int millis, int serverport); extern void servermsinit(const char *master, char *ip, int serverport, char *sdesc, bool listen); extern bool serverpickup(int i, int sender); +extern bool valid_client(int cn); +extern void extinfo_cnbuf(ucharbuf &p, int cn); +extern void extinfo_statsbuf(ucharbuf &p, int pid, int bpos, ENetSocket &pongsock, ENetAddress &addr, ENetBuffer &buf, int len); +extern void extinfo_teamscorebuf(ucharbuf &p); // demo struct demoheader diff --git a/source/src/server.cpp b/source/src/server.cpp index 03c8f084..81cc56cb 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -14,7 +14,6 @@ servercontroller *svcctrl = NULL; -#define valid_client(c) (clients.inrange(c) && clients[c]->type!=ST_EMPTY) #define valid_flag(f) (f >= 0 && f < 2) static const int DEATHMILLIS = 250; @@ -124,7 +123,7 @@ struct clientstate : playerstate int lastshot; projectilestate<8> grenades; int akimbos, akimbomillis; - int flagscore, frags; + int flagscore, frags, cleanfrags, teamkills, deaths; clientstate() : state(CS_DEAD) {} @@ -148,7 +147,7 @@ struct clientstate : playerstate akimbos = 0; akimbomillis = 0; flagscore = frags = 0; - + teamkills = deaths = cleanfrags = 0; respawn(); } @@ -234,6 +233,11 @@ struct client // server side version of "dynent" type vector clients; +bool valid_client(int cn) +{ + return clients.inrange(cn) && clients[cn]->type != ST_EMPTY; +} + struct worldstate { enet_uint32 uses; @@ -1025,6 +1029,13 @@ void serverdamage(client *target, client *actor, int damage, int gun, bool gib, } if(ts.health<=0) { + target->state.deaths++; + if(actor->clientnum != target->clientnum) + { + if(isteam(actor->team, target->team)) actor->state.teamkills++; + else actor->state.cleanfrags++; // frags (excl. teamkills) + } + if(target!=actor && !isteam(target->team, actor->team)) actor->state.frags += gib ? 2 : 1; else actor->state.frags--; sendf(-1, 1, "ri4", gib ? SV_GIBDIED : SV_DIED, target->clientnum, actor->clientnum, actor->state.frags); @@ -1119,6 +1130,7 @@ void processevent(client *c, suicideevent &e) clientstate &gs = c->state; if(gs.state!=CS_ALIVE) return; gs.frags--; + gs.deaths++; sendf(-1, 1, "ri4", SV_DIED, c->clientnum, c->clientnum, gs.frags); c->position.setsizenodelete(0); gs.state = CS_DEAD; @@ -2190,6 +2202,103 @@ void cleanupserver() if(svcctrl) svcctrl->stop(); } +void extinfo_cnbuf(ucharbuf &p, int cn) +{ + if(cn == -1) // add all available player ids + { + loopv(clients) if(clients[i]->type != ST_EMPTY) + putint(p,clients[i]->clientnum); + } + else if(valid_client(cn)) // add single player only + { + putint(p,clients[cn]->clientnum); + } +} + +void extinfo_statsbuf(ucharbuf &p, int pid, int bpos, ENetSocket &pongsock, ENetAddress &addr, ENetBuffer &buf, int len) +{ + loopv(clients) + { + if(clients[i]->type == ST_EMPTY) continue; + if(pid>-1 && clients[i]->clientnum!=pid) continue; + + putint(p,EXT_PLAYERSTATS_RESP_STATS); // send player stats following + putint(p,clients[i]->clientnum); //add player id + sendstring(clients[i]->name,p); //Name + sendstring(clients[i]->team,p); //Team + putint(p,clients[i]->state.cleanfrags); //Frags + putint(p,clients[i]->state.deaths); //Death + putint(p,clients[i]->state.teamkills); //Teamkills + putint(p,clients[i]->state.health); //Health + putint(p,clients[i]->state.armour); //Armour + putint(p,clients[i]->state.gunselect); //Gun selected + putint(p,clients[i]->role); //Role + putint(p,clients[i]->state.state); //State (Alive,Dead,Spawning,Lagged,Editing) + uint ip = ntohl(clients[i]->peer->address.host) >> 8; // only 3 byte of the ip address (privacy protected) + p.put((uchar*)&ip,3); + + buf.dataLength = len + p.length(); + enet_socket_send(pongsock, &addr, &buf, 1); + + if(pid>-1) break; + p.len=bpos; + } +} + +void extinfo_teamscorebuf(ucharbuf &p) +{ + if(!m_teammode) + { + putint(p,EXT_ERROR); // send error + putint(p,minremain); //remaining play time + return; + } + + putint(p,EXT_ERROR_NONE); // send no error + + cvector teams; + bool flag; + loopv(clients) + { + flag = true; + loopvj(teams) + { + if(strcmp(clients[i]->team,teams[j])==0 || !clients[i]->team[0]) + { + flag = false; + break; + } + } + if(flag) teams.add(clients[i]->team); + } + + putint(p,minremain); //remaining play time + + loopv(teams) + { + sendstring(teams[i],p); //team + int fragscore = 0; + int flagscore = 0; + loopvj(clients) + { + if(!(strcmp(clients[j]->team,teams[i])==0)) continue; + fragscore += clients[j]->state.frags; + flagscore += clients[j]->state.flagscore; + } + putint(p,fragscore); //add fragscore per team + if(m_ctf) //when capture mode + { + putint(p,flagscore); //add flagscore per team + } + else //all other team modes + { + putint(p,-1); //flagscore not available + } + putint(p,-1); + } +} + + #ifndef STANDALONE void localdisconnect() { diff --git a/source/src/serverms.cpp b/source/src/serverms.cpp index 978b8e05..a2b8d0ad 100644 --- a/source/src/serverms.cpp +++ b/source/src/serverms.cpp @@ -158,24 +158,76 @@ void serverms(int mode, int numplayers, int minremain, char *smapname, int milli // reply all server info requests ENetBuffer buf; ENetAddress addr; - uchar pong[MAXTRANS]; + uchar data[MAXTRANS]; + buf.data = data; int len; enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE; - buf.data = pong; while(enet_socket_wait(pongsock, &events, 0) >= 0 && events) { - buf.dataLength = sizeof(pong); + buf.dataLength = sizeof(data); len = enet_socket_receive(pongsock, &addr, &buf, 1); if(len < 0) return; - ucharbuf p(&pong[len], sizeof(pong)-len); - putint(p, PROTOCOL_VERSION); - putint(p, mode); - putint(p, numplayers); - putint(p, minremain); - sendstring(smapname, p); - sendstring(serverdesc, p); - putint(p, maxclients); - buf.dataLength = len + p.length(); + + // ping & pong buf + ucharbuf pi(data, sizeof(data)); + ucharbuf po(&data[len], sizeof(data)-len); + + if(getint(pi) != 0) // std pong + { + putint(po, PROTOCOL_VERSION); + putint(po, mode); + putint(po, numplayers); + putint(po, minremain); + sendstring(smapname, po); + sendstring(serverdesc, po); + putint(po, maxclients); + } + else // ext pong - additional server infos + { + int extcmd = getint(pi); + putint(po, EXT_ACK); + putint(po, EXT_VERSION); + + switch(extcmd) + { + case EXT_UPTIME: // uptime in seconds + { + putint(po, millis/1000); + break; + } + + case EXT_PLAYERSTATS: // playerstats + { + int cn = getint(pi); // get requested player, -1 for all + if(!valid_client(cn) && cn != -1) + { + putint(po, EXT_ERROR); + break; + } + putint(po, EXT_ERROR_NONE); // add no error flag + + int bpos = po.length(); // remember buffer position + putint(po, EXT_PLAYERSTATS_RESP_IDS); // send player ids following + extinfo_cnbuf(po, cn); + buf.dataLength = len + po.length(); + enet_socket_send(pongsock, &addr, &buf, 1); // send all available player ids + po.len = bpos; + + extinfo_statsbuf(po, cn, bpos, pongsock, addr, buf, len); + return; + } + + case EXT_TEAMSCORE: + extinfo_teamscorebuf(po); + break; + + default: + putint(po,EXT_ERROR); + break; + } + } + + buf.dataLength = len + po.length(); enet_socket_send(pongsock, &addr, &buf, 1); } }