World TimeSeconds vs. ServerWorld TimeSeconds

Recently I revisited the idea of throwing out acknowledged moves and replaying a local simulation and it dawned on me that the client’s local world time and the server’s world time would likely be different because of a world start time delta. In this lecture, when we decide which moves to throw out based on time, we’re actually comparing the client’s local world time to the server’s world time. Since we’re essentially starting both up at the same time using the editor’s built-in server/client feature, the difference in world time is likely very small.

However, in a case where client and server might be a considerable distance from each other, I’m wondering what the effect would be in our Move.Time comparison. I attempted to draw this out and I think if the client and server timelines deviated too far due to a large enough start-up time delta, the server might actually appear to be running ahead of the client, and thus all moves would be thrown out and we might get some ‘jerk’ because we’re still resetting the actor to an out-of-date position.

Again this is all theoretical - client and server world times may never deviate far enough to matter. However, I did want to point out some interesting functions in GameState:

float AGameStateBase::GetServerWorldTimeSeconds() const
{
	UWorld* World = GetWorld();
	if (World)
	{
		return World->GetTimeSeconds() + ServerWorldTimeSecondsDelta;
	}

	return 0.f;
}

void AGameStateBase::UpdateServerTimeSeconds()
{
	UWorld* World = GetWorld();
	if (World)
	{
		ReplicatedWorldTimeSeconds = World->GetTimeSeconds();
	}
}

void AGameStateBase::OnRep_ReplicatedWorldTimeSeconds()
{
	UWorld* World = GetWorld();
	if (World)
	{
		ServerWorldTimeSecondsDelta = ReplicatedWorldTimeSeconds - World->GetTimeSeconds();
	}
}

As well as this property in GameStateBase.h:

/** Server TimeSeconds. Useful for syncing up animation and gameplay. */
UPROPERTY(Transient, ReplicatedUsing=OnRep_ReplicatedWorldTimeSeconds)
float ReplicatedWorldTimeSeconds;

In theory, if we’re the client we could use GetServerWorldTimeSeconds() instead of GetWorld()->TimeSeconds, and then when we’re comparing move times in ClearAcknowledgedMoves we’d actually be comparing two times that exist relative to the server’s timeline.

Again it’s hard to determine how much this would matter in a real-world simulation without observing the extreme cases. As a side project, I was thinking about adding some logic to KrazyKarts to dump time and position to a csv file so I can actually plot the movement-over-time graphs in a spreadsheet and observe the effects of using different timelines. I’ll post the results if I find anything interesting.

Anyway, I wanted to bring this up for discussion if anybody is interested. I’d love to hear your feedback.

13 Likes

Very good point. I had naively assumed the times were being synchronised but it seems like one should indeed use the above functions instead. Nice find!

I mentioned this post in this text lecture

1 Like

How do I correctly implement this method call?

I included “GameFramework/GameStateBase.h” and tried using like this:

AGameStateBase* GameState;
Move.Time = GameState->GetServerWorldTimeSeconds();

And got this error: error C4700: uninitialized local variable ‘GameState’ used.
I also tried using AGameStateBase::GetServerWorldTimeSeconds() but it says it’s nonstatic.

Note: using AGameStateBase instead of AGameStateBase* results in 100% of memory use hence editor crash without the window with log and send info option.

You can’t just create a null pointer and try to access it. You need to get hold of the actual game state object:

GetWorld()->GetGameState()
3 Likes

Thanks, Sam! I’ll try it out. Now I have another question, this is probably not the apropriate place to ask it, but in one of the first lectures about Menu System you told you were going to show how to export our system to other projects and you ended up not showing it. I tried to do by myself manually moving the source code(menu system and game instance) and migrating the maps and the WBPs. I did the whole process by changing “PuzzlePlatforms” ocurrencies in the codes to the name of my new project. I also built the blueprints parts again(level BP on MainMenu map and ServerRow - hovering and click stuff). I also changed the default maps, transition maps and default game instance. But when I click play the Menu doesn’t appear. There is an important step that I’m missing?
Note: the compilation succeeded.
Note2: I already finished the course and loved it! Great job.
Note3: I managed this to work by only copying the MenuSystem files and creating the whole blueprint stuff from scratch and copying the gameinstance to a new game instance class. I was missing the reparenting of WBPs.

Don’t we do just that in the 3rd section?

Congratulations on finishing! Nice job.

this guy sounds smart. nice job.

1 Like

You also need to include game state;
#include "GameFrameWork/GameState.h"

Move.Time = GetWorld()->GetGameState()->GetServerWorldTimeSeconds();

You could also include Engine instead

//#include "GameFrameWork/GameState.h"
#include Engine.h

I was wondering why the code was still aware of GameState. I guess because of how CoreMinimal.h works.

3 Likes

Is there any more efficient way to export ManuSystem project to another project. example static or dynamic library or module or plugin ?

You could but I’m not sure it’s worth it if you aren’t going to be updating it.

Thanks for this Steve, the #include directory was causing me issues.

Hold up, Move is created on the client and Time is set to the client’s time. The server does not set or modify Time, it sends it back as the LastMove property. The comparison is done back on the client with the client’s unacknowledged times vs the time it set earlier.

There may be scenarios where the time is coming from or being modified by the server, but I don’t see an issue with the way it’s working here.

Good point. Maybe I had this in mind when I first created the course and forgot by the time @SteveMerritt made his post. @SteveMerritt, did you actually see any issues arise from this?

1 Like

Right, I think the way that time is being used here is kind of like an incremental index that’s determined only by the client and compared only agains the client’s “indeces”.

ClearAcknowledgedMoves gets called on OnRep_ServerState which is client sided since it’s an OnRep function.

I think it’s good to know GetServerWorldTimeSeconds() exists but I dont think it’s needed in this case.

Yes but the time change may not be exact. If a frame dropped for whatever reason, the interval would perhaps be double. Also, given time is a float you have to consider rounding errors too.

Privacy & Terms