From 9ea5997f535da18a94a5c46bc1e88708f50b95e9 Mon Sep 17 00:00:00 2001 From: driAn Date: Sun, 12 Oct 2008 11:41:36 +0000 Subject: [PATCH] even more debugging tools git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@2979 e016b1c5-ed68-44aa-a2f7-31cac88cffde --- source/src/protocol.cpp | 17 +- source/src/protocol.h | 3 + source/src/server.cpp | 850 ++++++++++++++++++++-------------------- 3 files changed, 448 insertions(+), 422 deletions(-) diff --git a/source/src/protocol.cpp b/source/src/protocol.cpp index 1d1e6325..8ee46ec1 100644 --- a/source/src/protocol.cpp +++ b/source/src/protocol.cpp @@ -3,6 +3,12 @@ #include "pch.h" #include "cube.h" +#ifdef _DEBUG +bool protocoldbg = false; +#define DEBUGCOND (protocoldbg) +void protocoldebug(bool enable) { protocoldbg = enable; } +#endif + // all network traffic is in 32bit ints, which are then compressed using the following simple scheme (assumes that most values are small). void putint(ucharbuf &p, int n) @@ -15,9 +21,13 @@ void putint(ucharbuf &p, int n) int getint(ucharbuf &p) { int c = (char)p.get(); - if(c==-128) { int n = p.get(); n |= char(p.get())<<8; return n; } - else if(c==-127) { int n = p.get(); n |= p.get()<<8; n |= p.get()<<16; return n|(p.get()<<24); } - else return c; + if(c==-128) { int n = p.get(); n |= char(p.get())<<8; DEBUGVAR(n); return n; } + else if(c==-127) { int n = p.get(); n |= p.get()<<8; n |= p.get()<<16; n |= (p.get()<<24); DEBUGVAR(n); return n; } + else + { + DEBUGVAR(c); + return c; + } } // much smaller encoding for unsigned integers up to 28 bits, but can handle signed @@ -54,6 +64,7 @@ int getuint(ucharbuf &p) if(n & (1<<21)) n += (p.get() << 21) - (1<<21); if(n & (1<<28)) n |= 0xF0000000; } + DEBUGVAR(n); return n; } diff --git a/source/src/protocol.h b/source/src/protocol.h index ec852fdf..2324c4a4 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -38,6 +38,9 @@ enum }; #ifdef _DEBUG + +extern void protocoldebug(bool enable); + // converts message code to char const static char *messagenames[] = { diff --git a/source/src/server.cpp b/source/src/server.cpp index eee39ea4..9aac0631 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -2317,14 +2317,6 @@ void welcomepacket(ucharbuf &p, int n, ENetPacket *packet) int checktype(int type, client *cl) { - if(type!=SV_POS && type!=SV_CLIENTPING && type!=SV_PING) - { - DEBUGVAR(cl->name); - ASSERT(type>=0 && typetype==ST_LOCAL) return type; // only allow edit messages in coop-edit mode static int edittypes[] = { SV_EDITENT, SV_EDITH, SV_EDITT, SV_EDITS, SV_EDITD, SV_EDITE }; @@ -2446,447 +2438,467 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1 enet_packet_resize(packet, buf.length()); int curmsg; - while((curmsg = p.length()) < p.maxlen) switch(type = checktype(getint(p), cl)) + while((curmsg = p.length()) < p.maxlen) { - case SV_TEAMTEXT: - getstring(text, p); - filtertext(text, text); - if(!spamdetect(cl, text)) - { - logger->writeline(log::info, "[%s] %s says to team %s: '%s'", cl->hostname, cl->name, cl->team, text); - sendteamtext(text, sender); - } - else - { - logger->writeline(log::info, "[%s] %s says to team %s: '%s', SPAM detected", cl->hostname, cl->name, cl->team, text); - sendservmsg("\f3please do not spam", sender); - } - break; - - case SV_TEXT: + type = checktype(getint(p), cl); + + #ifdef _DEBUG + if(type!=SV_POS && type!=SV_CLIENTPING && type!=SV_PING) { - int mid1 = curmsg, mid2 = p.length(); - getstring(text, p); - filtertext(text, text); - if(!spamdetect(cl, text)) - { - logger->writeline(log::info, "[%s] %s says: '%s'", cl->hostname, cl->name, text); - if(cl->type==ST_TCPIP) while(mid1messages.add(p.buf[mid1++]); - QUEUE_STR(text); - } - else - { - logger->writeline(log::info, "[%s] %s says: '%s', SPAM detected", cl->hostname, cl->name, text); - sendservmsg("\f3please do not spam", sender); - } - break; + DEBUGVAR(cl->name); + ASSERT(type>=0 && typename, text)) logger->writeline(log::info,"[%s] %s changed his name to %s", cl->hostname, cl->name, text); - s_strncpy(cl->name, text, MAXNAMELEN+1); - getstring(text, p); - filtertext(cl->team, text, 0, MAXTEAMLEN); - QUEUE_STR(text); - getint(p); - QUEUE_MSG; - break; - } - - case SV_ITEMLIST: - { - int n; - while((n = getint(p))!=-1) - { - server_entity se = { getint(p), false, 0 }; - if(notgotitems) + case SV_TEAMTEXT: + getstring(text, p); + filtertext(text, text); + if(!spamdetect(cl, text)) { - while(sents.length()<=n) sents.add(se); - sents[n].spawned = true; + logger->writeline(log::info, "[%s] %s says to team %s: '%s'", cl->hostname, cl->name, cl->team, text); + sendteamtext(text, sender); } - } - notgotitems = false; - break; - } + else + { + logger->writeline(log::info, "[%s] %s says to team %s: '%s', SPAM detected", cl->hostname, cl->name, cl->team, text); + sendservmsg("\f3please do not spam", sender); + } + break; - case SV_SPAWNLIST: - { - if(getint(p) > 0) + case SV_TEXT: { - loopi(3) clnumspawn[i] = getint(p); - loopi(2) clnumflagspawn[i] = getint(p); - } - QUEUE_MSG; - break; - } - - case SV_ITEMPICKUP: - { - int n = getint(p); - gameevent &pickup = cl->addevent(); - pickup.type = GE_PICKUP; - pickup.pickup.ent = n; - break; - } - - case SV_WEAPCHANGE: - { - int gunselect = getint(p); - if(gunselect<0 && gunselect>=NUMGUNS) break; - cl->state.gunselect = gunselect; - QUEUE_MSG; - break; - } - - case SV_PRIMARYWEAP: - { - int nextprimary = getint(p); - if(nextprimary<0 && nextprimary>=NUMGUNS) break; - cl->state.nextprimary = nextprimary; - break; - } - - case SV_CHANGETEAM: - cl->state.state = CS_DEAD; - cl->state.respawn(); - sendf(-1, 1, "rii", SV_FORCEDEATH, cl->clientnum); - break; - - case SV_TRYSPAWN: - if(cl->state.state!=CS_DEAD || cl->state.lastspawn>=0 || !canspawn(cl)) break; - if(cl->state.lastdeath) cl->state.respawn(); - sendspawn(cl); - break; - - case SV_SPAWN: - { - int ls = getint(p), gunselect = getint(p); - if((cl->state.state!=CS_ALIVE && cl->state.state!=CS_DEAD) || ls!=cl->state.lifesequence || cl->state.lastspawn<0 || gunselect<0 || gunselect>=NUMGUNS) break; - cl->state.lastspawn = -1; - cl->state.state = CS_ALIVE; - cl->state.gunselect = gunselect; - QUEUE_BUF(5*(5 + 2*NUMGUNS), - { - putint(buf, SV_SPAWN); - putint(buf, cl->state.lifesequence); - putint(buf, cl->state.health); - putint(buf, cl->state.armour); - putint(buf, cl->state.gunselect); - loopi(NUMGUNS) putint(buf, cl->state.ammo[i]); - loopi(NUMGUNS) putint(buf, cl->state.mag[i]); - }); - break; - } - - case SV_SUICIDE: - { - gameevent &suicide = cl->addevent(); - suicide.type = GE_SUICIDE; - break; - } - - case SV_SHOOT: - { - gameevent &shot = cl->addevent(); - shot.type = GE_SHOT; - #define seteventmillis(event) \ - { \ - event.id = getint(p); \ - if(!cl->timesync || (cl->events.length()==1 && cl->state.waitexpired(gamemillis))) \ - { \ - cl->timesync = true; \ - cl->gameoffset = gamemillis - event.id; \ - event.millis = gamemillis; \ - } \ - else event.millis = cl->gameoffset + event.id; \ - } - seteventmillis(shot.shot); - shot.shot.gun = getint(p); - loopk(3) shot.shot.from[k] = getint(p)/DMF; - loopk(3) shot.shot.to[k] = getint(p)/DMF; - int hits = getint(p); - loopk(hits) - { - gameevent &hit = cl->addevent(); - hit.type = GE_HIT; - hit.hit.target = getint(p); - hit.hit.lifesequence = getint(p); - hit.hit.info = getint(p); - loopk(3) hit.hit.dir[k] = getint(p)/DNF; - } - break; - } - - case SV_EXPLODE: - { - gameevent &exp = cl->addevent(); - exp.type = GE_EXPLODE; - seteventmillis(exp.explode); - exp.explode.gun = getint(p); - exp.explode.id = getint(p); - int hits = getint(p); - loopk(hits) - { - gameevent &hit = cl->addevent(); - hit.type = GE_HIT; - hit.hit.target = getint(p); - hit.hit.lifesequence = getint(p); - hit.hit.dist = getint(p)/DMF; - loopk(3) hit.hit.dir[k] = getint(p)/DNF; - } - break; - } - - case SV_AKIMBO: - { - gameevent &akimbo = cl->addevent(); - akimbo.type = GE_AKIMBO; - seteventmillis(akimbo.akimbo); - break; - } - - case SV_RELOAD: - { - gameevent &reload = cl->addevent(); - reload.type = GE_RELOAD; - seteventmillis(reload.reload); - reload.reload.gun = getint(p); - break; - } - - case SV_PING: - sendf(sender, 1, "ii", SV_PONG, getint(p)); - break; - - case SV_POS: - { - int cn = getint(p); - if(cn!=sender) - { - disconnect_client(sender, DISC_CN); -#ifndef STANDALONE - conoutf("ERROR: invalid client (msg %i)", type); -#endif - return; - } - loopi(3) clients[cn]->state.o[i] = getuint(p)/DMF; - getuint(p); - loopi(5) getint(p); - getuint(p); - if(cl->type==ST_TCPIP && (cl->state.state==CS_ALIVE || cl->state.state==CS_EDITING)) - { - cl->position.setsizenodelete(0); - while(curmsgposition.add(p.buf[curmsg++]); - } - break; - } - - case SV_NEXTMAP: - { - getstring(text, p); - filtertext(text, text); - int mode = getint(p); - if(mapreload || numclients() == 1) resetmap(text, mode); - break; - } - - case SV_SENDMAP: - { - getstring(text, p); - filtertext(text, text); - int mapsize = getint(p); - int cfgsize = getint(p); - int cfgsizegz = getint(p); - if(p.remaining() < mapsize + cfgsizegz) - { - p.forceoverread(); + int mid1 = curmsg, mid2 = p.length(); + getstring(text, p); + filtertext(text, text); + if(!spamdetect(cl, text)) + { + logger->writeline(log::info, "[%s] %s says: '%s'", cl->hostname, cl->name, text); + if(cl->type==ST_TCPIP) while(mid1messages.add(p.buf[mid1++]); + QUEUE_STR(text); + } + else + { + logger->writeline(log::info, "[%s] %s says: '%s', SPAM detected", cl->hostname, cl->name, text); + sendservmsg("\f3please do not spam", sender); + } break; } - if(sendmapserv(sender, text, mapsize, cfgsize, cfgsizegz, &p.buf[p.len])) + + case SV_VOICECOM: + getint(p); + QUEUE_MSG; + break; + + case SV_VOICECOMTEAM: + sendvoicecomteam(getint(p), sender); + break; + + case SV_INITC2S: { - logger->writeline(log::info,"[%s] %s sent map %s, %d + %d(%d) bytes written", - clients[sender]->hostname, clients[sender]->name, text, mapsize, cfgsize, cfgsizegz); + QUEUE_MSG; + getstring(text, p); + filtertext(text, text, 0, MAXNAMELEN); + if(!text[0]) s_strcpy(text, "unarmed"); + QUEUE_STR(text); + if(strcmp(cl->name, text)) logger->writeline(log::info,"[%s] %s changed his name to %s", cl->hostname, cl->name, text); + s_strncpy(cl->name, text, MAXNAMELEN+1); + getstring(text, p); + filtertext(cl->team, text, 0, MAXTEAMLEN); + QUEUE_STR(text); + getint(p); + QUEUE_MSG; + break; } - p.len += mapsize + cfgsizegz; - break; - } - case SV_RECVMAP: - { - ENetPacket *mappacket = getmapserv(cl->clientnum); - if(mappacket) sendpacket(cl->clientnum, 2, mappacket); - else sendservmsg("no map to get", cl->clientnum); - break; - } - - case SV_FLAGACTION: - { - if(!m_flags) { disconnect_client(sender, DISC_TAGT); return; } - int action = getint(p); - int flag = getint(p); - if(flag < 0 || flag > 1 || action < 0 || action > FA_NUM) return; - flagaction(flag, action, sender); - break; - } - - case SV_SETADMIN: - { - bool claim = getint(p) != 0; - getstring(text, p); - changeclientrole(sender, claim ? CR_ADMIN : CR_DEFAULT, text); - break; - } - - case SV_CALLVOTE: - { - voteinfo *vi = new voteinfo; - int type = getint(p); - switch(type) + case SV_ITEMLIST: { - case SA_MAP: + int n; + while((n = getint(p))!=-1) { - getstring(text, p); - filtertext(text, text); - int mode = getint(p); - if(mode==GMODE_DEMO) vi->action = new demoplayaction(text); - else vi->action = new mapaction(newstring(text), mode); + server_entity se = { getint(p), false, 0 }; + if(notgotitems) + { + while(sents.length()<=n) sents.add(se); + sents[n].spawned = true; + } + } + notgotitems = false; + break; + } + + case SV_SPAWNLIST: + { + if(getint(p) > 0) + { + loopi(3) clnumspawn[i] = getint(p); + loopi(2) clnumflagspawn[i] = getint(p); + } + QUEUE_MSG; + break; + } + + case SV_ITEMPICKUP: + { + int n = getint(p); + gameevent &pickup = cl->addevent(); + pickup.type = GE_PICKUP; + pickup.pickup.ent = n; + break; + } + + case SV_WEAPCHANGE: + { + int gunselect = getint(p); + if(gunselect<0 && gunselect>=NUMGUNS) break; + cl->state.gunselect = gunselect; + QUEUE_MSG; + break; + } + + case SV_PRIMARYWEAP: + { + int nextprimary = getint(p); + if(nextprimary<0 && nextprimary>=NUMGUNS) break; + cl->state.nextprimary = nextprimary; + break; + } + + case SV_CHANGETEAM: + cl->state.state = CS_DEAD; + cl->state.respawn(); + sendf(-1, 1, "rii", SV_FORCEDEATH, cl->clientnum); + break; + + case SV_TRYSPAWN: + if(cl->state.state!=CS_DEAD || cl->state.lastspawn>=0 || !canspawn(cl)) break; + if(cl->state.lastdeath) cl->state.respawn(); + sendspawn(cl); + break; + + case SV_SPAWN: + { + int ls = getint(p), gunselect = getint(p); + if((cl->state.state!=CS_ALIVE && cl->state.state!=CS_DEAD) || ls!=cl->state.lifesequence || cl->state.lastspawn<0 || gunselect<0 || gunselect>=NUMGUNS) break; + cl->state.lastspawn = -1; + cl->state.state = CS_ALIVE; + cl->state.gunselect = gunselect; + QUEUE_BUF(5*(5 + 2*NUMGUNS), + { + putint(buf, SV_SPAWN); + putint(buf, cl->state.lifesequence); + putint(buf, cl->state.health); + putint(buf, cl->state.armour); + putint(buf, cl->state.gunselect); + loopi(NUMGUNS) putint(buf, cl->state.ammo[i]); + loopi(NUMGUNS) putint(buf, cl->state.mag[i]); + }); + break; + } + + case SV_SUICIDE: + { + gameevent &suicide = cl->addevent(); + suicide.type = GE_SUICIDE; + break; + } + + case SV_SHOOT: + { + gameevent &shot = cl->addevent(); + shot.type = GE_SHOT; + #define seteventmillis(event) \ + { \ + event.id = getint(p); \ + if(!cl->timesync || (cl->events.length()==1 && cl->state.waitexpired(gamemillis))) \ + { \ + cl->timesync = true; \ + cl->gameoffset = gamemillis - event.id; \ + event.millis = gamemillis; \ + } \ + else event.millis = cl->gameoffset + event.id; \ + } + seteventmillis(shot.shot); + shot.shot.gun = getint(p); + loopk(3) shot.shot.from[k] = getint(p)/DMF; + loopk(3) shot.shot.to[k] = getint(p)/DMF; + int hits = getint(p); + loopk(hits) + { + gameevent &hit = cl->addevent(); + hit.type = GE_HIT; + hit.hit.target = getint(p); + hit.hit.lifesequence = getint(p); + hit.hit.info = getint(p); + loopk(3) hit.hit.dir[k] = getint(p)/DNF; + } + break; + } + + case SV_EXPLODE: + { + gameevent &exp = cl->addevent(); + exp.type = GE_EXPLODE; + seteventmillis(exp.explode); + exp.explode.gun = getint(p); + exp.explode.id = getint(p); + int hits = getint(p); + loopk(hits) + { + gameevent &hit = cl->addevent(); + hit.type = GE_HIT; + hit.hit.target = getint(p); + hit.hit.lifesequence = getint(p); + hit.hit.dist = getint(p)/DMF; + loopk(3) hit.hit.dir[k] = getint(p)/DNF; + } + break; + } + + case SV_AKIMBO: + { + gameevent &akimbo = cl->addevent(); + akimbo.type = GE_AKIMBO; + seteventmillis(akimbo.akimbo); + break; + } + + case SV_RELOAD: + { + gameevent &reload = cl->addevent(); + reload.type = GE_RELOAD; + seteventmillis(reload.reload); + reload.reload.gun = getint(p); + break; + } + + case SV_PING: + sendf(sender, 1, "ii", SV_PONG, getint(p)); + break; + + case SV_POS: + { + int cn = getint(p); + if(cn!=sender) + { + disconnect_client(sender, DISC_CN); + #ifndef STANDALONE + conoutf("ERROR: invalid client (msg %i)", type); + #endif + return; + } + loopi(3) clients[cn]->state.o[i] = getuint(p)/DMF; + getuint(p); + loopi(5) getint(p); + getuint(p); + if(cl->type==ST_TCPIP && (cl->state.state==CS_ALIVE || cl->state.state==CS_EDITING)) + { + cl->position.setsizenodelete(0); + while(curmsgposition.add(p.buf[curmsg++]); + } + break; + } + + case SV_NEXTMAP: + { + getstring(text, p); + filtertext(text, text); + int mode = getint(p); + if(mapreload || numclients() == 1) resetmap(text, mode); + break; + } + + case SV_SENDMAP: + { + getstring(text, p); + filtertext(text, text); + int mapsize = getint(p); + int cfgsize = getint(p); + int cfgsizegz = getint(p); + if(p.remaining() < mapsize + cfgsizegz) + { + p.forceoverread(); break; } - case SA_KICK: - vi->action = new kickaction(getint(p)); - break; - case SA_BAN: - vi->action = new banaction(getint(p)); - break; - case SA_REMBANS: - vi->action = new removebansaction(); - break; - case SA_MASTERMODE: - vi->action = new mastermodeaction(getint(p)); - break; - case SA_AUTOTEAM: - vi->action = new autoteamaction(getint(p) > 0); - break; - case SA_SHUFFLETEAMS: - vi->action = new shuffleteamaction(); - break; - case SA_FORCETEAM: - vi->action = new forceteamaction(getint(p)); - break; - case SA_GIVEADMIN: - vi->action = new giveadminaction(getint(p)); - break; - case SA_RECORDDEMO: - vi->action = new recorddemoaction(getint(p)!=0); - break; - case SA_STOPDEMO: - vi->action = new stopdemoaction(); - break; - case SA_CLEARDEMOS: - vi->action = new cleardemosaction(getint(p)); - break; - case SA_SERVERDESC: - getstring(text, p); - filtertext(text, text); - vi->action = new serverdescaction(newstring(text)); - break; + if(sendmapserv(sender, text, mapsize, cfgsize, cfgsizegz, &p.buf[p.len])) + { + logger->writeline(log::info,"[%s] %s sent map %s, %d + %d(%d) bytes written", + clients[sender]->hostname, clients[sender]->name, text, mapsize, cfgsize, cfgsizegz); + } + p.len += mapsize + cfgsizegz; + break; } - vi->owner = sender; - vi->callmillis = servmillis; - MSG_PACKET(msg); - if(!callvote(vi, msg)) delete vi; - if(!msg->referenceCount) enet_packet_destroy(msg); - break; - } - case SV_VOTE: - { - int n = getint(p); - MSG_PACKET(msg); - vote(sender, n, msg); - if(!msg->referenceCount) enet_packet_destroy(msg); - break; - } - - case SV_WHOIS: - { - sendwhois(sender, getint(p)); - break; - } - - case SV_LISTDEMOS: - listdemos(sender); - break; - - case SV_GETDEMO: - senddemo(sender, getint(p)); - break; - - case SV_EXTENSION: - { - // AC server extensions - // - // rules: - // 1. extensions MUST NOT modify gameplay or the beavior of the game in any way - // 2. extensions may ONLY be used to extend or automate server administration tasks - // 3. extensions may ONLY operate on the server and must not send any additional data to the connected clients - // 4. extensions not adhering to these rules may cause the hosting server being banned from the masterserver - // - // also note that there is no guarantee that custom extensions will work in future AC versions - - - getstring(text, p, 64); - char *ext = text; // extension specifier in the form of OWNER::EXTENSION, see sample below - int n = getint(p); // length of data after the specifier - - // sample - if(!strcmp(ext, "driAn::writelog")) + case SV_RECVMAP: { - // owner: driAn - root@sprintf.org - // extension: writelog - WriteLog v1.0 - // description: writes a custom string to the server log - // access: requires admin privileges - // usage: /serverextension driAn::writelog "your log message here.." - - getstring(text, p, n); - if(valid_client(sender) && clients[sender]->role==CR_ADMIN && logger) - logger->writeline(log::info, text); + ENetPacket *mappacket = getmapserv(cl->clientnum); + if(mappacket) sendpacket(cl->clientnum, 2, mappacket); + else sendservmsg("no map to get", cl->clientnum); + break; } - // else if() - // add other extensions here + case SV_FLAGACTION: + { + if(!m_flags) { disconnect_client(sender, DISC_TAGT); return; } + int action = getint(p); + int flag = getint(p); + if(flag < 0 || flag > 1 || action < 0 || action > FA_NUM) return; + flagaction(flag, action, sender); + break; + } - else while(n--) getint(p); // ignore unknown extensions + case SV_SETADMIN: + { + bool claim = getint(p) != 0; + getstring(text, p); + changeclientrole(sender, claim ? CR_ADMIN : CR_DEFAULT, text); + break; + } - break; - } + case SV_CALLVOTE: + { + voteinfo *vi = new voteinfo; + int type = getint(p); + switch(type) + { + case SA_MAP: + { + getstring(text, p); + filtertext(text, text); + int mode = getint(p); + if(mode==GMODE_DEMO) vi->action = new demoplayaction(text); + else vi->action = new mapaction(newstring(text), mode); + break; + } + case SA_KICK: + vi->action = new kickaction(getint(p)); + break; + case SA_BAN: + vi->action = new banaction(getint(p)); + break; + case SA_REMBANS: + vi->action = new removebansaction(); + break; + case SA_MASTERMODE: + vi->action = new mastermodeaction(getint(p)); + break; + case SA_AUTOTEAM: + vi->action = new autoteamaction(getint(p) > 0); + break; + case SA_SHUFFLETEAMS: + vi->action = new shuffleteamaction(); + break; + case SA_FORCETEAM: + vi->action = new forceteamaction(getint(p)); + break; + case SA_GIVEADMIN: + vi->action = new giveadminaction(getint(p)); + break; + case SA_RECORDDEMO: + vi->action = new recorddemoaction(getint(p)!=0); + break; + case SA_STOPDEMO: + vi->action = new stopdemoaction(); + break; + case SA_CLEARDEMOS: + vi->action = new cleardemosaction(getint(p)); + break; + case SA_SERVERDESC: + getstring(text, p); + filtertext(text, text); + vi->action = new serverdescaction(newstring(text)); + break; + } + vi->owner = sender; + vi->callmillis = servmillis; + MSG_PACKET(msg); + if(!callvote(vi, msg)) delete vi; + if(!msg->referenceCount) enet_packet_destroy(msg); + break; + } - default: - { - int size = msgsizelookup(type); - if(size==-1) { if(sender>=0) disconnect_client(sender, DISC_TAGT); return; } - loopi(size-1) getint(p); - QUEUE_MSG; - break; + case SV_VOTE: + { + int n = getint(p); + MSG_PACKET(msg); + vote(sender, n, msg); + if(!msg->referenceCount) enet_packet_destroy(msg); + break; + } + + case SV_WHOIS: + { + sendwhois(sender, getint(p)); + break; + } + + case SV_LISTDEMOS: + listdemos(sender); + break; + + case SV_GETDEMO: + senddemo(sender, getint(p)); + break; + + case SV_EXTENSION: + { + // AC server extensions + // + // rules: + // 1. extensions MUST NOT modify gameplay or the beavior of the game in any way + // 2. extensions may ONLY be used to extend or automate server administration tasks + // 3. extensions may ONLY operate on the server and must not send any additional data to the connected clients + // 4. extensions not adhering to these rules may cause the hosting server being banned from the masterserver + // + // also note that there is no guarantee that custom extensions will work in future AC versions + + + getstring(text, p, 64); + char *ext = text; // extension specifier in the form of OWNER::EXTENSION, see sample below + int n = getint(p); // length of data after the specifier + + // sample + if(!strcmp(ext, "driAn::writelog")) + { + // owner: driAn - root@sprintf.org + // extension: writelog - WriteLog v1.0 + // description: writes a custom string to the server log + // access: requires admin privileges + // usage: /serverextension driAn::writelog "your log message here.." + + getstring(text, p, n); + if(valid_client(sender) && clients[sender]->role==CR_ADMIN && logger) + logger->writeline(log::info, text); + } + // else if() + + // add other extensions here + + else while(n--) getint(p); // ignore unknown extensions + + break; + } + + default: + { + int size = msgsizelookup(type); + if(size==-1) { if(sender>=0) disconnect_client(sender, DISC_TAGT); return; } + loopi(size-1) getint(p); + QUEUE_MSG; + break; + } } } if(p.overread() && sender>=0) disconnect_client(sender, DISC_EOP); + + #ifdef _DEBUG + protocoldebug(false); + #endif } void localclienttoserver(int chan, ENetPacket *packet)