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|<modelist> <modeflags> list: lists current modeinfo entries get: returns list of current modeinfo entries clear: erases current list of modeinfo entries <modelist> <modeflags>: specifies flags for a list of modes DOCREF modeinfo mapartist
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
+92
-24
@@ -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)
|
||||
{
|
||||
|
||||
+108
-2
@@ -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<char> 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<uchar> 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;
|
||||
|
||||
Reference in New Issue
Block a user