From 624dd348d7fee9524fd37098cb1fd3112c99d441 Mon Sep 17 00:00:00 2001 From: Lucas GAUTHERON Date: Sun, 26 May 2013 16:40:57 +0000 Subject: [PATCH] make the server store more map data (more accurate pickup calculations) git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@7487 e016b1c5-ed68-44aa-a2f7-31cac88cffde --- source/src/server.cpp | 6 +- source/src/server.h | 2 +- source/src/serveractions.h | 7 ++- source/src/serverfiles.h | 71 +--------------------- source/src/tools.cpp | 118 +++++++++++++++++++++++++++++++------ source/src/world.h | 45 ++++++++++++++ 6 files changed, 156 insertions(+), 93 deletions(-) diff --git a/source/src/server.cpp b/source/src/server.cpp index 1ff3de15..c41b671e 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -1413,7 +1413,7 @@ bool serverpickup(int i, int sender) // server side item pickup, acknowl if(cl->type==ST_TCPIP) { if( cl->state.state!=CS_ALIVE || !cl->state.canpickup(e.type) || ( m_arena && !free_items(sender) ) ) return false; - vec v(e.x, e.y, cl->state.o.z); + vec v(e.x, e.y, float(SS(e.x, e.y)->floor) + float(e.elevation)); float dist = cl->state.o.dist(v); int pdist = check_pdist(cl,dist); if (pdist) @@ -1955,12 +1955,12 @@ void startgame(const char *newname, int newmode, int newtime, bool notify) { 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]}; + server_entity se = { e.type, false, false, false, 0, smapstats.entposs[i * 3], smapstats.entposs[i * 3 + 1], smapstats.entposs[i * 3 + 2] }; sents.add(se); if(e.fitsmode(smode)) sents[i].spawned = 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); + logline(ACLOG_INFO, "Map height density information for %s: AvgHeight = %.2f Vol = %d, Area = %d and MxOpenArea = %d", smapname, ms->height, ms->volume, ms->area, ms->maxopenarea); items_blocked = false; } else if(isdedicated) sendservmsg("\f3server error: map not found - please start another map or send this map to the server"); diff --git a/source/src/server.h b/source/src/server.h index 221e97dc..5a92f414 100644 --- a/source/src/server.h +++ b/source/src/server.h @@ -371,7 +371,7 @@ struct server_entity // server side version of "entity" type int type; bool spawned, legalpickup, twice; int spawntime; - short x, y; + short x, y, elevation; }; struct clientidentity diff --git a/source/src/serveractions.h b/source/src/serveractions.h index 2b2e1419..9cd5b97f 100644 --- a/source/src/serveractions.h +++ b/source/src/serveractions.h @@ -65,7 +65,12 @@ struct mapaction : serveraction bool notify = valid_client(caller); int maploc = MAP_NOTFOUND; mapstats *ms = map[0] ? getservermapstats(map, false, &maploc) : NULL; - mapok = (ms != NULL) && ( (mode != GMODE_COOPEDIT && mapisok(ms)) || (mode == GMODE_COOPEDIT && !readonlymap(maploc)) ); + mapok = (ms != NULL) && mode == GMODE_COOPEDIT && !readonlymap(maploc); + if(ms && mode != GMODE_COOPEDIT && !ms->goodmap) + { + mapok = false; + loopv(ms->errors) if(ms->errors[i]) logline(ACLOG_INFO, "map check fail: %s", ms->errors[i]); + } if(!mapok) { if(notify) sendservmsg(ms ? diff --git a/source/src/serverfiles.h b/source/src/serverfiles.h index 756760a5..36170c55 100644 --- a/source/src/serverfiles.h +++ b/source/src/serverfiles.h @@ -232,75 +232,6 @@ 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)", Mheight); 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; -} struct servermaprot : serverconfigfile { @@ -371,7 +302,7 @@ struct servermaprot : serverconfigfile 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; + if((ms = getservermapstats(c->mapname)) && ms->goodmap) break; else logline(ACLOG_INFO, "maprot error: map '%s' %s", c->mapname, (ms ? "does not satisfy some basic requirements" : "not found")); } if(i >= 3 * csl) fatal("maprot unusable"); // not a single map in rotation can be found... diff --git a/source/src/tools.cpp b/source/src/tools.cpp index a65c4628..1a5b3fb1 100644 --- a/source/src/tools.cpp +++ b/source/src/tools.cpp @@ -27,8 +27,7 @@ const char *numtime() int mapdims[8]; // min/max X/Y and delta X/Y and min/max Z extern char *maplayout, *testlayout; -extern int maplayout_factor, testlayout_factor, Mvolume, Marea, Mopen, SHhits; -extern float Mheight; +extern int maplayout_factor, testlayout_factor; extern int checkarea(int, char *); mapstats *loadmapstats(const char *filename, bool getlayout) @@ -67,7 +66,7 @@ mapstats *loadmapstats(const char *filename, bool getlayout) 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; + entposs[i * 3] = e.x; entposs[i * 3 + 1] = e.y; entposs[i * 3 + 2] = e.attr1; } DELETEA(testlayout); int minfloor = 0; @@ -79,13 +78,14 @@ mapstats *loadmapstats(const char *filename, bool getlayout) bool fail = false; testlayout = new char[layoutsize + 256]; memset(testlayout, 0, layoutsize * sizeof(char)); - char *t = NULL; + ssqr *t = NULL; char floor = 0, ceil; int diff = 0; - Mvolume = Marea = SHhits = 0; + s.initworld(); loopk(layoutsize) { char *c = testlayout + k; + ssqr *ss = &s.serverworld[k]; int type = f->getchar(); int n = 1; switch(type) @@ -93,20 +93,23 @@ mapstats *loadmapstats(const char *filename, bool getlayout) case 255: { if(!t || (n = f->getchar()) < 0) { fail = true; break; } - memset(c, *t, n); - k += n - 1; + char tmp = *(c-1); + memset(c, tmp, n); + for(int i = 0; igetchar(); 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 + ss->type = type; + ss->floor = floor = f->getchar(); + ss->ceil = ceil = f->getchar(); + if(floor >= ceil && ceil > -128) ss->floor = floor = ceil - 1; // for pre 12_13 diff = ceil - floor; if(type == FHF) floor = -128; if(floor!=-128 && floorgetchar(); f->getchar(); if(s.hdr.version>=2) f->getchar(); if(s.hdr.version>=5) f->getchar(); - + *c = ss->floor; case SOLID: + ss->type = SOLID; *c = type == SOLID ? 127 : floor; f->getchar(); f->getchar(); if(s.hdr.version<=2) { f->getchar(); f->getchar(); } @@ -124,18 +128,18 @@ mapstats *loadmapstats(const char *filename, bool getlayout) 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 (diff > MAXMHEIGHT) s.heighthits += /*pow2*/(diff-MAXMHEIGHT)*n; // linear is more permissive + s.area += n; + s.volume += diff * n; } if(fail) break; - t = c; + t = ss; } if(fail) { DELETEA(testlayout); } else { - Mheight = Marea ? (float)Mvolume/Marea : 0; - Mopen = checkarea(testlayout_factor, testlayout); + s.height = s.area ? float(s.volume)/s.area : 0; + s.maxopenarea = checkarea(testlayout_factor, testlayout); } } if(getlayout) @@ -172,9 +176,87 @@ mapstats *loadmapstats(const char *filename, bool getlayout) s.enttypes = enttypes; s.entposs = entposs; s.cgzsize = getfilesize(filename); + s.mapcheck(); return &s; } +#define logmaperror(format, ...) { if(this->errors.length() >= 20) return false; \ + formatstring(error)(format, __VA_ARGS__); \ + this->errors.add(newstring(error)); \ + this->goodmap = false; } \ + +bool mapstats::mapcheck() +{ + this->goodmap = true; + this->errors.deletearrays(); + string error; + if(this->height > MAXMHEIGHT) logmaperror("The overall ceil height is too high (%.1f cubes)", this->height); + if(this->maxopenarea > MAXMAREA) logmaperror("There is a big open area in this map (%d cubes*cubes) (hint: use more solid walls)", this->maxopenarea); + if(this->heighthits > MAXHHITS) logmaperror("Too high height in some parts of the map (%d hits)", this->heighthits); + + if (this->hasflags) // Check if flags are ok + { + struct { short x, y; } fl[2]; + loopi(2) + { + if(this->flags[i] == 1) + { + short *fe = this->entposs + this->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 + } + float FlagFlag = pow2(fl[0].x - fl[1].x) + pow2(fl[0].y - fl[1].y); + + if (FlagFlag < MINFF) logmaperror("The flags are too close to each other (distance = %.1f, min required = %.1f)", sqrt(FlagFlag), sqrt(float(MINFF))); + } + //else FlagFlag = MINFF * 1000; // the map has no flags + + for (int i = 0; i < this->hdr.numents; i++) + { + int v = this->enttypes[i]; + if (v < I_CLIPS || v > I_AKIMBO) continue; + short *p = &this->entposs[i*3]; + float density = 0, hdensity = 0; + for(int j = 0; j < this->hdr.numents; j++) + { + int w = this->enttypes[j]; + if (w < I_CLIPS || w > I_AKIMBO || i == j) continue; + short *q = &this->entposs[j*3]; + float pz = SWS(serverworld, p[0], p[1], hdr.sfactor)->floor, + qz = SWS(serverworld, q[0], q[1], hdr.sfactor)->floor; + float r2 = vec(p[0], p[1], p[2] + pz).dist(vec(q[0], q[1], q[2] + qz)); + if ( r2 == 0.0f) logmaperror("Items too close %s %s (%hd,%hd)", entnames[v], entnames[w], p[0], p[1]); + 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) logmaperror("Items too close %s %.2f (%hd,%hd)", entnames[v],hdensity,p[0],p[1]); + switch(v) + { +#define LOGTHISSWITCH(X) if( density > X ) logmaperror("Items too close %s %.2f (%hd,%hd)", entnames[v],density,p[0],p[1]); + 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 this->goodmap; +} +#undef logmaperror + ///////////////////////// debugging /////////////////////// #if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__) diff --git a/source/src/world.h b/source/src/world.h index 41e819ac..fec822ed 100644 --- a/source/src/world.h +++ b/source/src/world.h @@ -23,6 +23,13 @@ struct sqr uchar reserved[2]; }; +// simplified square +struct ssqr +{ + uchar type; + char floor, ceil; +}; + enum // hardcoded texture numbers { DEFAULT_SKY = 0, @@ -64,6 +71,40 @@ struct mapstats bool hasffaspawns; bool hasteamspawns; bool hasflags; + + int area, volume; + float height; // average height + int heighthits; // sum (height-maxheight) for each cube if height > maxheight + int numheighthits; // number of cubes with height > maxheight + int maxopenarea; // biggest open area + bool goodmap; // pass requirements ? + vector errors; + ssqr *serverworld; + + void initworld() + { + DELETEA(serverworld); + int size = 1 << (hdr.sfactor*2); + serverworld = new ssqr[size]; + memset(serverworld, 0, size*sizeof(ssqr)); + loopi(size) + { + ssqr *ss = &serverworld[i]; + if(!ss) continue; + ss->floor = 0; + ss->ceil = 16; + ss->type = SOLID; + } + + volume = area = heighthits = numheighthits = 0; + } + + bool mapstats::mapcheck(); + + ~mapstats() + { + DELETEA(serverworld); + } }; #define TRANSFORMOLDENTITIES(headr) \ @@ -117,6 +158,10 @@ struct mapstats #define SWS(w,x,y,s) (&(w)[((y)<<(s))+(x)]) #define SW(w,x,y) SWS(w,x,y,sfactor) #define S(x,y) SW(world,x,y) // convenient lookup of a lowest mip cube + +#define SSW(w,x,y) SWS(w,x,y,smapstats.hdr.sfactor) +#define SS(x,y) SSW(smapstats.serverworld,x,y) + #define SMALLEST_FACTOR 6 // determines number of mips there can be #define DEFAULT_FACTOR 8 #define LARGEST_FACTOR 11 // 10 is already insane