Beispiel #1
0
        void DebugPlayerState(GameState state)
        {
            if (state.PlayerFlags != state.PrevPlayerFlags)
            {
                string addedList = String.Empty;
                string removedList = String.Empty;
                foreach (FL flag in Enum.GetValues(typeof(FL)))
                {
                    if (state.PlayerFlags.HasFlag(flag) && !state.PrevPlayerFlags.HasFlag(flag))
                        addedList += Enum.GetName(typeof(FL), flag) + " ";
                    else if (!state.PlayerFlags.HasFlag(flag) && state.PrevPlayerFlags.HasFlag(flag))
                        removedList += Enum.GetName(typeof(FL), flag) + " ";
                }
                if (addedList.Length > 0)
                    Debug.WriteLine("player flags added: " + addedList);
                if (removedList.Length > 0)
                    Debug.WriteLine("player flags removed: " + removedList);
            }

            if (state.PlayerViewEntityIndex != state.PrevPlayerViewEntityIndex)
            {
                Debug.WriteLine("player view entity changed: " + state.PlayerViewEntityIndex);
            }

            if (state.PlayerParentEntityHandle != state.PrevPlayerParentEntityHandle)
            {
                Debug.WriteLine("player parent entity changed: " + state.PlayerParentEntityHandle.ToString("X"));
            }

            #if false
            if (!state.PlayerPosition.BitEquals(state.PrevPlayerPosition))
            {
                Debug.WriteLine("player pos changed: " + state.PlayerParentEntityHandle);
            }
            #endif
        }
Beispiel #2
0
        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)
        }
Beispiel #3
0
        void InitGameState(GameState state)
        {
            string absoluteGameDir;
            state.GameProcess.ReadASCIIString(state.GameOffsets.GameDirPtr, out absoluteGameDir, 260);
            state.GameDir = new DirectoryInfo(absoluteGameDir).Name.ToLower();
            Debug.WriteLine("gameDir = " + state.GameDir);

            state.CurrentMap = String.Empty;

            // inspect memory layout to determine CEntInfo's version
            const int SERIAL_MASK = 0x7FFF;
            int serial;
            state.GameProcess.ReadInt32(state.GameOffsets.GlobalEntityListPtr + (4 * 7), out serial);
            state.GameOffsets.EntInfoSize = (serial > 0 && serial < SERIAL_MASK) ? CEntInfoSize.Portal2 : CEntInfoSize.HL2;

            state.GameSupport = GameSupport.FromGameDir(state.GameDir);
            if (state.GameSupport != null)
            {
                Debug.WriteLine("running game-specific code for: " + state.GameDir);
                state.GameSupport.OnGameAttached(state);
            }
        }
Beispiel #4
0
        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();
            }
        }
Beispiel #5
0
        void HandleGameSupportResult(GameSupportResult result, GameState state)
        {
            if (result == GameSupportResult.DoNothing)
                return;

            switch (result)
            {
                case GameSupportResult.PlayerGainedControl:
                    this.SendGainedControlEvent(state.GameSupport.StartOffsetTicks * state.IntervalPerTick);
                    break;
                case GameSupportResult.PlayerLostControl:
                    this.SendLostControlEvent(state.GameSupport.EndOffsetTicks * state.IntervalPerTick);
                    break;
            }
        }
Beispiel #6
0
        void CheckGameState(GameState state)
        {
            if (state.SignOnState != state.PrevSignOnState)
                Debug.WriteLine("SignOnState changed to " + state.SignOnState);

            // if player is fully in game
            if (state.SignOnState == SignOnState.Full && state.HostState == HostState.Run)
            {
                // note: seems to be slow sometimes. ~3ms
                this.SendSessionTimeUpdateEvent(state.TickTime);

                // first tick when player is fully in game
                if (state.SignOnState != state.PrevSignOnState)
                {
                    Debug.WriteLine("session started");
                    this.SendSessionStartedEvent(state.CurrentMap);

                    if (state.GameSupport != null)
                        state.GameSupport.OnSessionStart(state);
                }

                if (state.GameSupport != null)
                    this.HandleGameSupportResult(state.GameSupport.OnUpdate(state), state);

            #if DEBUG
                if (state.PlayerEntInfo.EntityPtr != IntPtr.Zero)
                    DebugPlayerState(state);
            #endif
            }

            if (state.HostState != state.PrevHostState)
            {
                if (state.PrevHostState == HostState.Run)
                {
                    // the map changed or a quicksave was loaded
                    Debug.WriteLine("session ended");

                    // the map changed or a save was loaded
                    this.SendSessionEndedEvent();

                    if (state.GameSupport != null)
                        state.GameSupport.OnSessionEnd(state);
                }

                Debug.WriteLine("host state changed to " + state.HostState);

                // HostState::m_levelName is changed much earlier than state.CurrentMap (CBaseServer::m_szMapName)
                // reading HostStateLevelNamePtr is only valid during these states (not LoadGame!)
                if (state.HostState == HostState.ChangeLevelSP || state.HostState == HostState.ChangeLevelMP
                    || state.HostState == HostState.NewGame)
                {
                    string levelName;
                    state.GameProcess.ReadASCIIString(state.GameOffsets.HostStateLevelNamePtr, out levelName, 256-1);
                    Debug.WriteLine("host state m_levelName changed to " + levelName);

                    if (state.HostState == HostState.NewGame)
                    {
                        if (state.GameSupport != null && levelName == state.GameSupport.FirstMap)
                            this.SendNewGameStartedEvent(levelName);
                    }
                    else // changelevel sp/mp
                    {
                        // state.CurrentMap should still be the previous map
                        this.SendMapChangedEvent(levelName, state.CurrentMap);
                    }
                }
            }
        }