SetIgnoreMoveInput Failing for Clients in Multiplayer

So, I’m trying to have my GameMode iterate through all PlayerControllers while MatchState == WaitingToStart to disable their movement. I can see that I’m successfully obtaining the PlayerController’s with a for loop using PlayerControllerIterator and the server is working as intended. There is a countdown in the Tick function of the GameMode for 5 seconds and after those five seconds I call ResetIgnoreMoveInput and the server PlayerController strolls along it’s merry way.

However, for whatever reason, the clients aren’t disabling movement. I’m pulling my hair out with this one and I think maybe it’s because of authority/replication but I’m not sure.

So far, I have tried:

  • Overriding PostLogin and disabling the PC as it logs in. Fails

  • Overriding OnPossess LOCALLY in the PC class and disabling during BeginPlay. Fails but succeeds when I spam it with Tick. This is not ideal though because it then takes the game a second or two to realize it needs to reset.

  • Brute forcing replication with UFUNCTION(Server/Client/NetMulticast, Reliable). I know this is not smart, I am still trying to understand how replication works exactly.

  • Creating a boolean that I update from the GameMode and check in the PC Tick. The boolean appeared to be going back and forth between True/False constantly - indicating maybe the server and client were conflicting.

I’m fairly certain this is either a timing issue or a replication issue but I could really use some help determining the right path to take toward a solution. I can post code if the explanation is not enough.

Thanks!

Hey @Dabrostache1

One way to solve this is to use a replicated boolean with an OnRep_Function.

Create bool bShouldIgnoreMoveInput. On the OnRep_ShouldIgnoreMoveInput apply your ignore or reset logic.

Then on the server change its value for all player characters or player controllers. You should do this when Waiting state begins and on login if state == waiting, that way no matter when a client connects it should work as soon as the bool is replicated.

Also, if you’re doing NetMuilticast or Client make sure you’re targeting or looking for the actual local controller of each client, cause a NetMulticast on one character only affects the copy of that character on each client, which will only be the actual local player for one of the clients.

You can also use GameState, which is a single instance replicated across all clients to create and replicate this bool bShouldIgnoreMoveInput. That way you will only need to change it once when MatchState changes and make the OnRep function look for the local player/controller.

@GRIM_Warlock thanks so much for your reply. I’ve tried to implement what you have so far with the exception being I have kept it in the GameMode for now just to see if I can get it working. I really like the OnRep solution, seems very elegant. That being said, I still don’t seem to be able to get it working and I think a big part of that is because I just don’t fully understand replication yet.

I have set the function in the LifetimeProps:

DOREPLIFETIME(AGymPlayerController, bSetMoveIgnore);

Here’s what it looks like in .h:

	UPROPERTY(ReplicatedUsing=OnRep_bSetMoveIgnore)
	bool bSetMoveIgnore;

	UFUNCTION()
	void OnRep_bSetMoveIgnore();

PlayerController.cpp:

void AGymPlayerController::OnRep_bSetMoveIgnore()
{
	GEngine->AddOnScreenDebugMessage(-1, 15.f, FColor::Red, FString::Printf(TEXT("Calling OnRep")));
	if (bSetMoveIgnore) {
		SetIgnoreMoveInput(true);
	}
	else {
		ResetIgnoreMoveInput();
	}
}

GameMode.h:

	UFUNCTION(Server, Reliable)
	void DisablePlayerMovement(bool DisableOrEnable);

And GameMode.cpp:

void AGymGameMode::DisablePlayerMovement_Implementation(bool DisableOrEnable)
{
		for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It) {
			AGymPlayerController* GymPlayer = Cast<AGymPlayerController>(*It);
			if (GymPlayer) {
				GymPlayer->bSetMoveIgnore = DisableOrEnable;
			}
		}
}

The only caveat for GameMode being that I have excluded the Tick function but essentially this is being called in a conditional that says as long as MatchState == WaitingToStart call this function and pass in true.

I appreciate all your help so far. I understand it’s not your problem to solve, but if you have any more input on something I might be missing it would be greatly appreciated.

Thanks

Well, I’m not sure what I’m doing wrong at this point. For anyone following this thread or seeing it in the future, I have done what GRIM suggested and am utilizing the OnRep function in the GameState, with a couple of additional caveats.

At first it wasn’t working at all. I found another thread which mentioned you have to specifically call the OnRep function from the server after setting the OnRep variable. I don’t know how true this is but it helped… A little.

I am having a 40% success rate with my function. It basically works the same way as I mentioned above in the GameMode, the GameMode is now just calling the function from the GameState. Which as I’m writing this I’m rubber ducky-ing and realizing that might be wrong so I will test some more. But again, 40% of the time (4/10 times) the client does not move when they first load in. I tried to override PostLogin again and call the function from there, that doesn’t make a difference. I am assuming at this point it’s something to do with the client not being loaded in but that doesn’t make sense really because I am calling the function in Tick of the GameMode until we move out of WaitingToStart. So in theory, and in my limited understanding, the client should still be getting disabled AT SOME POINT even if they were able to move for a second or something. But that’s not the case.

I’ll update if I ever find a solution. Going to try moving everything into the GameState I guess? Not sure.

Update: it just failed like 25 times in a row so maybe my project was trolling me earlier. Doesn’t appear to be working for clients at all.

I think I would tackle this problem with the following steps:

  1. Authority specifies when to enable processing inputs in the Game State.

  2. Start all Clients not processing any input.

  3. Have all Clients look in the Game State to find when to begin processing input.

  4. When each Client determines that the time stamp has been met, then they can enable input processing.

It is my understanding that Game State is replicated for you, so I wouldn’t think you should need to even concern yourself with replicating anything. Let each Client think in terms of their local copy of the Game State.

In addition, I wouldn’t loop through Clients’ Controllers, but keep the scripting local on each Client to their Game State. Let each Client process their own scripts with their own Game State.

I think that should work.

(If anyone thinks it won’t work, I am open to learning why.)