diff --git a/config/servercmdline.txt b/config/servercmdline.txt index b753b7da..30e6f3a9 100644 --- a/config/servercmdline.txt +++ b/config/servercmdline.txt @@ -8,6 +8,10 @@ // * you can separate the switch and it's argument by whitespace // // see docs/commandline.html for full parameter descriptions +// +// If you want to make COOP only possible for admins: you need to set (at least) -P and -M like so: +// -P fkbpMASRCDEPtwX +// -M CRU // example MOTD: -o \f3server using example cmdline file (config/servercmdline.txt)\n\f2feel free to change this :) @@ -63,13 +67,13 @@ // x vote to ban without clear reasons // p vote to pause or resume match // t send private messages to any player - // default: fkbpMASRCDEPtwX (lowercase: a user can perform the action; uppercase: admin role is required to perform the action) + // default: fkbpMASRCDePtwX (lowercase: a user can perform the action; uppercase: admin role is required to perform the action) // -M // sendmap permission string (read docs/commandline.html) // c create new map (initial map upload) // u update existing map (same or newer revision) // r revert existing map to older revision - // d delete map (default: no one can) - // default: CRU (lowercase: any user is allowed to do it; uppercase: admin role is required) + // d delete map + // default: cruD (lowercase: any user is allowed to do it; uppercase: admin role is required) // -Z // sendmap/incoming limit in MB (default = 10MB) // -I // serverinfo text filename prefix, default config/serverinfo // -O // MOTD text filename prefix, default config/motd diff --git a/source/src/server.cpp b/source/src/server.cpp index 771ba5e4..8a4c333d 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -122,6 +122,8 @@ SERVPARLIST(dumpparameters, 0, 0, 0, endis, "ddump server parameters when update SERVPAR(vitaautosave, 0, 10, 24 * 60, "sVita file autosave interval in minutes (0: only when server is empty)"); SERVPAR(vitamaxage, 3, 12, 120, "sOmit vitas from autosave, if the last login has been more than the specified number of months ago"); +bool triggerpollrestart = false; + void poll_serverthreads() // called once per mainloop-timeslice { static vector servermapstodelete; @@ -141,6 +143,7 @@ void poll_serverthreads() // called once per mainloop-timeslice readmapsthread_sem->post(); stage = 1; lastworkerthreadstart = servmillis; + triggerpollrestart = false; break; } case 1: // readmapsthread building/updating the list of maps in memory @@ -155,14 +158,14 @@ void poll_serverthreads() // called once per mainloop-timeslice { if(!strcmp(servermaps[i]->fname, fresh->fname)) // we don't check paths here - map filenames have to be unique { // found map of same name - mlog(ACLOG_INFO,"marked servermap %s%s for deletion", servermaps[i]->fpath, servermaps[i]->fname); + mlog(ACLOG_VERBOSE,"marked servermap %s%s for deletion", servermaps[i]->fpath, servermaps[i]->fname); servermapstodelete.add(servermaps.remove(i)); // mark old version for deletion } } if(fresh->isok) { servermaps.add(fresh); - mlog(ACLOG_INFO,"added servermap %s%s", fresh->fpath, fresh->fname); + mlog(servmillis>1234?ACLOG_INFO:ACLOG_VERBOSE,"added servermap %s%s", fresh->fpath, fresh->fname); // 1st time only in VERBOSE maprot.initmap(fresh, NULL); servermaps.sort(servermapsortname); // keep list sorted at all times } @@ -174,6 +177,7 @@ void poll_serverthreads() // called once per mainloop-timeslice else if(!startnewservermapsepoch) { // readmapsthread is done + if(servmillis<2000)mlog(ACLOG_INFO,"added %d servermaps",servermaps.length()); while(!readmapsthread_sem->trywait()) ; stage++; @@ -264,7 +268,7 @@ void poll_serverthreads() // called once per mainloop-timeslice case 6: // pause worker threads for a while (restart once a minute) { - if(servmillis - lastworkerthreadstart > 60 * 1000) stage = 0; + if(triggerpollrestart || servmillis - lastworkerthreadstart > 60 * 1000) stage = 0; else if(numclients() == 0) { // empty server and nothing to do: loopvrev(servermapstodelete) @@ -379,7 +383,7 @@ servermap *servermaprot::recalcgamesuggestions(int numpl) // regenerate list of servermap *getservermap(const char *mapname) // check, if the server knows a map (by filename) { - servermap *k = (servermap *)&mapname, **res = servermaps.search(&k, servermapsortname); + servermap *k = new servermap(mapname,""), **res = servermaps.search(&k, servermapsortname); return res ? *res : NULL; } @@ -3714,7 +3718,7 @@ void process(ENetPacket *packet, int sender, int chan) int actualrevision = h ? h->maprevision : 0; if(sm && sm->isro()) { - reject = "map is ro"; + reject = "map is RO"; defformatstring(msg)("\f3map upload rejected: map %s is readonly", sentmap); sendservmsg(msg, sender); } @@ -3723,12 +3727,12 @@ void process(ENetPacket *packet, int sender, int chan) reject = "server incoming reached its limits"; sendservmsg("\f3server does not support more incomings: limit reached", sender); } - else if(!sm && strchr(scl.mapperm, 'C') && cl->role < CR_ADMIN) + else if(!sm && strchr(scl.mapperm, 'C') && cl->role < CR_ADMIN)// COOP is considered wonderful { reject = "no permission for initial upload"; sendservmsg("\f3initial map upload rejected: you need to be admin", sender); } - else if(sm && !sm->isro() && revision >= sm->maprevision && !strchr(scl.mapperm, 'u') && cl->role < CR_ADMIN) // default: only admins can update maps + else if(sm && !sm->isro() && revision >= sm->maprevision && strchr(scl.mapperm, 'U') && cl->role < CR_ADMIN) // COOP is considered wonderful { reject = "no permission to update"; sendservmsg("\f3map update rejected: you need to be admin", sender); @@ -3737,7 +3741,7 @@ void process(ENetPacket *packet, int sender, int chan) { reject = "fake revision number"; } - else if(sm && !sm->isro() && revision < sm->maprevision && !strchr(scl.mapperm, 'r') && cl->role < CR_ADMIN) // default: only admins can revert maps to older revisions + else if(sm && !sm->isro() && revision < sm->maprevision && strchr(scl.mapperm, 'R') && cl->role < CR_ADMIN) // COOP is considered wonderful { reject = "no permission to revert revision"; sendservmsg("\f3map revert to older revision rejected: you need to be admin to upload an older map", sender); @@ -3818,7 +3822,9 @@ void process(ENetPacket *packet, int sender, int chan) string filename; const char *rmmap = behindpath(text), *reject = NULL; servermap *sm = getservermap(rmmap); - int reqrole = strchr(scl.mapperm, 'D') ? CR_ADMIN : (strchr(scl.mapperm, 'd') ? CR_DEFAULT : CR_ADMIN + 100); + //WAS: To allow "delete map" you need to set an *explicit* D or d via the -M switch. Otherwise *nobody* can delete maps. + //WAS: int reqrole = strchr(scl.mapperm, 'D') ? CR_ADMIN : (strchr(scl.mapperm, 'd') ? CR_DEFAULT : CR_ADMIN + 100); + int reqrole = strchr(scl.mapperm, 'd') ? CR_DEFAULT : CR_ADMIN;//COOP is considered wonderful, but admins should be allowed to delete maps by default if(cl->role < reqrole) reject = "no permission"; else if(sm && sm->isro()) reject = "map is readonly"; else if(!sm) reject = "map not found"; diff --git a/source/src/server.h b/source/src/server.h index b629351a..eb1495d6 100644 --- a/source/src/server.h +++ b/source/src/server.h @@ -12,6 +12,7 @@ enum { GE_NONE = 0, GE_SHOT, GE_EXPLODE, GE_HIT, GE_AKIMBO, GE_RELOAD, GE_SUICID enum { ST_EMPTY, ST_LOCAL, ST_TCPIP }; extern int servmillis; +extern bool triggerpollrestart; struct shotevent { diff --git a/source/src/serveractions.h b/source/src/serveractions.h index 13299ce1..d4b5f9fc 100644 --- a/source/src/serveractions.h +++ b/source/src/serveractions.h @@ -57,6 +57,7 @@ struct mapaction : serveraction } } bool isvalid() { return serveraction::isvalid() && mode != GMODE_DEMO && map[0] && mapok && !(isdedicated && !m_mp(mode)); } + bool isdisabled() { return false /*maprot.current() && !maprot.current()->vote*/; } mapaction(char *map, int mode, int time, int caller, bool q) : map(map), mode(mode), time(time), queue(q) { @@ -71,20 +72,30 @@ struct mapaction : serveraction if(notify) { if(!validname) sendservmsg("invalid map name", caller); - else sendservmsg("the server does not have this map", caller); + else{ + sendservmsg("the server does not have this map", caller); + if( !strchr(scl.voteperm,'E') || ( clients[caller]->role == CR_ADMIN || clients[caller]->role == CR_OWNER ) ){ //COOP is considered wonderful + defformatstring(hintsendmap)("first do a: \f1sendmap %s", map); + sendservmsg(hintsendmap, caller); + } + } } } else { // check, if map supports mode - if(mode == GMODE_COOPEDIT && !strchr(scl.voteperm, 'e')) role = CR_ADMIN; + if(mode == GMODE_COOPEDIT && strchr(scl.voteperm, 'E')) role = CR_ADMIN; // COOP is considered wonderful bool romap = mode == GMODE_COOPEDIT && sm->isro(); int gmmask = 1 << mode; bool spawns = mode == GMODE_COOPEDIT || ((gmmask & GMMASK__TEAMSPAWN) ? sm->entstats.hasteamspawns : sm->entstats.hasffaspawns); bool flags = (gmmask & GMMASK__FLAGENTS) ? sm->entstats.hasflags : true; if(!spawns || !flags || romap) { // unsupported mode + /* + * RETHINK: voteperm 'p/P' is documented as 'vote for pause/resume' .. what is it doing here? if(strchr(scl.voteperm, 'P')) role = CR_ADMIN; else if(!strchr(scl.voteperm, 'p')) mapok = false; // default: no one can vote for unsupported mode/map combinations + */ + role = CR_ADMIN; // quickfix@RETHINK: an admin can vote for unsupported mode/map combination defformatstring(msg)("\f3map \"%s\" does not support \"%s\": ", behindpath(map), modestr(mode, false)); if(romap) concatstring(msg, "map is readonly"); else @@ -181,7 +192,7 @@ struct giveadminaction : playeraction giveadminaction(int cn) : playeraction(cn) { role = CR_ADMIN; -// role = roleconf('G'); + //role = roleconf('G'); // RETHINK: no role toggle g/G established } }; diff --git a/source/src/serverfiles.h b/source/src/serverfiles.h index 43e507ab..8ea267d4 100644 --- a/source/src/serverfiles.h +++ b/source/src/serverfiles.h @@ -640,20 +640,34 @@ bool serverwritemap(const char *mapname, int mapsize, int cfgsize, int cfgsizegz { fwrite(data, 1, mapsize, fp); fclose(fp); - formatstring(name)(SERVERMAP_PATH_INCOMING "%s.cfg", mapname); - path(name); - fp = fopen(name, "wb"); - if(fp) - { - uLongf rawsize = cfgsize; - uchar *gzbuf = new uchar[cfgsize]; - if(uncompress(gzbuf, &rawsize, data + mapsize, cfgsizegz) == Z_OK && rawsize - cfgsize == 0) - fwrite(gzbuf, 1, cfgsize, fp); - fclose(fp); - delete[] gzbuf; + if(cfgsize > 0){ + formatstring(name)(SERVERMAP_PATH_INCOMING "%s.cfg", mapname); + path(name); + fp = fopen(name, "wb"); + if(fp) + { + uLongf rawsize = cfgsize; + uchar *gzbuf = new uchar[cfgsize]; + if(uncompress(gzbuf, &rawsize, data + mapsize, cfgsizegz) == Z_OK && rawsize - cfgsize == 0) + fwrite(gzbuf, 1, cfgsize, fp); + fclose(fp); + delete[] gzbuf; + written = true; + } + } + else + { written = true; } } + if( written ){ + servermap *sm = new servermap(mapname, SERVERMAP_PATH_INCOMING); + sm->load(); + if(sm->isok){ + servermapdropbox = sm; + triggerpollrestart = true; + } + } return written; }