public unsafe void GetBonePosition(Edict edict, int bone, out Vector origin, out Vector angles) { EngineFuncs.pfnGetBonePosition(edict.Data, bone, out origin, out angles); //TODO: engine never sets angles angles = new Vector(); }
public unsafe void Free(Edict edict) { if (edict?.Free != false) { return; } var index = EntityIndex(edict); if (HighestInUse == index) { //Recalculate //if this is the last entity then we won't find a new highest HighestInUse = -1; for (var i = index - 1; i >= 0; --i) { if (!EdictByIndex(i).Free) { HighestInUse = i; break; } } } EngineFuncs.pfnRemoveEntity(edict.Data); }
public static void ScriptEntityCancel(Edict pentCine) { // make sure they are a scripted_sequence if (FClassnameIs(pentCine, CLASSNAME)) { CCineMonster pCineTarget = GetClassPtr((CCineMonster)VARS(pentCine)); // make sure they have a monster in mind for the script BaseEntity pEntity = pCineTarget.m_hTargetEnt; CBaseMonster pTarget = null; if (pEntity != null) { pTarget = pEntity.MyMonsterPointer(); } if (pTarget) { // make sure their monster is actually playing a script if (pTarget.m_MonsterState == MONSTERSTATE_SCRIPT) { // tell them do die pTarget.m_scriptState = CCineMonster.SCRIPT_CLEANUP; // do it now pTarget.CineCleanup(); } } } }
public unsafe void GetAttachment(Edict edict, int attachment, out Vector origin, out Vector angles) { EngineFuncs.pfnGetAttachment(edict.Data, attachment, out origin, out angles); //TODO: engine never sets angles angles = new Vector(); }
public BaseEntity CreateInstance(EntityInfo info, Edict edict) { if (info == null) { throw new ArgumentNullException(nameof(info)); } try { var entity = (BaseEntity)Activator.CreateInstance(info.EntityClass); edict.PrivateData = entity; entity.pev = edict.Vars; entity.ClassName = info.PreferredName; //Inform of creation entity.OnCreate(); return(entity); } catch (Exception e) { Log.Message($"Couldn't create entity \"{info.PreferredName}\":"); Log.Exception(e); //On failure always free the edict //This will also free the entity instance if it has been assigned EntityDictionary.Free(edict); return(null); } }
public void Blocked(Edict pentBlocked, Edict pentOther) { var blocked = pentBlocked.Entity(); var other = pentOther.Entity(); blocked?.Blocked(other); }
public int Spawn(Edict pent) { var pEntity = pent.Entity(); if (pEntity != null) { // Initialize these or entities who don't link to the world won't have anything in here pEntity.AbsMin = pEntity.Origin - new Vector(1, 1, 1); pEntity.AbsMax = pEntity.Origin + new Vector(1, 1, 1); pEntity.Spawn(); // Try to get the pointer again, in case the spawn function deleted the entity. // UNDONE: Spawn() should really return a code to ask that the entity be deleted, but // that would touch too much code for me to do that right now. pEntity = pent.Entity(); if (pEntity != null) { if (!Engine.GameRules.IsAllowedToSpawn(pEntity)) { return(-1); // return that this entity should be deleted } if (0 != (pEntity.Flags & EntFlags.KillMe)) { return(-1); } } // Handle global stuff here if (pEntity != null && !string.IsNullOrEmpty(pEntity.GlobalName)) { var pGlobal = Globals.GlobalState.EntityFromTable(pEntity.GlobalName); if (pGlobal != null) { // Already dead? delete if (pGlobal.State == GlobalEState.Dead) { return(-1); } else if (Engine.Globals.MapName != pGlobal.LevelName) { pEntity.MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive } // In this level & not dead, continue on as normal } else { // Spawned entities default to 'On' Globals.GlobalState.EntityAdd(pEntity.GlobalName, Engine.Globals.MapName, GlobalEState.On); // Log.Alert(AlertType.Console, $"Added global entity {pEntity.ClassName} ({pEntity.GlobalName})\n"); } } } return(0); //TODO: define return codes }
public unsafe int EntityIndex(Edict edict) { if (edict == null) { throw new ArgumentNullException(nameof(edict)); } return(EntityIndex(edict.Data)); }
public unsafe IInfoBuffer GetClientPhysicsInfoBuffer(Edict pClient) { if (!ServerAPIUtils.IsClient(pClient, EntityDictionary, Globals)) { throw new ArgumentException("Edict must be a client", nameof(pClient)); } return(new ClientPhysicsInfoBuffer(EngineFuncs, pClient.Data, EntityDictionary.EntityIndex(pClient))); }
public unsafe IntPtr GetUnmanagedClientPhysicsInfoBuffer(Edict pClient) { if (!ServerAPIUtils.IsClient(pClient, EntityDictionary, Globals)) { throw new ArgumentException("Edict must be a client", nameof(pClient)); } return(EngineFuncs.pfnGetPhysicsInfoString(pClient.Data)); }
public void OnFreeEntPrivateData(Edict pEnt) { if (pEnt.PrivateData != null) { //Inform of destruction pEnt.Entity().OnDestroy(); //Mark the private data as freed, and set the managed instance to be garbage collected pEnt.PrivateData = null; } }
/// <summary> /// Called when a new client connects (unsquelches its entity for everyone) /// </summary> /// <param name="edict"></param> public void ClientConnected(Edict edict) { var index = EntUtils.EntIndex(edict) - 1; var data = Players[index]; // Clear out everything we use for deltas on this guy. data.WantEnable = true; data.SentGameRulesMasks = new BitVector32(); data.SentBanMasks = new BitVector32(); }
internal static bool IsClient(Edict edict, IEntityDictionary entityDictionary, IGlobalVars globalVars) { if (edict == null) { return(false); } var index = entityDictionary.EntityIndex(edict); return(0 < index && index <= globalVars.MaxClients); }
public void SetAbsBox(Edict pent) { var entity = pent.TryGetEntity(); if (entity == null) { throw new InvalidOperationException($"Error: Entity \"{pent.Vars.ClassName}\" (index {EntUtils.EntIndex(pent)}) has no entity instance assigned for SetAbsBox call"); } entity.SetObjectCollisionBox(); }
public void Use(Edict pentUsed, Edict pentOther) { var used = pentUsed.Entity(); var other = pentOther.Entity(); if (used != null && (used.Flags & EntFlags.KillMe) == 0) { used.Use(other, other, UseType.Toggle, 0); } }
public unsafe INetworkMessage Begin(MsgDest msg_dest, int msg_type, Edict ed = null) { if (CurrentMessage != null) { throw new InvalidOperationException("Cannot start a network message while another message is active"); } EngineFuncs.pfnMessageBegin(msg_dest, msg_type, IntPtr.Zero, ed.Data); CurrentMessage = new NetworkMessage(EngineFuncs, this); return(CurrentMessage); }
public void KeyValue(Edict pentKeyvalue, KeyValueData pkvd) { //Log.Message($"Entity {EntityDictionary.EntityIndex(pentKeyvalue)}/{EntityDictionary.Max} KeyValue ClassName=\"{pkvd.ClassName}\" Key=\"{pkvd.KeyName}\" Value=\"{pkvd.Value}\""); if (pkvd.KeyName == "classname") { if (pentKeyvalue.PrivateData == null) { Log.Message($"Creating entity \"{pkvd.Value}\""); //Create the entity instance EntityRegistry.CreateInstance <BaseEntity>(pentKeyvalue); Log.Message("Created entity"); } else { var ent = (BaseEntity)pentKeyvalue.PrivateData; //This should never happen if (pkvd.Value != ent.ClassName) { throw new InvalidOperationException($"Second occurence of classname keyvalue has different value (Expected: {ent.ClassName}, actual:{pkvd.Value})"); } pkvd.Handled = true; return; } } if (pentKeyvalue.PrivateData == null) { throw new InvalidOperationException($"Cannot set keyvalue \"{pkvd.KeyName}={pkvd.Value}\" on null entity of class {pkvd.ClassName}"); } pkvd.Handled = false; var entity = (BaseEntity)pentKeyvalue.PrivateData; //TODO: uniformly handle keyvalue initialization switch (pkvd.KeyName) { case "classname": { entity.ClassName = pkvd.Value; pkvd.Handled = true; break; } } }
public void Think(Edict pent) { var thinker = pent.Entity(); if (thinker != null) { if ((thinker.Flags & EntFlags.Dormant) != 0) { Log.Alert(AlertType.Error, $"Dormant entity {thinker.ClassName} is thinking!!"); } thinker.Think(); } }
internal unsafe EntityDictionary(EngineFuncs engineFuncs) { EngineFuncs = engineFuncs ?? throw new ArgumentNullException(nameof(engineFuncs)); var size = Marshal.SizeOf <Edict.Native>(); var dummyNative = Marshal.AllocHGlobal(size); //Zero memory using a zero initialized array var bytes = new byte[size]; Marshal.Copy(bytes, 0, dummyNative, size); EmptyEdict = new Edict((Edict.Native *)dummyNative.ToPointer()); }
public void PutInServer(Edict pEntity) { var player = EntityRegistry.CreateInstance <BasePlayer>(pEntity); player.SetCustomDecalFrames(-1); // Assume none; // Allocate a CBasePlayer for pev, and call spawn player.Spawn(); // Reset interpolation during first frame player.Effects |= EntityEffects.NoInterp; player.pev.UserInt1 = 0; // disable any spec modes player.pev.UserInt2 = 0; }
public void Touch(Edict pentTouched, Edict pentOther) { if (TouchDisabled) { return; } var touched = pentTouched.Entity(); var other = pentOther.Entity(); if (touched != null && other != null && ((touched.Flags | other.Flags) & EntFlags.KillMe) == 0) { touched.Touch(other); } }
public void EmitSound(Edict entity, SoundChannel channel, string sample, float volume, float attenuation, SoundFlags flags, int pitch) { if (sample?.FirstOrDefault() == '!') { if (Sentences.GetSentenceIndex(sample, out var name) >= 0) { EmitSound2(entity, channel, name, volume, attenuation, flags, pitch); } else { Log.Alert(AlertType.AIConsole, $"Unable to find ^{sample} in sentences.txt"); } } else { EmitSound2(entity, channel, sample, volume, attenuation, flags, pitch); } }
public void SetModel(Edict edict, string modelName) { if (modelName == null) { throw new ArgumentNullException(nameof(modelName)); } //TODO: brush models should be precached manaully to match the engine //TODO: use a default model so it will always succeed if (!BrushModelNameRegex.IsMatch(modelName) && EngineServer.ModelCache.Find(modelName) == null) { throw new NotPrecachedException($"Model \"{modelName}\" not precached"); } //The engine sets the model name using what we've passed in var pooled = StringPool.GetPooledString(modelName); EngineFuncs.pfnSetModel(edict.Data, pooled); }
public unsafe object GetModel(Edict edict) { if (edict == null) { return(null); } var modelName = edict.Vars.ModelName; if (!string.IsNullOrEmpty(modelName)) { return(null); } if (Models.TryGetValue(modelName, out var model)) { return(model); } var nativeData = EngineFuncs.pfnGetModelPtr(edict.Data); if (modelName.StartsWith('*')) { //BSP model } else if (modelName.EndsWith(".mdl")) { //Studio model model = new StudioHeader((StudioHeader.Native *)nativeData.ToPointer()); } else if (modelName.EndsWith(".spr")) { //Sprite } if (model != null) { Models.Add(modelName, model); } return(model); }
public void Command(Edict pEntity, ICommand command) { // Is the client spawned yet? var player = pEntity.TryGetEntity <BasePlayer>(); if (player == null) { return; } var commandName = command.Name; //TODO: implement commands if (commandName == PlayerUtils.SayCommandName) { PlayerUtils.HostSay(player, command, false); } else if (commandName == PlayerUtils.SayTeamCommandName) { PlayerUtils.HostSay(player, command, true); } else if (commandName == "closemenus") { // just ignore it } else if (Engine.GameRules.ClientCommand(player, command)) { // MenuSelect returns true only if the command is properly handled, so don't print a warning } else { // check the length of the command (prevents crash) // max total length is 192 ...and we're adding a string below ("Unknown command: %s\n") var printableName = commandName.Length > 127 ? commandName.Substring(0, 127) : commandName; // tell the user they entered an unknown command PlayerUtils.ClientPrint(player, HudPrint.Console, $"Unknown command: {printableName}\n"); } }
public void SetupVisibility(Edict pViewEntity, Edict pClient, out IntPtr pvs, out IntPtr pas) { var client = pClient.Entity(); // Find the client's PVS var view = pViewEntity?.TryGetEntity() ?? client; if ((client.Flags & EntFlags.Proxy) != 0) { pvs = IntPtr.Zero; // the spectator proxy sees pas = IntPtr.Zero; // and hears everything return; } var org = view.Origin + view.ViewOffset; if ((view.Flags & EntFlags.Ducking) != 0) { org += (WorldConstants.HULL_MIN - WorldConstants.DUCK_HULL_MIN); } pvs = EngineServer.SetFatPVS(org); pas = EngineServer.SetFatPAS(org); }
public void UpdateClientData(Edict ent, bool sendWeapons, ClientData cd) { if (ent == null || ent.PrivateData == null) { return; } var player = ent.TryGetEntity <BasePlayer>(); BasePlayer originalPlayer = null; // if user is spectating different player in First person, override some vars if (player?.pev.UserInt1 == (int)ObserverMode.InEye && player.m_hObserverTarget) { originalPlayer = player; player = player.m_hObserverTarget; } cd.Flags = player.Flags; cd.Health = player.Health; cd.ViewModel = EngineModel.IndexOf(player.ViewModelName); cd.WaterLevel = player.WaterLevel; cd.WaterType = player.WaterType; cd.Weapons = player.pev.Weapons; // Vectors cd.Origin = player.Origin; cd.Velocity = player.Velocity; cd.ViewOffset = player.ViewOffset; cd.PunchAngle = player.PunchAngle; cd.InDuck = player.pev.InDuck; cd.TimeStepSound = player.pev.TimeStepSound; cd.DuckTime = player.pev.DuckTime; cd.SwimTime = player.pev.SwimTime; cd.WaterJumpTime = (int)player.pev.TeleportTime; cd.SetPhysInfo(EngineServer.GetUnmanagedClientPhysicsInfoBuffer(ent)); cd.MaxSpeed = player.pev.MaxSpeed; cd.FieldOfView = player.pev.FieldOfView; cd.WeaponAnim = player.pev.WeaponAnim; cd.PushMSec = player.pev.PushMSec; //Spectator mode if (originalPlayer != null) { // don't use spec vars from chased player cd.UserInt1 = originalPlayer.pev.UserInt1; cd.UserInt2 = originalPlayer.pev.UserInt2; } else { cd.UserInt1 = player.pev.UserInt1; cd.UserInt2 = player.pev.UserInt2; } #if CLIENT_WEAPONS if (sendWeapons && player != null) { cd.NextAttack = player.m_flNextAttack; cd.UserFloat2 = player.m_flNextAmmoBurn; cd.UserFloat3 = player.m_flAmmoStartCharge; cd.UserVector1 = new Vector( player.ammo_9mm, player.ammo_357, player.ammo_argrens ); cd.AmmoNails = player.ammo_bolts; cd.AmmoShells = player.ammo_buckshot; cd.AmmoRockets = player.ammo_rockets; cd.AmmoCells = player.ammo_uranium; var vector2 = cd.UserVector2; vector2.x = player.ammo_hornets; cd.UserVector2 = vector2; if (player.m_pActiveItem != null) { BasePlayerWeapon gun = player.m_pActiveItem as BasePlayerWeapon; if (gun?.IsPredicted() == true) { //TODO: #if false ItemInfo II; memset(&II, 0, sizeof(II)); gun.GetItemInfo(&II); cd.m_iId = II.iId; cd.vuser3.z = gun.m_iSecondaryAmmoType; cd.vuser4.x = gun.m_iPrimaryAmmoType; cd.vuser4.y = player.m_rgAmmo[gun.m_iPrimaryAmmoType]; cd.vuser4.z = player.m_rgAmmo[gun.m_iSecondaryAmmoType]; if (player.m_pActiveItem.m_iId == WEAPON_RPG) { cd.vuser2.y = ((Rpg)player.m_pActiveItem).m_fSpotActive; cd.vuser2.z = ((Rpg)player.m_pActiveItem).m_cActiveRockets; } #endif } } } #endif }
public void CreateBaseline(bool isPlayer, int eindex, EntityState baseline, Edict entity, int playermodelindex, in Vector playerMins, in Vector playerMaxs)
public bool AddToFullPack(EntityState state, int e, Edict ent, Edict host, HostFlags hostFlags, bool isPlayer, IntPtr pSet) { //Never add entities that aren't in use if (ent.Free) { return(false); } var entity = ent.Entity(); var hostEntity = host.Entity(); // don't send if flagged for NODRAW and it's not the host getting the message if ((entity.Effects & EntityEffects.NoDraw) != 0 && (entity != hostEntity)) { return(false); } // Ignore ents without valid / visible models if (entity.ModelIndex == 0 || string.IsNullOrEmpty(entity.ModelName)) { return(false); } // Don't send spectators to other players if ((entity.Flags & EntFlags.Spectator) != 0 && (entity != hostEntity)) { return(false); } // Ignore if not the host and not touching a PVS/PAS leaf // If pSet is NULL, then the test will always succeed and the entity will be added to the update if (entity != hostEntity) { if (!EngineServer.CheckVisibility(ent, pSet)) { return(false); } } // Don't send entity to local client if the client says it's predicting the entity itself. if ((entity.Flags & EntFlags.SkipLocalHost) != 0) { if ((hostFlags & HostFlags.SkipLocalEnts) != 0 && (entity.Owner == hostEntity)) { return(false); } } if (hostEntity.GroupInfo != 0) { Trace.PushGroupTrace(hostEntity.GroupInfo, GroupOp.And); try { Trace.GetGroupTrace(out var currentMask, out var currentOp); // Should always be set, of course if (entity.GroupInfo != 0) { if (currentOp == GroupOp.And) { if ((entity.GroupInfo & hostEntity.GroupInfo) == 0) { return(false); } } else if (currentOp == GroupOp.NAnd) { if ((entity.GroupInfo & hostEntity.GroupInfo) != 0) { return(false); } } } } finally { //There is a bug in the SDK that can cause the last group trace to remain if it failed the tests above Trace.PopGroupTrace(); } } //This is done by the wrapper since there's no memset in managed code //memset(state, 0, sizeof( * state) ); // Assign index so we can track this entity from frame to frame and // delta from it. state.Number = e; state.EntityType = EntityType.Normal; // Flag custom entities. if ((entity.Flags & EntFlags.CustomEntity) != 0) { state.EntityType = EntityType.Beam; } // // Copy state data // // Round animtime to nearest millisecond state.AnimTime = (float)((int)(1000.0 * entity.AnimationTime) / 1000.0); state.Origin = entity.Origin; state.Angles = entity.Angles; state.Mins = entity.Mins; state.Maxs = entity.Maxs; state.EndPos = entity.EndPosition; state.StartPos = entity.StartPosition; state.ImpactTime = entity.ImpactTime; state.StartTime = entity.StartTime; state.ModelIndex = entity.ModelIndex; state.Frame = entity.Frame; state.Skin = (short)entity.Skin; state.Effects = entity.Effects; // This non-player entity is being moved by the game .dll and not the physics simulation system // make sure that we interpolate it's position on the client if it moves if (!isPlayer && entity.AnimationTime != 0 && entity.Velocity[0] == 0 && entity.Velocity[1] == 0 && entity.Velocity[2] == 0) { state.EFlags |= EntityStateFlags.SLerp; } state.Scale = entity.Scale; state.Solid = entity.Solid; state.ColorMap = entity.ColorMap; state.MoveType = entity.MoveType; state.Sequence = entity.Sequence; state.FrameRate = entity.FrameRate; state.Body = entity.Body; for (var i = 0; i < 4; ++i) { state.SetController(i, entity.GetController(i)); } for (var i = 0; i < 2; ++i) { state.SetBlending(i, entity.GetBlending(i)); } state.RenderMode = entity.RenderMode; state.RenderAmount = (int)entity.RenderAmount; state.RenderEffect = entity.RenderEffect; state.RenderColor = new Color24 { r = (byte)entity.RenderColor.x, g = (byte)entity.RenderColor.y, b = (byte)entity.RenderColor.z }; state.AimEnt = 0; if (entity.AimEntity != null) { state.AimEnt = entity.AimEntity.EntIndex(); } state.Owner = 0; if (entity.Owner != null) { var owner = entity.Owner.EntIndex(); // Only care if owned by a player if (owner >= 1 && owner <= Globals.MaxClients) { state.Owner = owner; } } // HACK: Somewhat... // Class is overridden for non-players to signify a breakable glass object ( sort of a class? ) if (!isPlayer) { state.PlayerClass = entity.PlayerClass; } // Special stuff for players only if (isPlayer) { state.BaseVelocity = entity.BaseVelocity; state.WeaponModel = EngineModel.IndexOf(entity.WeaponModelName); state.GaitSequence = entity.GaitSequence; state.Spectator = (entity.Flags & EntFlags.Spectator) != 0; state.Friction = entity.Friction; state.Gravity = entity.Gravity; // state.Team = entity.Team; // state.UseHull = (entity.Flags & EntFlags.Ducking) != 0 ? 1 : 0; state.Health = (int)entity.Health; } return(true); }
public unsafe INetworkMessage Begin(MsgDest msg_dest, int msg_type, Vector pOrigin, Edict ed = null) { if (CurrentMessage != null) { throw new InvalidOperationException("Cannot start a network message while another message is active"); } var originAddress = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Vector))); try { Marshal.StructureToPtr(pOrigin, originAddress, false); EngineFuncs.pfnMessageBegin(msg_dest, msg_type, originAddress, ed.Data); } finally { Marshal.FreeHGlobal(originAddress); } CurrentMessage = new NetworkMessage(EngineFuncs, this); return(CurrentMessage); }