From 1ca4ce54ebb8ff183cd25645d17dd8f00cfbd96c Mon Sep 17 00:00:00 2001 From: ac-stef Date: Thu, 25 Aug 2016 19:12:56 +0200 Subject: [PATCH] some server changes tbc --- source/src/clientgame.cpp | 19 +- source/src/clients2c.cpp | 64 ---- source/src/cube.h | 1 - source/src/protocol.h | 1 + source/src/protos.h | 5 +- source/src/server.cpp | 612 ++++++++++++++++++++----------------- source/src/server.h | 20 +- source/src/serveractions.h | 43 ++- source/src/serverchecks.h | 108 ++----- source/src/serverevents.h | 18 +- source/src/serverfiles.h | 303 ++++-------------- source/src/serverms.cpp | 11 +- source/src/tools.cpp | 187 +----------- source/src/tools.h | 1 - source/src/worldio.cpp | 9 +- 15 files changed, 472 insertions(+), 930 deletions(-) diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index 5882c963..010172ae 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -809,15 +809,6 @@ void respawnself() } } -extern int checkarea(int maplayout_factor, char *maplayout); -extern int MA; -extern float Mh; - -bool bad_map() // this function makes a pair with good_map from clients2c -{ - return (gamemode != GMODE_COOPEDIT && ( Mh >= MAXMHEIGHT || MA >= MAXMAREA )); -} - inline const char * spawn_message() { if (spawnpermission == SP_WRONGMAP) @@ -839,11 +830,7 @@ int waiting_permission = 0; bool tryrespawn() { - if(m_mp(gamemode) && multiplayer(NULL) && bad_map()) - { - hudoutf("This map is not supported in multiplayer. Read the docs about map quality/dimensions."); - } - else if(spawnpermission > SP_OK_NUM) + if(spawnpermission > SP_OK_NUM) { hudeditf(HUDMSG_TIMER, "\f%s", spawn_message()); } @@ -1176,7 +1163,7 @@ void resetmap(bool mrproper) } int suicided = -1; -extern bool good_map(); + extern bool item_fail; extern int MA, F2F, Ma, Hhits; extern float Mh; @@ -1215,8 +1202,6 @@ void startmap(const char *name, bool reset, bool norespawn) // called just aft player1->followplayercn = -1; if(m_valid(gamemode) && !m_mp(gamemode)) respawnself(); else findplayerstart(player1); - if(good_map()==MAP_IS_BAD) conoutf("You cannot play in this map due to quality requisites. Please, report this incident."); - if (mapstats_hud) showmapstats(); if(!reset) return; diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index fcdb6e21..565bce87 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -236,7 +236,6 @@ void parsepositions(ucharbuf &p) } } -extern int checkarea(int maplayout_factor, char *maplayout); char *mlayout = NULL; int Mv = 0, Ma = 0, F2F = 1000 * MINFF; // moved up:, MA = 0; float Mh = 0; @@ -245,68 +244,6 @@ extern bool noflags; bool item_fail = false; int map_quality = MAP_IS_EDITABLE; -/// TODO: many functions and variables are redundant between client and server... someone should redo the entire server code and unify client and server. -bool good_map() // call this function only at startmap -{ - return true; - if (mlayout) MA = checkarea(sfactor, mlayout); - - F2F = 1000 * MINFF; - if(m_flags) - { -// flaginfo &f0 = flaginfos[0]; - // flaginfo &f1 = flaginfos[1]; -#define DIST(x) (f0.pos.x - f1.pos.x) - F2F = 1000;//(!numflagspawn[0] || !numflagspawn[1]) ? 1000 * MINFF : DIST(x)*DIST(x)+DIST(y)*DIST(y); -#undef DIST - } - - item_fail = false; - loopv(ents) - { - entity &e1 = ents[i]; - if (e1.type < I_CLIPS || e1.type > I_AKIMBO) continue; - float density = 0, hdensity = 0; - loopvj(ents) - { - entity &e2 = ents[j]; - if (e2.type < I_CLIPS || e2.type > I_AKIMBO || i == j) continue; - // only I_CLIPS, I_AMMO, I_GRENADE, I_HEALTH, I_HELMET, I_ARMOUR, I_AKIMBO -#define DIST(x) (e1.x - e2.x) -#define DIST_ATT ((e1.z + float(e1.attr1) / entscale[e1.type][0]) - (e2.z + float(e2.attr1) / entscale[e2.type][0])) - float r2 = DIST(x)*DIST(x) + DIST(y)*DIST(y) + DIST_ATT*DIST_ATT; -#undef DIST_ATT -#undef DIST - if ( r2 == 0.0f ) { conoutf("\f3MAP CHECK FAIL: Items too close %s %s (%hd,%hd)", entnames[e1.type], entnames[e2.type],e1.x,e1.y); item_fail = true; break; } - r2 = 1/r2; - if (r2 < 0.0025f) continue; - if (e1.type != e2.type) - { - hdensity += r2; - continue; - } - density += r2; - } - if ( hdensity > 0.5f ) { conoutf("\f3MAP CHECK FAIL: Items too close %s %.2f (%hd,%hd)", entnames[e1.type],hdensity,e1.x,e1.y); item_fail = true; break; } - switch(e1.type) - { -#define LOGTHISSWITCH(X) if( density > X ) { conoutf("\f3MAP CHECK FAIL: Items too close %s %.2f (%hd,%hd)", entnames[e1.type],density,e1.x,e1.y); item_fail = true; break; } - case I_CLIPS: - case I_HEALTH: LOGTHISSWITCH(0.24f); break; - case I_AMMO: LOGTHISSWITCH(0.04f); break; - case I_HELMET: LOGTHISSWITCH(0.02f); break; - case I_ARMOUR: - case I_GRENADE: - case I_AKIMBO: LOGTHISSWITCH(0.005f); break; - default: break; -#undef LOGTHISSWITCH - } - } - - map_quality = (!item_fail && F2F > MINFF && MA < MAXMAREA && Mh < MAXMHEIGHT && Hhits < MAXHHITS) ? MAP_IS_GOOD : MAP_IS_BAD; - if ( (!connected || gamemode == GMODE_COOPEDIT) && map_quality == MAP_IS_BAD ) map_quality = MAP_IS_EDITABLE; - return map_quality > 0; -} VARP(hudextras, 0, 0, 3); @@ -1532,7 +1469,6 @@ void receivefile(uchar *data, int len) int mapsize = getint(p); int cfgsize = getint(p); int cfgsizegz = getint(p); - /* int revision = */ getint(p); int size = mapsize + cfgsizegz; if(MAXMAPSENDSIZE < mapsize + cfgsizegz || cfgsize > MAXCFGFILESIZE) { // sam's suggestion conoutf("map %s is too large to receive", text); diff --git a/source/src/cube.h b/source/src/cube.h index 8f59d162..14988da6 100644 --- a/source/src/cube.h +++ b/source/src/cube.h @@ -41,7 +41,6 @@ extern vector ents; // map entities extern vec worldpos, camup, camright, camdir; // current target of the crosshair in the world extern int lastmillis, totalmillis, skipmillis; // last time extern int curtime; // current frame time -extern int interm; extern int gamemode, nextmode; extern int gamespeed; extern int xtraverts; diff --git a/source/src/protocol.h b/source/src/protocol.h index ab07823b..725fb4c4 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -143,6 +143,7 @@ enum GMMASK_BOTTEAMONESHOTONKILL = 1 << GMODE_BOTTEAMONESHOTONKILL, GMMASK__ALL = (1 << GMODE_NUM) - 1, GMMASK__FLAGS = GMMASK_CTF | GMMASK_HUNTTHEFLAG | GMMASK_KEEPTHEFLAG | GMMASK_TEAMKEEPTHEFLAG, + GMMASK__FLAGENTS = GMMASK_CTF | GMMASK_KEEPTHEFLAG | GMMASK_TEAMKEEPTHEFLAG, GMMASK__BOT = GMMASK_BOTDEATHMATCH | GMMASK_BOTLSS | GMMASK_BOTONESHOTONEKILL | GMMASK_BOTPISTOLFRENZY | GMMASK_BOTTEAMDEATHMATCH | GMMASK_BOTTEAMONESHOTONKILL | GMMASK_BOTTEAMSURVIVOR, GMMASK__MP = GMMASK_TEAMDEATHMATCH | GMMASK_COOPEDIT | GMMASK_DEATHMATCH | GMMASK_SURVIVOR | GMMASK_TEAMSURVIVOR | GMMASK_CTF | GMMASK_PISTOLFRENZY | GMMASK_LASTSWISSSTANDING | GMMASK_ONESHOTONEKILL | GMMASK_TEAMONESHOTONEKILL | GMMASK_HUNTTHEFLAG | GMMASK_TEAMKEEPTHEFLAG | GMMASK_KEEPTHEFLAG | GMMASK_TEAMPF | GMMASK_TEAMLSS, diff --git a/source/src/protos.h b/source/src/protos.h index f5336758..a90ac798 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -843,7 +843,7 @@ extern const char *particletypenames[MAXPARTYPES + 1]; // worldio enum { LWW_ENTATTROVERFLOW = 0x1, LWW_DECODEERR = 0x10, LWW_WORLDERROR = 0x100, LWW_MISSINGMEDIA = 0x1000, LWW_CONFIGERR = 0x10000, LWW_MODELERR = 0x100000, LWW_SCRIPTERR = 0x1000000 }; -extern const char *setnames(const char *name); +extern const char **setnames(const char *name); extern void save_world(char *mname, bool skipoptimise = false, bool addcomfort = false); extern int _ignoreillegalpaths; extern int load_world(char *mname); @@ -1063,7 +1063,7 @@ extern void trimtrailingwhitespace(char *s); extern string mastername; extern int masterport; extern ENetSocket connectmaster(); -extern void serverms(int mode, int numplayers, int minremain, char *smapname, int millis, const ENetAddress &localaddr, int *mnum, int *msend, int *mrec, int *cnum, int *csend, int *crec, int protocol_version); +extern void serverms(int mode, int numplayers, int minremain, char *smapname, int millis, const ENetAddress &localaddr, int *mnum, int *msend, int *mrec, int *cnum, int *csend, int *crec, int protocol_version, const char *servdesccur, int _interm); extern int msgsizelookup(int msg); extern const char *genpwdhash(const char *name, const char *pwd, int salt); extern void servermsinit(const char *master, const char *ip, int serverport, bool listen); @@ -1076,6 +1076,7 @@ extern char *votestring(int type, char *arg1, char *arg2, char *arg3); extern int wizardmain(int argc, char **argv); // some tools +extern header *peekmapheader(uchar *data, int len); extern servsqr *createservworld(const sqr *s, int _cubicsize); extern int calcmapdims(mapdim_s &md, const servsqr *s, int _ssize); extern int calcmapareastats(mapareastats_s &ms, servsqr *s, int _ssize, const mapdim_s &md); diff --git a/source/src/server.cpp b/source/src/server.cpp index 841b1f5e..59128ab5 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -13,9 +13,65 @@ VARP(serverdebug, 0, 0, 1); #include "server.h" #include "servercontroller.h" #include "serverfiles.h" -// 2011feb05:ft: quitproc + #include "signal.h" +struct servergame +{ + int mastermode; + bool autoteam; + int matchteamsize; + bool forceintermission; + string servdesc_current; + ENetAddress servdesc_caller; + bool custom_servdesc; + + // current game + string smapname, nextmapname; + int smode, nextgamemode; + int interm; + int minremain, gamemillis, gamelimit, nextsendscore; + int arenaround, arenaroundstartmillis; + int lastfillup; + vector sents; + sflaginfo sflaginfos[2]; + + servermap *curmap; + char *layout; // NULL: edit mode or singleplayer without properly loaded map + uchar *coop_mapdata; + int coop_cgzlen, coop_cfglen, coop_cfglengz; + + void init() + { + mastermode = MM_OPEN; + autoteam = true; + matchteamsize = 0; + forceintermission = false; + custom_servdesc = false; + smode = 0; + interm = 0; + minremain = 0; + gamemillis = 0; + gamelimit = 0; + nextsendscore = 0; + arenaround = 0; + arenaroundstartmillis = 0; + lastfillup = 0; + sents.setsize(0); + loopi(2) sflaginfos[i].actor_cn = -1; + curmap = NULL; + layout = NULL; + coop_mapdata = NULL; + coop_cgzlen = coop_cfglen = coop_cfglengz = 0; + } + + void free() + { + DELETEA(layout); + DELETEA(coop_mapdata); + } +} cursgame, *sg = &cursgame; + // config vector servermaps; // all available maps kept in memory @@ -32,7 +88,7 @@ serverinfofile infofiles; bool isdedicated = false; ENetHost *serverhost = NULL; -int laststatus = 0, servmillis = 0, lastfillup = 0; +int laststatus = 0, servmillis = 0; vector clients; vector worldstates; @@ -40,29 +96,8 @@ vector savedscores; vector bans; vector demofiles; -int mastermode = MM_OPEN; -static bool autoteam = true; -int matchteamsize = 0; - long int incoming_size = 0; -static bool forceintermission = false; - -string servdesc_current; -ENetAddress servdesc_caller; -bool custom_servdesc = false; - -// current game -string smapname, nextmapname; -int smode = 0, nextgamemode; -int interm = 0; -static int minremain = 0, gamemillis = 0, gamelimit = 0, /*lmsitemtype = 0,*/ nextsendscore = 0; -mapstats smapstats; -vector sents; -char *maplayout = NULL, *testlayout = NULL; -int maplayout_factor, testlayout_factor, maplayoutssize; -servermapbuffer mapbuffer; - // cmod char *global_name; int totalclients = 0; @@ -153,6 +188,11 @@ void poll_serverthreads() // called once per mainloop-timeslice } } +servermap *getservermap(const char *mapname) // check, if the server knows a map (by filename) +{ + loopv(servermaps) if(!strcmp(servermaps[i]->fname, mapname)) return servermaps[i]; + return NULL; +} bool valid_client(int cn) @@ -318,7 +358,7 @@ int *numteamclients(int exclude = -1) int sendservermode(bool send = true) { - int sm = (autoteam ? AT_ENABLED : AT_DISABLED) | ((mastermode & MM_MASK) << 2) | (matchteamsize << 4); + int sm = (sg->autoteam ? AT_ENABLED : AT_DISABLED) | ((sg->mastermode & MM_MASK) << 2) | (sg->matchteamsize << 4); if(send) sendf(-1, 1, "ri2", SV_SERVERMODE, sm); return sm; } @@ -326,19 +366,19 @@ int sendservermode(bool send = true) void changematchteamsize(int newteamsize) { if(newteamsize < 0) return; - if(matchteamsize != newteamsize) + if(sg->matchteamsize != newteamsize) { - matchteamsize = newteamsize; + sg->matchteamsize = newteamsize; sendservermode(); } - if(mastermode == MM_MATCH && matchteamsize && m_teammode) + if(sg->mastermode == MM_MATCH && sg->matchteamsize && m_teammode) { int size[2] = { 0 }; loopv(clients) if(clients[i]->type!=ST_EMPTY && clients[i]->isauthed && clients[i]->isonrightmap) { if(team_isactive(clients[i]->team)) { - if(++size[clients[i]->team] > matchteamsize) updateclientteam(i, team_tospec(clients[i]->team), FTR_SILENTFORCE); + if(++size[clients[i]->team] > sg->matchteamsize) updateclientteam(i, team_tospec(clients[i]->team), FTR_SILENTFORCE); } } } @@ -346,18 +386,18 @@ void changematchteamsize(int newteamsize) void changemastermode(int newmode) { - if(mastermode != newmode) + if(sg->mastermode != newmode) { - mastermode = newmode; + sg->mastermode = newmode; senddisconnectedscores(-1); - if(mastermode != MM_MATCH) + if(sg->mastermode != MM_MATCH) { loopv(clients) if(clients[i]->type!=ST_EMPTY && clients[i]->isauthed) { if(clients[i]->team == TEAM_CLA_SPECT || clients[i]->team == TEAM_RVSF_SPECT) updateclientteam(i, TEAM_SPECT, FTR_SILENTFORCE); } } - else if(matchteamsize) changematchteamsize(matchteamsize); + else if(sg->matchteamsize) changematchteamsize(sg->matchteamsize); sendservermode(); } } @@ -375,7 +415,7 @@ int findcnbyaddress(ENetAddress *address) savedscore *findscore(client &c, bool insert) { if(c.type!=ST_TCPIP) return NULL; - enet_uint32 mask = ENET_HOST_TO_NET_32(mastermode == MM_MATCH ? 0xFFFF0000 : 0xFFFFFFFF); // in match mode, reconnecting from /16 subnet is allowed + enet_uint32 mask = ENET_HOST_TO_NET_32(sg->mastermode == MM_MATCH ? 0xFFFF0000 : 0xFFFFFFFF); // in match mode, reconnecting from /16 subnet is allowed if(!insert) { loopv(clients) @@ -450,12 +490,12 @@ void sendf(int cn, int chan, const char *format, ...) void sendextras() { - if ( gamemillis < nextsendscore ) return; + if (sg->gamemillis < sg->nextsendscore ) return; int count = 0, list[MAXCLIENTS]; loopv(clients) { client &c = *clients[i]; - if ( c.type!=ST_TCPIP || !c.isauthed || !(c.md.updated && c.md.upmillis < gamemillis) ) continue; + if ( c.type!=ST_TCPIP || !c.isauthed || !(c.md.updated && c.md.upmillis < sg->gamemillis) ) continue; if ( c.md.combosend ) { sendf(c.clientnum, 1, "ri2", SV_HUDEXTRAS, min(c.md.combo,c.md.combofrags)-1 + HE_COMBO); @@ -467,7 +507,7 @@ void sendextras() count++; } } - nextsendscore = gamemillis + 160; // about 4 cicles + sg->nextsendscore = sg->gamemillis + 160; // about 4 cicles if ( !count ) return; packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); @@ -494,13 +534,13 @@ void sendspawn(client *c) if(team_isspect(c->team)) return; clientstate &gs = c->state; gs.respawn(); - gs.spawnstate(smode); + gs.spawnstate(sg->smode); gs.lifesequence++; sendf(c->clientnum, 1, "ri7vv", SV_SPAWNSTATE, gs.lifesequence, gs.health, gs.armour, gs.primary, gs.gunselect, m_arena ? c->spawnindex : -1, NUMGUNS, gs.ammo, NUMGUNS, gs.mag); - gs.lastspawn = gamemillis; + gs.lastspawn = sg->gamemillis; } // demo @@ -511,7 +551,7 @@ int nextplayback = 0; void writedemo(int chan, void *data, int len) { if(!demorecord) return; - int stamp[3] = { gamemillis, chan, len }; + int stamp[3] = { sg->gamemillis, chan, len }; lilswap(stamp, 3); demorecord->write(stamp, sizeof(stamp)); demorecord->write(data, len); @@ -650,7 +690,7 @@ void enddemorecord() if(!demotmp) return; - if(gamemillis < DEMO_MINTIME) + if(sg->gamemillis < DEMO_MINTIME) { delete demotmp; demotmp = NULL; @@ -665,25 +705,20 @@ void enddemorecord() delete[] demofiles[0].data; demofiles.remove(0); } - int mr = gamemillis >= gamelimit ? 0 : (gamelimit - gamemillis + 60000 - 1)/60000; + int mr = sg->gamemillis >= sg->gamelimit ? 0 : (sg->gamelimit - sg->gamemillis + 60000 - 1)/60000; demofile &d = demofiles.add(); - //2010oct10:ft: suggests : formatstring(d.info)("%s, %s, %.2f%s", modestr(gamemode), smapname, len > 1024*1024 ? len/(1024*1024.f) : len/1024.0f, len > 1024*1024 ? "MB" : "kB"); // the datetime bit is pretty useless in the servmesg, no?! - formatstring(d.info)("%s: %s, %s, %.2f%s", asctimestr(), modestr(gamemode), smapname, len > 1024*1024 ? len/(1024*1024.f) : len/1024.0f, len > 1024*1024 ? "MB" : "kB"); + formatstring(d.info)("%s: %s, %s, %.2f%s", asctimestr(), modestr(gamemode), sg->smapname, len > 1024*1024 ? len/(1024*1024.f) : len/1024.0f, len > 1024*1024 ? "MB" : "kB"); if(mr) { concatformatstring(d.info, ", %d mr", mr); concatformatstring(d.file, "_%dmr", mr); } defformatstring(msg)("Demo \"%s\" recorded\nPress F10 to download it from the server..", d.info); sendservmsg(msg); logline(ACLOG_INFO, "Demo \"%s\" recorded.", d.info); - // 2011feb05:ft: previously these two static formatstrings were used .. - //formatstring(d.file)("%s_%s_%s", timestring(), behindpath(smapname), modestr(gamemode, true)); // 20100522_10.08.48_ac_mines_DM.dmo - //formatstring(d.file)("%s_%s_%s", modestr(gamemode, true), behindpath(smapname), timestring( true, "%Y.%m.%d_%H%M")); // DM_ac_mines.2010.05.22_1008.dmo - // .. now we use client-side parseable fileattribs - int mPLAY = gamemillis >= gamelimit ? gamelimit/1000 : gamemillis/1000; - int mDROP = gamemillis >= gamelimit ? 0 : (gamelimit - gamemillis)/1000; + int mPLAY = sg->gamemillis >= sg->gamelimit ? sg->gamelimit/1000 : sg->gamemillis/1000; + int mDROP = sg->gamemillis >= sg->gamelimit ? 0 : (sg->gamelimit - sg->gamemillis)/1000; int iTIME = time(NULL); const char *mTIME = numtime(); - const char *sMAPN = behindpath(smapname); + const char *sMAPN = behindpath(sg->smapname); string iMAPN; copystring(iMAPN, sMAPN); formatstring(d.file)( "%d:%d:%d:%s:%s", gamemode, mPLAY, mDROP, mTIME, iMAPN); @@ -740,9 +775,9 @@ void setupdemorecord() lilswap(&hdr.version, 1); lilswap(&hdr.protocol, 1); memset(hdr.desc, 0, DHDR_DESCCHARS); - defformatstring(desc)("%s, %s, %s %s", modestr(gamemode, false), behindpath(smapname), asctimestr(), servdesc_current); + defformatstring(desc)("%s, %s, %s %s", modestr(gamemode, false), behindpath(sg->smapname), asctimestr(), sg->servdesc_current); if(strlen(desc) > DHDR_DESCCHARS) - formatstring(desc)("%s, %s, %s %s", modestr(gamemode, true), behindpath(smapname), asctimestr(), servdesc_current); + formatstring(desc)("%s, %s, %s %s", modestr(gamemode, true), behindpath(sg->smapname), asctimestr(), sg->servdesc_current); desc[DHDR_DESCCHARS - 1] = '\0'; strcpy(hdr.desc, desc); memset(hdr.plist, 0, DHDR_PLISTCHARS); @@ -793,7 +828,7 @@ void senddemo(int cn, int num) { client *cl = cn>=0 ? clients[cn] : NULL; bool is_admin = (cl && cl->role == CR_ADMIN); - if(scl.demo_interm && (!interm || totalclients > 2) && !is_admin) + if(scl.demo_interm && (!sg->interm || totalclients > 2) && !is_admin) { sendservmsg("\f3sorry, but this server only sends demos at intermission.\n wait for the end of this game, please", cn); return; @@ -819,7 +854,7 @@ void senddemo(int cn, int num) ci.ip = cl->peer->address.host; ci.clientnum = cl->clientnum; - if (interm) sending_demo = true; + if (sg->interm) sending_demo = true; packetbuf p(MAXTRANS + d.len, ENET_PACKET_FLAG_RELIABLE); putint(p, SV_SENDDEMO); sendstring(d.file, p); @@ -850,7 +885,7 @@ void setupdemoplayback() demoheader hdr; string msg; msg[0] = '\0'; - defformatstring(file)("demos/%s.dmo", smapname); + defformatstring(file)("demos/%s.dmo", sg->smapname); path(file); demoplayback = opengzfile(file, "rb"); if(!demoplayback) formatstring(msg)("could not read demo \"%s\"", file); @@ -874,7 +909,7 @@ void setupdemoplayback() formatstring(msg)("playing demo \"%s\"", file); sendservmsg(msg); - sendf(-1, 1, "risi", SV_DEMOPLAYBACK, smapname, -1); + sendf(-1, 1, "risi", SV_DEMOPLAYBACK, sg->smapname, -1); watchingdemo = true; if(demoplayback->read(&nextplayback, sizeof(nextplayback))!=sizeof(nextplayback)) @@ -888,7 +923,7 @@ void setupdemoplayback() void readdemo() { if(!demoplayback) return; - while(gamemillis>=nextplayback) + while(sg->gamemillis >= nextplayback) { int chan, len; if(demoplayback->read(&chan, sizeof(chan))!=sizeof(chan) || @@ -917,21 +952,9 @@ void readdemo() } } -struct sflaginfo -{ - int state; - int actor_cn; - float pos[3]; - int lastupdate; - int stolentime; - short x, y; // flag entity location - - sflaginfo() { actor_cn = -1; } -} sflaginfos[2]; - void putflaginfo(packetbuf &p, int flag) { - sflaginfo &f = sflaginfos[flag]; + sflaginfo &f = sg->sflaginfos[flag]; putint(p, SV_FLAGINFO); putint(p, flag); putint(p, f.state); @@ -949,7 +972,7 @@ void putflaginfo(packetbuf &p, int flag) inline void send_item_list(packetbuf &p) { putint(p, SV_ITEMLIST); - loopv(sents) if(sents[i].spawned) putint(p, i); + loopv(sg->sents) if(sg->sents[i].spawned) putint(p, i); putint(p, -1); if(m_flags) loopi(2) putflaginfo(p, i); } @@ -978,7 +1001,7 @@ bool flagdistance(sflaginfo &f, int cn) { c.farpickups++; logline(ACLOG_INFO, "[%s] %s %s the %s flag at distance %.2f (%d)", - c.hostname, c.name, (pdist==2?"tried to touch":"touched"), team_string(&f == sflaginfos + 1), dist, c.farpickups); + c.hostname, c.name, (pdist==2?"tried to touch":"touched"), team_string(&f == sg->sflaginfos + 1), dist, c.farpickups); if (pdist==2) return false; } return lagging ? false : true; // today I found a lag hacker :: Brahma, 19-oct-2010... lets test it a bit @@ -995,7 +1018,7 @@ void sendflaginfo(int flag = -1, int cn = -1) void flagmessage(int flag, int message, int actor, int cn = -1) { if(message == FM_KTFSCORE) - sendf(cn, 1, "riiiii", SV_FLAGMSG, flag, message, actor, (gamemillis - sflaginfos[flag].stolentime) / 1000); + sendf(cn, 1, "riiiii", SV_FLAGMSG, flag, message, actor, (sg->gamemillis - sg->sflaginfos[flag].stolentime) / 1000); else sendf(cn, 1, "riiii", SV_FLAGMSG, flag, message, actor); } @@ -1003,8 +1026,8 @@ void flagmessage(int flag, int message, int actor, int cn = -1) void flagaction(int flag, int action, int actor) { if(!valid_flag(flag)) return; - sflaginfo &f = sflaginfos[flag]; - sflaginfo &of = sflaginfos[team_opposite(flag)]; + sflaginfo &f = sg->sflaginfos[flag]; + sflaginfo &of = sg->sflaginfos[team_opposite(flag)]; bool deadactor = valid_client(actor) ? clients[actor]->state.state != CS_ALIVE || team_isspect(clients[actor]->team): true; int abort = 0; int score = 0; @@ -1070,7 +1093,7 @@ void flagaction(int flag, int action, int actor) if(deadactor || f.state != CTFF_INBASE || !flagdistance(f, actor)) { abort = 20; break; } f.state = CTFF_STOLEN; f.actor_cn = actor; - f.stolentime = gamemillis; + f.stolentime = sg->gamemillis; message = FM_PICKUP; break; case FA_SCORE: // f = carried by actor flag @@ -1133,7 +1156,7 @@ void flagaction(int flag, int action, int actor) logline(ACLOG_INFO, "[%s] %s scored with the flag for %s, new score %d", c.hostname, c.name, team_string(c.team), c.state.flagscore); break; case FM_KTFSCORE: - logline(ACLOG_INFO, "[%s] %s scored, carrying for %d seconds, new score %d", c.hostname, c.name, (gamemillis - f.stolentime) / 1000, c.state.flagscore); + logline(ACLOG_INFO, "[%s] %s scored, carrying for %d seconds, new score %d", c.hostname, c.name, (sg->gamemillis - f.stolentime) / 1000, c.state.flagscore); break; case FM_SCOREFAIL: logline(ACLOG_INFO, "[%s] %s failed to score", c.hostname, c.name); @@ -1157,7 +1180,7 @@ void flagaction(int flag, int action, int actor) } } - f.lastupdate = gamemillis; + f.lastupdate = sg->gamemillis; sendflaginfo(flag); if(message >= 0) flagmessage(flag, message, valid_client(actor) ? actor : -1); @@ -1167,7 +1190,7 @@ int clienthasflag(int cn) { if(m_flags && valid_client(cn)) { - loopi(2) { if(sflaginfos[i].state==CTFF_STOLEN && sflaginfos[i].actor_cn==cn) return i; } + loopi(2) { if(sg->sflaginfos[i].state==CTFF_STOLEN && sg->sflaginfos[i].actor_cn==cn) return i; } } return -1; } @@ -1177,9 +1200,9 @@ void ctfreset() int idleflag = m_ktf ? rnd(2) : -1; loopi(2) { - sflaginfos[i].actor_cn = -1; - sflaginfos[i].state = i == idleflag ? CTFF_IDLE : CTFF_INBASE; - sflaginfos[i].lastupdate = -1; + sg->sflaginfos[i].actor_cn = -1; + sg->sflaginfos[i].state = i == idleflag ? CTFF_IDLE : CTFF_INBASE; + sg->sflaginfos[i].lastupdate = -1; } } @@ -1197,7 +1220,7 @@ void resetflag(int cn) void htf_forceflag(int flag) { - sflaginfo &f = sflaginfos[flag]; + sflaginfo &f = sg->sflaginfos[flag]; int besthealth = 0; vector clientnumbers; loopv(clients) if(clients[i]->type!=ST_EMPTY) @@ -1228,18 +1251,17 @@ void htf_forceflag(int flag) flagmessage(flag, FM_PICKUP, cl->clientnum); logline(ACLOG_INFO,"[%s] %s got forced to pickup the flag", cl->hostname, cl->name); } - f.lastupdate = gamemillis; + f.lastupdate = sg->gamemillis; } -int arenaround = 0, arenaroundstartmillis = 0; - int cmpscore(const int *a, const int *b) { return clients[*a]->at3_score - clients[*b]->at3_score; } vector tdistrib; vector sdistrib; void distributeteam(int team) { - int numsp = team == 100 ? smapstats.spawns[2] : smapstats.spawns[team]; + int numsp = 0; + if(sg->curmap) numsp = team == 100 ? sg->curmap->entstats.spawns[2] : sg->curmap->entstats.spawns[team & 1]; if(!numsp) numsp = 30; // no spawns: try to distribute anyway twoint ti; tdistrib.shrink(0); @@ -1285,24 +1307,16 @@ void distributespawns() } } -bool items_blocked = false; -bool free_items(int n) -{ - client *c = clients[n]; - int waitspawn = min(c->ping,200) + c->state.spawn; // flowtron to Brahma: can't this be removed now? (re: rev. 5270) - return !items_blocked && (waitspawn < gamemillis); -} - void checkitemspawns(int); void arenacheck() { - if(!m_arena || interm || gamemillisinterm || sg->gamemillis < sg->arenaround || !numactiveclients()) return; - if(arenaround) + if(sg->arenaround) { // start new arena round - arenaround = 0; - arenaroundstartmillis = gamemillis; + sg->arenaround = 0; + sg->arenaroundstartmillis = sg->gamemillis; distributespawns(); checkitemspawns(60*1000); // the server will respawn all items now loopv(clients) if(clients[i]->type!=ST_EMPTY && clients[i]->isauthed) @@ -1310,7 +1324,6 @@ void arenacheck() if(clients[i]->isonrightmap && team_isactive(clients[i]->team)) sendspawn(clients[i]); } - items_blocked = false; return; } @@ -1332,7 +1345,7 @@ void arenacheck() if(enemies && (!alive_enemies || player1->state == CS_DEAD)) { sendf(-1, 1, "ri2", SV_ARENAWIN, m_teammode ? (alive ? alive->clientnum : -1) : (alive && alive->type == ENT_BOT ? -2 : player1->state == CS_ALIVE ? player1->clientnum : -1)); - arenaround = gamemillis+5000; + sg->arenaround = sg->gamemillis + 5000; } return; } @@ -1356,16 +1369,15 @@ void arenacheck() } } - if(autoteam && m_teammode && mastermode != MM_MATCH) + if(sg->autoteam && m_teammode && sg->mastermode != MM_MATCH) { int *ntc = numteamclients(); if((!ntc[0] || !ntc[1]) && (ntc[0] > 1 || ntc[1] > 1)) refillteams(true, FTR_AUTOTEAM); } - if(!dead || gamemillis < lastdeath + 500) return; - items_blocked = true; + if(!dead || sg->gamemillis < lastdeath + 500) return; sendf(-1, 1, "ri2", SV_ARENAWIN, alive ? alive->clientnum : -1); - arenaround = gamemillis+5000; - if(autoteam && m_teammode) refillteams(true); + sg->arenaround = sg->gamemillis + 5000; + if(sg->autoteam && m_teammode) refillteams(true); } #define SPAMREPEATINTERVAL 20 // detect doubled lines only if interval < 20 seconds @@ -1498,12 +1510,12 @@ int spawntime(int type) bool serverpickup(int i, int sender) // server side item pickup, acknowledge first client that gets it { const char *hn = sender >= 0 && clients[sender]->type == ST_TCPIP ? clients[sender]->hostname : NULL; - if(!sents.inrange(i)) + if(!sg->sents.inrange(i)) { if(hn && !m_coop) logline(ACLOG_INFO, "[%s] tried to pick up entity #%d - doesn't exist on this map", hn, i); return false; } - server_entity &e = sents[i]; + server_entity &e = sg->sents[i]; if(!e.spawned) { if(!e.legalpickup && hn && !m_demo) logline(ACLOG_INFO, "[%s] tried to pick up entity #%d (%s) - can't be picked up in this gamemode or at all", hn, i, entnames[e.type]); @@ -1514,7 +1526,7 @@ bool serverpickup(int i, int sender) // server side item pickup, acknowl client *cl = clients[sender]; if(cl->type==ST_TCPIP) { - if( cl->state.state!=CS_ALIVE || !cl->state.canpickup(e.type) || ( m_arena && !free_items(sender) ) ) return false; + if(cl->state.state != CS_ALIVE || !cl->state.canpickup(e.type)) return false; vec v(e.x, e.y, cl->state.o.z); float dist = cl->state.o.dist(v); int pdist = check_pdist(cl,dist); @@ -1527,8 +1539,8 @@ bool serverpickup(int i, int sender) // server side item pickup, acknowl } } sendf(-1, 1, "ri3", SV_ITEMACC, i, sender); - cl->state.pickup(sents[i].type); - if (m_lss && sents[i].type == I_GRENADE) cl->state.pickup(sents[i].type); // get two nades at lss + cl->state.pickup(sg->sents[i].type); + if (m_lss && sg->sents[i].type == I_GRENADE) cl->state.pickup(sg->sents[i].type); // get two nades at lss } e.spawned = false; if(!m_lms) e.spawntime = spawntime(e.type); @@ -1538,13 +1550,13 @@ bool serverpickup(int i, int sender) // server side item pickup, acknowl void checkitemspawns(int diff) { if(!diff) return; - loopv(sents) if(sents[i].spawntime) + loopv(sg->sents) if(sg->sents[i].spawntime) { - sents[i].spawntime -= diff; - if(sents[i].spawntime<=0) + sg->sents[i].spawntime -= diff; + if(sg->sents[i].spawntime<=0) { - sents[i].spawntime = 0; - sents[i].spawned = true; + sg->sents[i].spawntime = 0; + sg->sents[i].spawned = true; sendf(-1, 1, "ri2", SV_ITEMSPAWN, i); } } @@ -1553,7 +1565,7 @@ void checkitemspawns(int diff) void serverdamage(client *target, client *actor, int damage, int gun, bool gib, const vec &hitpush = vec(0, 0, 0)) { if (!m_demo && !m_coop && !validdamage(target, actor, damage, gun, gib)) return; - if ( m_arena && gun == GUN_GRENADE && arenaroundstartmillis + 2000 > gamemillis && target != actor ) return; + if ( m_arena && gun == GUN_GRENADE && sg->arenaroundstartmillis + 2000 > sg->gamemillis && target != actor ) return; clientstate &ts = target->state; ts.dodamage(damage, gun); if(damage < INT_MAX) @@ -1602,7 +1614,7 @@ void serverdamage(client *target, client *actor, int damage, int gun, bool gib, } target->position.setsize(0); ts.state = CS_DEAD; - ts.lastdeath = gamemillis; + ts.lastdeath = sg->gamemillis; if(!suic) logline(ACLOG_INFO, "[%s] %s %s%s %s", actor->hostname, actor->name, valid_weapon(gun) ? killmessages[gib ? 1 : 0][gun] : "smurfed", tk ? " their teammate" : "", target->name); if(m_flags && targethasflag >= 0) { @@ -1621,7 +1633,7 @@ void serverdamage(client *target, client *actor, int damage, int gun, bool gib, if( actor->state.frags < scl.banthreshold || /** teamkilling more than 6 (defaults), more than 2 per minute and less than 4 frags per tk */ ( actor->state.teamkills >= -scl.banthreshold && - actor->state.teamkills * 30 * 1000 > gamemillis && + actor->state.teamkills * 30 * 1000 > sg->gamemillis && actor->state.frags < 4 * actor->state.teamkills ) ) { addban(actor, DISC_AUTOBAN); @@ -1629,7 +1641,7 @@ void serverdamage(client *target, client *actor, int damage, int gun, bool gib, else if( actor->state.frags < scl.kickthreshold || /** teamkilling more than 5 (defaults), more than 1 tk per minute and less than 4 frags per tk */ ( actor->state.teamkills >= -scl.kickthreshold && - actor->state.teamkills * 60 * 1000 > gamemillis && + actor->state.teamkills * 60 * 1000 > sg->gamemillis && actor->state.frags < 4 * actor->state.teamkills ) ) disconnect_client(actor->clientnum, DISC_AUTOKICK); } } else if ( target!=actor && isteam(target->team, actor->team) ) check_ffire (target, actor, damage); // friendly fire counter @@ -1643,30 +1655,30 @@ void updatesdesc(const char *newdesc, ENetAddress *caller = NULL) { if(!newdesc || !newdesc[0] || !updatedescallowed()) { - copystring(servdesc_current, scl.servdesc_full); - custom_servdesc = false; + copystring(sg->servdesc_current, scl.servdesc_full); + sg->custom_servdesc = false; } else { - formatstring(servdesc_current)("%s%s%s", scl.servdesc_pre, newdesc, scl.servdesc_suf); - custom_servdesc = true; - if(caller) servdesc_caller = *caller; + formatstring(sg->servdesc_current)("%s%s%s", scl.servdesc_pre, newdesc, scl.servdesc_suf); + sg->custom_servdesc = true; + if(caller) sg->servdesc_caller = *caller; } } int canspawn(client *c) // beware: canspawn() doesn't check m_arena! { if(!c || c->type == ST_EMPTY || !c->isauthed || !team_isvalid(c->team) || - (c->type == ST_TCPIP && (c->state.lastdeath > 0 ? gamemillis - c->state.lastdeath : servmillis - c->connectmillis) < (m_arena ? 0 : (m_flags ? 5000 : 2000))) || + (c->type == ST_TCPIP && (c->state.lastdeath > 0 ? sg->gamemillis - c->state.lastdeath : servmillis - c->connectmillis) < (m_arena ? 0 : (m_flags ? 5000 : 2000))) || (c->type == ST_TCPIP && (servmillis - c->connectmillis < 1000 + c->state.reconnections * 2000 && - gamemillis > 10000 && totalclients > 3 && !team_isspect(c->team)))) return SP_OK_NUM; // equivalent to SP_DENY + sg->gamemillis > 10000 && totalclients > 3 && !team_isspect(c->team)))) return SP_OK_NUM; // equivalent to SP_DENY if(!c->isonrightmap) return SP_WRONGMAP; - if(mastermode == MM_MATCH && matchteamsize) + if(sg->mastermode == MM_MATCH && sg->matchteamsize) { if(c->team == TEAM_SPECT || (team_isspect(c->team) && !m_teammode)) return SP_SPECT; if(c->team == TEAM_CLA_SPECT || c->team == TEAM_RVSF_SPECT) { - if(numteamclients()[team_base(c->team)] >= matchteamsize) return SP_SPECT; + if(numteamclients()[team_base(c->team)] >= sg->matchteamsize) return SP_SPECT; else return SP_REFILLMATCH; } } @@ -1675,7 +1687,7 @@ int canspawn(client *c) // beware: canspawn() doesn't check m_arena! void autospawncheck() { - if(mastermode != MM_MATCH || !m_autospawn || interm) return; + if(sg->mastermode != MM_MATCH || !m_autospawn || sg->interm) return; loopv(clients) if(clients[i]->type!=ST_EMPTY && clients[i]->isauthed && team_isactive(clients[i]->team)) { @@ -1696,17 +1708,17 @@ bool updateclientteam(int cln, int newteam, int ftr) client &cl = *clients[cln]; if(cl.team == newteam && ftr != FTR_AUTOTEAM) return true; // no change int *teamsizes = numteamclients(cln); - if( mastermode == MM_OPEN && cl.state.forced && ftr == FTR_PLAYERWISH && + if( sg->mastermode == MM_OPEN && cl.state.forced && ftr == FTR_PLAYERWISH && newteam < TEAM_SPECT && team_base(cl.team) != team_base(newteam) ) return false; // no free will changes to forced people if(newteam == TEAM_ANYACTIVE) // when spawning from spect { - if(mastermode == MM_MATCH && cl.team < TEAM_SPECT) + if(sg->mastermode == MM_MATCH && cl.team < TEAM_SPECT) { newteam = team_base(cl.team); } else { - if(autoteam && teamsizes[TEAM_CLA] != teamsizes[TEAM_RVSF]) newteam = teamsizes[TEAM_CLA] < teamsizes[TEAM_RVSF] ? TEAM_CLA : TEAM_RVSF; + if(sg->autoteam && teamsizes[TEAM_CLA] != teamsizes[TEAM_RVSF]) newteam = teamsizes[TEAM_CLA] < teamsizes[TEAM_RVSF] ? TEAM_CLA : TEAM_RVSF; else { // join weaker team int teamscore[2] = {0, 0}, sum = calcscores(); @@ -1720,23 +1732,23 @@ bool updateclientteam(int cln, int newteam, int ftr) } if(ftr == FTR_PLAYERWISH) { - if(mastermode == MM_MATCH && matchteamsize && m_teammode) + if(sg->mastermode == MM_MATCH && sg->matchteamsize && m_teammode) { if(newteam != TEAM_SPECT && (team_base(newteam) != team_base(cl.team) || !m_teammode)) return false; // no switching sides in match mode when teamsize is set } if(team_isactive(newteam)) { if(!m_teammode && cl.state.state == CS_ALIVE) return false; // no comments - if(mastermode == MM_MATCH) + if(sg->mastermode == MM_MATCH) { - if(m_teammode && matchteamsize && teamsizes[newteam] >= matchteamsize) return false; // ensure maximum team size + if(m_teammode && sg->matchteamsize && teamsizes[newteam] >= sg->matchteamsize) return false; // ensure maximum team size } else { - if(m_teammode && autoteam && teamsizes[newteam] > teamsizes[team_opposite(newteam)]) return false; // don't switch to an already bigger team + if(m_teammode && sg->autoteam && teamsizes[newteam] > teamsizes[team_opposite(newteam)]) return false; // don't switch to an already bigger team } } - else if(mastermode != MM_MATCH || !m_teammode) newteam = TEAM_SPECT; // only match mode (team) has more than one spect team + else if(sg->mastermode != MM_MATCH || !m_teammode) newteam = TEAM_SPECT; // only match mode (team) has more than one spect team } if(cl.team == newteam && ftr != FTR_AUTOTEAM) return true; // no change if(cl.team != newteam) sdropflag(cl.clientnum); @@ -1745,7 +1757,7 @@ bool updateclientteam(int cln, int newteam, int ftr) if(ftr != FTR_INFO && !team_isspect(newteam) && team_isspect(cl.team)) sendspawn(&cl); if (team_isspect(newteam)) { cl.state.state = CS_SPECTATE; - cl.state.lastdeath = gamemillis; + cl.state.lastdeath = sg->gamemillis; } cl.team = newteam; return true; @@ -1768,7 +1780,7 @@ void shuffleteams(int ftr = FTR_AUTOTEAM) { int numplayers = numclients(); int team, sums = calcscores(); - if(gamemillis < 2 * 60 *1000) + if(sg->gamemillis < 2 * 60 *1000) { // random int teamsize[2] = {0, 0}; loopv(clients) if(clients[i]->type!=ST_EMPTY && clients[i]->isonrightmap && !team_isspect(clients[i]->team)) // only shuffle active players @@ -1808,7 +1820,7 @@ void shuffleteams(int ftr = FTR_AUTOTEAM) bool balanceteams(int ftr) // pro vs noobs never more { - if(mastermode != MM_OPEN || totalclients < 3 ) return true; + if(sg->mastermode != MM_OPEN || totalclients < 3 ) return true; int tsize[2] = {0, 0}, tscore[2] = {0, 0}; int totalscore = 0, nplayers = 0; int flagmult = (m_ctf ? 50 : (m_htf ? 25 : 12)); @@ -1819,7 +1831,7 @@ bool balanceteams(int ftr) // pro vs noobs never more if(c->isauthed && team_isactive(c->team)) { int time = servmillis - c->connectmillis + 5000; - if ( time > gamemillis ) time = gamemillis + 5000; + if(time > sg->gamemillis ) time = sg->gamemillis + 5000; tsize[c->team]++; // effective score per minute, thanks to wtfthisgame for the nice idea // in a normal game, normal players will do 500 points in 10 minutes @@ -1863,7 +1875,7 @@ bool balanceteams(int ftr) // pro vs noobs never more if ( hid >= 0 ) { updateclientteam(hid, l, ftr); - clients[hid]->at3_lastforce = gamemillis; + clients[hid]->at3_lastforce = sg->gamemillis; clients[hid]->state.forced = true; return true; } @@ -1893,7 +1905,7 @@ bool balanceteams(int ftr) // pro vs noobs never more { updateclientteam(bestpair[h], l, ftr); updateclientteam(bestpair[l], h, ftr); - clients[bestpair[h]]->at3_lastforce = clients[bestpair[l]]->at3_lastforce = gamemillis; + clients[bestpair[h]]->at3_lastforce = clients[bestpair[l]]->at3_lastforce = sg->gamemillis; clients[bestpair[h]]->state.forced = clients[bestpair[l]]->state.forced = true; return true; } @@ -1905,7 +1917,7 @@ int lastbalance = 0, waitbalance = 2 * 60 * 1000; bool refillteams(bool now, int ftr) // force only minimal amounts of players { - if(mastermode == MM_MATCH) return false; + if(sg->mastermode == MM_MATCH) return false; static int lasttime_eventeams = 0; int teamsize[2] = {0, 0}, teamscore[2] = {0, 0}, moveable[2] = {0, 0}; bool switched = false; @@ -1933,10 +1945,10 @@ bool refillteams(bool now, int ftr) // force only minimal amounts of players int allplayers = teamsize[0] + teamsize[1]; int diffnum = teamsize[bigteam] - teamsize[!bigteam]; int diffscore = teamscore[bigteam] - teamscore[!bigteam]; - if(lasttime_eventeams > gamemillis) lasttime_eventeams = 0; + if(lasttime_eventeams > sg->gamemillis) lasttime_eventeams = 0; if(diffnum > 1) { - if(now || gamemillis - lasttime_eventeams > 8000 + allplayers * 1000 || diffnum > 2 + allplayers / 10) + if(now || sg->gamemillis - lasttime_eventeams > 8000 + allplayers * 1000 || diffnum > 2 + allplayers / 10) { // time to even out teams loopv(clients) if(clients[i]->type!=ST_EMPTY && clients[i]->team != bigteam) clients[i]->at3_dontmove = true; // dont move small team players @@ -1950,7 +1962,7 @@ bool refillteams(bool now, int ftr) // force only minimal amounts of players { int fit = targetscore - clients[i]->at3_score; if(fit < 0 ) fit = -(fit * 15) / 10; // avoid too good players - int forcedelay = clients[i]->at3_lastforce ? (1000 - (gamemillis - clients[i]->at3_lastforce) / (5 * 60)) : 0; + int forcedelay = clients[i]->at3_lastforce ? (1000 - (sg->gamemillis - clients[i]->at3_lastforce) / (5 * 60)) : 0; if(forcedelay > 0) fit += (fit * forcedelay) / 600; // avoid lately forced players if(fit < bestfit + fit * rnd(100) / 400) // search 'almost' best fit { @@ -1966,7 +1978,7 @@ bool refillteams(bool now, int ftr) // force only minimal amounts of players { diffnum -= 2; diffscore -= 2 * clients[pick]->at3_score; - clients[pick]->at3_lastforce = gamemillis; // try not to force this player again for the next 5 minutes + clients[pick]->at3_lastforce = sg->gamemillis; // try not to force this player again for the next 5 minutes switched = true; } } @@ -1974,22 +1986,22 @@ bool refillteams(bool now, int ftr) // force only minimal amounts of players } if(diffnum < 2) { - if ( ( gamemillis - lastbalance ) > waitbalance && ( gamelimit - gamemillis ) > 4*60*1000 ) + if ( ( sg->gamemillis - lastbalance ) > waitbalance && ( sg->gamelimit - sg->gamemillis ) > 4*60*1000 ) { if ( balanceteams (ftr) ) { - waitbalance = 2 * 60 * 1000 + gamemillis / 3; + waitbalance = 2 * 60 * 1000 + sg->gamemillis / 3; switched = true; } else waitbalance = 20 * 1000; - lastbalance = gamemillis; + lastbalance = sg->gamemillis; } - else if ( lastbalance > gamemillis ) + else if ( lastbalance > sg->gamemillis ) { lastbalance = 0; waitbalance = 2 * 60 * 1000; } - lasttime_eventeams = gamemillis; + lasttime_eventeams = sg->gamemillis; } return switched; } @@ -1999,27 +2011,29 @@ void resetserver(const char *newname, int newmode, int newtime) if(m_demo) enddemoplayback(); else enddemorecord(); - smode = newmode; - copystring(smapname, newname); + sg->free(); // for now: clean out old servergame states + sg->curmap = NULL; - minremain = newtime > 0 ? newtime : defaultgamelimit(newmode); - gamemillis = 0; - gamelimit = minremain*60000; - arenaround = arenaroundstartmillis = 0; - memset(&smapstats, 0, sizeof(smapstats)); + sg->smode = newmode; + copystring(sg->smapname, newname); - interm = nextsendscore = 0; - lastfillup = servmillis; - sents.shrink(0); - if(mastermode == MM_PRIVATE) + sg->minremain = newtime > 0 ? newtime : defaultgamelimit(newmode); + sg->gamemillis = 0; + sg->gamelimit = sg->minremain * 60000; + sg->arenaround = sg->arenaroundstartmillis = 0; + + sg->interm = sg->nextsendscore = 0; + sg->lastfillup = servmillis; + sg->sents.shrink(0); + if(sg->mastermode == MM_PRIVATE) { loopv(savedscores) savedscores[i].valid = false; } else savedscores.shrink(0); ctfreset(); - nextmapname[0] = '\0'; - forceintermission = false; + sg->nextmapname[0] = '\0'; + sg->forceintermission = false; } void startdemoplayback(const char *newname) @@ -2041,68 +2055,76 @@ void startgame(const char *newname, int newmode, int newtime, bool notify) bool lastteammode = m_teammode; resetserver(newname, newmode, newtime); // beware: may clear *newname - if(custom_servdesc && findcnbyaddress(&servdesc_caller) < 0) + if(sg->custom_servdesc && findcnbyaddress(&sg->servdesc_caller) < 0) { updatesdesc(NULL); if(notify) { sendservmsg("server description reset to default"); - logline(ACLOG_INFO, "server description reset to '%s'", servdesc_current); + logline(ACLOG_INFO, "server description reset to '%s'", sg->servdesc_current); } } - int maploc = MAP_VOID; - mapstats *ms = getservermapstats(smapname, isdedicated, &maploc); - mapbuffer.clear(); - if(isdedicated && distributablemap(maploc)) mapbuffer.load(); - if(ms) + servermap *sm = NULL; + if(isdedicated) + { // dedicated server: only use pre-loaded maps + sm = getservermap(sg->smapname); + } +#ifndef STANDALONE + else + { // local server: load map temporarily + static servermap *localmap = NULL; + DELETEP(localmap); + const char *lpath = checklocalmap(sg->smapname); + if(lpath) localmap = new servermap(sg->smapname, lpath); + if(localmap) + { + localmap->load(); + if(localmap->isok) sm = localmap; + else conoutf("\f3local server failed to load map \"%s%s\", error: %s", lpath, sg->smapname, localmap->err); + } + } +#endif + sg->curmap = sm; + if(sm) { - smapstats = *ms; + sg->layout = sm->getlayout(); // get floorplan + loopi(2) { - sflaginfo &f = sflaginfos[i]; - if(smapstats.flags[i] == 1) // don't check flag positions, if there is more than one flag per team + sflaginfo &f = sg->sflaginfos[i]; + if(sg->curmap->entstats.hasflags) // don't check flag positions, if there is more than one flag per team { - short *fe = smapstats.entposs + smapstats.flagents[i] * 3; - f.x = *fe; - fe++; - f.y = *fe; + f.x = sg->curmap->entpos_x[sg->curmap->entstats.flagents[i]]; + f.y = sg->curmap->entpos_y[sg->curmap->entstats.flagents[i]]; } else f.x = f.y = -1; - } - if (smapstats.flags[0] == 1 && smapstats.flags[1] == 1) - { - sflaginfo &f0 = sflaginfos[0], &f1 = sflaginfos[1]; - FlagFlag = pow2(f0.x - f1.x) + pow2(f0.y - f1.y); - coverdist = FlagFlag > 6 * COVERDIST ? COVERDIST : FlagFlag / 6; + f.actor_cn = -1; } entity e; - loopi(smapstats.hdr.numents) + loopi(sg->curmap->numents) { - e.type = smapstats.enttypes[i]; - e.transformtype(smode); - server_entity se = { e.type, false, false, false, 0, smapstats.entposs[i * 3], smapstats.entposs[i * 3 + 1]}; - sents.add(se); - if(e.fitsmode(smode)) sents[i].spawned = sents[i].legalpickup = true; + e.type = sg->curmap->enttypes[i]; + e.transformtype(sg->smode); + server_entity se = { e.type, false, false, false, 0, sg->curmap->entpos_x[i], sg->curmap->entpos_y[i] }; + sg->sents.add(se); + if(e.fitsmode(sg->smode)) sg->sents[i].spawned = sg->sents[i].legalpickup = true; } - mapbuffer.setrevision(); - logline(ACLOG_INFO, "Map height density information for %s: H = %.2f V = %d, A = %d and MA = %d", smapname, Mheight, Mvolume, Marea, Mopen); - items_blocked = false; } - else if(isdedicated) sendservmsg("\f3server error: map not found - please start another map or send this map to the server"); + else if(isdedicated && sg->smode != GMODE_COOPEDIT) fatal("servermap not found"); // FIXME: coop should also start with a map if(notify) { // change map - sendf(-1, 1, "risiii", SV_MAPCHANGE, smapname, smode, mapbuffer.available(), mapbuffer.revision); - if(smode>1 || (smode==0 && numnonlocalclients()>0)) sendf(-1, 1, "ri3", SV_TIMEUP, gamemillis, gamelimit); + sendf(-1, 1, "risiii", SV_MAPCHANGE, sg->smapname, sg->smode, sm && sm->isdistributable() ? sm->cgzlen : 0, sm && sm->isdistributable() ? sm->maprevision : 0); + if(sg->smode > 1 || (sg->smode == 0 && numnonlocalclients() > 0)) sendf(-1, 1, "ri3", SV_TIMEUP, sg->gamemillis, sg->gamelimit); } packetbuf q(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); send_item_list(q); // always send the item list when a game starts sendpacket(-1, 1, q.finalize()); - defformatstring(gsmsg)("Game start: %s on %s, %d players, %d minutes, mastermode %d, ", modestr(smode), smapname, numclients(), minremain, mastermode); - if(mastermode == MM_MATCH) concatformatstring(gsmsg, "teamsize %d, ", matchteamsize); - if(ms) concatformatstring(gsmsg, "(map rev %d/%d, %s, 'getmap' %sprepared)", smapstats.hdr.maprevision, smapstats.cgzsize, maplocstr[maploc], mapbuffer.available() ? "" : "not "); - else concatformatstring(gsmsg, "error: failed to preload map (map: %s)", maplocstr[maploc]); + defformatstring(gsmsg)("Game start: %s on %s, %d players, %d minutes, mastermode %d, ", modestr(sg->smode), sg->smapname, numclients(), sg->minremain, sg->mastermode); + if(sg->mastermode == MM_MATCH) concatformatstring(gsmsg, "teamsize %d, ", sg->matchteamsize); + if(sm) concatformatstring(gsmsg, "(map rev %d/%d, %s, 'getmap' %sprepared)", sm->maprevision, sm->cgzlen, sm->getpathdesc(), sm->isdistributable() ? "" : "not "); + else concatformatstring(gsmsg, "error: failed to preload map"); logline(ACLOG_INFO, "\n%s", gsmsg); if(m_arena) distributespawns(); if(notify) @@ -2112,7 +2134,7 @@ void startgame(const char *newname, int newmode, int newtime, bool notify) { if(!lastteammode) shuffleteams(FTR_INFO); - else if(autoteam) + else if(sg->autoteam) refillteams(true, FTR_INFO); } // prepare spawns; players will spawn, once they've loaded the correct map @@ -2430,7 +2452,7 @@ void senddisconnectedscores(int cn) { packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); putint(p, SV_DISCSCORES); - if(mastermode == MM_MATCH) + if(sg->mastermode == MM_MATCH) { loopv(savedscores) { @@ -2481,7 +2503,7 @@ void disconnect_client(int n, int reason) clients[n]->zap(); sendf(-1, 1, "rii", SV_CDIS, n); if(curvote) curvote->evaluate(); - if(*scoresaved && mastermode == MM_MATCH) senddisconnectedscores(-1); + if(*scoresaved && sg->mastermode == MM_MATCH) senddisconnectedscores(-1); } // for AUTH: WIP @@ -2636,26 +2658,26 @@ void welcomeinitclient(packetbuf &p, int exclude = -1) void welcomepacket(packetbuf &p, int n) { - if(!smapname[0]) maprot.next(false); + if(!sg->smapname[0]) maprot.next(false); client *c = valid_client(n) ? clients[n] : NULL; int numcl = numclients(); putint(p, SV_WELCOME); - putint(p, smapname[0] && !m_demo ? numcl : -1); - if(smapname[0] && !m_demo) + putint(p, sg->smapname[0] && !m_demo ? numcl : -1); + if(sg->smapname[0] && !m_demo) { putint(p, SV_MAPCHANGE); - sendstring(smapname, p); - putint(p, smode); - putint(p, mapbuffer.available()); - putint(p, mapbuffer.revision); - if(smode>1 || (smode==0 && numnonlocalclients()>0)) + sendstring(sg->smapname, p); + putint(p, sg->smode); + putint(p, sg->curmap && sg->curmap->isdistributable() ? sg->curmap->cgzlen : 0); + putint(p, sg->curmap && sg->curmap->isdistributable() ? sg->curmap->maprevision : 0); + if(sg->smode>1 || (sg->smode==0 && numnonlocalclients()>0)) { putint(p, SV_TIMEUP); - putint(p, (gamemillis>=gamelimit || forceintermission) ? gamelimit : gamemillis); - putint(p, gamelimit); - //putint(p, minremain*60); + putint(p, (sg->gamemillis>=sg->gamelimit || sg->forceintermission) ? sg->gamelimit : sg->gamemillis); + putint(p, sg->gamelimit); + //putint(p, sg->minremain*60); } send_item_list(p); // this includes the flags } @@ -2663,7 +2685,7 @@ void welcomepacket(packetbuf &p, int n) if(c) { if(c->type == ST_TCPIP && serveroperator() != -1) sendserveropinfo(n); - c->team = mastermode == MM_MATCH && sc ? team_tospec(sc->team) : TEAM_SPECT; + c->team = sg->mastermode == MM_MATCH && sc ? team_tospec(sc->team) : TEAM_SPECT; putint(p, SV_SETTEAM); putint(p, n); putint(p, c->team | (FTR_INFO << 4)); @@ -2743,7 +2765,7 @@ int checktype(int type, client *cl) if(cl) { loopi(sizeof(servtypes)/sizeof(int)) if(type == servtypes[i]) return -1; - loopi(sizeof(edittypes)/sizeof(int)) if(type == edittypes[i]) return smode==GMODE_COOPEDIT ? type : -1; + loopi(sizeof(edittypes)/sizeof(int)) if(type == edittypes[i]) return sg->smode==GMODE_COOPEDIT ? type : -1; if(++cl->overflow >= 200) return -2; } return type; @@ -2785,8 +2807,8 @@ void process(ENetPacket *packet, int sender, int chan) int bantype = getbantype(sender); bool banned = bantype > BAN_NONE; bool srvfull = numnonlocalclients() > scl.maxclients; - bool srvprivate = mastermode == MM_PRIVATE || mastermode == MM_MATCH; - bool matchreconnect = mastermode == MM_MATCH && findscore(*cl, false); + bool srvprivate = sg->mastermode == MM_PRIVATE || sg->mastermode == MM_MATCH; + bool matchreconnect = sg->mastermode == MM_MATCH && findscore(*cl, false); int bl = 0, wl = nickblacklist.checkwhitelist(*cl); if(wl == NWL_PASS) concatstring(tags, ", nickname whitelist match"); if(wl == NWL_UNLISTED) bl = nickblacklist.checkblacklist(cl->name); @@ -2901,8 +2923,7 @@ void process(ENetPacket *packet, int sender, int chan) if(type!=SV_POS && type!=SV_POSC && type!=SV_CLIENTPING && type!=SV_PING && type!=SV_CLIENT) { DEBUGVAR(cl->name); - ASSERT(type>=0 && type= 0) { DEBUGVAR(messagenames[type]); } protocoldebug(DEBUGCOND); } else protocoldebug(false); @@ -2954,7 +2975,7 @@ void process(ENetPacket *packet, int sender, int chan) bool canspeech = forbiddenlist.canspeech(text); if(!spamdetect(cl, text) && canspeech) { - if(mastermode != MM_MATCH || !matchteamsize || team_isactive(cl->team) || (cl->team == TEAM_SPECT && cl->role == CR_ADMIN)) // common chat + if(sg->mastermode != MM_MATCH || !sg->matchteamsize || team_isactive(cl->team) || (cl->team == TEAM_SPECT && cl->role == CR_ADMIN)) // common chat { logline(ACLOG_INFO, "[%s] %s%s says: '%s'", cl->hostname, type == SV_TEXTME ? "(me) " : "", cl->name, text); if(cl->type==ST_TCPIP) while(mid1messages.add(p.buf[mid1++]); @@ -3000,7 +3021,7 @@ void process(ENetPacket *packet, int sender, int chan) bool canspeech = forbiddenlist.canspeech(text); if(!spamdetect(cl, text) && canspeech) { - bool allowed = !(mastermode == MM_MATCH && cl->team != target->team) && cl->role >= roleconf('t'); + bool allowed = !(sg->mastermode == MM_MATCH && cl->team != target->team) && cl->role >= roleconf('t'); logline(ACLOG_INFO, "[%s] %s says to %s: '%s' (%s)", cl->hostname, cl->name, target->name, text, allowed ? "allowed":"disallowed"); if(allowed) sendf(target->clientnum, 1, "riis", SV_TEXTPRIVATE, cl->clientnum, text); } @@ -3046,7 +3067,7 @@ void process(ENetPacket *packet, int sender, int chan) { int gzs = getint(p); int rev = getint(p); - if(!isdedicated || (smapstats.cgzsize == gzs && smapstats.hdr.maprevision == rev)) + if(!isdedicated || (sg->curmap && sg->curmap->cgzlen == gzs && sg->curmap->maprevision == rev)) { // here any game really starts for a client: spawn, if it's a new game - don't spawn if the game was already running cl->isonrightmap = true; int sp = canspawn(cl); @@ -3056,7 +3077,7 @@ void process(ENetPacket *packet, int sender, int chan) bool spawn = false; if(team_isspect(cl->team)) { - if(numclients() < 2 && !m_demo && mastermode != MM_MATCH) // spawn on empty servers + if(numclients() < 2 && !m_demo && sg->mastermode != MM_MATCH) // spawn on empty servers { spawn = updateclientteam(cl->clientnum, TEAM_ANYACTIVE, FTR_INFO); } @@ -3083,7 +3104,7 @@ void process(ENetPacket *packet, int sender, int chan) case SV_ITEMPICKUP: { int n = getint(p); - if(!arenaround || arenaround - gamemillis > 2000) + if(!sg->arenaround || sg->arenaround - sg->gamemillis > 2000) { gameevent &pickup = cl->addevent(); pickup.type = GE_PICKUP; @@ -3186,7 +3207,7 @@ void process(ENetPacket *packet, int sender, int chan) updateclientteam(sender, TEAM_ANYACTIVE, FTR_PLAYERWISH); sp = canspawn(cl); } - if( !m_arena && sp < SP_OK_NUM && gamemillis > cl->state.lastspawn + 1000 ) sendspawn(cl); + if( !m_arena && sp < SP_OK_NUM && sg->gamemillis > cl->state.lastspawn + 1000 ) sendspawn(cl); break; } @@ -3196,7 +3217,7 @@ void process(ENetPacket *packet, int sender, int chan) if((cl->state.state!=CS_ALIVE && cl->state.state!=CS_DEAD && cl->state.state!=CS_SPECTATE) || ls!=cl->state.lifesequence || cl->state.lastspawn<0 || !valid_weapon(gunselect) || gunselect == GUN_CPISTOL) break; cl->state.lastspawn = -1; - cl->state.spawn = gamemillis; + cl->state.spawn = sg->gamemillis; cl->autospawn = false; cl->upspawnp = false; cl->state.state = CS_ALIVE; @@ -3228,11 +3249,11 @@ void process(ENetPacket *packet, int sender, int chan) #define seteventmillis(event) \ { \ event.id = getint(p); \ - if(!cl->timesync || (cl->events.length()==1 && cl->state.waitexpired(gamemillis))) \ + if(!cl->timesync || (cl->events.length()==1 && cl->state.waitexpired(sg->gamemillis))) \ { \ cl->timesync = true; \ - cl->gameoffset = gamemillis - event.id; \ - event.millis = gamemillis; \ + cl->gameoffset = sg->gamemillis - event.id; \ + event.millis = sg->gamemillis; \ } \ else event.millis = cl->gameoffset + event.id; \ } @@ -3393,8 +3414,9 @@ void process(ENetPacket *packet, int sender, int chan) int g2 = q.getbits(1); // shooting cl->g = (g1<<4) | (g2<<5); + if(!sg->curmap) break; // no compact POS packets without loaded servermap if(!cl->isonrightmap || p.remaining() || p.overread()) { p.flags = 0; break; } - if(((cl->f >> 6) & 1) != (cl->state.lifesequence & 1) || usefactor != (smapstats.hdr.sfactor < 7 ? 7 : smapstats.hdr.sfactor)) break; + if(((cl->f >> 6) & 1) != (cl->state.lifesequence & 1) || usefactor != (sg->curmap->sfactor < 7 ? 7 : sg->curmap->sfactor)) break; cl->state.o[0] = xt / DMF; cl->state.o[1] = yt / DMF; cl->state.o[2] = zt / DMF; @@ -3421,42 +3443,56 @@ void process(ENetPacket *packet, int sender, int chan) p.forceoverread(); break; } - int mp = findmappath(sentmap); - if(readonlymap(mp)) + servermap *sm = getservermap(sentmap); + header *h = peekmapheader(&p.buf[p.len], mapsize); + int actualrevision = h ? h->maprevision : 0; + if(sm && sm->isro()) { reject = "map is ro"; defformatstring(msg)("\f3map upload rejected: map %s is readonly", sentmap); sendservmsg(msg, sender); } - else if( scl.incoming_limit && ( scl.incoming_limit << 20 ) < incoming_size + mapsize + cfgsizegz ) + else if(scl.incoming_limit && (scl.incoming_limit << 20) - incoming_size < mapsize + cfgsizegz) { reject = "server incoming reached its limits"; sendservmsg("\f3server does not support more incomings: limit reached", sender); } - else if(mp == MAP_NOTFOUND && strchr(scl.mapperm, 'C') && cl->role < CR_ADMIN) + else if(!sm && strchr(scl.mapperm, 'C') && cl->role < CR_ADMIN) { reject = "no permission for initial upload"; sendservmsg("\f3initial map upload rejected: you need to be admin", sender); } - else if(mp == MAP_TEMP && revision >= mapbuffer.revision && !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) // default: only admins can update maps { reject = "no permission to update"; sendservmsg("\f3map update rejected: you need to be admin", sender); } - else if(mp == MAP_TEMP && revision < mapbuffer.revision && !strchr(scl.mapperm, 'r') && cl->role < CR_ADMIN) // default: only admins can revert maps to older revisions + else if(revision != actualrevision) + { + 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 { reject = "no permission to revert revision"; sendservmsg("\f3map revert to older revision rejected: you need to be admin to upload an older map", sender); } else - { - if(mapbuffer.sendmap(sentmap, mapsize, cfgsize, cfgsizegz, &p.buf[p.len])) + { // map accepted + if(serverwritemap(sentmap, mapsize, cfgsize, cfgsizegz, &p.buf[p.len])) { - incoming_size += mapsize + cfgsizegz; + if(sg->smode == GMODE_COOPEDIT && !strcmp(sentmap, behindpath(sg->smapname))) + { // update buffer only in coopedit mode (and on same map) + sg->coop_cgzlen = mapsize; + sg->coop_cfglen = cfgsize; + sg->coop_cfglengz = cfgsizegz; + DELETEA(sg->coop_mapdata); + sg->coop_mapdata = new uchar[mapsize + cfgsizegz]; + memcpy(sg->coop_mapdata, &p.buf[p.len], mapsize + cfgsizegz); + } + incoming_size += mapsize + cfgsize; logline(ACLOG_INFO,"[%s] %s sent map %s, rev %d, %d + %d(%d) bytes written", clients[sender]->hostname, clients[sender]->name, sentmap, revision, mapsize, cfgsize, cfgsizegz); - defformatstring(msg)("%s (%d) up%sed map %s, rev %d%s", clients[sender]->name, sender, mp == MAP_NOTFOUND ? "load": "dat", sentmap, revision, - /*strcmp(sentmap, behindpath(smapname)) || smode == GMODE_COOPEDIT ? "" :*/ "\f3 (restart game to use new map version)"); + defformatstring(msg)("%s (%d) up%sed map %s, rev %d", clients[sender]->name, sender, !sm ? "load": "dat", sentmap, revision); sendservmsg(msg); } else @@ -3476,16 +3512,22 @@ void process(ENetPacket *packet, int sender, int chan) case SV_RECVMAP: { - if(mapbuffer.available()) + if(sg->smode == GMODE_COOPEDIT && sg->coop_mapdata) // "getmap" only in coopedit { resetflag(cl->clientnum); // drop ctf flag savedscore *sc = findscore(*cl, true); // save score if(sc) sc->save(cl->state, cl->team); - mapbuffer.sendmap(cl, 2); + packetbuf p(MAXTRANS + sg->coop_cgzlen + sg->coop_cfglengz, ENET_PACKET_FLAG_RELIABLE); + putint(p, SV_RECVMAP); + sendstring(sg->smapname, p); + putint(p, sg->coop_cgzlen); + putint(p, sg->coop_cfglen); + putint(p, sg->coop_cfglengz); + p.put(sg->coop_mapdata, sg->coop_cgzlen + sg->coop_cfglengz); + sendpacket(cl->clientnum, 2, p.finalize()); cl->mapchange(true); sendwelcome(cl, 2); // resend state properly } - else sendservmsg("no map to get", cl->clientnum); break; } @@ -3495,11 +3537,11 @@ void process(ENetPacket *packet, int sender, int chan) filtertext(text, text, FTXT__MAPNAME); string filename; const char *rmmap = behindpath(text), *reject = NULL; - int mp = findmappath(rmmap); + servermap *sm = getservermap(rmmap); int reqrole = strchr(scl.mapperm, 'D') ? CR_ADMIN : (strchr(scl.mapperm, 'd') ? CR_DEFAULT : CR_ADMIN + 100); if(cl->role < reqrole) reject = "no permission"; - else if(readonlymap(mp)) reject = "map is readonly"; - else if(mp == MAP_NOTFOUND) reject = "map not found"; + else if(sm && sm->isro()) reject = "map is readonly"; + else if(!sm) reject = "map not found"; else { formatstring(filename)(SERVERMAP_PATH_INCOMING "%s.cgz", rmmap); @@ -3705,10 +3747,10 @@ void process(ENetPacket *packet, int sender, int chan) // intermediate solution to set the teamsize (will be voteable) getstring(text, p, n); - if(valid_client(sender) && clients[sender]->role==CR_ADMIN && mastermode == MM_MATCH) + if(valid_client(sender) && clients[sender]->role==CR_ADMIN && sg->mastermode == MM_MATCH) { changematchteamsize(atoi(text)); - defformatstring(msg)("match team size set to %d", matchteamsize); + defformatstring(msg)("match team size set to %d", sg->matchteamsize); sendservmsg(msg, -1); } } @@ -3776,23 +3818,23 @@ client &addclient() void checkintermission() { - if(minremain>0) + if(sg->minremain>0) { - minremain = (gamemillis>=gamelimit || forceintermission) ? 0 : (gamelimit - gamemillis + 60000 - 1)/60000; - sendf(-1, 1, "ri3", SV_TIMEUP, (gamemillis>=gamelimit || forceintermission) ? gamelimit : gamemillis, gamelimit); + sg->minremain = (sg->gamemillis >= sg->gamelimit || sg->forceintermission) ? 0 : (sg->gamelimit - sg->gamemillis + 60000 - 1) / 60000; + sendf(-1, 1, "ri3", SV_TIMEUP, (sg->gamemillis >= sg->gamelimit || sg->forceintermission) ? sg->gamelimit : sg->gamemillis, sg->gamelimit); } - if(!interm && minremain<=0) interm = gamemillis+10000; - forceintermission = false; + if(!sg->interm && sg->minremain <= 0) sg->interm = sg->gamemillis + 10000; + sg->forceintermission = false; } void resetserverifempty() { loopv(clients) if(clients[i]->type!=ST_EMPTY) return; resetserver("", 0, 10); - matchteamsize = 0; - autoteam = true; + sg->matchteamsize = 0; + sg->autoteam = true; changemastermode(MM_OPEN); - nextmapname[0] = '\0'; + sg->nextmapname[0] = '\0'; } void sendworldstate() @@ -3820,10 +3862,10 @@ void loggamestatus(const char *reason) { int fragscore[2] = {0, 0}, flagscore[2] = {0, 0}, pnum[2] = {0, 0}; string text; - formatstring(text)("%d minutes remaining", minremain); + formatstring(text)("%d minutes remaining", sg->minremain); logline(ACLOG_INFO, ""); logline(ACLOG_INFO, "Game status: %s on %s, %s, %s, %d clients%c %s", - modestr(gamemode), smapname, reason ? reason : text, mmfullname(mastermode), totalclients, custom_servdesc ? ',' : '\0', servdesc_current); + modestr(gamemode), sg->smapname, reason ? reason : text, mmfullname(sg->mastermode), totalclients, sg->custom_servdesc ? ',' : '\0', sg->servdesc_current); if(!scl.loggamestatus) return; logline(ACLOG_INFO, "cn name %s%s score frag death %sping role host", m_teammode ? "team " : "", m_flags ? "flag " : "", m_teammode ? "tk " : ""); loopv(clients) @@ -3845,7 +3887,7 @@ void loggamestatus(const char *reason) pnum[t] += 1; } } - if(mastermode == MM_MATCH) + if(sg->mastermode == MM_MATCH) { loopv(savedscores) { @@ -3945,7 +3987,7 @@ void serverslice(uint timeout) // main server update, called from cube main lo int nextmillis = isdedicated ? (int)enet_time_get() : lastmillis; #endif int diff = nextmillis - servmillis; - gamemillis += diff; + sg->gamemillis += diff; servmillis = nextmillis; servertime = ((diff + 3 * servertime)>>2); if (servertime > 40) serverlagged = servmillis; @@ -3955,24 +3997,24 @@ void serverslice(uint timeout) // main server update, called from cube main lo { readdemo(); extern void silenttimeupdate(int milliscur, int millismax); - silenttimeupdate(gamemillis, gametimemaximum); + silenttimeupdate(sg->gamemillis, gametimemaximum); } #endif - if(minremain>0) + if(sg->minremain>0) { processevents(); checkitemspawns(diff); bool ktfflagingame = false; if(m_flags) loopi(2) { - sflaginfo &f = sflaginfos[i]; - if(f.state == CTFF_DROPPED && gamemillis-f.lastupdate > (m_ctf ? 30000 : 10000)) flagaction(i, FA_RESET, -1); - if(m_htf && f.state == CTFF_INBASE && gamemillis-f.lastupdate > (smapstats.hasflags ? 10000 : 1000)) + sflaginfo &f = sg->sflaginfos[i]; + if(f.state == CTFF_DROPPED && sg->gamemillis-f.lastupdate > (m_ctf ? 30000 : 10000)) flagaction(i, FA_RESET, -1); + if(m_htf && f.state == CTFF_INBASE && sg->gamemillis-f.lastupdate > (sg->curmap && sg->curmap->entstats.hasflags ? 10000 : 1000)) { htf_forceflag(i); } - if(m_ktf && f.state == CTFF_STOLEN && gamemillis-f.lastupdate > 15000) + if(m_ktf && f.state == CTFF_STOLEN && sg->gamemillis-f.lastupdate > 15000) { flagaction(i, FA_SCORE, -1); } @@ -3983,7 +4025,7 @@ void serverslice(uint timeout) // main server update, called from cube main lo else if(m_autospawn) autospawncheck(); // if(m_lms) lmscheck(); sendextras(); - if ( scl.afk_limit && mastermode == MM_OPEN && next_afk_check < servmillis && gamemillis > 20 * 1000 ) check_afk(); + if ( scl.afk_limit && sg->mastermode == MM_OPEN && next_afk_check < servmillis && sg->gamemillis > 20 * 1000 ) check_afk(); } if(curvote) @@ -3994,20 +4036,20 @@ void serverslice(uint timeout) // main server update, called from cube main lo int nonlocalclients = numnonlocalclients(); - if(forceintermission || ((smode>1 || (gamemode==0 && nonlocalclients)) && gamemillis-diff>0 && gamemillis/60000!=(gamemillis-diff)/60000)) + if(sg->forceintermission || ((sg->smode>1 || (gamemode==0 && nonlocalclients)) && sg->gamemillis-diff>0 && sg->gamemillis/60000!=(sg->gamemillis-diff)/60000)) checkintermission(); if(m_demo && !demoplayback) maprot.restart(); - else if(interm && ( (scl.demo_interm && sending_demo) ? gamemillis>(interm<<1) : gamemillis>interm ) ) + else if(sg->interm && ( (scl.demo_interm && sending_demo) ? sg->gamemillis > (sg->interm<<1) : sg->gamemillis > sg->interm ) ) { sending_demo = false; loggamestatus("game finished"); if(demorecord) enddemorecord(); - interm = nextsendscore = 0; + sg->interm = sg->nextsendscore = 0; //start next game - if(nextmapname[0]) startgame(nextmapname, nextgamemode); + if(sg->nextmapname[0]) startgame(sg->nextmapname, sg->nextgamemode); else maprot.next(); - nextmapname[0] = '\0'; + sg->nextmapname[0] = '\0'; map_queued = false; } @@ -4017,9 +4059,9 @@ void serverslice(uint timeout) // main server update, called from cube main lo poll_serverthreads(); - serverms(smode, numclients(), minremain, smapname, servmillis, serverhost->address, &mnum, &msend, &mrec, &cnum, &csend, &crec, SERVER_PROTOCOL_VERSION); + serverms(sg->smode, numclients(), sg->minremain, sg->smapname, servmillis, serverhost->address, &mnum, &msend, &mrec, &cnum, &csend, &crec, SERVER_PROTOCOL_VERSION, sg->servdesc_current, sg->interm); - if(autoteam && m_teammode && !m_arena && !interm && servmillis - lastfillup > 5000 && refillteams()) lastfillup = servmillis; + if(sg->autoteam && m_teammode && !m_arena && !sg->interm && servmillis - sg->lastfillup > 5000 && refillteams()) sg->lastfillup = servmillis; static unsigned int lastThrottleEpoch = 0; if(serverhost->bandwidthThrottleEpoch != lastThrottleEpoch) @@ -4109,7 +4151,7 @@ void cleanupserver() int getpongflags(enet_uint32 ip) { - int flags = mastermode << PONGFLAG_MASTERMODE; + int flags = sg->mastermode << PONGFLAG_MASTERMODE; flags |= scl.serverpassword[0] ? 1 << PONGFLAG_PASSWORD : 0; loopv(bans) if(bans[i].address.host == ip) { flags |= 1 << PONGFLAG_BANNED; break; } flags |= ipblacklist.check(ip) ? 1 << PONGFLAG_BLACKLIST : 0; @@ -4183,7 +4225,7 @@ void extinfo_statsbuf(ucharbuf &p, int pid, int bpos, ENetSocket &pongsock, ENet if(clients[i]->type != ST_TCPIP) continue; if(pid>-1 && clients[i]->clientnum!=pid) continue; - bool ismatch = mastermode == MM_MATCH; + bool ismatch = sg->mastermode == MM_MATCH; putint(p,EXT_PLAYERSTATS_RESP_STATS); // send player stats following putint(p,clients[i]->clientnum); //add player id putint(p,clients[i]->ping); //Ping @@ -4216,7 +4258,7 @@ void extinfo_teamscorebuf(ucharbuf &p) { putint(p, m_teammode ? EXT_ERROR_NONE : EXT_ERROR); putint(p, gamemode); - putint(p, minremain); // possible TODO: use gamemillis, gamelimit here too? + putint(p, sg->minremain); if(!m_teammode) return; int teamsizes[TEAM_NUM] = { 0 }, fragscores[TEAM_NUM] = { 0 }, flagscores[TEAM_NUM] = { 0 }; @@ -4310,7 +4352,7 @@ void initserver(bool dedicated, int argc, char **argv) if ( strlen(scl.servdesc_full) ) global_name = scl.servdesc_full; else global_name = server_name; - smapname[0] = '\0'; + sg->smapname[0] = '\0'; string identity; if(scl.logident[0]) filtertext(identity, scl.logident, FTXT__LOGIDENT); @@ -4320,7 +4362,7 @@ void initserver(bool dedicated, int argc, char **argv) printf("WARNING: logging not started!\n"); logline(ACLOG_INFO, "logging local AssaultCube server (version %d, protocol %d/%d) now..", AC_VERSION, SERVER_PROTOCOL_VERSION, EXT_VERSION); - copystring(servdesc_current, scl.servdesc_full); + copystring(sg->servdesc_current, scl.servdesc_full); servermsinit(scl.master ? scl.master : AC_MASTER_URI, scl.ip, CUBE_SERVINFO_PORT(scl.serverport), dedicated); if((isdedicated = dedicated)) @@ -4332,7 +4374,7 @@ void initserver(bool dedicated, int argc, char **argv) loopi(scl.maxclients) serverhost->peers[i].data = (void *)-1; maprot.init(scl.maprot); - maprot.next(false, true); // ensure minimum maprot length of '1' +// maprot.next(false, true); // ensure minimum maprot length of '1' passwords.init(scl.pwdfile, scl.adminpasswd); ipblacklist.init(scl.blfile); nickblacklist.init(scl.nbfile); diff --git a/source/src/server.h b/source/src/server.h index eb599be5..25024b75 100644 --- a/source/src/server.h +++ b/source/src/server.h @@ -1,6 +1,6 @@ // server.h -#define gamemode smode // allows the gamemode macros to work with the server mode +#define gamemode sg->smode // allows the gamemode macros to work with the server mode #define SERVER_PROTOCOL_VERSION (PROTOCOL_VERSION) // server without any gameplay modification //#define SERVER_PROTOCOL_VERSION (-PROTOCOL_VERSION) // server with gameplay modification but compatible to vanilla client (using /modconnect) @@ -11,7 +11,7 @@ enum { GE_NONE = 0, GE_SHOT, GE_EXPLODE, GE_HIT, GE_AKIMBO, GE_RELOAD, GE_SUICIDE, GE_PICKUP }; enum { ST_EMPTY, ST_LOCAL, ST_TCPIP }; -extern int smode, servmillis; +extern int servmillis; struct shotevent { @@ -292,7 +292,7 @@ struct client // server side version of "dynent" type events.setsize(0); overflow = 0; timesync = false; - isonrightmap = m_coop; + isonrightmap = false; spawnperm = SP_WRONGMAP; spawnpermsent = servmillis; autospawn = false; @@ -376,6 +376,16 @@ struct server_entity // server side version of "entity" type short x, y; }; +struct sflaginfo +{ + int state; + int actor_cn; + float pos[3]; + int lastupdate; + int stolentime; + short x, y; // flag entity location +}; + struct clientidentity { uint ip; @@ -397,7 +407,6 @@ void sendiplist(int receiver, int cn = -1); int clienthasflag(int cn); bool refillteams(bool now = false, int ftr = FTR_AUTOTEAM); void changeclientrole(int client, int role, char *pwd = NULL, bool force=false); -mapstats *getservermapstats(const char *mapname, bool getlayout = false, int *maploc = NULL); int findmappath(const char *mapname, char *filename = NULL); int calcscores(); void recordpacket(int chan, void *data, int len); @@ -412,9 +421,6 @@ void forcedeath(client *cl); void sendf(int cn, int chan, const char *format, ...); extern bool isdedicated; -extern string smapname; -extern mapstats smapstats; -extern char *maplayout; const char *messagenames[SV_NUM] = { diff --git a/source/src/serveractions.h b/source/src/serveractions.h index 0f04a1a6..e9f6c5e6 100644 --- a/source/src/serveractions.h +++ b/source/src/serveractions.h @@ -42,14 +42,14 @@ struct mapaction : serveraction { if(queue) { - nextgamemode = mode; - copystring(nextmapname, map); + sg->nextgamemode = mode; + copystring(sg->nextmapname, map); } - else if(isdedicated && numclients() > 2 && smode >= 0 && smode != 1 && ( gamemillis > gamelimit/4 || scl.demo_interm )) + else if(isdedicated && numclients() > 2 && sg->smode >= 0 && sg->smode != 1 && ( sg->gamemillis > sg->gamelimit/4 || scl.demo_interm )) { - forceintermission = true; - nextgamemode = mode; - copystring(nextmapname, map); + sg->forceintermission = true; + sg->nextgamemode = mode; + copystring(sg->nextmapname, map); } else { @@ -63,32 +63,24 @@ struct mapaction : serveraction if(isdedicated) { bool notify = valid_client(caller); - int maploc = MAP_NOTFOUND; - mapstats *ms = map[0] ? getservermapstats(map, false, &maploc) : NULL; + servermap *sm = getservermap(map); bool validname = validmapname(map); - mapok = (ms != NULL) && validname && ( (mode != GMODE_COOPEDIT && mapisok(ms)) || (mode == GMODE_COOPEDIT && !readonlymap(maploc)) ); + mapok = sm && validname; if(!mapok) { if(notify) { - if(!validname) - sendservmsg("invalid map name", caller); - else - { - sendservmsg(ms ? - ( mode == GMODE_COOPEDIT ? "this map cannot be coopedited in this server" : "sorry, but this map does not satisfy some quality requisites to be played in MultiPlayer Mode" ) : - "the server does not have this map", - caller); - } + if(!validname) sendservmsg("invalid map name", caller); + else sendservmsg("the server does not have this map", caller); } } else { // check, if map supports mode if(mode == GMODE_COOPEDIT && !strchr(scl.voteperm, 'e')) role = CR_ADMIN; - bool romap = mode == GMODE_COOPEDIT && readonlymap(maploc); - int smode = mode; // 'borrow' the mode macros by replacing a global by a local var - bool spawns = mode == GMODE_COOPEDIT || (m_teammode && !m_ktf ? ms->hasteamspawns : ms->hasffaspawns); - bool flags = mode != GMODE_COOPEDIT && m_flags && !m_htf ? ms->hasflags : true; + 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 if(strchr(scl.voteperm, 'P')) role = CR_ADMIN; @@ -130,10 +122,9 @@ struct mapaction : serveraction } } else - { - int mp = findmappath(map); - mapok = mp == MAP_LOCAL || mp == MAP_OFFICIAL; // clients can only load from packages/maps and packages/maps/official + { // local server #ifndef STANDALONE + mapok = checklocalmap(map) != NULL; // clients can only load from packages/maps and packages/maps/official if(!mapok) conoutf("\f3map '%s' not found", map); #endif } @@ -264,7 +255,7 @@ struct autoteamaction : enableaction { void perform() { - autoteam = enable; + sg->autoteam = enable; sendservermode(); if(m_teammode && enable) refillteams(true); } diff --git a/source/src/serverchecks.h b/source/src/serverchecks.h index 9a13a0a8..d263f717 100644 --- a/source/src/serverchecks.h +++ b/source/src/serverchecks.h @@ -5,15 +5,15 @@ inline bool is_lagging(client *cl) inline bool outside_border(vec &po) { - return (po.x < 0 || po.y < 0 || po.x >= maplayoutssize || po.y >= maplayoutssize); + return sg->curmap && (po.x < sg->curmap->x1 || po.y < sg->curmap->y1 || po.x > sg->curmap->x2 || po.y > sg->curmap->y2 || po.z < sg->curmap->zmin || po.z > sg->curmap->zmax); } inline void checkclientpos(client *cl) { vec &po = cl->state.o; - if( outside_border(po) || maplayout[((int) po.x) + (((int) po.y) << maplayout_factor)] > po.z + 3) + if(outside_border(po) || (sg->curmap && sg->layout && sg->layout[((int) po.x) + (((int) po.y) << sg->curmap->sfactor)] > po.z + 3)) { - if(gamemillis > 10000 && (servmillis - cl->connectmillis) > 10000) cl->mapcollisions++; + if(sg->gamemillis > 10000 && (servmillis - cl->connectmillis) > 10000) cl->mapcollisions++; if(cl->mapcollisions && !(cl->mapcollisions % 25)) { logline(ACLOG_INFO, "[%s] %s is colliding with the map", cl->hostname, cl->name); @@ -31,75 +31,6 @@ extern inline void addban(client *cl, int reason, int type = BAN_AUTO); #define MINELINE 50 -//FIXME -/* There are smarter ways to implement this function, but most probably they will be very complex */ -int getmaxarea(int inversed_x, int inversed_y, int transposed, int ml_factor, char *ml) -{ - int ls = (1 << ml_factor); - int xi = 0, oxi = 0, xf = 0, oxf = 0, fx = 0, fy = 0; - int area = 0, maxarea = 0; - bool sav_x = false, sav_y = false; - - if (transposed) fx = ml_factor; - else fy = ml_factor; - - // walk on x for each y - for ( int y = (inversed_y ? ls-1 : 0); (inversed_y ? y >= 0 : y < ls); (inversed_y ? y-- : y++) ) { - - /* Analyzing each cube of the line */ - for ( int x = (inversed_x ? ls-1 : 0); (inversed_x ? x >= 0 : x < ls); (inversed_x ? x-- : x++) ) { - if ( ml[ ( x << fx ) + ( y << fy ) ] != 127 ) { // if it is not solid - if ( sav_x ) { // if the last cube was saved - xf = x; // new end for this line - } - else { - xi = x; // new begin of the line - sav_x = true; // accumulating cubes from now - } - } else { // solid - if ( xf - xi > MINELINE ) break; // if the empty line is greater than a minimum, get out - sav_x = false; // stop the accumulation of cubes - } - } - - /* Analyzing this line with the previous one */ - if ( xf - xi > MINELINE ) { // if the line has the minimun threshold of emptiness - if ( sav_y ) { // if the last line was saved - if ( 2*oxi + MINELINE < 2*xf && - 2*xi + MINELINE < 2*oxf ) { // if the last line intersect this one - area += xf - xi; - } else { - oxi = xi; // new area vertices - oxf = xf; - } - } - else { - oxi = xi; - oxf = xf; - sav_y = true; // accumulating lines from now - } - } else { - sav_y = false; // stop the accumulation of lines - if (area > maxarea) maxarea = area; // new max area - area=0; - } - - sav_x = false; // reset x - xi = xf = 0; - } - return maxarea; -} - -int checkarea(int maplayout_factor, char *maplayout) -{ - int area = 0, maxarea = 0; - for (int i=0; i < 8; i++) { - area = getmaxarea((i & 1),(i & 2),(i & 4), maplayout_factor, maplayout); - if ( area > maxarea ) maxarea = area; - } - return maxarea; -} - /** This part is related to medals system. WIP */ @@ -116,7 +47,7 @@ inline void addpt(client *c, int points, int n = -1) { c->state.points += points; c->md.dpt += points; c->md.updated = true; - c->md.upmillis = gamemillis + 240; // about 2 AR shots + c->md.upmillis = sg->gamemillis + 240; // about 2 AR shots print_medal_messages(c,n); } @@ -278,9 +209,9 @@ int checkteamrequests(int sender) loopv(clients) { client *prot = clients[i]; if ( i!=sender && prot->type != ST_EMPTY && prot->team == ant->team && - prot->state.state == CS_ALIVE && prot->md.askmillis > gamemillis && prot->md.ask >= 0 ) { + prot->state.state == CS_ALIVE && prot->md.askmillis > sg->gamemillis && prot->md.ask >= 0 ) { float dist = POW2XY(ant->state.o,prot->state.o); - if ( dist < REPLYDIST && (dtime=prot->md.askmillis-gamemillis) > besttime) { + if ( dist < REPLYDIST && (dtime=prot->md.askmillis - sg->gamemillis) > besttime) { bestid = i; besttime = dtime; } @@ -298,14 +229,14 @@ void checkteamplay(int s, int sender) if ( actor->state.state != CS_ALIVE ) return; switch(s){ case S_IMONDEFENSE: // informs - actor->md.linkmillis = gamemillis + 20000; + actor->md.linkmillis = sg->gamemillis + 20000; actor->md.linkreason = s; break; case S_COVERME: // demands case S_STAYTOGETHER: case S_STAYHERE: actor->md.ask = s; - actor->md.askmillis = gamemillis + 5000; + actor->md.askmillis = sg->gamemillis + 5000; break; case S_AFFIRMATIVE: // replies case S_ALLRIGHTSIR: @@ -315,8 +246,8 @@ void checkteamplay(int s, int sender) if ( id >= 0 ) { client *sgt = clients[id]; actor->md.linked = id; - if ( actor->md.linkmillis < gamemillis ) addpt(actor,REPLYPT); - actor->md.linkmillis = gamemillis + 30000; + if ( actor->md.linkmillis < sg->gamemillis ) addpt(actor,REPLYPT); + actor->md.linkmillis = sg->gamemillis + 30000; actor->md.linkreason = sgt->md.ask; sendf(actor->clientnum, 1, "ri2", SV_HUDEXTRAS, HE_NUM+id); switch( actor->md.linkreason ) { // check demands @@ -336,7 +267,7 @@ void computeteamwork(int team, int exclude) // testing loopv(clients) { client *actor = clients[i]; - if ( i == exclude || actor->type == ST_EMPTY || actor->team != team || actor->state.state != CS_ALIVE || actor->md.linkmillis < gamemillis ) continue; + if ( i == exclude || actor->type == ST_EMPTY || actor->team != team || actor->state.state != CS_ALIVE || actor->md.linkmillis < sg->gamemillis ) continue; vec position; bool teamworkdone = false; switch( actor->md.linkreason ) @@ -387,7 +318,7 @@ inline bool testcover(int msg, int factor, client *actor) bool validlink(client *actor, int cn) { - return actor->md.linked >= 0 && actor->md.linked == cn && gamemillis < actor->md.linkmillis && valid_client(actor->md.linked); + return actor->md.linked >= 0 && actor->md.linked == cn && sg->gamemillis < actor->md.linkmillis && valid_client(actor->md.linked); } /** WIP */ @@ -402,8 +333,8 @@ void checkcover(client *target, client *actor) int cnumber = totalclients < 13 ? totalclients : 12; if ( m_flags ) { - sflaginfo &f = sflaginfos[team]; - sflaginfo &of = sflaginfos[oteam]; + sflaginfo &f = sg->sflaginfos[team]; + sflaginfo &of = sg->sflaginfos[oteam]; if ( m_ctf ) { @@ -511,7 +442,7 @@ void check_afk() client &c = *clients[i]; if ( c.type != ST_TCPIP || c.connectmillis + 60 * 1000 > servmillis || c.inputmillis + scl.afk_limit > servmillis || clienthasflag(c.clientnum) != -1 ) continue; - if ( ( c.state.state == CS_DEAD && !m_arena && c.state.lastdeath + 45 * 1000 < gamemillis) || + if ( ( c.state.state == CS_DEAD && !m_arena && c.state.lastdeath + 45 * 1000 < sg->gamemillis) || ( c.state.state == CS_ALIVE && c.upspawnp ) /*|| ( c.state.state == CS_SPECTATE && totalclients >= scl.maxclients ) // only kick spectator if server is full - 2011oct16:flowtron: mmh, that seems reasonable enough .. still, kicking spectators for inactivity seems harsh! disabled ATM, kick them manually if you must. */ @@ -530,9 +461,9 @@ void check_afk() In normal games, the players go over 6 tks only in the worst cases */ void check_ffire(client *target, client *actor, int damage) { - if ( mastermode != MM_OPEN ) return; + if ( sg->mastermode != MM_OPEN ) return; actor->ffire += damage; - if ( actor->ffire > 300 && actor->ffire * 600 > gamemillis) { + if ( actor->ffire > 300 && actor->ffire * 600 > sg->gamemillis) { logline(ACLOG_INFO, "[%s] %s %s", actor->hostname, actor->name, "kicked for excessive friendly fire"); defformatstring(msg)("%s %s", actor->name, "kicked for excessive friendly fire"); sendservmsg(msg); @@ -573,8 +504,8 @@ If you know nothing about these detections, please, just ignore it. inline void checkmove(client *cl) { - cl->ldt = gamemillis - cl->lmillis; - cl->lmillis = gamemillis; + cl->ldt = sg->gamemillis - cl->lmillis; + cl->lmillis = sg->gamemillis; if ( cl->ldt < 40 ) cl->ldt = 40; cl->t += cl->ldt; cl->spj = (( 7 * cl->spj + cl->ldt ) >> 3); @@ -585,7 +516,6 @@ inline void checkmove(client *cl) cl->inputmillis = servmillis; } - if(maplayout) checkclientpos(cl); #ifdef ACAC m_engine(cl); diff --git a/source/src/serverevents.h b/source/src/serverevents.h index 248abbc6..19a06318 100644 --- a/source/src/serverevents.h +++ b/source/src/serverevents.h @@ -37,18 +37,18 @@ void processevent(client *c, shotevent &e) { clientstate &gs = c->state; int wait = e.millis - gs.lastshot; - if(!gs.isalive(gamemillis) || + if(!gs.isalive(sg->gamemillis) || !valid_weapon(e.gun) || waitgamemillis) return; if(e.gun!=GUN_KNIFE) gs.mag[e.gun]--; loopi(NUMGUNS) if(gs.gunwait[i]) gs.gunwait[i] = max(gs.gunwait[i] - (e.millis-gs.lastshot), 0); gs.lastshot = e.millis; gs.gunwait[e.gun] = attackdelay(e.gun); - if(e.gun==GUN_PISTOL && gs.akimbomillis>gamemillis) gs.gunwait[e.gun] /= 2; + if(e.gun==GUN_PISTOL && gs.akimbomillis>sg->gamemillis) gs.gunwait[e.gun] /= 2; sendf(-1, 1, "ri6x", SV_SHOTFX, c->clientnum, e.gun, // int(e.from[0]*DMF), int(e.from[1]*DMF), int(e.from[2]*DMF), int(e.to[0]*DMF), int(e.to[1]*DMF), int(e.to[2]*DMF), @@ -115,20 +115,20 @@ void processevent(client *c, suicideevent &e) void processevent(client *c, pickupevent &e) { clientstate &gs = c->state; - if(m_mp(gamemode) && !gs.isalive(gamemillis)) return; + if(m_mp(gamemode) && !gs.isalive(sg->gamemillis)) return; serverpickup(e.ent, c->clientnum); } void processevent(client *c, reloadevent &e) { clientstate &gs = c->state; - if(!gs.isalive(gamemillis) || + if(!gs.isalive(sg->gamemillis) || !valid_weapon(e.gun) || !reloadable_gun(e.gun) || gs.ammo[e.gun]<=0) return; - if(e.gun == GUN_AKIMBO && gs.akimbomillis < gamemillis) return; + if(e.gun == GUN_AKIMBO && gs.akimbomillis < sg->gamemillis) return; bool akimbo = e.gun==GUN_PISTOL && gs.akimbomillis>e.millis; int mag = (akimbo ? 2 : 1) * magsize(e.gun), numbullets = min(gs.ammo[e.gun], mag - gs.mag[e.gun]); if(numbullets<=0) return; @@ -150,7 +150,7 @@ void processevent(client *c, reloadevent &e) void processevent(client *c, akimboevent &e) { clientstate &gs = c->state; - if(!gs.isalive(gamemillis) || gs.akimbomillis) return; + if(!gs.isalive(sg->gamemillis) || gs.akimbomillis) return; gs.akimbomillis = e.millis+30000; } @@ -167,13 +167,13 @@ void processevents() { client *c = clients[i]; if(c->type==ST_EMPTY) continue; - if(c->state.akimbomillis && c->state.akimbomillis < gamemillis) { c->state.akimbomillis = 0; c->state.akimbo = false; } + if(c->state.akimbomillis && c->state.akimbomillis < sg->gamemillis) { c->state.akimbomillis = 0; c->state.akimbo = false; } while(c->events.length()) { gameevent &e = c->events[0]; if(e.typegamemillis) break; + if(e.shot.millis>sg->gamemillis) break; if(e.shot.millislastevent) { clearevent(c); continue; } c->lastevent = e.shot.millis; } diff --git a/source/src/serverfiles.h b/source/src/serverfiles.h index 294c25e1..db30fd69 100644 --- a/source/src/serverfiles.h +++ b/source/src/serverfiles.h @@ -5,10 +5,12 @@ #define SERVERMAXMAPFACTOR 10 // 10 is huge... if you're running low on RAM, consider 9 as maximum (don't go higher than 10, or players will name their ugly dog after you) #define SERVERMAP_PATH_BUILTIN "packages" PATHDIVS "maps" PATHDIVS "official" PATHDIVS +#define SERVERMAP_PATH_LOCAL "packages" PATHDIVS "maps" PATHDIVS #define SERVERMAP_PATH "packages" PATHDIVS "maps" PATHDIVS "servermaps" PATHDIVS #define SERVERMAP_PATH_INCOMING "packages" PATHDIVS "maps" PATHDIVS "servermaps" PATHDIVS "incoming" PATHDIVS const char *servermappath_off = SERVERMAP_PATH_BUILTIN; +const char *servermappath_local = SERVERMAP_PATH_LOCAL; const char *servermappath_serv = SERVERMAP_PATH; const char *servermappath_incom = SERVERMAP_PATH_INCOMING; @@ -38,6 +40,7 @@ struct servermap // in-memory version of a map file on a server uchar *enttypes; // table of entity types short *entpos_x, *entpos_y; + const char *err; bool isok; // definitive flag! #ifdef _DEBUG char maptitle[129]; @@ -48,15 +51,36 @@ struct servermap // in-memory version of a map file on a server bool isro() { return fpath == servermappath_off || fpath == servermappath_serv; } bool isofficial() { return fpath == servermappath_off; } + bool isdistributable() { return fpath == servermappath_serv || fpath == servermappath_incom; } int getmemusage() { return sizeof(struct servermap) + cgzlen + cfggzlen + layoutgzlen + numents * (sizeof(uchar) + sizeof(short) * 3); } + char *getlayout() // uncompress prefabricated floorplan + { + if(isok && layoutgz && layoutgzlen > 0 && layoutgzlen < layoutlen) + { + char *lo = new char[layoutlen]; + uLongf rawsize = layoutlen; + if(lo && uncompress((Bytef*)lo, &rawsize, layoutgz, layoutgzlen) == Z_OK && rawsize - layoutlen == 0) return lo; + delete[] lo; + } + return NULL; + } + + const char *getpathdesc() + { + if(fpath == servermappath_off) return "official"; + if(fpath == servermappath_local) return "local"; + if(fpath == servermappath_serv) return "custom"; + if(fpath == servermappath_incom) return "temporary"; + ASSERT(0); return "unknown"; + } + void load(void) // load map into memory and extract everything important about it (assumes struct to be zeroed: can only be called once) { static uchar *staticbuffer = NULL; if(!staticbuffer) staticbuffer = new uchar[FLOORPLANBUFSIZE]; // this buffer gets reused for every map load several times (also: because of this, load() is not thread safe) - const char *err = NULL; stream *f = NULL; int restofhead; @@ -292,6 +316,7 @@ struct servermap // in-memory version of a map file on a server if(err) goto loadfailed; // run mipmapper (to get corners right) + // TODO :) // calculate area statistics from type & vdelta values (destroys vdelta!) @@ -504,189 +529,45 @@ int readmapsthread(void *logfileprefix) return 0; } - - - - - - - -struct servermapbuffer // sending of maps between clients +#ifndef STANDALONE +const char *checklocalmap(const char *mapname) // check, if a map file exists in packages/maps/official or packages/maps - return proper servermap-path, if found { - string mapname; - int cgzsize, cfgsize, cfgsizegz, revision, datasize; - uchar *data, *gzbuf; + const char **fn = setnames(behindpath(mapname)), *locs[2] = { servermappath_off, servermappath_local }; + int l = 0; + if(!isdedicated && (getfilesize(fn[l]) > 10 || getfilesize(fn[++l]) > 10)) return locs[l]; + return NULL; +} +#endif - servermapbuffer() : data(NULL) { gzbuf = new uchar[GZBUFSIZE]; } - ~servermapbuffer() { delete[] gzbuf; } - - void clear() { DELETEA(data); revision = 0; } - - int available() +bool serverwritemap(const char *mapname, int mapsize, int cfgsize, int cfgsizegz, uchar *data) +{ + FILE *fp; + bool written = false; + if(!mapname[0] || mapsize <= 0 || cfgsizegz < 0 || mapsize + cfgsizegz > MAXMAPSENDSIZE || cfgsize > MAXCFGFILESIZE) return false; // malformed: probably modded client + defformatstring(name)(SERVERMAP_PATH_INCOMING "%s.cgz", mapname); + path(name); + fp = fopen(name, "wb"); + if(fp) { - if(data && !strcmp(mapname, behindpath(smapname)) && cgzsize == smapstats.cgzsize) - { - if( !revision || (revision == smapstats.hdr.maprevision)) return cgzsize; - } - return 0; - } - - void setrevision() - { - if(available() && !revision) revision = smapstats.hdr.maprevision; - } - - void load(void) // load currently played map into the buffer (if distributable), clear buffer otherwise - { - string cgzname, cfgname; - const char *name = behindpath(smapname); // no paths allowed here - - clear(); - formatstring(cgzname)(SERVERMAP_PATH "%s.cgz", name); - path(cgzname); - if(fileexists(cgzname, "r")) - { - formatstring(cfgname)(SERVERMAP_PATH "%s.cfg", name); - } - else - { - formatstring(cgzname)(SERVERMAP_PATH_INCOMING "%s.cgz", name); - path(cgzname); - formatstring(cfgname)(SERVERMAP_PATH_INCOMING "%s.cfg", name); - } - path(cfgname); - uchar *cgzdata = (uchar *)loadfile(cgzname, &cgzsize); - uchar *cfgdata = (uchar *)loadfile(cfgname, &cfgsize); - if(cgzdata && (!cfgdata || cfgsize < MAXCFGFILESIZE)) - { - uLongf gzbufsize = GZBUFSIZE; - if(!cfgdata || compress2(gzbuf, &gzbufsize, cfgdata, cfgsize, 9) != Z_OK) - { - cfgsize = 0; - gzbufsize = 0; - } - cfgsizegz = (int) gzbufsize; - if(cgzsize + cfgsizegz < MAXMAPSENDSIZE) - { // map is ok, fill buffer - copystring(mapname, name); - datasize = cgzsize + cfgsizegz; - data = new uchar[datasize]; - memcpy(data, cgzdata, cgzsize); - memcpy(data + cgzsize, gzbuf, cfgsizegz); - logline(ACLOG_INFO,"loaded map %s, %d + %d(%d) bytes.", cgzname, cgzsize, cfgsize, cfgsizegz); - } - } - DELETEA(cgzdata); - DELETEA(cfgdata); - } - - bool sendmap(const char *nmapname, int nmapsize, int ncfgsize, int ncfgsizegz, uchar *ndata) - { - FILE *fp; - bool written = false; - - if(!nmapname[0] || nmapsize <= 0 || ncfgsizegz < 0 || nmapsize + ncfgsizegz > MAXMAPSENDSIZE || ncfgsize > MAXCFGFILESIZE) return false; // malformed: probably modded client - int cfgsize = ncfgsize; - if(smode == GMODE_COOPEDIT && !strcmp(nmapname, behindpath(smapname))) - { // update mapbuffer only in coopedit mode (and on same map) - copystring(mapname, nmapname); - datasize = nmapsize + ncfgsizegz; - revision = 0; - DELETEA(data); - data = new uchar[datasize]; - memcpy(data, ndata, datasize); - } - - defformatstring(name)(SERVERMAP_PATH_INCOMING "%s.cgz", nmapname); + fwrite(data, 1, mapsize, fp); + fclose(fp); + formatstring(name)(SERVERMAP_PATH_INCOMING "%s.cfg", mapname); path(name); fp = fopen(name, "wb"); if(fp) { - fwrite(ndata, 1, nmapsize, 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); - formatstring(name)(SERVERMAP_PATH_INCOMING "%s.cfg", nmapname); - path(name); - fp = fopen(name, "wb"); - if(fp) - { - uLongf rawsize = ncfgsize; - if(uncompress(gzbuf, &rawsize, ndata + nmapsize, ncfgsizegz) == Z_OK && rawsize - ncfgsize == 0) - fwrite(gzbuf, 1, cfgsize, fp); - fclose(fp); - written = true; - } + delete[] gzbuf; + written = true; } - return written; } - - void sendmap(client *cl, int chan) - { - if(!available()) return; - packetbuf p(MAXTRANS + datasize, ENET_PACKET_FLAG_RELIABLE); - putint(p, SV_RECVMAP); - sendstring(mapname, p); - putint(p, cgzsize); - putint(p, cfgsize); - putint(p, cfgsizegz); - putint(p, revision); - p.put(data, datasize); - sendpacket(cl->clientnum, chan, p.finalize()); - } -}; - - -// provide maps by the server - -enum { MAP_NOTFOUND = 0, MAP_TEMP, MAP_CUSTOM, MAP_LOCAL, MAP_OFFICIAL, MAP_VOID }; -static const char * const maplocstr[] = { "not found", "temporary", "custom", "local", "official", "void" }; -#define readonlymap(x) ((x) >= MAP_CUSTOM) -#define distributablemap(x) ((x) == MAP_TEMP || (x) == MAP_CUSTOM) - -int findmappath(const char *mapname, char *filename) -{ - if(!mapname[0]) return MAP_NOTFOUND; - string tempname; - if(!filename) filename = tempname; - const char *name = behindpath(mapname); - formatstring(filename)(SERVERMAP_PATH_BUILTIN "%s.cgz", name); - path(filename); - int loc = MAP_NOTFOUND; - if(getfilesize(filename) > 10) loc = MAP_OFFICIAL; - else - { -#ifndef STANDALONE - copystring(filename, setnames(name)); - if(!isdedicated && getfilesize(filename) > 10) loc = MAP_LOCAL; - else - { -#endif - formatstring(filename)(SERVERMAP_PATH "%s.cgz", name); - path(filename); - if(isdedicated && getfilesize(filename) > 10) loc = MAP_CUSTOM; - else - { - formatstring(filename)(SERVERMAP_PATH_INCOMING "%s.cgz", name); - path(filename); - if(isdedicated && getfilesize(filename) > 10) loc = MAP_TEMP; - } -#ifndef STANDALONE - } -#endif - } - return loc; + return written; } -mapstats *getservermapstats(const char *mapname, bool getlayout, int *maploc) -{ - string filename; - int ml; - if(!maploc) maploc = &ml; - *maploc = findmappath(mapname, filename); - if(getlayout) DELETEA(maplayout); - return *maploc == MAP_NOTFOUND ? NULL : loadmapstats(filename, getlayout); -} - - // server config files void serverconfigfile::init(const char *name) @@ -734,76 +615,7 @@ struct configset }; }; -int FlagFlag = MINFF * 1000; -int Mvolume, Marea, SHhits, Mopen = 0; -float Mheight = 0; - -bool mapisok(mapstats *ms) -{ - if ( Mheight > MAXMHEIGHT ) { logline(ACLOG_INFO, "MAP CHECK FAIL: The overall ceil height is too high (%.1f cubes)", Mheight); return false; } - if ( Mopen > MAXMAREA ) { logline(ACLOG_INFO, "MAP CHECK FAIL: There is a big open area in this (hint: use more solid walls)"); return false; } - if ( SHhits > MAXHHITS ) { logline(ACLOG_INFO, "MAP CHECK FAIL: Too high height in some parts of the map (%d hits)", SHhits); return false; } - - if ( ms->hasflags ) // Check if flags are ok - { - struct { short x, y; } fl[2]; - loopi(2) - { - if(ms->flags[i] == 1) - { - short *fe = ms->entposs + ms->flagents[i] * 3; - fl[i].x = *fe; fe++; fl[i].y = *fe; - } - else fl[i].x = fl[i].y = 0; // the map has no valid flags - } - FlagFlag = pow2(fl[0].x - fl[1].x) + pow2(fl[0].y - fl[1].y); - } - else FlagFlag = MINFF * 1000; // the map has no flags - - if ( FlagFlag < MINFF ) { logline(ACLOG_INFO, "MAP CHECK FAIL: The flags are too close to each other"); return false; } - - for (int i = 0; i < ms->hdr.numents; i++) - { - int v = ms->enttypes[i]; - if (v < I_CLIPS || v > I_AKIMBO) continue; - short *p = &ms->entposs[i*3]; - float density = 0, hdensity = 0; - for(int j = 0; j < ms->hdr.numents; j++) - { - int w = ms->enttypes[j]; - if (w < I_CLIPS || w > I_AKIMBO || i == j) continue; - short *q = &ms->entposs[j*3]; - float r2 = 0; - loopk(3){ r2 += (p[k]-q[k])*(p[k]-q[k]); } - if ( r2 == 0.0f ) { logline(ACLOG_INFO, "MAP CHECK FAIL: Items too close %s %s (%hd,%hd)", entnames[v], entnames[w],p[0],p[1]); return false; } - r2 = 1/r2; - if (r2 < 0.0025f) continue; - if (w != v) - { - hdensity += r2; - continue; - } - density += r2; - } -/* if (hdensity > 0.0f) { logline(ACLOG_INFO, "ITEM CHECK H %s %f", entnames[v], hdensity); } - if (density > 0.0f) { logline(ACLOG_INFO, "ITEM CHECK D %s %f", entnames[v], density); }*/ - if ( hdensity > 0.5f ) { logline(ACLOG_INFO, "MAP CHECK FAIL: Items too close %s %.2f (%hd,%hd)", entnames[v],hdensity,p[0],p[1]); return false; } - switch(v) - { -#define LOGTHISSWITCH(X) if( density > X ) { logline(ACLOG_INFO, "MAP CHECK FAIL: Items too close %s %.2f (%hd,%hd)", entnames[v],density,p[0],p[1]); return false; } - case I_CLIPS: - case I_HEALTH: LOGTHISSWITCH(0.24f); break; - case I_AMMO: LOGTHISSWITCH(0.04f); break; - case I_HELMET: LOGTHISSWITCH(0.02f); break; - case I_ARMOUR: - case I_GRENADE: - case I_AKIMBO: LOGTHISSWITCH(0.005f); break; - default: break; -#undef LOGTHISSWITCH - } - } - return true; -} +servermap *getservermap(const char *mapname); struct servermaprot : serverconfigfile { @@ -873,9 +685,8 @@ struct servermaprot : serverconfigfile c = &configsets[ccs]; if((n >= c->minplayer || i >= csl) && (!c->maxplayer || n <= c->maxplayer || i >= 2 * csl)) { - mapstats *ms = NULL; - if((ms = getservermapstats(c->mapname)) && mapisok(ms)) break; - else logline(ACLOG_INFO, "maprot error: map '%s' %s", c->mapname, (ms ? "does not satisfy some basic requirements" : "not found")); + if(getservermap(c->mapname)) break; + else logline(ACLOG_INFO, "maprot error: map '%s' not found", c->mapname); } if(i >= 3 * csl) fatal("maprot unusable"); // not a single map in rotation can be found... } @@ -896,7 +707,7 @@ struct servermaprot : serverconfigfile return; } #endif - startgame(smapname, smode, -1, notify); + startgame("ac_desert", 0, -1, notify); } configset *current() { return configsets.inrange(curcfgset) ? &configsets[curcfgset] : NULL; } diff --git a/source/src/serverms.cpp b/source/src/serverms.cpp index 3db4c09a..5d335097 100644 --- a/source/src/serverms.cpp +++ b/source/src/serverms.cpp @@ -165,9 +165,9 @@ extern int totalclients; // send alive signal to masterserver after 40 minutes of uptime and if currently in intermission (so theoretically <= 1 hour) // TODO?: implement a thread to drop this "only in intermission" business, we'll need it once AUTH gets active! -static inline void updatemasterserver(int millis, int port) +static inline void updatemasterserver(int millis, int port, int _interm) { - if(!lastupdatemaster || ((millis-lastupdatemaster)>40*60*1000 && (interm || !totalclients))) + if(!lastupdatemaster || ((millis-lastupdatemaster)>40*60*1000 && (_interm || !totalclients))) { char servername[30]; memset(servername,'\0',30); filtertext(servername, global_name, FTXT__GLOBALNAME, 20); if(mastername[0]) requestmasterf("regserv %d %s %d\n", port, servername[0] ? servername : "noname", AC_VERSION); @@ -178,10 +178,10 @@ static inline void updatemasterserver(int millis, int port) ENetSocket pongsock = ENET_SOCKET_NULL, lansock = ENET_SOCKET_NULL; extern int getpongflags(enet_uint32 ip); -void serverms(int mode, int numplayers, int minremain, char *smapname, int millis, const ENetAddress &localaddr, int *mnum, int *msend, int *mrec, int *cnum, int *csend, int *crec, int protocol_version) +void serverms(int mode, int numplayers, int minremain, char *smapname, int millis, const ENetAddress &localaddr, int *mnum, int *msend, int *mrec, int *cnum, int *csend, int *crec, int protocol_version, const char *servdesccur, int _interm) { flushmasteroutput(); - updatemasterserver(millis, localaddr.port); + updatemasterserver(millis, localaddr.port, _interm); static ENetSocketSet sockset; ENET_SOCKETSET_EMPTY(sockset); @@ -221,14 +221,13 @@ void serverms(int mode, int numplayers, int minremain, char *smapname, int milli if(getint(pi) != 0) // std pong { extern struct servercommandline scl; - extern string servdesc_current; (*mnum)++; *mrec += len; std = true; putint(po, protocol_version); putint(po, mode); putint(po, numplayers); putint(po, minremain); sendstring(smapname, po); - sendstring(servdesc_current, po); + sendstring(servdesccur, po); putint(po, scl.maxclients); putint(po, getpongflags(addr.host)); if(pi.remaining()) diff --git a/source/src/tools.cpp b/source/src/tools.cpp index 928a4afc..ef7943a9 100644 --- a/source/src/tools.cpp +++ b/source/src/tools.cpp @@ -79,6 +79,20 @@ int fixmapheadersize(int version, int headersize) // we can't trust hdr.header return headersize; } +header *peekmapheader(uchar *data, int len) // extract the header from an in-memory mapfile (doesn't process all values: only used to get the map revision and timestamp) +{ + static header h; + uLongf rawsize = (int)sizeof(header); + if(uncompress((Bytef*)&h, &rawsize, data, len) == Z_BUF_ERROR && (!strncmp(h.head, "CUBE", 4) || !strncmp(h.head, "ACMP",4))) + { + lilswap(&h.version, 4); // version, headersize, sfactor, numents + if(h.version < 4) memset(&h.waterlevel, 0, sizeof(int) * 16); + else lilswap(&h.maprevision, 4); // maprevision, ambient, flags, timestamp + return &h; + } + return NULL; +} + // map geometry statistics servsqr *createservworld(const sqr *s, int _cubicsize) // create a server-style floorplan on the client, to use same statistics functions on client and server @@ -316,8 +330,6 @@ const char *rateentitystats(entitystats_s &es) } #endif - - int cmpintasc(const int *a, const int *b) { return *a - *b; } // leads to ascending sort order int cmpintdesc(const int *a, const int *b) { return *b - *a; } // leads to descending sort order int stringsort(const char **a, const char **b) { return strcmp(*a, *b); } @@ -325,177 +337,6 @@ int stringsortrev(const char **a, const char **b) { return strcmp(*b, *a); } int stringsortignorecase(const char **a, const char **b) { return strcasecmp(*a, *b); } int stringsortignorecaserev(const char **a, const char **b) { return strcasecmp(*b, *a); } -extern char *maplayout, *testlayout; -extern int maplayout_factor, testlayout_factor, Mvolume, Marea, Mopen, SHhits; -extern float Mheight; -extern int checkarea(int, char *); - -mapstats *loadmapstats(const char *filename, bool getlayout) -{ - const int sizeof_header = sizeof(header), sizeof_baseheader = sizeof_header - sizeof(int) * 16; - static mapstats s; - static uchar *enttypes = NULL; - static short *entposs = NULL; - - DELETEA(enttypes); - loopi(MAXENTTYPES) s.entcnt[i] = 0; - loopi(3) s.spawns[i] = 0; - loopi(2) s.flags[i] = 0; - - stream *f = opengzfile(filename, "rb"); - if(!f) return NULL; - memset(&s.hdr, 0, sizeof_header); - if(f->read(&s.hdr, sizeof_baseheader) != sizeof_baseheader || (strncmp(s.hdr.head, "CUBE", 4) && strncmp(s.hdr.head, "ACMP",4))) { delete f; return NULL; } - lilswap(&s.hdr.version, 4); - s.hdr.headersize = fixmapheadersize(s.hdr.version, s.hdr.headersize); - int restofhead = min(s.hdr.headersize, sizeof_header) - sizeof_baseheader; - if(s.hdr.version > MAPVERSION || s.hdr.numents > MAXENTITIES || - f->read(&s.hdr.waterlevel, restofhead) != restofhead || - !f->seek(clamp(s.hdr.headersize - sizeof_header, 0, MAXHEADEREXTRA), SEEK_CUR)) { delete f; return NULL; } - if(s.hdr.version>=4) - { - lilswap(&s.hdr.waterlevel, 1); - lilswap(&s.hdr.maprevision, 2); - } - else s.hdr.waterlevel = -100000; - entity e; - enttypes = new uchar[s.hdr.numents]; - entposs = new short[s.hdr.numents * 3]; - loopi(s.hdr.numents) - { - f->read(&e, s.hdr.version < 10 ? 12 : sizeof(persistent_entity)); - lilswap((short *)&e, 4); - transformoldentitytypes(s.hdr.version, e.type); - 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.flagents[e.attr2] = i; } - s.entcnt[e.type]++; - enttypes[i] = e.type; - entposs[i * 3] = e.x; entposs[i * 3 + 1] = e.y; entposs[i * 3 + 2] = e.z + e.attr1; - } - DELETEA(testlayout); - int minfloor = 0; - int maxceil = 0; - if(s.hdr.sfactor <= LARGEST_FACTOR && s.hdr.sfactor >= SMALLEST_FACTOR) - { - testlayout_factor = s.hdr.sfactor; - int layoutsize = 1 << (testlayout_factor * 2); - bool fail = false; - testlayout = new char[layoutsize + 256]; - memset(testlayout, 0, layoutsize * sizeof(char)); - char *t = NULL; - char floor = 0, ceil; - int diff = 0; - Mvolume = Marea = SHhits = 0; - loopk(layoutsize) - { - char *c = testlayout + k; - int type = f->getchar(); - int n = 1; - switch(type) - { - case 255: - { - if(!t || (n = f->getchar()) < 0) { fail = true; break; } - memset(c, *t, n); - k += n - 1; - break; - } - case 254: // only in MAPVERSION<=2 - if(!t) { fail = true; break; } - *c = *t; - f->getchar(); f->getchar(); - break; - default: - if(type<0 || type>=MAXTYPE) { fail = true; break; } - floor = f->getchar(); - ceil = f->getchar(); - if(floor >= ceil && ceil > -128) floor = ceil - 1; // for pre 12_13 - diff = ceil - floor; - if(type == FHF) floor = -128; - if(floor!=-128 && floormaxceil) maxceil = ceil; - f->getchar(); f->getchar(); - if(s.hdr.version>=2) f->getchar(); - if(s.hdr.version>=5) f->getchar(); - - case SOLID: - *c = type == SOLID ? 127 : floor; - f->getchar(); f->getchar(); - if(s.hdr.version<=2) { f->getchar(); f->getchar(); } - break; - } - if ( type != SOLID && diff > 6 ) - { - // Lucas (10mar2013): Removed "pow2" because it was too strict - if (diff > MAXMHEIGHT) SHhits += /*pow2*/(diff-MAXMHEIGHT)*n; - Marea += n; - Mvolume += diff * n; - } - if(fail) break; - t = c; - } - if(fail) { DELETEA(testlayout); } - else - { - Mheight = Marea ? (float)Mvolume/Marea : 0; - Mopen = checkarea(testlayout_factor, testlayout); - } - } - if(getlayout) - { - DELETEA(maplayout); - if (testlayout) - { - maplayout_factor = testlayout_factor; - extern int maplayoutssize; - maplayoutssize = 1 << testlayout_factor; - int layoutsize = 1 << (testlayout_factor * 2); - maplayout = new char[layoutsize + 256]; - memcpy(maplayout, testlayout, layoutsize * sizeof(char)); - -/* memset(&mapdims, 0, sizeof(struct mapdim_s)); - mapdims.x1 = mapdims.y1 = maplayoutssize; - loopk(layoutsize) if (testlayout[k] != 127) - { - int cwx = k%maplayoutssize, - cwy = k/maplayoutssize; - if(cwx < mapdims.x1) mapdims.x1 = cwx; - if(cwy < mapdims.y1) mapdims.y1 = cwy; - if(cwx > mapdims.x2) mapdims.x2 = cwx; - if(cwy > mapdims.y2) mapdims.y2 = cwy; - } - mapdims.xspan = mapdims.x2 - mapdims.x1; - mapdims.yspan = mapdims.y2 - mapdims.y1; - mapdims.minfloor = minfloor; - mapdims.maxceil = maxceil; -*/ } - } - delete 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; - s.entposs = entposs; - s.cgzsize = getfilesize(filename); - return &s; -} - - - - - - - - - - - - - - - - - ///////////////////////// debugging /////////////////////// #if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__) diff --git a/source/src/tools.h b/source/src/tools.h index fd688c81..fffc93b2 100644 --- a/source/src/tools.h +++ b/source/src/tools.h @@ -968,7 +968,6 @@ extern void *zipmanualopen(stream *f, vector &files); extern stream *zipmanualstream(void *a, int n); extern int zipmanualread(void *a, int n, stream *f, int maxlen = INT_MAX); extern void zipmanualclose(void *a); -extern struct mapstats *loadmapstats(const char *filename, bool getlayout); extern bool cmpb(void *b, int n, enet_uint32 c); extern bool cmpf(char *fn, enet_uint32 c); extern enet_uint32 adler(unsigned char *data, size_t len); diff --git a/source/src/worldio.cpp b/source/src/worldio.cpp index 47cf343b..450c4288 100644 --- a/source/src/worldio.cpp +++ b/source/src/worldio.cpp @@ -7,8 +7,9 @@ VARP(worldiodebug, 0, 0, 1); static string cgzname, ocgzname, bakname, cbakname, mcfname, omcfname; -const char *setnames(const char *name) +const char **setnames(const char *name) { + static const char *paths[2] = { ocgzname, cgzname }; const char *mapname = behindpath(name), *nt = numtime(); formatstring(cgzname) ("packages" PATHDIVS "maps" PATHDIVS "%s.cgz", mapname); formatstring(mcfname) ("packages" PATHDIVS "maps" PATHDIVS "%s.cfg", mapname); @@ -16,7 +17,7 @@ const char *setnames(const char *name) formatstring(omcfname)("packages" PATHDIVS "maps" PATHDIVS "official" PATHDIVS "%s.cfg", mapname); formatstring(bakname) ("packages" PATHDIVS "maps" PATHDIVS "%s_%s.BAK", mapname, nt); formatstring(cbakname)("packages" PATHDIVS "maps" PATHDIVS "%s.cfg_%s.BAK", mapname, nt); - return cgzname; + return paths; } // the optimize routines below are here to reduce the detrimental effects of messy mapping by @@ -55,8 +56,8 @@ uchar *readmap(char *name, int *size, int *revision) setnames(name); uchar *data = (uchar *)loadfile(cgzname, size); if(!data) { conoutf("\f3could not read map %s", cgzname); return NULL; } - mapstats *ms = loadmapstats(cgzname, false); - if(revision) *revision = ms->hdr.maprevision; + header *h = peekmapheader(data, *size); + if(revision) *revision = h ? h->maprevision : 0; return data; }