I've made this for my game, it works pretty well. I record all my inputs during process, then when the physics process happens, it consumes any inputs recorded, like so:
func _process(delta):
if Input.is_action_just_pressed("jump"):
jump = true
func _physics_process(delta):
if jump:
#Do the jump stuff
jump = false
This setups means I can run a frame counter that adds 1 every physics process. Using that counter, I create a dictionary, and every single frame I record the players inputs. This is done through a script on the player which exports the status of any inputs I want to a separate node, which then stores those inputs in a dictionary, under the key named after the current frame. So the final output looks something like:
replay = {
"1" : {
"jump" : false
}
"2" : {
"jump" : true
}
"3" : {
"jump" : false
}
etc.
}
Then, when I play the replay, instead of using process and the Input manager to record my player's inputs, I simply slot in the appropriate inputs by checking the replay's dictionary under the current frame's key.
If you do the process exactly like I have laid out, it'll work, but it'll be super slow to save long replays. I have optimized it so that it only records CHANGES in input, to save on space, and I use an input coder to reduce the characters of each input to save on space as well, this is just what a first draft would look like, to get the basic idea across.