void HandleProcess(Process game, GameOffsets offsets, CancellationTokenSource cts) { Debug.WriteLine("HandleProcess " + game.ProcessName); var state = new GameState(game, offsets); this.InitGameState(state); var profiler = Stopwatch.StartNew(); while (!game.HasExited && !cts.IsCancellationRequested) { // iteration must never take longer than 1 tick this.UpdateGameState(state); this.CheckGameState(state); state.UpdateCount++; TimedTraceListener.Instance.UpdateCount = state.UpdateCount; if (profiler.ElapsedMilliseconds >= TARGET_UPDATE_RATE) { Debug.WriteLine("**** update iteration took too long: " + profiler.ElapsedMilliseconds); } //var sleep = Stopwatch.StartNew(); //MapTimesForm.Instance.Text = profiler.Elapsed.ToString(); Thread.Sleep(Math.Max(TARGET_UPDATE_RATE - (int)profiler.ElapsedMilliseconds, 1)); //MapTimesForm.Instance.Text = sleep.Elapsed.ToString(); profiler.Restart(); } }
public GameState(Process game, GameOffsets offsets) { this.GameProcess = game; this.GameOffsets = offsets; RawTickCount = new ValueWatcher <int>(0); PlayerViewEntityIndex = new ValueWatcher <int>(0); PlayerFlags = new ValueWatcher <FL>(new FL()); PlayerPosition = new ValueWatcher <Vector3f>(new Vector3f()); PlayerParentEntityHandle = new ValueWatcher <int>(0); HostState = new ValueWatcher <HostState>(SourceSplit.HostState.NewGame); SignOnState = new ValueWatcher <SignOnState>(SourceSplit.SignOnState.None); ServerState = new ValueWatcher <ServerState>(SourceSplit.ServerState.Dead); }
void UpdateGameState(GameState state) { Process game = state.GameProcess; GameOffsets offsets = state.GameOffsets; // update all the stuff that doesn't depend on the signon state game.ReadInt32(offsets.TickCountPtr, out state.RawTickCount); game.ReadFloat(offsets.IntervalPerTickPtr, out state.IntervalPerTick); state.PrevSignOnState = state.SignOnState; game.ReadEnum32(offsets.SignOnStatePtr, out state.SignOnState); state.PrevHostState = state.HostState; game.ReadEnum32(offsets.HostStatePtr, out state.HostState); bool firstTick = false; // update the stuff that's only valid during signon state full if (state.SignOnState == SignOnState.Full) { // if signon state just became full (where demos start timing from) if (state.SignOnState != state.PrevSignOnState) { firstTick = true; // start rebasing from this tick state.TickBase = state.RawTickCount; Debug.WriteLine("rebasing ticks from " + state.TickBase); // player was just spawned, get it's ptr state.PlayerEntInfo = state.GetEntInfoByIndex(GameState.ENT_INDEX_PLAYER); // update map name state.GameProcess.ReadASCIIString(state.GameOffsets.CurMapPtr, out state.CurrentMap, 64); } // update time and rebase it against the first signon state full tick state.TickCount = state.RawTickCount - state.TickBase; state.TickTime = state.TickCount * state.IntervalPerTick; TimedTraceListener.Instance.TickCount = state.TickCount; // update player related things if (state.PlayerEntInfo.EntityPtr != IntPtr.Zero && state.GameSupport != null) { // flags if (state.GameSupport.RequiredProperties.HasFlag(PlayerProperties.Flags)) { state.PrevPlayerFlags = state.PlayerFlags; game.ReadEnum32(state.PlayerEntInfo.EntityPtr + offsets.BaseEntityFlagsOffset, out state.PlayerFlags); } // position if (state.GameSupport.RequiredProperties.HasFlag(PlayerProperties.Position)) { state.PrevPlayerPosition = state.PlayerPosition; game.ReadVector3f(state.PlayerEntInfo.EntityPtr + offsets.BaseEntityAbsOriginOffset, out state.PlayerPosition); } // view entity if (state.GameSupport.RequiredProperties.HasFlag(PlayerProperties.ViewEntity)) { const int ENT_ENTRY_MASK = 0x7FF; state.PrevPlayerViewEntityIndex = state.PlayerViewEntityIndex; int viewEntityHandle; // EHANDLE game.ReadInt32(state.PlayerEntInfo.EntityPtr + offsets.BasePlayerViewEntity, out viewEntityHandle); state.PlayerViewEntityIndex = viewEntityHandle == -1 ? GameState.ENT_INDEX_PLAYER : viewEntityHandle & ENT_ENTRY_MASK; } // parent entity if (state.GameSupport.RequiredProperties.HasFlag(PlayerProperties.ParentEntity)) { state.PrevPlayerParentEntityHandle = state.PlayerParentEntityHandle; // EHANDLE game.ReadInt32(state.PlayerEntInfo.EntityPtr + offsets.BaseEntityParentHandleOffset, out state.PlayerParentEntityHandle); } // if it's the first tick, don't use stuff from the previous map if (firstTick) { state.PrevPlayerFlags = state.PlayerFlags; state.PrevPlayerPosition = state.PlayerPosition; state.PrevPlayerViewEntityIndex = state.PlayerViewEntityIndex; state.PrevPlayerParentEntityHandle = state.PlayerParentEntityHandle; } } } // if (state.SignOnState == SignOnState.Full) }
// TODO: log fails bool TryGetGameProcess(out Process p, out GameOffsets offsets) { #if DEBUG var sw = Stopwatch.StartNew(); #endif string[] procs = _settings.GameProcesses.Select(x => x.ToLower().Replace(".exe", String.Empty)).ToArray(); p = Process.GetProcesses().FirstOrDefault(x => procs.Contains(x.ProcessName.ToLower())); offsets = new GameOffsets(); if (p == null || p.HasExited || Util.IsVACProtectedProcess(p)) { return(false); } // process is up, check if engine and server are both loaded yet ProcessModule engine = p.Modules.Cast <ProcessModule>().FirstOrDefault(x => x.ModuleName.ToLower() == "engine.dll"); ProcessModule server = p.Modules.Cast <ProcessModule>().FirstOrDefault(x => x.ModuleName.ToLower() == "server.dll"); if (engine == null || server == null) { return(false); } // required engine stuff var scanner = new SignatureScanner(p, engine.BaseAddress, engine.ModuleMemorySize); if ((offsets.CurMapPtr = scanner.Scan(_curMapTarget)) == IntPtr.Zero || (offsets.CurTimePtr = scanner.Scan(_curTimeTarget)) == IntPtr.Zero || (offsets.GameDirPtr = scanner.Scan(_gameDirTarget)) == IntPtr.Zero || (offsets.HostStatePtr = scanner.Scan(_hostStateTarget)) == IntPtr.Zero) { return(false); } if ((offsets.SignOnStatePtr = scanner.Scan(_signOnStateTarget1)) == IntPtr.Zero && (offsets.SignOnStatePtr = scanner.Scan(_signOnStateTarget2)) == IntPtr.Zero) { return(false); } // required server stuff var serverScanner = new SignatureScanner(p, server.BaseAddress, server.ModuleMemorySize); if ((offsets.GlobalEntityListPtr = serverScanner.Scan(_globalEntityListTarget)) == IntPtr.Zero) { return(false); } // entity offsets if (!GetBaseEntityMemberOffset("m_fFlags", p, serverScanner, out offsets.BaseEntityFlagsOffset) || !GetBaseEntityMemberOffset("m_vecAbsOrigin", p, serverScanner, out offsets.BaseEntityAbsOriginOffset) || !GetBaseEntityMemberOffset("m_iName", p, serverScanner, out offsets.BaseEntityTargetNameOffset) || !GetBaseEntityMemberOffset("m_hViewEntity", p, serverScanner, out offsets.BasePlayerViewEntity)) { return(false); } // find m_pParent offset. the string "m_pParent" occurs more than once so we have to do something else // in old engine it's right before m_iParentAttachment. in new engine it's right before m_nTransmitStateOwnedCounter // TODO: test on all engines int tmp; if (!GetBaseEntityMemberOffset("m_nTransmitStateOwnedCounter", p, serverScanner, out tmp)) { if (!GetBaseEntityMemberOffset("m_iParentAttachment", p, serverScanner, out tmp)) { return(false); } tmp -= 4; // sizeof m_iParentAttachment } tmp -= 4; // sizeof m_nTransmitStateOwnedCounter (4 aligned byte) offsets.BaseEntityParentHandleOffset = tmp; Debug.WriteLine("CBaseServer::m_szMapname ptr = 0x" + offsets.CurMapPtr.ToString("X")); Debug.WriteLine("CGlobalVarsBase::curtime ptr = 0x" + offsets.CurTimePtr.ToString("X")); Debug.WriteLine("CBaseClientState::m_nSignonState ptr = 0x" + offsets.SignOnStatePtr.ToString("X")); Debug.WriteLine("CBaseEntityList::(CEntInfo)m_EntPtrArray ptr = 0x" + offsets.GlobalEntityListPtr.ToString("X")); Debug.WriteLine("CBaseEntity::m_fFlags offset = 0x" + offsets.BaseEntityFlagsOffset.ToString("X")); Debug.WriteLine("CBaseEntity::m_vecAbsOrigin offset = 0x" + offsets.BaseEntityAbsOriginOffset.ToString("X")); Debug.WriteLine("CBaseEntity::m_iName offset = 0x" + offsets.BaseEntityTargetNameOffset.ToString("X")); Debug.WriteLine("CBaseEntity::m_pParent offset = 0x" + offsets.BaseEntityParentHandleOffset.ToString("X")); Debug.WriteLine("CBasePlayer::m_hViewEntity offset = 0x" + offsets.BasePlayerViewEntity.ToString("X")); #if DEBUG Debug.WriteLine("TryGetGameProcess took: " + sw.Elapsed); #endif return(true); }
// TODO: log fails bool TryGetGameProcess(out Process p, out GameOffsets offsets) { #if DEBUG var sw = Stopwatch.StartNew(); #endif string[] procs = _settings.GameProcesses.Select(x => x.ToLower().Replace(".exe", String.Empty)).ToArray(); p = Process.GetProcesses().FirstOrDefault(x => procs.Contains(x.ProcessName.ToLower())); offsets = new GameOffsets(); if (p == null || p.HasExited || Util.IsVACProtectedProcess(p)) return false; // process is up, check if engine and server are both loaded yet ProcessModule engine = p.Modules.Cast<ProcessModule>().FirstOrDefault(x => x.ModuleName.ToLower() == "engine.dll"); ProcessModule server = p.Modules.Cast<ProcessModule>().FirstOrDefault(x => x.ModuleName.ToLower() == "server.dll"); if (engine == null || server == null) return false; // required engine stuff var scanner = new SignatureScanner(p, engine.BaseAddress, engine.ModuleMemorySize); if ((offsets.CurMapPtr = scanner.Scan(_curMapTarget)) == IntPtr.Zero || (offsets.CurTimePtr = scanner.Scan(_curTimeTarget)) == IntPtr.Zero || (offsets.GameDirPtr = scanner.Scan(_gameDirTarget)) == IntPtr.Zero || (offsets.HostStatePtr = scanner.Scan(_hostStateTarget)) == IntPtr.Zero) return false; if ((offsets.SignOnStatePtr = scanner.Scan(_signOnStateTarget1)) == IntPtr.Zero && (offsets.SignOnStatePtr = scanner.Scan(_signOnStateTarget2)) == IntPtr.Zero) return false; // required server stuff var serverScanner = new SignatureScanner(p, server.BaseAddress, server.ModuleMemorySize); if ((offsets.GlobalEntityListPtr = serverScanner.Scan(_globalEntityListTarget)) == IntPtr.Zero) return false; // entity offsets if ( !GetBaseEntityMemberOffset("m_fFlags", p, serverScanner, out offsets.BaseEntityFlagsOffset) || !GetBaseEntityMemberOffset("m_vecAbsOrigin", p, serverScanner, out offsets.BaseEntityAbsOriginOffset) || !GetBaseEntityMemberOffset("m_iName", p, serverScanner, out offsets.BaseEntityTargetNameOffset) || !GetBaseEntityMemberOffset("m_hViewEntity", p, serverScanner, out offsets.BasePlayerViewEntity)) return false; // find m_pParent offset. the string "m_pParent" occurs more than once so we have to do something else // in old engine it's right before m_iParentAttachment. in new engine it's right before m_nTransmitStateOwnedCounter // TODO: test on all engines int tmp; if (!GetBaseEntityMemberOffset("m_nTransmitStateOwnedCounter", p, serverScanner, out tmp)) { if (!GetBaseEntityMemberOffset("m_iParentAttachment", p, serverScanner, out tmp)) return false; tmp -= 4; // sizeof m_iParentAttachment } tmp -= 4; // sizeof m_nTransmitStateOwnedCounter (4 aligned byte) offsets.BaseEntityParentHandleOffset = tmp; Debug.WriteLine("CBaseServer::m_szMapname ptr = 0x" + offsets.CurMapPtr.ToString("X")); Debug.WriteLine("CGlobalVarsBase::curtime ptr = 0x" + offsets.CurTimePtr.ToString("X")); Debug.WriteLine("CBaseClientState::m_nSignonState ptr = 0x" + offsets.SignOnStatePtr.ToString("X")); Debug.WriteLine("CBaseEntityList::(CEntInfo)m_EntPtrArray ptr = 0x" + offsets.GlobalEntityListPtr.ToString("X")); Debug.WriteLine("CBaseEntity::m_fFlags offset = 0x" + offsets.BaseEntityFlagsOffset.ToString("X")); Debug.WriteLine("CBaseEntity::m_vecAbsOrigin offset = 0x" + offsets.BaseEntityAbsOriginOffset.ToString("X")); Debug.WriteLine("CBaseEntity::m_iName offset = 0x" + offsets.BaseEntityTargetNameOffset.ToString("X")); Debug.WriteLine("CBaseEntity::m_pParent offset = 0x" + offsets.BaseEntityParentHandleOffset.ToString("X")); Debug.WriteLine("CBasePlayer::m_hViewEntity offset = 0x" + offsets.BasePlayerViewEntity.ToString("X")); #if DEBUG Debug.WriteLine("TryGetGameProcess took: " + sw.Elapsed); #endif return true; }
void HandleProcess(Process game, GameOffsets offsets, CancellationTokenSource cts) { Debug.WriteLine("HandleProcess " + game.ProcessName); var state = new GameState(game, offsets); this.InitGameState(state); var profiler = Stopwatch.StartNew(); while (!game.HasExited && !cts.IsCancellationRequested) { // iteration must never take longer than 1 tick this.UpdateGameState(state); this.CheckGameState(state); state.UpdateCount++; TimedTraceListener.Instance.UpdateCount = state.UpdateCount; if (profiler.ElapsedMilliseconds >= TARGET_UPDATE_RATE) Debug.WriteLine("**** update iteration took too long: " + profiler.ElapsedMilliseconds); //var sleep = Stopwatch.StartNew(); //MapTimesForm.Instance.Text = profiler.Elapsed.ToString(); Thread.Sleep(Math.Max(TARGET_UPDATE_RATE - (int)profiler.ElapsedMilliseconds, 1)); //MapTimesForm.Instance.Text = sleep.Elapsed.ToString(); profiler.Restart(); } }
public GameState(Process game, GameOffsets offsets) { this.GameProcess = game; this.GameOffsets = offsets; }