Ejemplo n.º 1
0
        public void ChangeDemoDir(string fileName)
        {
            const string newDir    = "sussy baka uwu";
            var          before    = GetDemo(fileName);
            MemoryStream outStream = new MemoryStream();

            OptChangeDemoDir.ChangeDemoDir(before, outStream, newDir);
            SourceDemo after = new SourceDemo(outStream.ToArray());

            after.Parse();
            // check that all the packets are unchanged
            CollectionAssert.AreEqual(
                before.Frames.Select(frame => frame.Type),
                after.Frames.Select(frame => frame.Type));
            // check that all messages are unchanged
            CollectionAssert.AreEqual(
                before.FilterForMessages().Select(t => t.messageType),
                after.FilterForMessages().Select(t => t.messageType));
            // check that the new demo has the new dir in header and SvcServerInfo messages
            Assert.AreEqual(after.Header.GameDirectory, newDir);
            Assert.That(after.FilterForMessage <SvcServerInfo>().Select(t => t.message.GameDir), Is.All.EqualTo(newDir));
        }
Ejemplo n.º 2
0
        /*
         * Okay so this is long and wacky and honestly I don't remember exactly how this works because I threw it
         * together for rendering glitchless v2 a while ago. Sooooooooo, the general idea is to smoothly transition the
         * player's origin and view offset during a jump.
         *
         *
         * Normally, a jump will look something like this (each column is a tick):
         *
         * cam pos:     👁️👁️👁️👁️👁️                               👁️👁️👁️👁️👁️
         *                        👁️👁️👁️👁️               👁️👁️👁️👁️
         *                                👁️👁️👁️   👁️👁️👁️
         *                                      👁️👁️
         *
         * origin pos:  🦶🦶🦶🦶🦶                               🦶🦶🦶🦶🦶
         *                        🦶🦶🦶🦶               🦶🦶🦶🦶
         *                                🦶🦶🦶   🦶🦶🦶
         *
         *
         *
         *                                      🦶🦶
         * floor:       ------------------------------------------------------------
         *
         * God bless you if you don't see emojis here. Notice that the player is in a crouched state until they get
         * close to the floor. For the 2 (in this case) ticks that the player is on the floor, they are standing. To
         * compensate for this, the view offset is increased and therein lies the problem. It seems like the player's
         * view offset transitions smoothly regardless of demo_interpolateview and always happens a tick too late or
         * something like that. This makes jumps extremely ugly without demo interp and slightly bouncy with interp.
         * The goal of this function is to "bridge the gap" if you will, and pull the player's feet up during a jump.
         * We find jumps by looking at "m_Local.m_flJumpTime", and then linearly interpolate the player's origin and
         * view offset across the ticks when they are on the ground. There's some spicy stuff below, this is probably
         * something that should be improved in the future.
         */
        public static void SmoothJumps(SourceDemo demo, Stream s, int maxGroundTicks)
        {
            // ReSharper disable once CompareOfFloatsByEqualityOperator
            var jumpTicks =
                (from msgTup in demo.FilterForMessage <SvcPacketEntities>()
                 from update in msgTup.message.Updates
                 where update.ServerClass.ClassName == "CPortal_Player" && update is Delta
                 from deltaProp in ((Delta)update).Props
                 where deltaProp.prop.Name == "m_Local.m_flJumpTime" &&
                 ((SingleEntProp <float>)deltaProp.prop).Value == 510.0
                 select msgTup.tick).ToList();


            Console.WriteLine($"found jump ticks: {jumpTicks.SequenceToString()}");
            BitStreamWriter bsw = new BitStreamWriter(demo.Reader.Data);

            const float interpThreshold = 15;             // this many units off the ground is considered a jump

            var frames = demo.Frames;

            foreach (int jumpTick in jumpTicks)
            {
                int jumpTickIdx = 0;
                for (; frames[jumpTickIdx].Type != PacketType.Packet; jumpTickIdx++)
                {
                    ;                                                                                  // find first packet
                }
                while (frames[++jumpTickIdx].Tick < jumpTick)
                {
                    ;                                                           // find the first frame with the matching tick
                }
                for (; frames[jumpTickIdx].Type != PacketType.Packet; jumpTickIdx++)
                {
                    ;                                                                                  // find the packet on this tick
                }
                Packet  groundPacket = (Packet)frames[jumpTickIdx].Packet !;
                Vector3 groundPos    = groundPacket.PacketInfo[0].ViewOrigin;              // roughly the ground pos

                int     endTick;
                Vector3 endVec;

                int idx = jumpTickIdx;
                while (frames[++idx].Type != PacketType.Packet)
                {
                    ;                                                             // jump to next packet
                }
                Vector3 curViewOrigin = ((Packet)frames[idx].Packet !).PacketInfo[0].ViewOrigin;

                if (curViewOrigin.Z - groundPos.Z > interpThreshold)
                {
                    endTick = ((Packet)frames[idx].Packet !).Tick;
                    endVec  = curViewOrigin;
                }
                else
                {
                    Console.WriteLine($"not patching jump on tick {jumpTick}, too many ticks before air time detected");
                    continue;
                }

                Vector3 startVec = new Vector3(float.PositiveInfinity);
                idx = jumpTickIdx;
                bool shouldInterp = true;

                for (int ticksBeforeJump = 0; ticksBeforeJump < maxGroundTicks; ticksBeforeJump++)
                {
                    while (frames[--idx].Type != PacketType.Packet)
                    {
                        ;                                                                 // jump to previous packet
                    }
                    Packet curPacket = (Packet)frames[idx].Packet !;
                    curViewOrigin = curPacket.PacketInfo[0].ViewOrigin;

                    if (curViewOrigin.Z - groundPos.Z > interpThreshold)
                    {
                        startVec = curViewOrigin;
                        break;
                    }
                    if (ticksBeforeJump == maxGroundTicks - 1)
                    {
                        shouldInterp = false;
                        Console.WriteLine($"not patching jump on tick {jumpTick}, too many ticks on ground (at least {maxGroundTicks}) before this jump");
                        break;
                    }
                }
                if (!shouldInterp)
                {
                    continue;
                }

                int startTick = frames[idx].Tick;                 // idx is currently the start tick (right before the first interp tick)

                while (frames[idx].Tick < endTick)
                {
                    while (frames[++idx].Type != PacketType.Packet)
                    {
                        ;                                                                // jump to next packet
                    }
                    BitStreamWriter vecBytes   = new BitStreamWriter();
                    Packet          packet     = (Packet)frames[idx].Packet !;
                    float           lerpAmount = (float)(frames[idx].Tick - startTick) / (endTick - startTick);
                    Vector3         lerpVec    = Vector3.Lerp(startVec, endVec, lerpAmount);
                    vecBytes.WriteVector3(lerpVec);
                    // edit the pos in the cmdinfo, we don't actually need to edit the origin in the ent data since that isn't used during demo playback
                    bsw.EditBitsAtIndex(vecBytes, packet.Reader.AbsoluteStart + 32);

                    var viewOffsetProp = (SingleEntProp <float>?)(
                        (Delta?)packet.FilterForMessage <SvcPacketEntities>().Single().Updates !
                        .SingleOrDefault(update => update.ServerClass.ClassName == "CPortal_Player"))
                                         ?.Props.SingleOrDefault(tuple => tuple.prop.Name == "m_vecViewOffset[2]").prop;

                    if (viewOffsetProp != null)
                    {
                        BitStreamWriter viewOffsetBytes = new BitStreamWriter();
                        viewOffsetBytes.WriteFloat(28);
                        bsw.EditBitsAtIndex(viewOffsetBytes, viewOffsetProp.Offset);
                    }
                }
            }
            s.Write(bsw, 0, bsw.ByteLength);
        }