/* * ============= SV_PointContents ============= */ public static int SV_PointContents(float[] p) { edict_t hit; int i, num; int contents, c2; int headnode; // get base contents from world contents = CM.PointContents(p, SV_INIT.sv.models[1].headnode); // or in contents from all the other entities num = SV_WORLD.SV_AreaEdicts(p, p, SV_WORLD.touch, Defines.MAX_EDICTS, Defines.AREA_SOLID); for (i = 0; i < num; i++) { hit = SV_WORLD.touch[i]; // might intersect, so do an exact clip headnode = SV_WORLD.SV_HullForEntity(hit); if (hit.solid != Defines.SOLID_BSP) { } c2 = CM.TransformedPointContents(p, headnode, hit.s.origin, hit.s.angles); contents |= c2; } return(contents); }
/* * ==================== SV_AreaEdicts_r * * ==================== */ public static void SV_AreaEdicts_r(areanode_t node) { link_t l, next, start; edict_t check; // touch linked edicts if (SV_WORLD.area_type == Defines.AREA_SOLID) { start = node.solid_edicts; } else { start = node.trigger_edicts; } for (l = start.next; l != start; l = next) { next = l.next; check = (edict_t)l.o; if (check.solid == Defines.SOLID_NOT) { continue; // deactivated } if (check.absmin[0] > SV_WORLD.area_maxs[0] || check.absmin[1] > SV_WORLD.area_maxs[1] || check.absmin[2] > SV_WORLD.area_maxs[2] || check.absmax[0] < SV_WORLD.area_mins[0] || check.absmax[1] < SV_WORLD.area_mins[1] || check.absmax[2] < SV_WORLD.area_mins[2]) { continue; // not touching } if (SV_WORLD.area_count == SV_WORLD.area_maxcount) { Com.Printf("SV_AreaEdicts: MAXCOUNT\n"); return; } SV_WORLD.area_list[SV_WORLD.area_count] = check; SV_WORLD.area_count++; } if (node.axis == -1) { return; // terminal node } // recurse down both sides if (SV_WORLD.area_maxs[node.axis] > node.dist) { SV_WORLD.SV_AreaEdicts_r(node.children[0]); } if (SV_WORLD.area_mins[node.axis] < node.dist) { SV_WORLD.SV_AreaEdicts_r(node.children[1]); } }
/* * =============== SV_UnlinkEdict =============== */ public static void SV_UnlinkEdict(edict_t ent) { if (null == ent.area.prev) { return; // not linked in anywhere } SV_WORLD.RemoveLink(ent.area); ent.area.prev = ent.area.next = null; }
/* * ================ SV_AreaEdicts ================ */ public static int SV_AreaEdicts(float[] mins, float[] maxs, edict_t[] list, int maxcount, int areatype) { SV_WORLD.area_mins = mins; SV_WORLD.area_maxs = maxs; SV_WORLD.area_list = list; SV_WORLD.area_count = 0; SV_WORLD.area_maxcount = maxcount; SV_WORLD.area_type = areatype; SV_WORLD.SV_AreaEdicts_r(SV_WORLD.sv_areanodes[0]); return(SV_WORLD.area_count); }
/** * SV_CheckForSavegame. */ public static void SV_CheckForSavegame() { string name; int i; if (SV_MAIN.sv_noreload.value != 0) { return; } if (Cvar.VariableValue("deathmatch") != 0) { return; } name = FS.Gamedir() + "/save/current/" + SV_INIT.sv.name + ".sav"; if (!File.Exists(name)) { return; } SV_WORLD.SV_ClearWorld(); // get configstrings and areaportals SV_CCMDS.SV_ReadLevelFile(); if (!SV_INIT.sv.loadgame) { // coming back to a level after being in a different // level, so run it for ten seconds // rlava2 was sending too many lightstyles, and overflowing the // reliable data. temporarily changing the server state to loading // prevents these from being passed down. int previousState; // PGM previousState = SV_INIT.sv.state; // PGM SV_INIT.sv.state = Defines.ss_loading; // PGM for (i = 0; i < 100; i++) { GameBase.G_RunFrame(); } SV_INIT.sv.state = previousState; // PGM } }
/* * =============== SV_ClearWorld * * =============== */ public static void SV_ClearWorld() { SV_WORLD.initNodes(); SV_WORLD.sv_numareanodes = 0; SV_WORLD.SV_CreateAreaNode(0, SV_INIT.sv.models[1].mins, SV_INIT.sv.models[1].maxs); /* * Com.p("areanodes:" + sv_numareanodes + " (sollten 32 sein)."); for * (int n = 0; n < sv_numareanodes; n++) { Com.Printf( "|%3i|%2i|%8.2f * |%8.2f|%8.2f|%8.2f| %8.2f|%8.2f|%8.2f|\n", new Vargs() .add(n) * .add(sv_areanodes[n].axis) .add(sv_areanodes[n].dist) * .add(sv_areanodes[n].mins_rst[0]) .add(sv_areanodes[n].mins_rst[1]) * .add(sv_areanodes[n].mins_rst[2]) .add(sv_areanodes[n].maxs_rst[0]) * .add(sv_areanodes[n].maxs_rst[1]) .add(sv_areanodes[n].maxs_rst[2])); } */ }
/* * =============== SV_CreateAreaNode * * Builds a uniformly subdivided tree for the given world size * =============== */ public static areanode_t SV_CreateAreaNode(int depth, float[] mins, float[] maxs) { areanode_t anode; float[] size = { 0, 0, 0 }; float[] mins1 = { 0, 0, 0 }, maxs1 = { 0, 0, 0 }, mins2 = { 0, 0, 0 }, maxs2 = { 0, 0, 0 }; anode = SV_WORLD.sv_areanodes[SV_WORLD.sv_numareanodes]; // just for debugging (rst) // Math3D.VectorCopy(mins, anode.mins_rst); // Math3D.VectorCopy(maxs, anode.maxs_rst); SV_WORLD.sv_numareanodes++; SV_WORLD.ClearLink(anode.trigger_edicts); SV_WORLD.ClearLink(anode.solid_edicts); if (depth == Defines.AREA_DEPTH) { anode.axis = -1; anode.children[0] = anode.children[1] = null; return(anode); } Math3D.VectorSubtract(maxs, mins, size); if (size[0] > size[1]) { anode.axis = 0; } else { anode.axis = 1; } anode.dist = 0.5f * (maxs[anode.axis] + mins[anode.axis]); Math3D.VectorCopy(mins, mins1); Math3D.VectorCopy(mins, mins2); Math3D.VectorCopy(maxs, maxs1); Math3D.VectorCopy(maxs, maxs2); maxs1[anode.axis] = mins2[anode.axis] = anode.dist; anode.children[0] = SV_WORLD.SV_CreateAreaNode(depth + 1, mins2, maxs2); anode.children[1] = SV_WORLD.SV_CreateAreaNode(depth + 1, mins1, maxs1); return(anode); }
/* * ================== SV_Trace * * Moves the given mins/maxs volume through the world from start to end. * * Passedict and edicts owned by passedict are explicitly not checked. * * ================== */ public static trace_t SV_Trace(float[] start, float[] mins, float[] maxs, float[] end, edict_t passedict, int contentmask) { moveclip_t clip = new(); if (mins == null) { mins = Globals.vec3_origin; } if (maxs == null) { maxs = Globals.vec3_origin; } // clip to world clip.trace = CM.BoxTrace(start, end, mins, maxs, 0, contentmask); clip.trace.ent = GameBase.g_edicts[0]; if (clip.trace.fraction == 0) { return(clip.trace); // blocked by the world } clip.contentmask = contentmask; clip.start = start; clip.end = end; clip.mins = mins; clip.maxs = maxs; clip.passedict = passedict; Math3D.VectorCopy(mins, clip.mins2); Math3D.VectorCopy(maxs, clip.maxs2); // create the bounding box of the entire move SV_WORLD.SV_TraceBounds(start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs); // clip to other solid entities SV_WORLD.SV_ClipMoveToEntities(clip); return(clip.trace); }
/** * PF_setmodel * * Also sets mins and maxs for inline bmodels. */ public static void PF_setmodel(edict_t ent, string name) { int i; cmodel_t mod; if (name == null) { Com.Error(Defines.ERR_DROP, "PF_setmodel: NULL"); } i = SV_INIT.SV_ModelIndex(name); ent.s.modelindex = i; // if it is an inline model, get the size information for it if (name.StartsWith("*")) { mod = CM.InlineModel(name); Math3D.VectorCopy(mod.mins, ent.mins); Math3D.VectorCopy(mod.maxs, ent.maxs); SV_WORLD.SV_LinkEdict(ent); } }
/** * SV_SpawnServer. * * Change the server to a new map, taking all connected clients along with * it. */ public static void SV_SpawnServer(string server, string spawnpoint, int serverstate, bool attractloop, bool loadgame) { int i; var checksum = 0; if (attractloop) { Cvar.Set("paused", "0"); } Com.Printf("------- Server Initialization -------\n"); Com.DPrintf("SpawnServer: " + server + "\n"); if (SV_INIT.sv.demofile != null) { try { SV_INIT.sv.demofile.Close(); } catch (Exception) { } } // any partially connected client will be restarted SV_INIT.svs.spawncount++; SV_INIT.sv.state = Defines.ss_dead; Globals.server_state = SV_INIT.sv.state; // wipe the entire per-level structure SV_INIT.sv = new(); SV_INIT.svs.realtime = 0; SV_INIT.sv.loadgame = loadgame; SV_INIT.sv.attractloop = attractloop; // save name for levels that don't set message SV_INIT.sv.configstrings[Defines.CS_NAME] = server; if (Cvar.VariableValue("deathmatch") != 0) { SV_INIT.sv.configstrings[Defines.CS_AIRACCEL] = "" + SV_MAIN.sv_airaccelerate.value; PMove.pm_airaccelerate = SV_MAIN.sv_airaccelerate.value; } else { SV_INIT.sv.configstrings[Defines.CS_AIRACCEL] = "0"; PMove.pm_airaccelerate = 0; } SZ.Init(SV_INIT.sv.multicast, SV_INIT.sv.multicast_buf, SV_INIT.sv.multicast_buf.Length); SV_INIT.sv.name = server; // leave slots at start for clients only for (i = 0; i < SV_MAIN.maxclients.value; i++) { // needs to reconnect if (SV_INIT.svs.clients[i].state > Defines.cs_connected) { SV_INIT.svs.clients[i].state = Defines.cs_connected; } SV_INIT.svs.clients[i].lastframe = -1; } SV_INIT.sv.time = 1000; SV_INIT.sv.name = server; SV_INIT.sv.configstrings[Defines.CS_NAME] = server; int[] iw = { checksum }; if (serverstate != Defines.ss_game) { SV_INIT.sv.models[1] = CM.CM_LoadMap("", false, iw); // no real map } else { SV_INIT.sv.configstrings[Defines.CS_MODELS + 1] = "maps/" + server + ".bsp"; SV_INIT.sv.models[1] = CM.CM_LoadMap(SV_INIT.sv.configstrings[Defines.CS_MODELS + 1], false, iw); } checksum = iw[0]; SV_INIT.sv.configstrings[Defines.CS_MAPCHECKSUM] = "" + checksum; // clear physics interaction links SV_WORLD.SV_ClearWorld(); for (i = 1; i < CM.CM_NumInlineModels(); i++) { SV_INIT.sv.configstrings[Defines.CS_MODELS + 1 + i] = "*" + i; // copy references SV_INIT.sv.models[i + 1] = CM.InlineModel(SV_INIT.sv.configstrings[Defines.CS_MODELS + 1 + i]); } // spawn the rest of the entities on the map // precache and static commands can be issued during // map initialization SV_INIT.sv.state = Defines.ss_loading; Globals.server_state = SV_INIT.sv.state; // load and spawn all other entities GameSpawn.SpawnEntities(SV_INIT.sv.name, CM.CM_EntityString(), spawnpoint); // run two frames to allow everything to settle GameBase.G_RunFrame(); GameBase.G_RunFrame(); // all precaches are complete SV_INIT.sv.state = serverstate; Globals.server_state = SV_INIT.sv.state; // create a baseline for more efficient communications SV_INIT.SV_CreateBaseline(); // check for a savegame SV_INIT.SV_CheckForSavegame(); // set serverinfo variable Cvar.FullSet("mapname", SV_INIT.sv.name, Defines.CVAR_SERVERINFO | Defines.CVAR_NOSET); }
public static void SV_ClipMoveToEntities(moveclip_t clip) { int i, num; edict_t touch; trace_t trace; int headnode; float[] angles; num = SV_WORLD.SV_AreaEdicts(clip.boxmins, clip.boxmaxs, SV_WORLD.touchlist, Defines.MAX_EDICTS, Defines.AREA_SOLID); // be careful, it is possible to have an entity in this // list removed before we get to it (killtriggered) for (i = 0; i < num; i++) { touch = SV_WORLD.touchlist[i]; if (touch.solid == Defines.SOLID_NOT) { continue; } if (touch == clip.passedict) { continue; } if (clip.trace.allsolid) { return; } if (clip.passedict != null) { if (touch.owner == clip.passedict) { continue; // don't clip against own missiles } if (clip.passedict.owner == touch) { continue; // don't clip against owner } } if (0 == (clip.contentmask & Defines.CONTENTS_DEADMONSTER) && 0 != (touch.svflags & Defines.SVF_DEADMONSTER)) { continue; } // might intersect, so do an exact clip headnode = SV_WORLD.SV_HullForEntity(touch); angles = touch.s.angles; if (touch.solid != Defines.SOLID_BSP) { angles = Globals.vec3_origin; // boxes don't rotate } if ((touch.svflags & Defines.SVF_MONSTER) != 0) { trace = CM.TransformedBoxTrace(clip.start, clip.end, clip.mins2, clip.maxs2, headnode, clip.contentmask, touch.s.origin, angles); } else { trace = CM.TransformedBoxTrace(clip.start, clip.end, clip.mins, clip.maxs, headnode, clip.contentmask, touch.s.origin, angles); } if (trace.allsolid || trace.startsolid || trace.fraction < clip.trace.fraction) { trace.ent = touch; if (clip.trace.startsolid) { clip.trace = trace; clip.trace.startsolid = true; } else { clip.trace.set(trace); } } else if (trace.startsolid) { clip.trace.startsolid = true; } } }
static SV_WORLD() { SV_WORLD.initNodes(); }
public static void SV_LinkEdict(edict_t ent) { areanode_t node; int num_leafs; int j, k; int area; var topnode = 0; if (ent.area.prev != null) { SV_WORLD.SV_UnlinkEdict(ent); // unlink from old position } if (ent == GameBase.g_edicts[0]) { return; // don't add the world } if (!ent.inuse) { return; } // set the size Math3D.VectorSubtract(ent.maxs, ent.mins, ent.size); // encode the size into the entity_state for client prediction if (ent.solid == Defines.SOLID_BBOX && 0 == (ent.svflags & Defines.SVF_DEADMONSTER)) { // assume that x/y are equal and symetric var i = (int)(ent.maxs[0] / 8); if (i < 1) { i = 1; } if (i > 31) { i = 31; } // z is not symetric j = (int)(-ent.mins[2] / 8); if (j < 1) { j = 1; } if (j > 31) { j = 31; } // and z maxs can be negative... k = (int)((ent.maxs[2] + 32) / 8); if (k < 1) { k = 1; } if (k > 63) { k = 63; } ent.s.solid = (k << 10) | (j << 5) | i; } else if (ent.solid == Defines.SOLID_BSP) { ent.s.solid = 31; // a solid_bbox will never create this value } else { ent.s.solid = 0; } // set the abs box if (ent.solid == Defines.SOLID_BSP && (ent.s.angles[0] != 0 || ent.s.angles[1] != 0 || ent.s.angles[2] != 0)) { // expand for rotation float max, v; max = 0; for (var i = 0; i < 3; i++) { v = Math.Abs(ent.mins[i]); if (v > max) { max = v; } v = Math.Abs(ent.maxs[i]); if (v > max) { max = v; } } for (var i = 0; i < 3; i++) { ent.absmin[i] = ent.s.origin[i] - max; ent.absmax[i] = ent.s.origin[i] + max; } } else { // normal Math3D.VectorAdd(ent.s.origin, ent.mins, ent.absmin); Math3D.VectorAdd(ent.s.origin, ent.maxs, ent.absmax); } // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch ent.absmin[0]--; ent.absmin[1]--; ent.absmin[2]--; ent.absmax[0]++; ent.absmax[1]++; ent.absmax[2]++; // link to PVS leafs ent.num_clusters = 0; ent.areanum = 0; ent.areanum2 = 0; // get all leafs, including solids int[] iw = { topnode }; num_leafs = CM.CM_BoxLeafnums(ent.absmin, ent.absmax, SV_WORLD.leafs, SV_WORLD.MAX_TOTAL_ENT_LEAFS, iw); topnode = iw[0]; // set areas for (var i = 0; i < num_leafs; i++) { SV_WORLD.clusters[i] = CM.CM_LeafCluster(SV_WORLD.leafs[i]); area = CM.CM_LeafArea(SV_WORLD.leafs[i]); if (area != 0) { // doors may legally straggle two areas, // but nothing should evern need more than that if (ent.areanum != 0 && ent.areanum != area) { if (ent.areanum2 != 0 && ent.areanum2 != area && SV_INIT.sv.state == Defines.ss_loading) { Com.DPrintf("Object touching 3 areas at " + ent.absmin[0] + " " + ent.absmin[1] + " " + ent.absmin[2] + "\n"); } ent.areanum2 = area; } else { ent.areanum = area; } } } if (num_leafs >= SV_WORLD.MAX_TOTAL_ENT_LEAFS) { // assume we missed some leafs, and mark by headnode ent.num_clusters = -1; ent.headnode = topnode; } else { ent.num_clusters = 0; for (var i = 0; i < num_leafs; i++) { if (SV_WORLD.clusters[i] == -1) { continue; // not a visible leaf } for (j = 0; j < i; j++) { if (SV_WORLD.clusters[j] == SV_WORLD.clusters[i]) { break; } } if (j == i) { if (ent.num_clusters == Defines.MAX_ENT_CLUSTERS) { // assume we missed some leafs, and mark by headnode ent.num_clusters = -1; ent.headnode = topnode; break; } ent.clusternums[ent.num_clusters++] = SV_WORLD.clusters[i]; } } } // if first time, make sure old_origin is valid if (0 == ent.linkcount) { Math3D.VectorCopy(ent.s.origin, ent.s.old_origin); } ent.linkcount++; if (ent.solid == Defines.SOLID_NOT) { return; } // find the first node that the ent's box crosses node = SV_WORLD.sv_areanodes[0]; while (true) { if (node.axis == -1) { break; } if (ent.absmin[node.axis] > node.dist) { node = node.children[0]; } else if (ent.absmax[node.axis] < node.dist) { node = node.children[1]; } else { break; // crosses the node } } // link it in if (ent.solid == Defines.SOLID_TRIGGER) { SV_WORLD.InsertLinkBefore(ent.area, node.trigger_edicts); } else { SV_WORLD.InsertLinkBefore(ent.area, node.solid_edicts); } }