How to playback on value changed data for replay?

I want to further optimize the replay stream by recording values of not frequently changing variables only when they change, instead of every keyframe. This will extremely reduce the replay filesize since I have alot of those variables.

.

A good example might be the health value of characters as they will not taking every 1/n of a second damage (where n is the recording rate per second).

.

When someone’s taking damage, the binaryWriter writes the changed health value to the stream. But this isnt enough. When replaying, the system needs also to know at which time of the replay the characters had the right health in order to wait retrieving the next health-bytes.

.

This is my approach:

public class HPKey {
	public byte hp;
	public ushort keyframe;
	public HPKey(byte hp, int kf){
		this.hp = hp;
		this.keyframe = (ushort)kf;
	}
}
private Dictionary<Health, HPKey> lastHP = new Dictionary<Health, HPKey>();
private void WriteHealth(Health health){
	if(!lastHP.ContainsKey(health)){
		lastHP.Add(health, new HPKey(0, 0));
	}
	if (health.hp != lastHP [health]) {
		lastHP [health].hp = health.hp;
		binaryWriter.Write (health.hp);
		binaryWriter.Write (currentKeyFrameIndex);
	}
}

private void ReadHealth(Health health){
	if(!lastHP.ContainsKey(health)){
		lastHP.Add(health, new HPKey(0, 0));
	}
    //currentKF should be compared to next healthchange kf, but how to retrieve on the bytestream?
	if (currentKeyFrameIndex != lastHP [health].keyframe) { 
		health.hp = binaryReader.ReadByte ();
		lastHP [health].keyframe = binaryReader.ReadUInt16 ();
	}
}

Example: Player has at keyframe 0 a hp-value of 100. The dictionary adds the player, stores its current health and the binary writer writes the hp and the current keyframe to the stream. The next keyframe, WriteHealth gets called, but since the current health remains the same, nothing gets written to the stream.
After 318 keyframes, the player gets hit by a bullet, now having 60 hp. Since the stored value in the dictionary is different (100) from the current (60), the code inside the condition gets called again.

.

But this approach is not working when replaying, since the system doesnt knows when the next keyframe the hp gets changed.

.

I would be very pleased if anyone ever implemented a replay system with this can help me out, since no implementation exists on the internet about this optimization, but only mentioned here (Step 5, Optimization).

Ok guys, Iam pretty sure there are better solutions to this, but since nothing on the internet covers the implementation I guess that doing it now will help someone in the future. If you have a better approach, please let us know!

public class ReplayData {
    public byte hp;
    public ushort skippedKeyframes_HP;

    public long hpByteOffset;
    public long lastHpByteOffset;
}

private void WriteHealth(Health health){
	data.skippedKeyframes_HP++; 
	if (data.hp != health.hp) {
		data.hp = health.hp;
		binaryWriter.Write(health.hp);
		binaryWriter.Write((ushort)0); //PlaceHolder

		if(data.hpByteOffset != data.lastHpByteOffset){ //dont write on first change
			binaryWriter.baseStream.Position = data.hpByteOffset; //move stream back to last PlaceHolder
			binaryWriter.Write (data.skippedKeyframes_HP); //overwrite PlaceHolder with accumulated frames
			binaryWriter.baseStream.Position = binaryWriter.baseStream.Length; //back to end of stream (new placeholder)

			data.lastHpByteOffset = data.hpByteOffset;
		}
		data.hpByteOffset = binaryWriter.baseStream.Position; //points to placeholder
		data.skippedKeyframes_HP = 0;
	}
}

private void ReadHealth(Health health){
    data.skippedKeyframes_HP += replaySpeed > 0 ? -1 : 1;
	if(data.skippedKeyframes_HP <= 0){
		health.hp = binaryReader.ReadByte ();
		data.skippedKeyframes_HP = binaryReader.ReadInt16 ();
	}
}