// ED_Count // // For debugging static void EdictCount() { int active = 0, models = 0, solid = 0, step = 0; for (int i = 0; i < Server.sv.num_edicts; i++) { edict_t ent = Server.EdictNum(i); if (ent.free) { continue; } active++; if (ent.v.solid != 0) { solid++; } if (ent.v.model != 0) { models++; } if (ent.v.movetype == Movetypes.MOVETYPE_STEP) { step++; } } Con.Print("num_edicts:{0}\n", Server.sv.num_edicts); Con.Print("active :{0}\n", active); Con.Print("view :{0}\n", models); Con.Print("touch :{0}\n", solid); Con.Print("step :{0}\n", step); }
/// <summary> /// PF_checkclient /// Returns a client (or object that has a client enemy) that would be a /// valid target. /// /// If there are more than one valid options, they are cycled each frame /// /// If (self.origin + self.viewofs) is not in the PVS of the current target, /// it is not returned at all. /// /// name checkclient () /// </summary> static void PF_checkclient() { // find a new check if on a new frame if (Server.sv.time - Server.sv.lastchecktime >= 0.1) { Server.sv.lastcheck = PF_newcheckclient(Server.sv.lastcheck); Server.sv.lastchecktime = Server.sv.time; } // return check if it might be visible edict_t ent = Server.EdictNum(Server.sv.lastcheck); if (ent.free || ent.v.health <= 0) { ReturnEdict(Server.sv.edicts[0]); return; } // if current entity can't possibly see the check entity, return 0 edict_t self = Server.ProgToEdict(Progs.GlobalStruct.self); Vector3 view = Common.ToVector(ref self.v.origin) + Common.ToVector(ref self.v.view_ofs); mleaf_t leaf = Mod.PointInLeaf(ref view, Server.sv.worldmodel); int l = Array.IndexOf(Server.sv.worldmodel.leafs, leaf) - 1; if ((l < 0) || (_CheckPvs[l >> 3] & (1 << (l & 7))) == 0) { _NotVisCount++; ReturnEdict(Server.sv.edicts[0]); return; } // might be able to see it _InVisCount++; ReturnEdict(ent); }
/// <summary> /// PF_Find /// entity (entity start, .string field, string match) find = #5; /// </summary> static void PF_Find() { int e = GetInt(OFS.OFS_PARM0); int f = GetInt(OFS.OFS_PARM1); string s = GetString(OFS.OFS_PARM2); if (s == null) { Progs.RunError("PF_Find: bad search string"); } for (e++; e < Server.sv.num_edicts; e++) { edict_t ed = Server.EdictNum(e); if (ed.free) { continue; } string t = Progs.GetString(ed.GetInt(f)); // E_STRING(ed, f); if (String.IsNullOrEmpty(t)) { continue; } if (t == s) { ReturnEdict(ed); return; } } ReturnEdict(Server.sv.edicts[0]); }
private static edict_t FindViewthing() { for (int i = 0; i < Server.sv.num_edicts; i++) { edict_t e = Server.EdictNum(i); if (Progs.GetString(e.v.classname) == "viewthing") { return(e); } } Con.Print("No viewthing on map\n"); return(null); }
/// <summary> /// ED_ParseEpair /// Can parse either fields or globals returns false if error /// Uze: Warning! value pointer is already with correct offset (value = base + key.ofs)! /// </summary> static unsafe bool ParsePair(void *value, ddef_t key, string s) { void *d = value;// (void *)((int *)base + key->ofs); switch ((etype_t)(key.type & ~DEF_SAVEGLOBAL)) { case etype_t.ev_string: *(int *)d = NewString(s); // - pr_strings; break; case etype_t.ev_float: *(float *)d = Common.atof(s); break; case etype_t.ev_vector: string[] vs = s.Split(' '); ((float *)d)[0] = Common.atof(vs[0]); ((float *)d)[1] = (vs.Length > 1 ? Common.atof(vs[1]) : 0); ((float *)d)[2] = (vs.Length > 2 ? Common.atof(vs[2]) : 0); break; case etype_t.ev_entity: *(int *)d = Server.EdictToProg(Server.EdictNum(Common.atoi(s))); break; case etype_t.ev_field: int f = IndexOfField(s); if (f == -1) { Con.Print("Can't find field {0}\n", s); return(false); } *(int *)d = GetInt32(_FieldDefs[f].ofs); break; case etype_t.ev_function: int func = IndexOfFunction(s); if (func == -1) { Con.Print("Can't find function {0}\n", s); return(false); } *(int *)d = func; // - pr_functions; break; default: break; } return(true); }
/* * ============= * PF_nextent * * entity nextent(entity) * ============= */ static void PF_nextent() { int i = Server.NumForEdict(GetEdict(OFS.OFS_PARM0)); while (true) { i++; if (i == Server.sv.num_edicts) { ReturnEdict(Server.sv.edicts[0]); return; } edict_t ent = Server.EdictNum(i); if (!ent.free) { ReturnEdict(ent); return; } } }
/// <summary> /// Host_Loadgame_f /// </summary> private static void Loadgame_f() { if (Cmd.Source != cmd_source_t.src_command) { return; } if (Cmd.Argc != 2) { Con.Print("load <savename> : load a game\n"); return; } Client.cls.demonum = -1; // stop demo loop in case this fails string name = Path.ChangeExtension(Path.Combine(Common.GameDir, Cmd.Argv(1)), ".sav"); // we can't call SCR_BeginLoadingPlaque, because too much stack space has // been used. The menu calls it before stuffing loadgame command // SCR_BeginLoadingPlaque (); Con.Print("Loading game from {0}...\n", name); FileStream fs = Sys.FileOpenRead(name); if (fs == null) { Con.Print("ERROR: couldn't open.\n"); return; } using (StreamReader reader = new StreamReader(fs, Encoding.ASCII)) { string line = reader.ReadLine(); int version = Common.atoi(line); if (version != SAVEGAME_VERSION) { Con.Print("Savegame is version {0}, not {1}\n", version, SAVEGAME_VERSION); return; } line = reader.ReadLine(); float[] spawn_parms = new float[Server.NUM_SPAWN_PARMS]; for (int i = 0; i < spawn_parms.Length; i++) { line = reader.ReadLine(); spawn_parms[i] = Common.atof(line); } // this silliness is so we can load 1.06 save files, which have float skill values line = reader.ReadLine(); float tfloat = Common.atof(line); Host.CurrentSkill = (int)(tfloat + 0.1); Cvar.Set("skill", (float)Host.CurrentSkill); string mapname = reader.ReadLine(); line = reader.ReadLine(); float time = Common.atof(line); Client.Disconnect_f(); Server.SpawnServer(mapname); if (!Server.sv.active) { Con.Print("Couldn't load map\n"); return; } Server.sv.paused = true; // pause until all clients connect Server.sv.loadgame = true; // load the light styles for (int i = 0; i < QDef.MAX_LIGHTSTYLES; i++) { line = reader.ReadLine(); Server.sv.lightstyles[i] = line; } // load the edicts out of the savegame file int entnum = -1; // -1 is the globals StringBuilder sb = new StringBuilder(32768); while (!reader.EndOfStream) { line = reader.ReadLine(); if (line == null) { Sys.Error("EOF without closing brace"); } sb.AppendLine(line); int idx = line.IndexOf('}'); if (idx != -1) { int length = 1 + sb.Length - (line.Length - idx); string data = Common.Parse(sb.ToString(0, length)); if (String.IsNullOrEmpty(Common.Token)) { break; // end of file } if (Common.Token != "{") { Sys.Error("First token isn't a brace"); } if (entnum == -1) { // parse the global vars Progs.ParseGlobals(data); } else { // parse an edict edict_t ent = Server.EdictNum(entnum); ent.Clear(); Progs.ParseEdict(data, ent); // link it into the bsp tree if (!ent.free) { Server.LinkEdict(ent, false); } } entnum++; sb.Remove(0, length); } } Server.sv.num_edicts = entnum; Server.sv.time = time; for (int i = 0; i < Server.NUM_SPAWN_PARMS; i++) { Server.svs.clients[0].spawn_parms[i] = spawn_parms[i]; } } if (Client.cls.state != cactive_t.ca_dedicated) { Client.EstablishConnection("local"); Reconnect_f(); } }
/// <summary> /// Host_Savegame_f /// </summary> private static void Savegame_f() { if (Cmd.Source != cmd_source_t.src_command) { return; } if (!Server.sv.active) { Con.Print("Not playing a local game.\n"); return; } if (Client.cl.intermission != 0) { Con.Print("Can't save in intermission.\n"); return; } if (Server.svs.maxclients != 1) { Con.Print("Can't save multiplayer games.\n"); return; } if (Cmd.Argc != 2) { Con.Print("save <savename> : save a game\n"); return; } if (Cmd.Argv(1).Contains("..")) { Con.Print("Relative pathnames are not allowed.\n"); return; } for (int i = 0; i < Server.svs.maxclients; i++) { if (Server.svs.clients[i].active && (Server.svs.clients[i].edict.v.health <= 0)) { Con.Print("Can't savegame with a dead player\n"); return; } } string name = Path.ChangeExtension(Path.Combine(Common.GameDir, Cmd.Argv(1)), ".sav"); Con.Print("Saving game to {0}...\n", name); FileStream fs = Sys.FileOpenWrite(name, true); if (fs == null) { Con.Print("ERROR: couldn't open.\n"); return; } using (StreamWriter writer = new StreamWriter(fs, Encoding.ASCII)) { writer.WriteLine(SAVEGAME_VERSION); writer.WriteLine(SavegameComment()); for (int i = 0; i < Server.NUM_SPAWN_PARMS; i++) { writer.WriteLine(Server.svs.clients[0].spawn_parms[i].ToString("F6", CultureInfo.InvariantCulture.NumberFormat)); } writer.WriteLine(Host.CurrentSkill); writer.WriteLine(Server.sv.name); writer.WriteLine(Server.sv.time.ToString("F6", CultureInfo.InvariantCulture.NumberFormat)); // write the light styles for (int i = 0; i < QDef.MAX_LIGHTSTYLES; i++) { if (!String.IsNullOrEmpty(Server.sv.lightstyles[i])) { writer.WriteLine(Server.sv.lightstyles[i]); } else { writer.WriteLine("m"); } } Progs.WriteGlobals(writer); for (int i = 0; i < Server.sv.num_edicts; i++) { Progs.WriteEdict(writer, Server.EdictNum(i)); writer.Flush(); } } Con.Print("done.\n"); }
/// <summary> /// Host_Spawn_f /// </summary> private static void Spawn_f() { if (Cmd.Source == cmd_source_t.src_command) { Con.Print("spawn is not valid from the console\n"); return; } if (Host.HostClient.spawned) { Con.Print("Spawn not valid -- allready spawned\n"); return; } edict_t ent; // run the entrance script if (Server.sv.loadgame) { // loaded games are fully inited allready // if this is the last client to be connected, unpause Server.sv.paused = false; } else { // set up the edict ent = Host.HostClient.edict; ent.Clear(); //memset(&ent.v, 0, progs.entityfields * 4); ent.v.colormap = Server.NumForEdict(ent); ent.v.team = (Host.HostClient.colors & 15) + 1; ent.v.netname = Progs.NewString(Host.HostClient.name); // copy spawn parms out of the client_t Progs.GlobalStruct.SetParams(Host.HostClient.spawn_parms); // call the spawn function Progs.GlobalStruct.time = (float)Server.sv.time; Progs.GlobalStruct.self = Server.EdictToProg(Server.Player); Progs.Execute(Progs.GlobalStruct.ClientConnect); if ((Sys.GetFloatTime() - Host.HostClient.netconnection.connecttime) <= Server.sv.time) { Con.DPrint("{0} entered the game\n", Host.HostClient.name); } Progs.Execute(Progs.GlobalStruct.PutClientInServer); } // send all current names, colors, and frag counts MsgWriter msg = Host.HostClient.message; msg.Clear(); // send time of update msg.WriteByte(Protocol.svc_time); msg.WriteFloat((float)Server.sv.time); for (int i = 0; i < Server.svs.maxclients; i++) { client_t client = Server.svs.clients[i]; msg.WriteByte(Protocol.svc_updatename); msg.WriteByte(i); msg.WriteString(client.name); msg.WriteByte(Protocol.svc_updatefrags); msg.WriteByte(i); msg.WriteShort(client.old_frags); msg.WriteByte(Protocol.svc_updatecolors); msg.WriteByte(i); msg.WriteByte(client.colors); } // send all current light styles for (int i = 0; i < QDef.MAX_LIGHTSTYLES; i++) { msg.WriteByte(Protocol.svc_lightstyle); msg.WriteByte((char)i); msg.WriteString(Server.sv.lightstyles[i]); } // // send some stats // msg.WriteByte(Protocol.svc_updatestat); msg.WriteByte(QStats.STAT_TOTALSECRETS); msg.WriteLong((int)Progs.GlobalStruct.total_secrets); msg.WriteByte(Protocol.svc_updatestat); msg.WriteByte(QStats.STAT_TOTALMONSTERS); msg.WriteLong((int)Progs.GlobalStruct.total_monsters); msg.WriteByte(Protocol.svc_updatestat); msg.WriteByte(QStats.STAT_SECRETS); msg.WriteLong((int)Progs.GlobalStruct.found_secrets); msg.WriteByte(Protocol.svc_updatestat); msg.WriteByte(QStats.STAT_MONSTERS); msg.WriteLong((int)Progs.GlobalStruct.killed_monsters); // // send a fixangle // Never send a roll angle, because savegames can catch the server // in a state where it is expecting the client to correct the angle // and it won't happen if the game was just loaded, so you wind up // with a permanent head tilt ent = Server.EdictNum(1 + Host.ClientNum); msg.WriteByte(Protocol.svc_setangle); msg.WriteAngle(ent.v.angles.x); msg.WriteAngle(ent.v.angles.y); msg.WriteAngle(0); Server.WriteClientDataToMessage(Server.Player, Host.HostClient.message); msg.WriteByte(Protocol.svc_signonnum); msg.WriteByte(3); Host.HostClient.sendsignon = true; }
/// <summary> /// ED_LoadFromFile /// The entities are directly placed in the array, rather than allocated with /// ED_Alloc, because otherwise an error loading the map would have entity /// number references out of order. /// /// Creates a server's entity / program execution context by /// parsing textual entity definitions out of an ent file. /// /// Used for both fresh maps and savegame loads. A fresh map would also need /// to call ED_CallSpawnFunctions () to let the objects initialize themselves. /// </summary> public static void LoadFromFile(string data) { edict_t ent = null; int inhibit = 0; Progs.GlobalStruct.time = (float)Server.sv.time; // parse ents while (true) { // parse the opening brace data = Common.Parse(data); if (data == null) { break; } if (Common.Token != "{") { Sys.Error("ED_LoadFromFile: found {0} when expecting {", Common.Token); } if (ent == null) { ent = Server.EdictNum(0); } else { ent = Server.AllocEdict(); } data = ParseEdict(data, ent); // remove things from different skill levels or deathmatch if (Host.Deathmatch != 0) { if (((int)ent.v.spawnflags & SpawnFlags.SPAWNFLAG_NOT_DEATHMATCH) != 0) { Server.FreeEdict(ent); inhibit++; continue; } } else if ((Host.CurrentSkill == 0 && ((int)ent.v.spawnflags & SpawnFlags.SPAWNFLAG_NOT_EASY) != 0) || (Host.CurrentSkill == 1 && ((int)ent.v.spawnflags & SpawnFlags.SPAWNFLAG_NOT_MEDIUM) != 0) || (Host.CurrentSkill >= 2 && ((int)ent.v.spawnflags & SpawnFlags.SPAWNFLAG_NOT_HARD) != 0)) { Server.FreeEdict(ent); inhibit++; continue; } // // immediately call spawn function // if (ent.v.classname == 0) { Con.Print("No classname for:\n"); Print(ent); Server.FreeEdict(ent); continue; } // look for the spawn function int func = IndexOfFunction(GetString(ent.v.classname)); if (func == -1) { Con.Print("No spawn function for:\n"); Print(ent); Server.FreeEdict(ent); continue; } Progs.GlobalStruct.self = Server.EdictToProg(ent); Execute(func); } Con.DPrint("{0} entities inhibited\n", inhibit); }
/// <summary> /// ED_PrintNum /// </summary> public static void PrintNum(int ent) { Print(Server.EdictNum(ent)); }
static int PF_newcheckclient(int check) { // cycle to the next one if (check < 1) { check = 1; } if (check > Server.svs.maxclients) { check = Server.svs.maxclients; } int i = check + 1; if (check == Server.svs.maxclients) { i = 1; } edict_t ent; for (; ; i++) { if (i == Server.svs.maxclients + 1) { i = 1; } ent = Server.EdictNum(i); if (i == check) { break; // didn't find anything else } if (ent.free) { continue; } if (ent.v.health <= 0) { continue; } if (((int)ent.v.flags & EdictFlags.FL_NOTARGET) != 0) { continue; } // anything that is a client, or has a client as an enemy break; } // get the PVS for the entity Vector3 org = Common.ToVector(ref ent.v.origin) + Common.ToVector(ref ent.v.view_ofs); mleaf_t leaf = Mod.PointInLeaf(ref org, Server.sv.worldmodel); byte[] pvs = Mod.LeafPVS(leaf, Server.sv.worldmodel); Buffer.BlockCopy(pvs, 0, _CheckPvs, 0, pvs.Length); return(i); }