From 6d2c6393790d180d142717bcd29c7d7205a1bf83 Mon Sep 17 00:00:00 2001 From: driAn Date: Sun, 14 Oct 2007 16:39:15 +0000 Subject: [PATCH] demo record backport git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@1358 e016b1c5-ed68-44aa-a2f7-31cac88cffde --- source/src/client.cpp | 16 +- source/src/clientgame.cpp | 15 +- source/src/clients2c.cpp | 93 +++++++--- source/src/cube.h | 3 +- source/src/protocol.cpp | 1 + source/src/protocol.h | 9 +- source/src/protos.h | 10 ++ source/src/server.cpp | 342 ++++++++++++++++++++++++++++++++++--- source/src/serveractions.h | 58 +++++-- source/src/tools.cpp | 2 - source/src/tools.h | 2 - 11 files changed, 474 insertions(+), 77 deletions(-) diff --git a/source/src/client.cpp b/source/src/client.cpp index def90c6c..31884f75 100644 --- a/source/src/client.cpp +++ b/source/src/client.cpp @@ -465,8 +465,22 @@ void getmap() sendpackettoserv(2, packet); } +void getdemo(int i) +{ + if(i<=0) conoutf("getting demo..."); + else conoutf("getting demo %d...", i); + addmsg(SV_GETDEMO, "ri", i); +} + +void listdemos() +{ + conoutf("listing demos..."); + addmsg(SV_LISTDEMOS, "r"); +} + COMMAND(sendmap, ARG_1STR); COMMAND(getmap, ARG_NONE); COMMAND(clearsecuremaps, ARG_NONE); COMMAND(securemap, ARG_1STR); - +COMMAND(getdemo, ARG_1INT); +COMMAND(listdemos, ARG_NONE); diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index 419ab88f..0749c7bf 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -630,7 +630,7 @@ votedisplayinfo *newvotedisplayinfo(playerent *owner, int type, char *arg1, char v->owner = owner; v->type = type; v->millis = lastmillis + (60+10)*1000; - char *msgs[] = { "kick player %s", "ban player %s", "remove all bans", "set mastermode to %s", "%s autoteam", "force player %s to the enemy team", "give master to player %s", "load map %s in mode %s" }; + char *msgs[] = { "kick player %s", "ban player %s", "remove all bans", "set mastermode to %s", "%s autoteam", "force player %s to the enemy team", "give master to player %s", "load map %s in mode %s", "%s demo recording for the next match", "stop demo recording", "clear all demos"}; char *msg = msgs[type]; switch(v->type) { @@ -649,6 +649,7 @@ votedisplayinfo *newvotedisplayinfo(playerent *owner, int type, char *arg1, char s_sprintf(v->desc)(msg, atoi(arg1) == 0 ? "Open" : "Private"); break; case SA_AUTOTEAM: + case SA_RECORDDEMO: s_sprintf(v->desc)(msg, atoi(arg1) == 0 ? "disable" : "enable"); break; case SA_MAP: @@ -663,10 +664,10 @@ votedisplayinfo *newvotedisplayinfo(playerent *owner, int type, char *arg1, char votedisplayinfo *curvote = NULL, *calledvote = NULL; -void callvote(char *type, char *arg1, char *arg2) +void callvote(int type, char *arg1, char *arg2) { - if(!type || (calledvote && multiplayer(false))) return; - votedisplayinfo *v = newvotedisplayinfo(player1, atoi(type), arg1, arg2); + if(calledvote) return; + votedisplayinfo *v = newvotedisplayinfo(player1, type, arg1, arg2); if(v) { if(multiplayer(false)) calledvote = v; @@ -680,6 +681,8 @@ void callvote(char *type, char *arg1, char *arg2) sendstring(arg1, p); putint(p, nextmode); break; + case SA_STOPDEMO: + break; default: putint(p, atoi(arg1)); break; @@ -690,6 +693,8 @@ void callvote(char *type, char *arg1, char *arg2) else conoutf("\f3invalid vote"); } +void scallvote(char *type, char *arg1, char *arg2) { if(type) callvote(atoi(type), arg1, arg2); } // fixme, ah + void vote(int v) { if(!curvote || v < 0 || v >= VOTE_NUM) return; @@ -744,7 +749,7 @@ void voteresult(int v) void clearvote() { DELETEP(curvote); DELETEP(calledvote); } -COMMAND(callvote, ARG_3STR); +COMMANDN(callvote, scallvote, ARG_3STR); //fixme,ah COMMAND(vote, ARG_1INT); void whois(int cn) diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index eebfc47c..6663b651 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -15,6 +15,7 @@ void neterr(char *s) void changemapserv(char *name, int mode) // forced map change from the server { gamemode = mode; + if(m_demo) return; load_world(name); } @@ -99,7 +100,7 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) { static char text[MAXTRANS]; int type, joining = 0; - bool mapchanged = false; + bool mapchanged = false, demoplayback = false; while(p.remaining()) switch(type = getint(p)) { @@ -429,27 +430,6 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) case SV_TIMEUP: timeupdate(getint(p)); break; - - case SV_RECVMAP: - { - getstring(text, p); - conoutf("received map \"%s\" from server, reloading..", &text); - int mapsize = getint(p); - if(p.remaining() < mapsize) - { - p.forceoverread(); - break; - } - if(securemapcheck(text)) - { - p.len += mapsize; - break; - } - writemap(path(text), mapsize, &p.buf[p.len]); - p.len += mapsize; - changemapserv(text, gamemode); - break; - } case SV_WEAPCHANGE: { @@ -606,12 +586,77 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) break; } + case SV_SENDDEMOLIST: + { + int demos = getint(p); + if(!demos) conoutf("no demos available"); + else loopi(demos) + { + getstring(text, p); + conoutf("%d. %s", i+1, text); + } + break; + } + + case SV_DEMOPLAYBACK: + { + int on = getint(p); + if(on) {} // player1->state = CS_SPECTATOR; // fixme,ah + //else stopdemo(); + else callvote(SA_STOPDEMO); + demoplayback = on!=0; + break; + } + default: neterr("type"); return; } } +void receivefile(uchar *data, int len) +{ + ucharbuf p(data, len); + int type = getint(p); + data += p.length(); + len -= p.length(); + switch(type) + { + case SV_SENDDEMO: + { + s_sprintfd(fname)("%d.dmo", lastmillis); + FILE *demo = openfile(fname, "wb"); + if(!demo) return; + conoutf("received demo \"%s\"", fname); + fwrite(data, 1, len, demo); + fclose(demo); + break; + } + + case SV_RECVMAP: + { + static char text[MAXTRANS]; + getstring(text, p); + conoutf("received map \"%s\" from server, reloading..", &text); + int mapsize = getint(p); + if(p.remaining() < mapsize) + { + p.forceoverread(); + break; + } + if(securemapcheck(text)) + { + p.len += mapsize; + break; + } + writemap(path(text), mapsize, &p.buf[p.len]); + p.len += mapsize; + changemapserv(text, gamemode); + break; + } + } +} + void localservertoclient(int chan, uchar *buf, int len) // processes any updates from the server { ucharbuf p(buf, len); @@ -619,7 +664,7 @@ void localservertoclient(int chan, uchar *buf, int len) // processes any updat switch(chan) { case 0: parsepositions(p); break; - case 1: - case 2: parsemessages(-1, NULL, p); break; + case 1: parsemessages(-1, NULL, p); break; + case 2: receivefile(p.buf, p.maxlen); break; } } diff --git a/source/src/cube.h b/source/src/cube.h index ccd4a7db..f970ccb2 100644 --- a/source/src/cube.h +++ b/source/src/cube.h @@ -44,9 +44,9 @@ #undef __glext_h__ #include "GL/glext.h" +#endif #include -#endif #include @@ -83,7 +83,6 @@ extern int gamemode, nextmode; extern int gamespeed; extern int xtraverts; extern bool minimap, reflecting, refracting; -extern bool demoplayback; extern bool intermission; extern int maxclients; extern hashtable mapinfo; diff --git a/source/src/protocol.cpp b/source/src/protocol.cpp index 3af0fee5..fe3d0545 100644 --- a/source/src/protocol.cpp +++ b/source/src/protocol.cpp @@ -119,6 +119,7 @@ char msgsizesl[] = // size inclusive message token, 0 for variable SV_CALLVOTE, 0, SV_CALLVOTESUC, 1, SV_CALLVOTEERR, 2, SV_VOTE, 2, SV_VOTERESULT, 2, SV_FORCETEAM, 2, SV_AUTOTEAM, 2, SV_WHOIS, 2, SV_WHOISINFO, 3, + SV_LISTDEMOS, 1, SV_SENDDEMOLIST, 0, SV_GETDEMO, 2, SV_SENDDEMO, 0, SV_DEMOPLAYBACK, 2, SV_CONNECT, 0, SV_CLIENT, 0, -1 diff --git a/source/src/protocol.h b/source/src/protocol.h index 9ea094b3..ce7053c2 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -4,6 +4,8 @@ #define CUBE_DEFAULT_SERVER_PORT 28763 #define CUBE_SERVINFO_PORT(serverport) (serverport+1) #define PROTOCOL_VERSION 1126 // bump when protocol changes +#define DEMO_VERSION 1 // bump when demo format changes +#define DEMO_MAGIC "ASSAULTCUBE_DEMO" // network messages codes, c2s, c2c, s2c enum @@ -24,11 +26,12 @@ enum SV_CALLVOTE, SV_CALLVOTESUC, SV_CALLVOTEERR, SV_VOTE, SV_VOTERESULT, SV_FORCETEAM, SV_AUTOTEAM, SV_WHOIS, SV_WHOISINFO, + SV_LISTDEMOS, SV_SENDDEMOLIST, SV_GETDEMO, SV_SENDDEMO, SV_DEMOPLAYBACK, SV_CONNECT, SV_CLIENT }; -enum { SA_KICK = 0, SA_BAN, SA_REMBANS, SA_MASTERMODE, SA_AUTOTEAM, SA_FORCETEAM, SA_GIVEMASTER, SA_MAP, SA_NUM}; +enum { SA_KICK = 0, SA_BAN, SA_REMBANS, SA_MASTERMODE, SA_AUTOTEAM, SA_FORCETEAM, SA_GIVEMASTER, SA_MAP, SA_RECORDDEMO, SA_STOPDEMO, SA_CLEARDEMOS, SA_NUM}; enum { VOTE_NEUTRAL = 0, VOTE_YES, VOTE_NO, VOTE_NUM }; enum { VOTEE_DISABLED = 0, VOTEE_CUR, VOTEE_MUL, VOTEE_MAX, VOTEE_DED, VOTEE_NUM }; enum { MM_OPEN, MM_PRIVATE, MM_NUM }; @@ -70,6 +73,6 @@ enum { DISC_NONE = 0, DISC_EOP, DISC_CN, DISC_MKICK, DISC_MBAN, DISC_TAGT, DISC_ #define m_teammode (gamemode==0 || gamemode==4 || gamemode==5 || gamemode==7 || gamemode==11) #define m_tarena (m_arena && m_teammode) #define m_botmode (gamemode==7 || gamemode == 8 || gamemode==12) -#define m_valid(mode) ((mode)>=0 && (mode)<=12) +#define m_valid(mode) ((mode)>=0 && (mode)<=12 || (mode) == -3) #define m_mp(mode) (m_valid(mode) && (mode)!=7 && (mode)!=8 && (mode)!=12) - +#define m_demo (gamemode==-3) diff --git a/source/src/protos.h b/source/src/protos.h index ee64c660..76a34f08 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -123,6 +123,8 @@ extern void changeteam(int team, bool forced = false); extern void newteam(char *name); extern bool securemapcheck(char *map); extern void sendintro(); +extern void getdemo(int i); +extern void listdemos(); // clientgame extern flaginfo flaginfos[2]; @@ -145,6 +147,7 @@ extern playerent *newclient(int cn); extern void timeupdate(int timeremain); extern void respawnself(); extern void setskin(playerent *pl, uint skin); +extern void callvote(int type, char *arg1 = NULL, char *arg2 = NULL); //game mode extras extern void flagpickup(); extern void tryflagdrop(bool reset = false); @@ -397,3 +400,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); + +// demo +struct demoheader +{ + char magic[16]; + int version, protocol; +}; diff --git a/source/src/server.cpp b/source/src/server.cpp index a220bdcd..4101b1fc 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -276,6 +276,7 @@ void sendpacket(int n, int chan, ENetPacket *packet, int exclude = -1) } static bool reliablemessages = false; +void recordpacket(int chan, void *data, int len); bool buildworldstate() { @@ -305,6 +306,8 @@ bool buildworldstate() } } int psize = ws.positions.length(), msize = ws.messages.length(); + if(psize) recordpacket(0, ws.positions.getbuf(), psize); + if(msize) recordpacket(1, ws.messages.getbuf(), msize); loopi(psize) { uchar c = ws.positions[i]; ws.positions.add(c); } loopi(msize) { uchar c = ws.messages[i]; ws.messages.add(c); } ws.uses = 0; @@ -353,19 +356,17 @@ string smapname; char *adminpasswd = NULL, *motd = NULL; -int numclients() +int countclients(int type, bool not=false) { int num = 0; - loopv(clients) if(clients[i]->type!=ST_EMPTY) num++; + loopv(clients) if((!not && clients[i]->type == type) || (not && clients[i]->type != type)) num++; return num; } -int numnonlocalclients() -{ - int nonlocalclients = 0; - loopv(clients) if(clients[i]->type==ST_TCPIP) nonlocalclients++; - return nonlocalclients; -} +int numclients() { return countclients(ST_EMPTY, true); } +int numlocalclients() { return countclients(ST_LOCAL); } +int numnonlocalclients() { return countclients(ST_TCPIP); } + void zapclient(int c) { @@ -443,6 +444,7 @@ ENetHost *serverhost = NULL; void process(ENetPacket *packet, int sender, int chan); void checkvotes(bool forceend=false); +void welcomepacket(ucharbuf &p, int n); void sendf(int cn, int chan, const char *format, ...) { @@ -513,6 +515,256 @@ void sendspawn(client *c) gs.lastspawn = gamemillis; } +// demo + +struct demofile +{ + string info; + uchar *data; + int len; +}; + +#define MAXDEMOS 5 +vector demos; + +bool demonextmatch = false; +FILE *demotmp = NULL; +gzFile demorecord = NULL, demoplayback = NULL; +int nextplayback = 0; + +void writedemo(int chan, void *data, int len) +{ + if(!demorecord) return; + int stamp[3] = { gamemillis, chan, len }; + endianswap(stamp, sizeof(int), 3); + gzwrite(demorecord, stamp, sizeof(stamp)); + gzwrite(demorecord, data, len); +} + +void recordpacket(int chan, void *data, int len) +{ + writedemo(chan, data, len); +} + +void enddemorecord() +{ + if(!demorecord) return; + + gzclose(demorecord); + demorecord = NULL; + +#ifdef WIN32 + demotmp = fopen("demorecord", "rb"); +#endif + if(!demotmp) return; + + fseek(demotmp, 0, SEEK_END); + int len = ftell(demotmp); + rewind(demotmp); + if(demos.length()>=MAXDEMOS) + { + delete[] demos[0].data; + demos.remove(0); + } + demofile &d = demos.add(); + time_t t = time(NULL); + char *timestr = ctime(&t), *trim = timestr + strlen(timestr); + while(trim>timestr && isspace(*--trim)) *trim = '\0'; + s_sprintf(d.info)("%s: %s, %s, %.2f%s", timestr, modestr(gamemode), smapname, len > 1024*1024 ? len/(1024*1024.f) : len/1024.0f, len > 1024*1024 ? "MB" : "kB"); + s_sprintfd(msg)("demo \"%s\" recorded", d.info); + sendservmsg(msg); + d.data = new uchar[len]; + d.len = len; + fread(d.data, 1, len, demotmp); + fclose(demotmp); + demotmp = NULL; +} + +void setupdemorecord() +{ + if(numlocalclients() || !m_mp(gamemode) || gamemode==1) return; +#ifdef WIN32 + demorecord = gzopen("demorecord", "wb9"); + if(!demorecord) return; +#else + demotmp = tmpfile(); + if(!demotmp) return; + setvbuf(demotmp, NULL, _IONBF, 0); + + demorecord = gzdopen(_dup(_fileno(demotmp)), "wb9"); + if(!demorecord) + { + fclose(demotmp); + demotmp = NULL; + return; + } +#endif + + sendservmsg("recording demo"); + + demoheader hdr; + memcpy(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic)); + hdr.version = DEMO_VERSION; + hdr.protocol = PROTOCOL_VERSION; + endianswap(&hdr.version, sizeof(int), 1); + endianswap(&hdr.protocol, sizeof(int), 1); + gzwrite(demorecord, &hdr, sizeof(demoheader)); + + uchar buf[MAXTRANS]; + ucharbuf p(buf, sizeof(buf)); + welcomepacket(p, -1); + writedemo(1, buf, p.len); + + loopv(clients) + { + client *ci = clients[i]; + uchar header[16]; + ucharbuf q(&buf[sizeof(header)], sizeof(buf)-sizeof(header)); + putint(q, SV_INITC2S); + sendstring(ci->name, q); + sendstring(ci->team, q); + + ucharbuf h(header, sizeof(header)); + putint(h, SV_CLIENT); + putint(h, ci->clientnum); + putuint(h, q.len); + + memcpy(&buf[sizeof(header)-h.len], header, h.len); + + writedemo(1, &buf[sizeof(header)-h.len], h.len+q.len); + } +} + +void listdemos(int cn) +{ + ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE); + if(!packet) return; + ucharbuf p(packet->data, packet->dataLength); + putint(p, SV_SENDDEMOLIST); + putint(p, demos.length()); + loopv(demos) sendstring(demos[i].info, p); + enet_packet_resize(packet, p.length()); + sendpacket(cn, 1, packet); + if(!packet->referenceCount) enet_packet_destroy(packet); +} + +static void cleardemos(int n) +{ + if(!n) + { + loopv(demos) delete[] demos[i].data; + demos.setsize(0); + sendservmsg("cleared all demos"); + } + else if(demos.inrange(n-1)) + { + delete[] demos[n-1].data; + demos.remove(n-1); + s_sprintfd(msg)("cleared demo %d", n); + sendservmsg(msg); + } +} + +void senddemo(int cn, int num) +{ + if(!num) num = demos.length(); + if(!demos.inrange(num-1)) return; + demofile &d = demos[num-1]; + sendf(cn, 2, "rim", SV_SENDDEMO, d.len, d.data); +} + +void enddemoplayback() +{ + if(!demoplayback) return; + gzclose(demoplayback); + demoplayback = NULL; + + sendf(-1, 1, "rii", SV_DEMOPLAYBACK, 0); + + sendservmsg("demo playback finished"); + + loopv(clients) + { + ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE); + ucharbuf p(packet->data, packet->dataLength); + welcomepacket(p, clients[i]->clientnum); + enet_packet_resize(packet, p.length()); + sendpacket(clients[i]->clientnum, 1, packet); + if(!packet->referenceCount) enet_packet_destroy(packet); + } +} + +void setupdemoplayback() +{ + demoheader hdr; + string msg; + msg[0] = '\0'; + s_sprintfd(file)("%s.dmo", smapname); + demoplayback = opengzfile(file, "rb9"); + if(!demoplayback) s_sprintf(msg)("could not read demo \"%s\"", file); + else if(gzread(demoplayback, &hdr, sizeof(demoheader))!=sizeof(demoheader) || memcmp(hdr.magic, DEMO_MAGIC, sizeof(hdr.magic))) + s_sprintf(msg)("\"%s\" is not a demo file", file); + else + { + endianswap(&hdr.version, sizeof(int), 1); + endianswap(&hdr.protocol, sizeof(int), 1); + if(hdr.version!=DEMO_VERSION) s_sprintf(msg)("demo \"%s\" requires an %s version of Sauerbraten", file, hdr.version=nextplayback) + { + int chan, len; + if(gzread(demoplayback, &chan, sizeof(chan))!=sizeof(chan) || + gzread(demoplayback, &len, sizeof(len))!=sizeof(len)) + { + enddemoplayback(); + return; + } + endianswap(&chan, sizeof(chan), 1); + endianswap(&len, sizeof(len), 1); + ENetPacket *packet = enet_packet_create(NULL, len, 0); + if(!packet || gzread(demoplayback, packet->data, len)!=len) + { + if(packet) enet_packet_destroy(packet); + enddemoplayback(); + return; + } + sendpacket(-1, chan, packet); + if(!packet->referenceCount) enet_packet_destroy(packet); + if(gzread(demoplayback, &nextplayback, sizeof(nextplayback))!=sizeof(nextplayback)) + { + enddemoplayback(); + return; + } + endianswap(&nextplayback, sizeof(nextplayback), 1); + } +} + +// + struct sflaginfo { int state; @@ -1027,6 +1279,9 @@ void shuffleteams() void resetmap(const char *newname, int newmode, int newtime = -1, bool notify = true) { + if(m_demo) enddemoplayback(); + else enddemorecord(); + bool lastteammode = m_teammode; smode = newmode; s_strcpy(smapname, newname); @@ -1058,6 +1313,12 @@ void resetmap(const char *newname, int newmode, int newtime = -1, bool notify = if(m_mp(smode)) sendspawn(c); } } + if(m_demo) setupdemoplayback(); + else if(demonextmatch) + { + demonextmatch = false; + setupdemorecord(); + } } void nextcfgset(bool notify = true) // load next maprotation set @@ -1122,9 +1383,17 @@ struct voteinfo { int owner, callmillis; serveraction *action; - void pass() { if(action) action->perform(); } + void pass() + { + if(action) + { + if(demorecord) enddemorecord(); + action->perform(); + } + } bool isvalid() { return valid_client(owner) && action != NULL && action->isvalid(); } bool isalive() { return servmillis < callmillis+40*1000; } + voteinfo() : owner(0), callmillis(0), action(NULL) {} }; static voteinfo *curvote = NULL; @@ -1136,7 +1405,7 @@ void checkvotes(bool forceend) loopv(clients) if(clients[i]->type!=ST_EMPTY && clients[i]->connectmillis < curvote->callmillis) { stats[clients[i]->vote]++; }; int total = stats[VOTE_NO]+stats[VOTE_YES]+stats[VOTE_NEUTRAL]; const float requiredcount = 0.51f; - if(stats[VOTE_YES]/(float)total > requiredcount) + if(stats[VOTE_YES]/(float)total > requiredcount || (curvote->isvalid() && clients[curvote->owner]->role > curvote->action->role)) { sendf(-1, 1, "ri2", SV_VOTERESULT, VOTE_YES); curvote->pass(); @@ -1184,12 +1453,14 @@ bool callvote(voteinfo *v) // true if a regular vote was called } else { - if(clients[v->owner]->role >= v->action->role) // pass server op commands + /*if(clients[v->owner]->role >= v->action->role) // pass server op commands { v->action->perform(); - return false; + return true; } - else // regular vote + else*/ + + // regular vote { if(curvote) { @@ -1201,7 +1472,7 @@ bool callvote(voteinfo *v) // true if a regular vote was called sendf(v->owner, 1, "ri2", SV_CALLVOTEERR, VOTEE_DISABLED); return false; } - else if(clients[v->owner]->lastvotecall && servmillis - clients[v->owner]->lastvotecall < 60*1000) + else if(clients[v->owner]->lastvotecall && servmillis - clients[v->owner]->lastvotecall < 60*1000 && clients[v->owner]->role < v->action->role) { sendf(v->owner, 1, "ri2", SV_CALLVOTEERR, VOTEE_MAX); return false; @@ -1252,10 +1523,8 @@ ENetPacket *getmapserv(int n) return packet; } -void sendwelcome(int n) +void welcomepacket(ucharbuf &p, int n) { - ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE); - ucharbuf p(packet->data, packet->dataLength); putint(p, SV_INITS2C); putint(p, n); putint(p, PROTOCOL_VERSION); @@ -1284,14 +1553,14 @@ void sendwelcome(int n) putint(p, -1); } } - client *c = clients[n]; - if(c->type == ST_TCPIP && serveroperator() != -1) sendserveropinfo(n); + client *c = valid_client(n) ? clients[n] : NULL; + if(c && c->type == ST_TCPIP && serveroperator() != -1) sendserveropinfo(n); if(autoteam && numcl>1) { putint(p, SV_FORCETEAM); putint(p, freeteam()); } - if(m_mp(smode)) + if(c && (m_demo || m_mp(smode))) { if(!canspawn(c, true)) { @@ -1337,9 +1606,6 @@ void sendwelcome(int n) putint(p, SV_TEXT); sendstring(motd, p); } - enet_packet_resize(packet, p.length()); - sendpacket(n, 1, packet); - if(smapname[0] && m_ctf) loopi(2) sendflaginfo(i, -1, n); } int checktype(int type, client *cl) @@ -1349,7 +1615,7 @@ int checktype(int type, client *cl) static int edittypes[] = { SV_EDITENT, SV_EDITH, SV_EDITT, SV_EDITS, SV_EDITD, SV_EDITE }; if(cl && smode!=1) loopi(sizeof(edittypes)/sizeof(int)) if(type == edittypes[i]) return -1; // server only messages - static int servtypes[] = { SV_INITS2C, SV_MAPRELOAD, SV_SERVMSG, SV_GIBDAMAGE, SV_DAMAGE, SV_HITPUSH, SV_SHOTFX, SV_DIED, SV_SPAWNSTATE, SV_FORCEDEATH, SV_ITEMACC, SV_ITEMSPAWN, SV_TIMEUP, SV_CDIS, SV_PONG, SV_RESUME, SV_FLAGINFO, SV_ARENAWIN, SV_CLIENT, SV_CALLVOTESUC, SV_CALLVOTEERR, SV_VOTERESULT, SV_WHOISINFO }; + static int servtypes[] = { SV_INITS2C, SV_MAPRELOAD, SV_SERVMSG, SV_GIBDAMAGE, SV_DAMAGE, SV_HITPUSH, SV_SHOTFX, SV_DIED, SV_SPAWNSTATE, SV_FORCEDEATH, SV_ITEMACC, SV_ITEMSPAWN, SV_TIMEUP, SV_CDIS, SV_PONG, SV_RESUME, SV_FLAGINFO, SV_ARENAWIN, SV_SENDDEMOLIST, SV_SENDDEMO, SV_DEMOPLAYBACK, SV_CLIENT, SV_CALLVOTESUC, SV_CALLVOTEERR, SV_VOTERESULT, SV_WHOISINFO }; if(cl) loopi(sizeof(servtypes)/sizeof(int)) if(type == servtypes[i]) return -1; return type; } @@ -1395,7 +1661,13 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1 else cl->isauthed = true; } if(!cl->isauthed) return; - sendwelcome(sender); + + ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE); + ucharbuf p(packet->data, packet->dataLength); + welcomepacket(p, sender); + enet_packet_resize(packet, p.length()); + sendpacket(sender, 1, packet); + if(smapname[0] && m_ctf) loopi(2) sendflaginfo(i, -1, sender); } if(packet->flags&ENET_PACKET_FLAG_RELIABLE) reliablemessages = true; @@ -1684,6 +1956,15 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1 case SA_GIVEMASTER: vi->action = new givemasteraction(getint(p) > 0); 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; } vi->owner = sender; vi->callmillis = servmillis; @@ -1704,6 +1985,14 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1 break; } + case SV_LISTDEMOS: + listdemos(sender); + break; + + case SV_GETDEMO: + senddemo(sender, getint(p)); + break; + default: { int size = msgsizelookup(type); @@ -1781,6 +2070,8 @@ void serverslice(uint timeout) // main server update, called from cube main lo gamemillis += diff; servmillis = nextmillis; + if(m_demo) readdemo(); + if(minremain>0) { processevents(); @@ -1801,6 +2092,7 @@ void serverslice(uint timeout) // main server update, called from cube main lo checkintermission(); if(interm && gamemillis>interm) { + if(demorecord) enddemorecord(); interm = 0; if(configsets.length()) nextcfgset(); diff --git a/source/src/serveractions.h b/source/src/serveractions.h index 5570467e..cfc46221 100644 --- a/source/src/serveractions.h +++ b/source/src/serveractions.h @@ -2,7 +2,7 @@ struct serveraction { - int type, role; + int role; bool dedicated; virtual ~serveraction() {} virtual void perform() = 0; @@ -16,7 +16,6 @@ struct mapaction : serveraction void perform() { resetmap(map, mode); } mapaction(char *map, int mode) : map(map), mode(mode) { - type = SA_MAP; role = CR_MASTER; dedicated = false; } @@ -36,7 +35,6 @@ struct forceteamaction : playeraction void perform() { forceteam(cn, team_opposite(team_int(clients[cn]->team))); } forceteamaction(int cn) : playeraction(cn) { - type = SA_FORCETEAM; role = CR_MASTER; dedicated = true; } @@ -47,7 +45,6 @@ struct givemasteraction : playeraction void perform() { changeclientrole(cn, CR_MASTER, NULL, true); } givemasteraction(int cn) : playeraction(cn) { - type = SA_GIVEMASTER; role = CR_ADMIN; dedicated = true; } @@ -58,7 +55,6 @@ struct kickaction : playeraction void perform() { disconnect(DISC_MKICK); } kickaction(int cn) : playeraction(cn) { - type = SA_KICK; role = CR_MASTER; dedicated = true; } @@ -74,7 +70,6 @@ struct banaction : playeraction } banaction(int cn) : playeraction(cn) { - type = SA_BAN; role = CR_MASTER; dedicated = true; } @@ -85,7 +80,6 @@ struct removebansaction : serveraction void perform() { bans.setsize(0); } removebansaction() { - type = SA_REMBANS; role = CR_MASTER; dedicated = true; } @@ -98,24 +92,62 @@ struct mastermodeaction : serveraction bool isvalid() { return mode >= 0 && mode < MM_NUM; } mastermodeaction(int mode) : mode(mode) { - type = SA_MASTERMODE; role = CR_MASTER; dedicated = true; } }; -struct autoteamaction : serveraction +struct enableaction : serveraction +{ + bool enable; + enableaction(bool enable) : enable(enable) {} +}; + +struct autoteamaction : enableaction { - bool enabled; void perform() { - sendf(-1, 1, "ri2", SV_AUTOTEAM, (autoteam = enabled) == 1 ? 1 : 0); + sendf(-1, 1, "ri2", SV_AUTOTEAM, (autoteam = enable) == 1 ? 1 : 0); if(m_teammode) shuffleteams(); } - autoteamaction(bool enabled) : enabled(enabled) + autoteamaction(bool enable) : enableaction(enable) { - type = SA_AUTOTEAM; role = CR_MASTER; dedicated = true; } +}; + +struct recorddemoaction : enableaction +{ + void perform() { demonextmatch = enable; } + recorddemoaction(bool enable) : enableaction(enable) + { + role = CR_MASTER; + dedicated = true; + } +}; + +struct stopdemoaction : serveraction +{ + void perform() + { + if(m_demo) enddemoplayback(); + else enddemorecord(); + } + stopdemoaction() + { + role = CR_MASTER; + dedicated = true; + } +}; + +struct cleardemosaction : serveraction +{ + int demo; + void perform() { cleardemos(demo); } + cleardemosaction(int demo) : demo(demo) + { + role = CR_MASTER; + dedicated = true; + } }; \ No newline at end of file diff --git a/source/src/tools.cpp b/source/src/tools.cpp index 97e7407d..714d1e4f 100644 --- a/source/src/tools.cpp +++ b/source/src/tools.cpp @@ -140,14 +140,12 @@ FILE *openfile(const char *filename, const char *mode) return fopen(found, mode); } -#ifndef STANDALONE gzFile opengzfile(const char *filename, const char *mode) { const char *found = findfile(filename, mode); if(!found) return NULL; return gzopen(found, mode); } -#endif char *loadfile(const char *fn, int *size) { diff --git a/source/src/tools.h b/source/src/tools.h index 8dcff8f3..b9aebf64 100644 --- a/source/src/tools.h +++ b/source/src/tools.h @@ -474,9 +474,7 @@ extern void sethomedir(const char *dir); extern void addpackagedir(const char *dir); extern const char *findfile(const char *filename, const char *mode); extern FILE *openfile(const char *filename, const char *mode); -#ifndef STANDALONE extern gzFile opengzfile(const char *filename, const char *mode); -#endif extern char *loadfile(const char *fn, int *size); extern bool listdir(const char *dir, const char *ext, vector &files); extern int listfiles(const char *dir, const char *ext, vector &files);