diff --git a/source/src/cube.h b/source/src/cube.h index 57dfee82..19cc0797 100644 --- a/source/src/cube.h +++ b/source/src/cube.h @@ -3,12 +3,12 @@ #include "tools.h" #include "geom.h" -#include "world.h" #include "model.h" #include "protocol.h" #include "sound.h" #include "weapon.h" #include "entity.h" +#include "world.h" #include "command.h" #include "vote.h" diff --git a/source/src/entities.cpp b/source/src/entities.cpp index af4616b2..8f943972 100644 --- a/source/src/entities.cpp +++ b/source/src/entities.cpp @@ -267,10 +267,8 @@ void checkitems(playerent *d) void putitems(ucharbuf &p) // puts items in network stream and also spawns them locally { - loopv(ents) if(isitem(ents[i].type) || (multiplayer(false) && gamespeed!=100 && (i=-1))) + loopv(ents) if(ents[i].fitsmode(gamemode) || (multiplayer(false) && gamespeed!=100 && (i=-1))) { - if(m_noitemsnade && ents[i].type!=I_GRENADE) continue; - else if(m_pistol && ents[i].type==I_AMMO) continue; putint(p, i); putint(p, ents[i].type); ents[i].spawned = true; @@ -282,12 +280,7 @@ void resetspawns() loopv(ents) ents[i].spawned = false; if(m_noitemsnade || m_pistol) { - loopv(ents) - { - entity &e = ents[i]; - if(m_noitemsnade && e.type == I_CLIPS) e.type = I_GRENADE; - else if(m_pistol && e.type==I_AMMO) e.type = I_CLIPS; - } + loopv(ents) ents[i].transformtype(gamemode); } } void setspawn(int i, bool on) { if(ents.inrange(i)) ents[i].spawned = on; } diff --git a/source/src/entity.h b/source/src/entity.h index 62b06475..82af654d 100644 --- a/source/src/entity.h +++ b/source/src/entity.h @@ -31,6 +31,8 @@ struct entity : public persistent_entity bool spawned; //the only dynamic state of a map entity entity(short x, short y, short z, uchar type, short attr1, uchar attr2, uchar attr3, uchar attr4) : persistent_entity(x, y, z, type, attr1, attr2, attr3, attr4), spawned(false) {} entity() {} + bool fitsmode(int gamemode) { return !m_noitems && isitem(type) && !(m_noitemsnade && type!=I_GRENADE) && !(m_pistol && type==I_AMMO); } + void transformtype(int gamemode) { type = (m_noitemsnade && type==I_CLIPS) ? I_GRENADE : ((m_pistol && type==I_AMMO) ? I_CLIPS : type); } }; struct itemstat { int add, start, max, sound; }; diff --git a/source/src/protocol.cpp b/source/src/protocol.cpp index c8655a1d..48bd7bab 100644 --- a/source/src/protocol.cpp +++ b/source/src/protocol.cpp @@ -146,6 +146,19 @@ void filterrichtext(char *dst, const char *src, int len) *dst = '\0'; } +void filterservdesc(char *dst, const char *src, int len) +{ // only colors and spaces allowed + for(int c = *src; c; c = *++src) + { + c &= 0x7F; // 7-bit ascii + if((!isspace(c) && isprint(c)) || c == ' ' || c == '\f') + { + *dst++ = c; + if(!--len) break; + } + } + *dst = '\0'; +} const char *modefullnames[] = { diff --git a/source/src/protos.h b/source/src/protos.h index c9baf636..cd13904f 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -675,7 +675,8 @@ extern int getuint(ucharbuf &p); extern void sendstring(const char *t, ucharbuf &p); extern void getstring(char *t, ucharbuf &p, int len = MAXTRANS); extern void filtertext(char *dst, const char *src, int whitespace = 1, int len = sizeof(string)-1); -extern void filterrichtext(char *dst, char const *src, int len = sizeof(string)-1); +extern void filterrichtext(char *dst, const char *src, int len = sizeof(string)-1); +extern void filterservdesc(char *dst, const char *src, int len = sizeof(string)-1); extern void startintermission(); extern void restoreserverstate(vector &ents); extern uchar *retrieveservers(uchar *buf, int buflen); diff --git a/source/src/server.cpp b/source/src/server.cpp index 59e11846..4d30b30a 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -20,6 +20,9 @@ void resetmap(const char *newname, int newmode, int newtime = -1, bool notify = void disconnect_client(int n, int reason = -1); bool refillteams(bool now = false, bool notify = true); void changeclientrole(int client, int role, char *pwd = NULL, bool force=false); +bool mapavailable(const char *mapname); +void getservermap(void); +mapstats *getservermapstats(const char *name); servercontroller *svcctrl = NULL; struct log *logger = NULL; @@ -1915,9 +1918,6 @@ bool refillteams(bool now, bool notify) // force only minimal amounts of player return switched; } -bool mapavailable(const char *mapname); -void getservermap(void); - void resetmap(const char *newname, int newmode, int newtime, bool notify) { if(m_demo) enddemoplayback(); @@ -1948,8 +1948,22 @@ void resetmap(const char *newname, int newmode, int newtime, bool notify) lastfillup = servmillis; resetvotes(VOTE_YES); // flowtron: VOTE_YES => reset lastvotecall too resetitems(); - loopi(3) clnumspawn[i] = 0; - loopi(2) clnumflagspawn[i] = 0; + mapstats *ms = getservermapstats(smapname); + loopi(3) clnumspawn[i] = ms ? ms->spawns[i] : 0; + loopi(2) clnumflagspawn[i] = ms ? ms->flags[i] : 0; + if(ms) + { + entity e; + loopi(ms->hdr.numents) + { + e.type = ms->enttypes[i]; + e.transformtype(smode); + server_entity se = { e.type, false, 0 }; + sents.add(se); + if(e.fitsmode(smode)) sents[i].spawned = true; + } + notgotitems = false; + } scores.setsize(0); ctfreset(); if(notify) @@ -1957,7 +1971,11 @@ void resetmap(const char *newname, int newmode, int newtime, bool notify) // change map sendf(-1, 1, "risii", SV_MAPCHANGE, smapname, smode, mapavailable(smapname) ? 1 : 0); if(smode>1 || (smode==0 && numnonlocalclients()>0)) sendf(-1, 1, "ri2", SV_TIMEUP, minremain); - logger->writeline(log::info, "\nGame start: %s on %s, %d players, %d minutes remaining, mastermode %d", modestr(smode), smapname, numclients(), minremain, mastermode); + } + if(newname[0]) + { + logger->writeline(log::info, "\nGame start: %s on %s, %d players, %d minutes remaining, mastermode %d, (itemlist %spreloaded, 'getmap' %sprepared)", + modestr(smode), smapname, numclients(), minremain, mastermode, ms ? "" : "not ", mapavailable(smapname) ? "" : "not "); } if(m_arena) { @@ -2215,7 +2233,7 @@ string copyname; int copysize, copymapsize, copycfgsize, copycfgsizegz; uchar *copydata = NULL; -bool mapavailable(const char *mapname) { return !strcmp(copyname, mapname); } +bool mapavailable(const char *mapname) { return copydata && !strcmp(copyname, behindpath(mapname)); } bool sendmapserv(int n, string mapname, int mapsize, int cfgsize, int cfgsizegz, uchar *data) { @@ -2259,7 +2277,7 @@ bool sendmapserv(int n, string mapname, int mapsize, int cfgsize, int cfgsizegz, ENetPacket *getmapserv(int n) { - if(!copydata || !mapavailable(smapname)) return NULL; + if(!mapavailable(smapname)) return NULL; ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + copysize, ENET_PACKET_FLAG_RELIABLE); ucharbuf p(packet->data, packet->dataLength); putint(p, SV_RECVMAP); @@ -2274,6 +2292,27 @@ ENetPacket *getmapserv(int n) // provide maps by the server +mapstats *getservermapstats(const char *mapname) +{ + const char *name = behindpath(mapname); + s_sprintfd(filename)(SERVERMAP_PATH "%s.cgz", name); + path(filename); + bool found = fileexists(filename, "r"); + if(!found) + { + s_sprintf(filename)(SERVERMAP_PATH_INCOMING "%s.cgz", name); + path(filename); + found = fileexists(filename, "r"); + if(!found) + { + s_sprintf(filename)(SERVERMAP_PATH_BUILTIN "%s.cgz", name); + path(filename); + found = fileexists(filename, "r"); + } + } + return found ? loadmapstats(filename) : NULL; +} + #define GZBUFSIZE ((MAXCFGFILESIZE * 11) / 10) void getservermap(void) @@ -2859,6 +2898,7 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1 { int ping = getint(p); if(cl) cl->ping = ping; + QUEUE_MSG; break; } diff --git a/source/src/tools.cpp b/source/src/tools.cpp index 46a133ea..303d5123 100644 --- a/source/src/tools.cpp +++ b/source/src/tools.cpp @@ -302,6 +302,43 @@ bool delfile(const char *path) return !remove(path); } +mapstats *loadmapstats(const char *filename) +{ + static mapstats s; + static uchar *enttypes = NULL; + + DELETEA(enttypes); + loopi(MAXENTTYPES) s.entcnt[i] = 0; + loopi(3) s.spawns[i] = 0; + loopi(2) s.flags[i] = 0; + + gzFile f = opengzfile(filename, "rb9"); + if(!f) return NULL; + memset(&s.hdr, 0, sizeof(header)); + if(gzread(f, &s.hdr, sizeof(header)-sizeof(int)*16)!=sizeof(header)-sizeof(int)*16 || (strncmp(s.hdr.head, "CUBE", 4) && strncmp(s.hdr.head, "ACMP",4))) { gzclose(f); return NULL; } + endianswap(&s.hdr.version, sizeof(int), 4); + if(s.hdr.version>MAPVERSION || (s.hdr.version>=4 && gzread(f, &s.hdr.waterlevel, sizeof(int)*16)!=sizeof(int)*16)) { gzclose(f); return false; } + if(s.hdr.version>=4) endianswap(&s.hdr.waterlevel, sizeof(int), 1); else s.hdr.waterlevel = -100000; + entity e; + enttypes = new uchar[s.hdr.numents]; + loopi(s.hdr.numents) + { + gzread(f, &e, sizeof(persistent_entity)); + endianswap(&e, sizeof(short), 4); + TRANSFORMOLDENTITIES(s.hdr) + if(e.type == PLAYERSTART && (e.attr2 == 0 || e.attr2 == 1 || e.attr2 == 100)) s.spawns[e.attr2 == 100 ? 2 : e.attr2]++; + if(e.type == CTF_FLAG && (e.attr2 == 0 || e.attr2 == 1)) s.flags[e.attr2]++; + s.entcnt[e.type]++; + enttypes[i] = e.type; + } + gzclose(f); + s.hasffaspawns = s.spawns[2] > 0; + s.hasteamspawns = s.spawns[0] > 0 && s.spawns[1] > 0; + s.hasflags = s.flags[0] > 0 && s.flags[1] > 0; + s.enttypes = enttypes; + return &s; +} + ///////////////////////// debugging /////////////////////// #if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__) diff --git a/source/src/tools.h b/source/src/tools.h index a697b3fd..b68216aa 100644 --- a/source/src/tools.h +++ b/source/src/tools.h @@ -583,6 +583,7 @@ extern char *loadfile(const char *fn, int *size, const char *mode = NULL); extern bool listdir(const char *dir, const char *ext, vector &files); extern int listfiles(const char *dir, const char *ext, vector &files); extern bool delfile(const char *path); +extern struct mapstats *loadmapstats(const char *filename); extern bool cmpb(void *b, int n, enet_uint32 c); extern bool cmpf(char *fn, enet_uint32 c); extern void endianswap(void *, int, int); diff --git a/source/src/world.h b/source/src/world.h index 8f69ab25..51870417 100644 --- a/source/src/world.h +++ b/source/src/world.h @@ -47,6 +47,61 @@ struct header // map file format header int reserved[14]; }; +struct mapstats +{ + struct header hdr; + int entcnt[MAXENTTYPES]; + uchar *enttypes; + int spawns[3]; + int flags[2]; + bool hasffaspawns; + bool hasteamspawns; + bool hasflags; +}; + +#define TRANSFORMOLDENTITIES(headr) \ + if(e.type==LIGHT) \ + { \ + if(!e.attr2) e.attr2 = 255; /* needed for MAPVERSION<=2 */ \ + if(e.attr1>32) e.attr1 = 32; /* 12_03 and below */ \ + } \ + if(headr.version32) e.attr1 = 32; // 12_03 and below - } - - if(hdr.version