From 49232e05e02a1a9313cfd4e2bb6c1fa22c44f199 Mon Sep 17 00:00:00 2001 From: ac-stef Date: Thu, 25 Aug 2016 19:18:10 +0200 Subject: [PATCH] new commands to add modeinfo and artist info to maps mapartist print|set|get|clear print: prints the map artist player ID set: sets the map artist player ID to the currently logged in player get: returns the current map artist player ID clear: erases an existing map artist record in the current map modeinfo list|get|clear| list: lists current modeinfo entries get: returns list of current modeinfo entries clear: erases current list of modeinfo entries : specifies flags for a list of modes DOCREF modeinfo mapartist --- config/scripts.cfg | 2 + config/serverparameters.cfg | 4 ++ source/src/entities.cpp | 1 + source/src/protocol.h | 2 + source/src/protos.h | 1 + source/src/server.cpp | 2 + source/src/server.h | 2 + source/src/serverfiles.h | 116 ++++++++++++++++++++++++++++-------- source/src/worldio.cpp | 110 +++++++++++++++++++++++++++++++++- 9 files changed, 214 insertions(+), 26 deletions(-) diff --git a/config/scripts.cfg b/config/scripts.cfg index e7dfbfdb..dc24f9e6 100644 --- a/config/scripts.cfg +++ b/config/scripts.cfg @@ -93,6 +93,8 @@ looplist [ listcomplete edittagclip "none plclip clip" listcomplete getvarrange "min max default" listcomplete mapareacheck "vdelta steepest total pprest pp" +listcomplete modeinfo "list clear get all ctf|dm|htf|ktf|lss|osok|pf|surv|tdm|tktf|tlss|tosok|tpf|tsurv" +listcomplete mapartist "print get set clear" listcomplete authsetup "pre priv pub ppass pass passd needpass genpre genpriv genpub newppass newpass unarmed" listcomplete authkey "clear list delete new add selfcert" looplist [ addzipmod zipmodremove zipmodgetdesc ] cmd [complete $cmd "mods" zip] diff --git a/config/serverparameters.cfg b/config/serverparameters.cfg index c15305a9..717b580a 100644 --- a/config/serverparameters.cfg +++ b/config/serverparameters.cfg @@ -46,6 +46,10 @@ mappenalty_cutoff:60 // integer [-100..100], default 0 mappenalty_weight:0 +// use_hx_modeinfo List of maprot keywords to copy from map headers +// string [0..159 chars], default "weight|time|mintime|maxtime|minplayers|maxplayers|maxteamsize|teamthreshold" +use_hx_modeinfo:weight|time|mintime|maxtime|minplayers|maxplayers|maxteamsize|teamthreshold + // **** Debug switches **** diff --git a/source/src/entities.cpp b/source/src/entities.cpp index 225a866f..43121f80 100644 --- a/source/src/entities.cpp +++ b/source/src/entities.cpp @@ -623,6 +623,7 @@ void entstats_(void) } if(es.entcnt[CTF_FLAG]) conoutf(" flag distance: %d", es.flagentdistance); conoutf(" map capabilities: has ffa spawns %d, has team spawns %d, has flags %d", es.hasffaspawns ? 1 : 0, es.hasteamspawns ? 1 : 0, es.hasflags ? 1 : 0); + if(es.modes_possible & GMMASK__MPNOCOOP) conoutf(" possible multiplayer modes: %s", gmode_enum(es.modes_possible & GMMASK__MPNOCOOP, txt)); conoutf("total entities: %d", ents.length()); intret(xmodels + xsounds); } diff --git a/source/src/protocol.h b/source/src/protocol.h index 43e29ddc..f3732c42 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -234,3 +234,5 @@ enum #define FTXT__CONSOLEPASTE (FTXT_TABTOBLANK) #define FTXT__VITACOMMENT (FTXT_NOWHITE | FTXT_ALLOWBLANKS | FTXT_NOCOLOR | FTXT_TABTOBLANK) #define FTXT__VITACLAN (FTXT_NOWHITE | FTXT_NOCOLOR) +#define FTXT__MAPROT (FTXT_NOWHITE | FTXT_NOCOLOR | FTXT_TOLOWER) + diff --git a/source/src/protos.h b/source/src/protos.h index 5e86b3af..fef3287c 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -1038,6 +1038,7 @@ extern void localservertoclient(int chan, uchar *buf, int len, bool demo = false extern const char *modestr(int n, bool acronyms = false); extern const char *voteerrorstr(int n); extern const char *mmfullname(int n); +extern void filterconfigset(char *list, void *cs = NULL); extern void fatal(const char *s, ...) PRINTFARGS(1, 2); extern void initserver(bool dedicated); extern void cleanupserver(); diff --git a/source/src/server.cpp b/source/src/server.cpp index 8d87490c..31b937e3 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -4758,6 +4758,8 @@ void initserver(bool dedicated) readmapsthread_sem = new sl_semaphore(0, NULL); defformatstring(readmapslogfilename)("%sreadmaps_log.txt", scl.logfilepath); sl_createthread(readmapsthread, (void *)path(readmapslogfilename)); + defformatstring(_modeinfologfilename)("%smisc/modeinfo.txt", scl.logfilepath); + modeinfologfilename = path(_modeinfologfilename); readserverconfigsthread_sem = new sl_semaphore(0, NULL); sl_createthread(readserverconfigsthread, NULL); diff --git a/source/src/server.h b/source/src/server.h index f3b9b59d..28f76ea7 100644 --- a/source/src/server.h +++ b/source/src/server.h @@ -454,6 +454,8 @@ void sendf(int cn, int chan, const char *format, ...); extern bool isdedicated; +const char *modeinfologfilename = NULL; + const char *messagenames[SV_NUM] = { "SV_SERVINFO", "SV_SERVINFO_RESPONSE", "SV_SERVINFO_CONTD", "SV_WELCOME", "SV_INITCLIENT", "SV_POS", "SV_POSC", "SV_POSN", diff --git a/source/src/serverfiles.h b/source/src/serverfiles.h index c48c3d79..b2910275 100644 --- a/source/src/serverfiles.h +++ b/source/src/serverfiles.h @@ -31,12 +31,8 @@ struct configsetvalues }; void empty() { weight = repeat = 0; time = mintime = maxtime = minplayers = maxplayers = maxteamsize = teamthreshold = manual = restrict = -1; } - void add(configsetvalues u) - { - weight = clamp(weight + u.weight, -100, 100); - repeat = clamp(repeat + u.repeat, -100, 100); - for(int i = 2; i < CONFIG_MAXPAR; i++) if(u.par[i] >= 0) par[i] = u.par[i]; - } + void add(configsetvalues u); + bool parse(char *keyval); }; // map management @@ -82,6 +78,9 @@ struct servermap // in-memory version of a map file on a server uchar *enttypes; // table of entity types short *entpos_x, *entpos_y; + configsetvalues *hx_modeinfo; // maprot parameters from map author + uchar *hx_mapartist; // pubkey of map artist + int modes_allowed; // modes_possible reduced to MP modes (if dedicated server) configsetvalues modes[GMODE_NUM]; // maprot parameters for this map int modes_auto; // modes to be suggested automatically @@ -100,7 +99,7 @@ struct servermap // in-memory version of a map file on a server #endif servermap(const char *mname, const char *mpath) { memset(&fname, 0, sizeof(struct servermap)); fname = newstring(mname); fpath = mpath; } - ~servermap() { delstring(fname); DELETEA(cgzraw); DELETEA(cfgrawgz); DELETEA(enttypes); DELETEA(entpos_x); DELETEA(entpos_y); DELETEA(layoutgz); } + ~servermap() { delstring(fname); DELETEA(cgzraw); DELETEA(cfgrawgz); DELETEA(enttypes); DELETEA(entpos_x); DELETEA(entpos_y); DELETEA(layoutgz); DELETEA(hx_modeinfo); DELETEA(hx_mapartist); } bool isro() { return fpath == servermappath_off || fpath == servermappath_serv; } bool isofficial() { return fpath == servermappath_off; } @@ -137,7 +136,7 @@ struct servermap // in-memory version of a map file on a server stream *f = NULL; int restofhead; - string filename; + string filename, tmp; if(!validmapname(fname)) err = "illegal filename"; if(err) goto loadfailed; @@ -225,9 +224,27 @@ struct servermap // in-memory version of a map file on a server case HX_CONFIG: break; - case HX_MAPINFO: - case HX_MODEINFO: // for new maprot... + case HX_MODEINFO: + { + hx_modeinfo = new configsetvalues[GMODE_NUM]; + char done[GMODE_NUM] = { 0 }; + loopi(GMODE_NUM) hx_modeinfo[i].empty(); + string line; + for(int mode = getuint(q); !q.overread() && mode > 0; mode = getuint(q)) + { + getstring(line, q, MAXSTRLEN); + loopi(GMODE_NUM) if((mode & (1 << i)) && !done[i]++) filterconfigset(line, hx_modeinfo + i); + stream *mr = modeinfologfilename ? openfile(modeinfologfilename, "a") : NULL; + if(mr) { mr->printf("%s %s %s\n", fname, gmode_enum(mode, tmp), line); delete mr; } // dump modeinfo lines to file, may be used as maprot + } + break; + } case HX_ARTIST: + if(len == 32) q.get((hx_mapartist = new uchar[32]), 32); + else err = "invalid HX_ARTIST record"; + break; + + case HX_MAPINFO: default: break; } @@ -288,6 +305,8 @@ struct servermap // in-memory version of a map file on a server entpos_x[i] = e.x; entpos_y[i] = e.y; } + // respect map author wishes about disallowed modes + if(hx_modeinfo) loopi(GMODE_NUM) if(hx_modeinfo[i].restrict == 81) entstats.modes_possible &= ~(1 << i); // disallowed by elvis^2 } if(err) goto loadfailed; @@ -760,6 +779,60 @@ int readserverconfigsthread(void *data) #define MODEHISTLEN 64 // remember the game modes of the last hour (roughly) +void configsetvalues::add(configsetvalues u) +{ + weight = clamp(weight + u.weight, -100, 100); + repeat = clamp(repeat + u.repeat, -100, 100); + for(int i = 2; i < CONFIG_MAXPAR; i++) if(u.par[i] >= 0) par[i] = u.par[i]; +} + +bool configsetvalues::parse(char *keyval) // parse one key:value pair +{ + char *kb, *k = strtok_r(keyval, ":", &kb); + if(k) + { + loopi(CONFIG_MAXPAR) if(!strcmp(k, maprotkeywords[i])) + { + if((k = strtok_r(NULL, ":", &kb))) + { + par[i] = clamp(atoi(k), -100, 100); + return true; + } + } + } + return false; +} + +void filterconfigset(char *list, void *cs) +{ + configsetvalues e, u, *c = cs ? (configsetvalues *) cs : &u; + e.empty(), c->empty(); + char *b, *kv = strtok_r(list, " ", &b); + while(kv) + { + c->parse(kv); + kv = strtok_r(NULL, " ", &b); + } + list[0] = '\0'; + loopi(CONFIG_MAXPAR) if(c->par[i] != e.par[i]) concatformatstring(list, "%s%s:%d", list[0] ? " " : "", maprotkeywords[i], c->par[i]); +} + +SERVSTRF(use_hx_modeinfo, "weight|time|mintime|maxtime|minplayers|maxplayers|maxteamsize|teamthreshold", 0, 159, FTXT__MAPROT, filter_use_hx_modeinfo, "gList of maprot keywords to copy from map headers"); + +int use_hx_modeinfo_mask = 0; + +void filter_use_hx_modeinfo() +{ + use_hx_modeinfo_mask = 0; + char *tmp = newstring(use_hx_modeinfo), *b, *kv = strtok_r(tmp, "|", &b); + while(kv) + { + loopi(CONFIG_MAXPAR) if(!strcmp(kv, maprotkeywords[i])) use_hx_modeinfo_mask |= 1 << i; + kv = strtok_r(NULL, "|", &b); + } + delstring(tmp); +} + struct configset : configsetvalues { char mapname[MAXMAPNAMELEN + 1]; @@ -816,19 +889,7 @@ struct servermaprot : serverconfigfile if(l) c.mmask = gmode_parse(l); while((l = strtok_r(NULL, " ", &b))) { - char *kb, *k = strtok_r(l, ":", &kb); - if(k) - { - loopi(CONFIG_MAXPAR) if(!strcmp(k, maprotkeywords[i])) - { - if((k = strtok_r(NULL, ":", &kb))) - { - c.par[i] = clamp(atoi(k), -100, 100); - haskeywords = true; - break; - } - } - } + if(c.parse(l)) haskeywords = true; } if(haskeywords) { @@ -855,7 +916,15 @@ struct servermaprot : serverconfigfile void initmap(servermap *m, stream *f) // set maprot parameters of a servermap { + configsetvalues e; memcpy(m->modes, base, sizeof(m->modes)); + if(m->hx_modeinfo && use_hx_modeinfo_mask) loopj(GMODE_NUM) + { + e.empty(); + loopk(CONFIG_MAXPAR) if(use_hx_modeinfo_mask & (1 << k)) e.par[k] = m->hx_modeinfo[j].par[k]; + m->modes[j].add(e); + } + loopv(configsets) { configset &c = configsets[i]; @@ -886,7 +955,6 @@ struct servermaprot : serverconfigfile { string s; f->printf("maprot parameters for: %s%s\n", m->fpath, m->fname); - configsetvalues e; e.empty(); loopj(GMODE_NUM) if((1 << j) & GMMASK__MPNOCOOP) { diff --git a/source/src/worldio.cpp b/source/src/worldio.cpp index 450c4288..3a92fc26 100644 --- a/source/src/worldio.cpp +++ b/source/src/worldio.cpp @@ -283,7 +283,7 @@ void unpackheaderextra(uchar *buf, int len) // break the extra data from the ma while(1) { int len = getuint(p), flags = getuint(p); - if(p.overread() || len > p.remaining()) break; + if(p.overread() || len > p.remaining() || len < 0 || flags < 0) break; DEBUGCODE(clientlogf(" found headerextra \"%s\", %d bytes%s", hx_name(flags & HX_TYPEMASK), len, flags & HX_FLAG_PERSIST ? ", persistent" : "")); // debug info headerextras.add(new headerextra(len, flags, p.subbuf(len).buf)); } @@ -306,7 +306,8 @@ void parseheaderextra(bool clearnonpersist = true, int ignoretypes = 0) // pars case HX_CONFIG: setcontext("map", "embedded"); - execute((const char *)q.buf); // needs to have '\0' at the end + if(headerextras[i]->len > 0 && headerextras[i]->data[headerextras[i]->len - 1] == '\0') execute((const char *)q.buf); // needs to have '\0' at the end, better check... + else conoutf("malformed emb config"); resetcontext(); break; @@ -541,6 +542,111 @@ bool gotovantagepoint() } COMMANDF(gotovantagepoint, "", () { intret((editmode || (!multiplayer(NULL) && player1->isspectating())) && gotovantagepoint() ? 1 : 0); }); +void modeinfo(char *mode, char *info) +{ + char *modeinfolines[GMODE_NUM] = { NULL }; + string line, tmp; + + // find and unpack HX_MODEINFO + int he = findheaderextra(HX_MODEINFO); + if(he >= 0) + { + ucharbuf q(headerextras[he]->data, headerextras[he]->len); + for(int mode = getuint(q); !q.overread() && mode > 0; mode = getuint(q)) + { + getstring(line, q, MAXSTRLEN); + filterconfigset(line); + loopi(GMODE_NUM) if((mode & (1 << i)) && !modeinfolines[i]) modeinfolines[i] = newstring(line); + } + } + + // parse command arguments and apply changes or generate output + vector res; + copystring(line, info); + filterconfigset(line); + int modes = gmode_parse(mode), n = 0; + if(!strcmp(mode, "*") || !strcmp(mode, "all")) modes = GMMASK__MPNOCOOP; + else if(!strcmp(mode, "list")) + { + loopi(GMODE_NUM) if(modeinfolines[i] && ++n) conoutf("%s: %s", gmode_enum(1 << i, tmp), escapestring(modeinfolines[i])); + if(!n) conoutf("no modeinfo lines set"); + } + else if(!strcmp(mode, "get")) + { + loopi(GMODE_NUM) if(modeinfolines[i]) cvecprintf(res, "%s %s\n", gmode_enum(1 << i, tmp), escapestring(modeinfolines[i])); + } + else if(!strcmp(mode, "clear")) + { + loopi(GMODE_NUM) if(modeinfolines[i]) { n++; DELSTRING(modeinfolines[i]); } + conoutf("deleted %d modeinfo lines", n); + } + if(modes) loopi(GMODE_NUM) + { + if(modes & (1 << i)) + { + DELSTRING(modeinfolines[i]); + modeinfolines[i] = newstring(line); + } + } + if(res.length()) res.last() = '\0'; + else res.add('\0'); + result(res.getbuf()); + + // recreate HX_MODEINFO + for(int n; (n = findheaderextra(HX_MODEINFO)) >= 0; ) deleteheaderextra(n); + vector p; + entitystats_s es; + calcentitystats(es, NULL, 0); + int todo = es.modes_possible; + bool have_modeinfo = false; + loopk(GMODE_NUM) if(modeinfolines[k] && modeinfolines[k][0] && (todo & (1 << k))) + { + int modes = 0; + for(int i = k; i < GMODE_NUM; i++) + { + if(modeinfolines[i] && !strcmp(modeinfolines[k], modeinfolines[i])) modes |= (1 << i); + } + putuint(p, modes); + sendstring(modeinfolines[k], p); + todo &= ~modes; + have_modeinfo = true; + } + putuint(p, 0); + if(have_modeinfo) headerextras.add(new headerextra(p.length(), HX_MODEINFO|HX_FLAG_PERSIST, p.getbuf())); +} +COMMAND(modeinfo, "ss"); + +void mapartist(char *what) +{ + uchar32 buf; + bool update = false, remove = false;; + + // find and unpack HX_ARTIST + int he = findheaderextra(HX_ARTIST); + if(he >= 0 && headerextras[he]->len == 32) memcpy(buf.u, headerextras[he]->data, 32); + + // parse command arguments and apply changes or generate output + string tmp; + if(!strcmp(what, "print")) conoutf("map artist key: %s", bin2hex(tmp, buf.u, 32)); + else if(!strcmp(what, "set")) + { + if(*sk) + { + memcpy(buf.u, sk + 32, 32); + update = true; + } + else conoutf("no player account in use"); + } + else if(!strcmp(what, "clear")) remove = true; + bin2hex(tmp, buf.u, 32); + result(he >= 0 || update ? tmp : ""); + + // recreate HX_ARTIST + if(update || remove) for(int n; (n = findheaderextra(HX_ARTIST)) >= 0; ) deleteheaderextra(n); + if(update) headerextras.add(new headerextra(32, HX_ARTIST|HX_FLAG_PERSIST, buf.u)); +} +COMMAND(mapartist, "s"); + void setmapinfo(const char *newlicense, const char *newcomment) { string s;