#multiplayer

1 messages ¡ Page 276 of 1

exotic wasp
#

I'm surprised it sends after TearOff

rustic ore
#

I even tried to call TearOff() on the client and update foo again but the client still receive the other foo update. I wonder if this is a PIE thing🤔

Nvm it stopped receiving updates after I called TearOff() on the client. But as long as the client haven't called their own TearOff() it's still receiving updates

lament flax
#

tested this again and it doesnt work lol

exotic wasp
#

also, foo is false to start, right?

rustic ore
#

Tried again with longer delay (10s) and it won't update foo!
But the question remains, do you think can I trust the game so that the client receive the last update right before calling TearOff()?

white smelt
#

using unreals standard replication system if i have 50 pawns the client is like updating every 2 seconds, im using mover 2.0, GAS and StateTrees on the Controller. no system running other then move to tasks! and its jiggy

#

any suggestions?

rustic ore
exotic wasp
lament flax
#

new log unlocked: Closed socket YYYYYY via the API, reason 1000

exotic wasp
white smelt
#

didnt set it to tick that way

exotic wasp
#

if it's using the cmc like movement I wouldn't be shocked by that performance

#

could adjust the priority

white smelt
#

its using mover!!

#

60.000,00 bytes each tick

white smelt
#

will this fix me

#

😄

compact harbor
compact harbor
# white smelt how many characters you got?

I'm using mass entity, I got 1500 of them. But the data size inside profiler shows me 10kb in total per update. And they are being delayed as well, I set them to replicate every 0.1 seconds, the more entities I have the more time it takes to replicate it all, with 1500 I end up with 0.8s delay between the updates

compact harbor
#

I tried quite literally everything to fix it, nothing helps

white smelt
#

man i dont know im a tech animator, dont know much about network at all, maybe the package size to big?

compact harbor
white smelt
#

on client

#

stat net

compact harbor
white smelt
#

hows yours looking?

#

this is bad still

compact harbor
#

the numbers are not bad at all, but for some reason engine reacts like this is something crazy

white smelt
#

well seems like it is bad, shaggy is laggy 😄

compact harbor
#

you actually also have outcoming traffic too, so it makes sense that your delay is around twice as much as mine. but even 120 kb/s isn't like horrendous. The delay shouldn't occur, but for some reason it does

#

you can try increasing bandwidth though, maybe it will work for you

#

(it didn't for me)

white smelt
#

nothing helps

#

laggy shaggy

white smelt
#

at this point only prayers going help

#

😄

white smelt
#

i know what i do, i design it around multiplayer but focus on single player and when the systems are in place i pay kaos a big check to fix it 😄

tame sapphire
#

@coarse cave Thank you for the update, moving the visibility into a function on the tile was kind of fine as in the function wasnt an issue.
I was trying to go then route of having the tile update its visibility per player (The screenshot before was from the player controller) however when trying either of your suggestions for 2 this all happens on the server and is no longer unique to each player? did I miss something or did that not come across in the previous message? Unless i should set if is dead on the player state somewhere so its replicated?

#

Its almost as if i want a desync on visibility or thats how i think id explain it

exotic wasp
#

wtf is an "owner" in iris?

#

uobjects dont have owners

white smelt
#

i dont know, but id assume the owner would be the server? or the client if ever?

tacit furnace
#

I got problem with server travel, server travel not working on client whatever who triggers server travel only server change level client stucks in same level how can i fix that?

coarse cave
tame sapphire
#

phase and unit are both re notify

#

phase is on game state

#

unit is on player controller

coarse cave
#

So just hook into those notifies from the tile on the client only. Add events (delegates) if necessary

tame sapphire
#

oh

#

my mistake I didn't do the game state binding on owning client

#

this in the tile seems to work, thank you

#

dont know if event tileon phase change needs to be an rpc

#

but thats great

lament flax
#

Im going nuts on this steam join issue in 5.6

#

Cant play test my game because of it

white smelt
ocean swallow
coarse cave
# tame sapphire this in the tile seems to work, thank you

That will probably work for your use case here, but generally you shouldn't pass arguments to your update function; it should query all relevant state internally. You want to do this so that when you rely on multiple pieces of state they can't stomp on each other when calling the update function.

dark parcel
#

Arguments are passed in RPC when you need to send data over the network.

pliant cypress
#

what is usually the best place to store if player is ready or not in lobby?
PlayerState is my guess?

short arrow
#

Playerstate is a great place for it

pliant cypress
#

yes

#

when server start, it get data via RPC

dark parcel
#

Why

#

just make it a replicated variable

#

Don't use RPC to set stateful data. Very common mistakes among youtubers.

pliant cypress
#

or normal one

dark parcel
#

If we are talking about ready state

#

it must be replicated accross all machine

pliant cypress
#

yes, like 4 player and all must ready

#

to get into gameplay map

dark parcel
#

yeah then just make a replicated variable

#

without condition

pliant cypress
#

okay, i'll try

lament flax
ocean swallow
pliant cypress
#

#include "SoldierPlayerState.h"
#include "LastBattalion/Widget/LobbyWidget.h"
#include "Net/UnrealNetwork.h"

void ASoldierPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME(ASoldierPlayerState, bIsReady);
}

void ASoldierPlayerState::Server_SetReady_Implementation(bool bSetReady)
{
    bIsReady = bSetReady;
    if (HasAuthority())
    {
        GetWorld()->ServerTravel(FString("/Game/Maps/Level_1/PrototypeLevel_01?listen"));;
    }
}

void ASoldierPlayerState::OnRep_IsReady()
{
        // UI call
    OnReadyStateChangedDelegate.Broadcast(bIsReady);
}
bool ULobbyWidget::Initialize()
{
    Super::Initialize();

    if (ReadyButton)
    {
        ReadyButton->OnClicked.AddDynamic(this, &ThisClass::OnClickReadyButton);
    }

    PlayerState = GetOwningPlayerState<ASoldierPlayerState>();
    if (PlayerState)
    {
        PlayerState->OnReadyStateChangedDelegate.AddDynamic(this, &ThisClass::OnStateReady);
    }

    return true;
}

void ULobbyWidget::OnClickReadyButton()
{
    if (PlayerState->bIsReady)
    {
        PlayerState->Server_SetReady(false);
    }
    else
    {
        PlayerState->Server_SetReady(true);
    }
}

void ULobbyWidget::OnStateReady_Implementation(bool bIsReady)
{
    ButtonText->SetText(bIsReady ? FText::FromString("Ready") : FText::FromString("Not Ready"));
}
#

is this correct Ready setup?

dark parcel
#

You can't call RPC as client through player state.

#

route it to the object owned by the client.

white smelt
pliant cypress
dark parcel
#

by default client own their controller. And the pawn they possess.

verbal ice
dark parcel
#

i will test but iirc player state is owned by the server.

verbal ice
#

pretty sure I've done RPCs on player states

dark parcel
#

by that I mean, Client Player State calling server rpc. That shouldn't be allowed from memory.

#

as server, it can pretty much do RPC where ever it please.

#

Yeah, nothing is printed.

verbal ice
#

99.9% sure the player state's owner is the player controller, that's weird

#

Add a delay to that RPC

dark parcel
#

I did :3

#

just for sanity sake

verbal ice
#

welp

#

not on my pc so cant check but I hope I haven't been hallucinating stuff all along

#

You're definitely running PIE as a client and not standalone/listen server?

dark parcel
#

oh wait, I ran as listen server lmao

#

testing again

verbal ice
#

LOL

dark parcel
#

@verbal ice you are right it work, I was wrong.

verbal ice
#

I'm happy I'm not going crazy

dark parcel
#

don't know where I got the info from. Even some google article share the same opinion.

As it turns out anyway, rpc work as client

verbal ice
#

yeah player controller, player state and possessed pawns are generally client owned

dark parcel
lament flax
#

thanks

#

will test when i can

ocean swallow
#

I want to know how to avoid operation latency caused by connection latency.

tardy fossil
#

i dont think that can be easily done without using c++

dark parcel
lament flax
#

thanks a ton, got it working for friend invites now

dark parcel
#

there's probably a hack with big cons. It's by playing around with the AddMovementInput. But honestly just don't go ahead with bp only imo.

dark parcel
ocean swallow
#

i have used c++ in unreal engine

dark parcel
#

https://discord.gg/uQjhcJSsRG
In this video I setup a new project and create a custom character movement component. I also implement movement safe sprinting which works at any ping.
https://github.com/delgoodie/Zippy

0:00 Create New Project
1:02 Setup File System
02:50 Create Custom CMC
04:43 Saved Move Class
08:37 Compressed Flags
13:35 Client...

▶ Play video
#

Cool, you can follow the tutorial above then =).

ocean swallow
dark parcel
#

Multiplayer isn't friendly to bp only designers.

#

especially for real time games.

ocean swallow
#

I didn't know that problem before yesterday.

dark parcel
#

Gotta bite the bullet or start with single player game imo.

ocean swallow
#

If I had more time I would try to implement this. But I only have less than a month left.

ocean swallow
#

Y

dark parcel
#

not sure what the game is about but perhaps you can turn it into split screen game.

#

That way no networking is required. It;s basically single player with multiple controllers.

#

Other than that, just let client have authorative movement.

#

They can cheat but who cares, it's a school project.

#

but at least you know now to not start a first project or second project with multiplayer.

ocean swallow
#

There is no lag on a single computer, but I am using Steam to connect.

dark parcel
#

no such thing as 0 ms

#

those Youtube bp sprint tutorial breaks the moment the game simulate any lag.

ocean swallow
#

I believe that using P2P connection in a LAN will have lower latency.

#

maybe

ocean swallow
dark parcel
ocean swallow
#

I didn't understand that until a few minutes ago

torn hull
#

Hey, finally I got proximity voice chat working and everyone has good attenuation settings for everyone. However, it took a lot of trials and errors because most of the time it wasn’t working either on clients, on the server, or for someone who joined late. Previously, I also wanted to include everything in OnRepNotify, but then it crashed for everyone who joined late.

My question is, is it really necessary to create this for loop and multicast init every time someone is spawned? It doesn’t feel like it, but that was the only scenario where everything was initialized for everyone.

lament flax
#

its been a while since i played with RPCs

in my widget (client) im calling a server RPC (in my game state)

i know that the local client doesnt own the game state, but shouldnt the call get the owning connection of the player controller ?

#

do i have to move to server RPC on the player controller and have it get the game state ?

dark parcel
#

Move the RPC somewhere the client own.

#

by the time the ServerRPC is executed it's already on the server, the server can access what ever instance it has in it's world.

untold rose
#

Does anyone know if there's a way to have the debug lines performed by a trace show on the owning client as well?

lost inlet
#

if you want to do that you'd have to send a client RPC with the relevant data

untold rose
lost inlet
#

the gameplay debugger does have replication support if you build your debug visualisation stuff around that

untold rose
pliant cypress
#
void UMultiplayerSessionSubsytem::CreateSession(int32 NumPublicConnection, FString MatchType)
{
    if (!SessionInterface.IsValid()) return;

    auto ExistingSession = SessionInterface->GetNamedSession(NAME_GameSession);
    if (ExistingSession != nullptr)
    {
        bCreateSessionOnDestroy = true;
        LastNumPublicConnections = NumPublicConnection;
        LastMatchType = MatchType;

        DestroySession();
    }

    // Store the delegate in FDelegateHandle so later can be removed from delegate list
    CreateSessionsCompleteDelegateHandle = SessionInterface->AddOnCreateSessionCompleteDelegate_Handle(CreateSessionsCompleteDelegate);

    LastSessionSettings = MakeShareable(new FOnlineSessionSettings);
    LastSessionSettings->bIsLANMatch = IOnlineSubsystem::Get()->GetSubsystemName() == "NULL" ? true : false;
    LastSessionSettings->NumPublicConnections = NumPublicConnection;
    LastSessionSettings->bAllowJoinInProgress = true;
    LastSessionSettings->bAllowJoinViaPresence = true;
    LastSessionSettings->bAllowInvites = true;
    LastSessionSettings->bShouldAdvertise = true;
    LastSessionSettings->bUsesPresence = true;
    LastSessionSettings->bUseLobbiesIfAvailable = true;
    LastSessionSettings->Set(SEARCH_KEYWORDS, FString("MatchType"), EOnlineDataAdvertisementType::ViaOnlineService);

    const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
    if (!SessionInterface->CreateSession(*LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, *LastSessionSettings))
    {
        SessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(CreateSessionsCompleteDelegateHandle);

        // Broadcast our own custom delegate
        MultiplayerOnCreateSessionComplete.Broadcast(false);
    }
}
#
void UMultiplayerSessionSubsytem::OnCreateSessionsComplete(FName SessionName, bool bWasSuccessful)
{
    if (SessionInterface)
    {
        SessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(CreateSessionsCompleteDelegateHandle);
    }

    MultiplayerOnCreateSessionComplete.Broadcast(bWasSuccessful);
}
#
MultiplayerSessionsSubsystem->MultiplayerOnCreateSessionComplete.AddDynamic(this, &ThisClass::OnCreateSession);


void UMenu::OnCreateSession(bool bWasSuccessful)
{
    if (bWasSuccessful)
    {
        if (GEngine)
        {
            GEngine->AddOnScreenDebugMessage(-1, 15.f, FColor::Yellow, FString(TEXT("Session Created Successfully")));
        }
        MenuTearDown();
        UWorld* World = GetWorld();
        if (World)
        {
            World->ServerTravel(PathToLobby);
        }
    }
    else {
        if (GEngine)
        {
            GEngine->AddOnScreenDebugMessage(-1, 15.f, FColor::Red, FString(TEXT("Session Failed")));
        }
        HostButton->SetIsEnabled(true);
    }
}
#

i am in a race condition

#

when i click on Host, it create session

#

but multiple times

thin stratus
#

@pliant cypress bCreateSessionOnDestroy = true; that sounds like you trigger create session from your DestroySession code. Shouldn't you return after DestroySession(); in that case?

#

Also not sure the term "race condition" applies here.

pliant cypress
thin stratus
#

That's not what a race condition is

#

If it calls multiple times then breakpoint it an check where the calls come from.

pliant cypress
# thin stratus <@771941181638574081> `bCreateSessionOnDestroy = true;` that sounds like you tri...
void UMultiplayerSessionsSubsystem::DestroySession()
{
    if (!SessionInterface.IsValid())
    {
        MultiplayerOnDestroySessionComplete.Broadcast(false);
        return;
    }

    DestroySessionCompleteDelegateHandle = SessionInterface->AddOnDestroySessionCompleteDelegate_Handle(DestroySessionCompleteDelegate);

    if (!SessionInterface->DestroySession(NAME_GameSession))
    {
        SessionInterface->ClearOnDestroySessionCompleteDelegate_Handle(DestroySessionCompleteDelegateHandle);
        MultiplayerOnDestroySessionComplete.Broadcast(false);
    }
}

void UMultiplayerSessionsSubsystem::OnDestroySessionComplete(FName SessionName, bool bWasSuccessful)
{
    if (SessionInterface)
    {
        SessionInterface->ClearOnDestroySessionCompleteDelegate_Handle(DestroySessionCompleteDelegateHandle);
    }
    if (bWasSuccessful && bCreateSessionOnDestroy)
    {
        bCreateSessionOnDestroy = false;
        CreateSession(LastNumPublicConnections, LastMatchType);
    }
    MultiplayerOnDestroySessionComplete.Broadcast(bWasSuccessful);
}
pliant cypress
thin stratus
#

Yeah then you are calling it twice through that I guess. Make sure you return early after DestroySession();

thin stratus
#
auto ExistingSession = SessionInterface->GetNamedSession(NAME_GameSession);
    if (ExistingSession != nullptr)
    {
        bCreateSessionOnDestroy = true;
        LastNumPublicConnections = NumPublicConnection;
        LastMatchType = MatchType;

        DestroySession();
    }
#

After destory session.

#

cause you are continueing with CreateSession even though you will trigger CreateSession again when destroy is done

pliant cypress
pliant cypress
tame sapphire
#

if having a pre lobby for customization skins/loadout would it be acceptable for the loadout to be saved to the lobby player controller and then the server collect all info here for all future spawn when in the actual game or would you approach it a different way? (this was a few days ago and another guy asking but he seems to have run off on his own way)

#

Im hoping to not have changes in gameplay and there stuck with the loadout they chose just looking for any details on approaching this from lobby to gameplay

dark edge
#

Like, is being in a lobby a state of the game server, or is it something that happens before connection through an OSS or something?

tame sapphire
#

@dark edge lobby would be on the server and the pre map the loadout screen would be local

#

there is an instance for both situations

#

ive seen on swap controllers so was maybe thinking of that for lobby on server to gameplay on server?

coarse cave
#

Dedicated servers?

tame sapphire
#

id like to move to that eventually but atm im on listen

coarse cave
#

Different engine, but I can give you a breakdown the pattern that TF/TF2/Apex used for that sort of thing. It's reasonably complex though, as it is designed to handle storage of said data in a backend and all of the issues related to transfering servers/reading/writing to a backend with latency

tame sapphire
#

anything is helpful atm as ive not approached this before

#

I may be better off with something more like a character select atm for now so nothing as involved or with a backend but it would be interesting to know anyway

gray blade
#

i need advice, my gamestate has a few variables such as days passed and gametime. When the players seamless travel to a new map ids like the value to persist. what the cleanest way to do it?

coarse cave
#

@tame sapphire

Connection

  • ClientA connects to DedicatedServerX
  • DedicatedServerX requests PersistentData for ClientA from Backend
  • DedicatedServerX sends ClientA a copy of their PersistentData during connection

Lobby

  • ClientA alteration of PersistentData happens only between ClientA and DedicatedServerX
    • DedicatedServerX periodically writes updated PersistentData for ClientA to Backend

Matchmaking

  • DedicatedServerX sends ClientA to new DedicatedServerY (e.g. after successful matchmaking)
    • DedicatedServerX sends a copy of PersistentData for ClientA along with ClientA to DedicatedServerY
    • DedicatedServerX sends a copy of PersistentData for ClientA to Backend

Match

  • DedicatedServerY is now in charge... until the process repeats when sending ClientA back to the lobby

This approach removes latency/communication with the backend from loop and prevents race conditions. By handing off the data between server transfers you remove the possibility of reading/writing stale PersistentData from Backend.

dark lance
#

Hey everyone 👋
I’m working on a multiplayer project and I need help setting up a scoring system.

Basically, when a player throws a ball into a goal, I want only that specific player to get a point so the system needs to identify which player threw it and update that player’s score correctly on the server (and show it on their UI).

I already have the throwing and goal overlap events working, and I’ve got a replicated IncreaseScore function in my GameMode, but I’m struggling with how to link the thrown ball to the correct player’s PlayerState in multiplayer.

Does anyone know how to set this up properly in Blueprints or have a video/tutorial that covers something like this?

Any help or examples would be super appreciated 🙏

coarse cave
#

Add a "LastThrower" field to ball that gets set every time a player picks it up?

tame sapphire
#

@coarse cave thank you, I think for now ill go with a server list the player can choose from and try and push the complete player list mapped to selected or look around how a team is passed as an option

dark lance
halcyon ore
#

Hi, so curious about I guess "more complex" networking/ replication methods.

From the pins I see push model
I see network manager
and then the network manager talks about dormancy

From how the network manager describes it, dormancy seems bad, cuz of responsive-ness.

But, what about the other 2?
Are they 2 different methods for the same thing?
Wouldn't I use less network/ NetBroadcastTick time, by using push-model, so no pulling, or computing is required?
But, then what about a network manager, that sounds like central control logic, and doesn't require special handling per actor, for push model.

Are they both "good", or does it just depend on the type of game being made?

#

Or, actually from actually reading it, the network manager seems worse, or at least far more time consuming?
I was under the impression it was a thing that came with UE, and was a base engine class I use.
Not a completely custom actor C++

#

Since, from a general overview, it seems like the network manager would waste a decent bit of data?
Cuz, the implementation in the dev tricks, doesn't seem to account for actual ref replication, so wouldn't all clients know about all health component values?
Or, am I just missing something obvious. 😛

thin stratus
#

@halcyon ore Not entirely sure what you want with the Network Manager, but Push Model is indeed meant to reduce or remove the overhead of polling.

#

Iris will require it iirc. Current replication system has it optional but you should usually use it anyway.

halcyon ore
#

I just wasn’t sure.
I saw stuff in the pins for it.
Wasn’t sure what it is, or how useful it is.

thin stratus
pliant cypress
#
void UMultiplayerSessionSubsytem::FindSession(int32 MaxSearchResults)
{
    if (!SessionInterface) return;

    FindSessionsCompleteDelegateHandle = SessionInterface->AddOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegate);

    LastSessionSearch = MakeShareable(new FOnlineSessionSearch());
    LastSessionSearch->MaxSearchResults = MaxSearchResults;
    LastSessionSearch->bIsLanQuery = (IOnlineSubsystem::Get()->GetSubsystemName() == "NULL");
    LastSessionSearch->QuerySettings.Set(SEARCH_KEYWORDS, FString("MatchType"), EOnlineComparisonOp::Equals);
    LastSessionSearch->QuerySettings.Set(SEARCH_LOBBIES, true, EOnlineComparisonOp::Equals);

    UE_LOG(LogTemp, Warning, TEXT("Session Search start"));
    const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
    if (!SessionInterface->FindSessions(*LocalPlayer->GetPreferredUniqueNetId(), LastSessionSearch.ToSharedRef()))
    {
        int32 NumResults = LastSessionSearch->SearchResults.Num();
        UE_LOG(LogTemp, Warning, TEXT("Number of sessions found: %d"), NumResults);
        SessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegateHandle);
        MultiplayerOnFindSessionComplete.Broadcast(TArray<FOnlineSessionSearchResult>(), false);
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("Session Search failed"));
    }
    
}
#

void UMultiplayerSessionSubsytem::JoinSession(const FOnlineSessionSearchResult& SessionResult)
{
    if (!SessionInterface.IsValid())
    {
        MultiplayerOnJoinSessionComplete.Broadcast(EOnJoinSessionCompleteResult::UnknownError);
        return;
    }

    JoinSessionsCompleteDelegateHandle = SessionInterface->AddOnJoinSessionCompleteDelegate_Handle(JoinSessionsCompleteDelegate);

    const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
    if (!SessionInterface->JoinSession(*LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, SessionResult))
    {
        SessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(JoinSessionsCompleteDelegateHandle);

        MultiplayerOnJoinSessionComplete.Broadcast(EOnJoinSessionCompleteResult::UnknownError);
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("Session join failed"));
    }
}
#
void UMultiplayerSessionSubsytem::OnFindSessionsComplete(bool bWasSuccessful)
{
    if (SessionInterface)
    {
        SessionInterface->ClearOnFindFriendSessionCompleteDelegate_Handle(0, FindSessionsCompleteDelegateHandle);
    }

    if (LastSessionSearch->SearchResults.Num() <= 0)
    {
        MultiplayerOnFindSessionComplete.Broadcast(TArray<FOnlineSessionSearchResult>(), false);
        return;
    }

    MultiplayerOnFindSessionComplete.Broadcast(LastSessionSearch->SearchResults, bWasSuccessful);
}

void UMultiplayerSessionSubsytem::OnJoinSessionsComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
    if (SessionInterface)
    {
        SessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(JoinSessionsCompleteDelegateHandle);
    }

    MultiplayerOnJoinSessionComplete.Broadcast(Result);
}
#
void UMenu::OnFindSession(const TArray<FOnlineSessionSearchResult>& SessionResults, bool bWasSuccessful)
{
    for (auto Result : SessionResults)
    {
        FString SettingsValue;
        Result.Session.SessionSettings.Get(NAME_GameSession, SettingsValue);
        Result.Session.SessionSettings.bUseLobbiesIfAvailable = true;
        if (SettingsValue == MatchType)
        {
            MultiplayerSessionsSubsystem->JoinSession(Result);
            return;
        }
    }
    if (!bWasSuccessful || SessionResults.Num() == 0)
    {
        JoinButton->SetIsEnabled(true);
    }
}

void UMenu::OnJoinSession(EOnJoinSessionCompleteResult::Type Result)
{
    IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get();
    if (Subsystem)
    {
        IOnlineSessionPtr SessionInterface = Subsystem->GetSessionInterface();
        if (SessionInterface.IsValid())
        {
            FString Address;
            SessionInterface->GetResolvedConnectString(NAME_GameSession, Address);
            //UE_LOG(LogTemp, Warning, TEXT("Address: %s"), *Address.ToString());
            APlayerController* PlayerController = GetGameInstance()->GetFirstLocalPlayerController();
            if (PlayerController)
            {
                MenuTearDown();
                PlayerController->ClientTravel(Address, ETravelType::TRAVEL_Absolute);
            }
        }
    }
    if (Result != EOnJoinSessionCompleteResult::Success)
    {
        JoinButton->SetIsEnabled(true);
    }
}
#

Now the issue is, i added some breakpoint because i wasn't able to join the session. first breakpoint hit at

if (!SessionInterface->FindSessions(*LocalPlayer->GetPreferredUniqueNetId(), LastSessionSearch.ToSharedRef()))

then it hit inside OnFindSession inside menu where bWasSuccessful got true and log printed Session Search Started.
then upon continuing, it should hit inside Join session instead it got back into find session and hit the else case which says Session Search failed

#

also, it never printed Num of sessions

#

i am not able to understand why this happens?

nova wasp
#

optimized code

#

means it won't be always linear when debugging (if that's what you mean, I don't really want to read all of this without even knowing where to start)

pliant cypress
#

also, my creating session code have setting like this

#
void UMultiplayerSessionSubsytem::CreateSession(int32 NumPublicConnection, FString MatchType)
{
    if (!SessionInterface.IsValid()) return;

    auto ExistingSession = SessionInterface->GetNamedSession(NAME_GameSession);
    if (ExistingSession != nullptr)
    {
        bCreateSessionOnDestroy = true;
        LastNumPublicConnections = NumPublicConnection;
        LastMatchType = MatchType;

        DestroySession();
    }

    // Store the delegate in FDelegateHandle so later can be removed from delegate list
    CreateSessionsCompleteDelegateHandle = SessionInterface->AddOnCreateSessionCompleteDelegate_Handle(CreateSessionsCompleteDelegate);

    LastSessionSettings = MakeShareable(new FOnlineSessionSettings);
    LastSessionSettings->bIsLANMatch = IOnlineSubsystem::Get()->GetSubsystemName() == "NULL" ? true : false;
    LastSessionSettings->NumPublicConnections = NumPublicConnection;
    LastSessionSettings->bAllowJoinInProgress = true;
    LastSessionSettings->bAllowJoinViaPresence = true;
    LastSessionSettings->bAllowInvites = true;
    LastSessionSettings->bShouldAdvertise = true;
    LastSessionSettings->bUsesPresence = true;
    LastSessionSettings->bUseLobbiesIfAvailable = true;
    LastSessionSettings->Set(SEARCH_KEYWORDS, FString("MatchType"), EOnlineDataAdvertisementType::ViaOnlineService);

    const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
    if (!SessionInterface->CreateSession(*LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, *LastSessionSettings))
    {
        SessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(CreateSessionsCompleteDelegateHandle);

        // Broadcast our own custom delegate
        MultiplayerOnCreateSessionComplete.Broadcast(false);
    }
}
#

i can find my session on EOS dev portal though

#

in logs it printed this

#
[[2025.10.18-09.06.59:447][397]]LogTemp: Warning: Session Search failed
[[2025.10.18-09.07.00:267][428]]LogOnlineSession: EOS: [FOnlineSessionEOS::StartLobbySearch] LobbySearch_Find was successful.```
pliant cypress
#

i don't understand

ocean swallow
#

How to safely destroy the session to avoid server crashes? My current blueprint(BP_player) is as shown in the picture.
Is this video trustworthy? https://youtu.be/7OdruBNGOM8

👨‍🏫 My Patreon link:
https://www.patreon.com/kekdot
Download Project Files | Premium Tutorials | Courses

🕹️ Get our Game on Steam | The Anomaly Project:
https://store.steampowered.com/app/2960770/The_Anomaly_Project/


In this tutorial we talk about Network and Traveling errors. We install the correct nodes in the game instance ...

▶ Play video
lament flax
#

client just has to destroy session (on client side iirc, doesnt need to run it on server)

#

server has to destroy the session for each client (locally iirc to) and then destroy on server for himself (tyo completly destroy the session online)

ocean swallow
#

The client does the above and the server does the below?

lament flax
#

nah

#

both use destroy

#

end is to start again

#

for phase manageemnt

ocean swallow
lament flax
#

yes

ocean swallow
#

how

thin stratus
#

I usually just have the DestroySession call in the MainMenu GameMode.

#

Just on BeginPlay for example.

#

Makes sure the Session is cleaned up for Server and Client whenever they get thrown back into the MainMenu after the Disconnect.

ocean swallow
lament flax
#

who calls it ? the sever or client ?

ocean swallow
#

When either the client or the server calls this event, the server will crash.

lament flax
#

whats the error when the client leaves and server crash

thin stratus
#

The Server crash could be totally unrelated. Need crash message and callstack .

ocean swallow
thin stratus
#

Crashes when trying to remove the Client. That's potentially not even related to the DestroySession call.

ocean swallow
#

If I use different player controllers for my preset map and game map, will they automatically switch when I open the level?

lament flax
#

how do you get the "player to kick"

thin stratus
#

Yeah, it takes the PlayerController of the GameMode that is assigned to the given level or defined via ?game=

ocean swallow
thin stratus
#

You should just share more of your code.

#

Show how this is all called from start to end.

lament flax
#

yeah, show us the code where you call the Quit event

ocean swallow
#

picture 1 is UI.

lament flax
#

i dont see anything wrong

thin stratus
#

Again, I would travel to the MainMenu or whatever that is for you and have the GameMode or PlayerController of that level call DestroySession

lament flax
#

should be working for client

thin stratus
#

If your player loses internet, you will be stuck with a Session locally.

#

Your code isn't covering all cases.

lament flax
#

iirc

thin stratus
#

Why would you need to?

lament flax
#

i remember having to do this some time ago

thin stratus
#

Let the Client call it locally once they traveled back.

ocean swallow
#

hmm.. I try

#

quit game event->openlevel->destroy session?

thin stratus
#

QuiteGameEvent -> OpenLevel

In the GameMode or PlayerController of that level -> DestroySession on BeginPlay.

ocean swallow
#

😮
can i use level blueprint to do that?

thin stratus
#

You can, but that's the worst you could do :P

ocean swallow
#

Or just assign an unused player controller

thin stratus
#

Your levels should have GameModes assigned that match what the level is for.

lament flax
#

just add a GM main menu

thin stratus
#

Usual simplistic setup is something like:

BP_GameMode_Base
-> BP_GameMode_MainMenu
-> BP_GameMode_Gameplay

ocean swallow
#

It seems that I haven't learned the basics well.

thin stratus
#

I would agree here.

ocean swallow
#

Is there anything else I need to set up besides the controller?
(Inherited from the game mode base)

thin stratus
#

I would usually inherit from the non-base version for Multiplayer. With C++ you could choose the Base one and do some of the non-base stuff yourself, but for BP that's probably more powerful.

#

And no, the PlayerController would be enough for the MainMenu.

#

Your PlayerController and the other classes should more or less mirror the same inheritance as your GameModes though

#

BP_PlayerControler_Base
-> BP_PlayerController_MainMenu
-> BP_PlayerController_Gameplay.

ocean swallow
#

Should I change it?

ocean swallow
lament flax
#

you dont have to

empty glade
#

Does anybody know a trick to getting Steam to immediately recognize an update is available?

I'm in the middle of troubleshooting some Steam stuff, which means deploying to Steam and then updating the game on a couple machines. So far the fastest way is to relaunch Steam because I don't know how to force Steam to refresh the list. I can't be the only one going crazy with this...

pliant cypress
#
void UMultiplayerSessionSubsytem::CreateSession(int32 NumPublicConnection, FString MatchType)
{
    if (!SessionInterface.IsValid()) return;

    auto ExistingSession = SessionInterface->GetNamedSession(NAME_GameSession);
    if (ExistingSession != nullptr)
    {
        bCreateSessionOnDestroy = true;
        LastNumPublicConnections = NumPublicConnection;
        LastMatchType = MatchType;

        DestroySession();
    }

    // Store the delegate in FDelegateHandle so later can be removed from delegate list
    CreateSessionsCompleteDelegateHandle = SessionInterface->AddOnCreateSessionCompleteDelegate_Handle(CreateSessionsCompleteDelegate);

    LastSessionSettings = MakeShareable(new FOnlineSessionSettings);
    LastSessionSettings->bIsLANMatch = IOnlineSubsystem::Get()->GetSubsystemName() == "NULL" ? true : false;
    LastSessionSettings->NumPublicConnections = NumPublicConnection;
    LastSessionSettings->bAllowJoinInProgress = true;
    LastSessionSettings->bAllowJoinViaPresence = true;
    LastSessionSettings->bAllowInvites = true;
    LastSessionSettings->bShouldAdvertise = true;
    LastSessionSettings->bUsesPresence = true;
    LastSessionSettings->bUseLobbiesIfAvailable = true;
    LastSessionSettings->Set(SEARCH_KEYWORDS, FString("MatchType"), EOnlineDataAdvertisementType::ViaOnlineService);

    const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
    if (!SessionInterface->CreateSession(*LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, *LastSessionSettings))
    {
        SessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(CreateSessionsCompleteDelegateHandle);

        // Broadcast our own custom delegate
        MultiplayerOnCreateSessionComplete.Broadcast(false);
    }
}
#
void UMultiplayerSessionSubsytem::FindSession(int32 MaxSearchResults)
{
    if (!SessionInterface) return;

    FindSessionsCompleteDelegateHandle = SessionInterface->AddOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegate);

    LastSessionSearch = MakeShareable(new FOnlineSessionSearch());
    LastSessionSearch->MaxSearchResults = MaxSearchResults;
    LastSessionSearch->bIsLanQuery = (IOnlineSubsystem::Get()->GetSubsystemName() == "NULL");
    LastSessionSearch->QuerySettings.Set(SEARCH_KEYWORDS, FString("MatchType"), EOnlineComparisonOp::Equals);
    LastSessionSearch->QuerySettings.Set(SEARCH_LOBBIES, true, EOnlineComparisonOp::Equals);

    UE_LOG(LogTemp, Warning, TEXT("Session Search start"));
    const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
    if (SessionInterface->FindSessions(*LocalPlayer->GetPreferredUniqueNetId(), LastSessionSearch.ToSharedRef()))
    {
        int32 NumResults = LastSessionSearch->SearchResults.Num();
        UE_LOG(LogTemp, Warning, TEXT("Number of sessions found: %d"), NumResults);
        SessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegateHandle);
        MultiplayerOnFindSessionComplete.Broadcast(LastSessionSearch->SearchResults,  true);
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("Session Search failed"));
    }
    
}
#

this is my create and find session

#

and session is created successfully

#

but when on find, it returns 0 session found

#

where di i made mistake in configuration on FindSession code?

thin stratus
empty glade
grizzled stirrup
#

but often yes restarting is just faster, depends on your game

#

I think there is a trick where you press the verify button but cancel the verify and get the download instantly

mystic estuary
empty glade
mystic estuary
#

So did I. I don't even know how I happened to discover that, I also had to restart it for the first couple of weeks, and then, maybe accidentally, tried to go offline and online lol

dark parcel
#

Do you guys client predict the visual aspect of your inventory?

Say drag and drop / splitting items in the grid?

Would it be an okay idea to just let client predict it. So when it does an action / something like transaction in mySql.
It just update locally. When the inventory is OnRep, client check if the content matches with the repped value.
If not then sync it to server value.

Or should we just let server decide? but then the input delay may feel pretty bad.

cold cipher
#

guys, how to send all the connected players back to a lobby map?

dark parcel
#

server travel

#

lobby in what sense though, is it still networked? is it where people waiting for each other to be ready?

thin stratus
#

The problem is often not the single predicted action, but the chain reaction of that prediction.

dark parcel
#

Yeah, when I thought about it I don't think that's worth it.

thin stratus
#

What if your backend takes a bit longer and the player moves items, swaps them, splits them, drops them, all before the first action even got resolved?
You'd need to handle a queue of stacked actions. This would basically become a movement simulation, just not on tick.

cold cipher
thin stratus
civic jackal
#

On what channel do they notify exactly when Epic gives away assets?

dark parcel
#

Right, I will try and see how it goes. Atm my idea is to just check the server and client value on the client on the latest OnRep (For personal inventory)

thin stratus
#

That works but only if those actions have no side-effects.

dark parcel
#

using and dropping item will still server auth, so those action can;t be predicted anyway.

thin stratus
#

Yus.

dark parcel
#

True, but as long the client doesn't modify their inventory with cheating, visually it should be fine? 🤞

thin stratus
#

In theory, you'd just need to keep track of the predicted actions locally.
Ensure they have some form of timestamp for ordering and then simply ack the changes when they are fine or correct them when not.
And after a correction you could "replay" the still newer, predicted ones.

civic jackal
thin stratus
#

Aka, how much pain do you want to go through :D

cold cipher
# dark parcel Do you guys client predict the visual aspect of your inventory? Say drag and dr...

if only the player has access to his/her own inventory it's ok to have client predictive methink

but for public inventory like a chest...
I'd say it depends on the game's nature
if it's rather coop than competitive, then client predictive might be ok

palworld had this bug where you can use your base's inventory to build, but if you step away from it last second you won't actually cost anything
and I think they still didn't patch it out

dark parcel
#

that may destroy my head a little :P,

what do you think of this approach.

Client do something on their inventory -> Client send Server RPC -> PredictedAction++
ClientOnRep -> PredictedAction -- -> If PredictedAction is 0 (So only attempt to update latest data) -> If current data != OnRep data, set current data to OnRepData.

#

and just reconstruct whole inventory :S, I kinda only have 30 slots in my backpack.

thin stratus
#

Could work. I would say try it out

dark parcel
#

will see how it goes, thx.

compact harbor
#

replicating a single FVector_NetQuanitize every 0.1 seconds using my custom version of a fast array (basically the normal fast array but with indices stored as bits)

#

but i decided to have my entities simulated on both clients and server, syncing their locations sometimes since server-only crowd isnt possible from what ive seen so far. No matter how much i increase my bandwidth, unreal still throttles it. And my data isnt THAT big (10 kb per send, 100kb per second). I tried to have 1 replicator per 250 entities, which had only made the situation worse. Screw it, ill just forget about this idea

meager spade
#

Do you really need to sync positions is it that crucial ?

#

They should.be pretty similar if running deterministic stiff

#

Stuf

wicked hearth
#

Hey all, i am using 5.6 and i just noticed that server travel is broken..

Seamless should work but that doesn't seem to be working for me? What are the things that i could be forgetting for it? I have enabled seamless travel in the gamemodes.

I can make a lobby, get both players in the same world.
When the server host runs, server travel mapname?listen the host will go.
But the other clients wont, they will stay in the lobby and after enough time no connections have been made so they go back to default map

pliant cypress
#

I'm currently working on my weapon system's ammo functionalities.

My plan is to have a magazine instead of typical ammo counts, so if a player reloads it doesn't just lose number but a whole magazine with the bullets he left in it. And the player cannot carry all weapon types magazine.

So this is my current design with respect to multiplayer

--- Weapon ---

int32 MagAmmo - current ammo in the weapon

int32 MaxMagAmmo - Maximum ammo magazine can have.

--- CombatComponent ---

TMap<EWeaponType, int32> CarriedMagMap - Number of magazines per weapon type

TArray<int32> CarriedMags - set the current magazine for the EquippedWeapon. (Takes from CarriedMagMap)


But I'm not able to connect the dot of reloading functionality and loadout system (contains 4 things, 2 weapon primary/secondary with prefix magazines and 2 grenades with prefix amount)

thin stratus
grim cypress
#

yo bois

#

I am bout to word wall you so im sorry in advance but any help is super appreciated

thin stratus
#

I feel like that could just live in the weapon or a data asset of such

#

Same with the TArray. Not sure why that all lives in a separate component.

grim cypress
#

Currently I am working on a FPS with a Player Actor BP "BP_TunnelPlayer" that uses Metahumans with an animation blueprint called "ABP_PlayersAims_MHRemake". I currently use a Blueprint Interface called "BPI_CharacterData" to move data between the Character's blueprint and the Animation blueprint in use. In first three pictures below you will see how I implemented an UNFINISHED pipeline for Character Gait Data (Sprinting, Slow-Walking, Jogging, etc.). Currently there is an issue where when another player sprints, they move at the correct speed but the animation being played is still the Default aka Jog animation.

I assume that this problem is because the Function Call "Update Gait" in the Player BP is Player Input Dependent, and is not replicated along with any associated variables and that is why is does not work for viewing other players in Multiplayer.

Later I implemented a GroundMatching system which needs a dependent variable "GroundDistance" which is also passed through the same BPI. It is visible in the LAST TWO pictures below and my question now becomes (and I could test it but I just figure I'd ask since I'll be away for some time): Will there be any similar issues with replicating and using the variable "GroundDistance"?

More generally speaking, since this variable is calculated in the Event Tick node, which later then uses the BPI, will the ABP for OTHER players on your screen maintain synced and current GroundDistance data? And if so, could a similar method be used for always passing current Gait Data or should that be a replicated call?
https://imgur.com/a/MRtg4tF https://imgur.com/a/rb1OYCZ https://imgur.com/a/iJgo2Fp https://imgur.com/a/prOUAOs https://imgur.com/a/xkFx6YR

#

ughh why do they have to come out like that, I removed the embeds on the picture so they don't nuke the entire page, but they are still usable

wind ridge
#

I'm having a bit of a struggle wrapping my head around whether things should be on the character or the controller. I am trying to make a HUD for the player with icons from the character abilities but can't seem to get the timing correct between initialization and possession.
Maybe someone has some insight in the correct process of generating the hud based on the character defaults?
Im using unreal 4.27

exotic wasp
#

server replicates proper inventory after a change so if theres a mispredict it resolves itself pretty easily

nocturne quail
#

when replicating a tarray of structs why changes to a property inside one of the structs not trigger the repnotify function on the array, how to work around this in a nested scenario where outer struct array contains inner tarrays?

fossil spoke
nocturne quail
# fossil spoke Use a Fast Array. Then you can mark it dirty, which will cause it to replicate.

Super thanks, and here is the code of the current issue... will try to change it to fast array now

#include "AdvancedNetworkComponent.h"
#include "Net/UnrealNetwork.h"

UAdvancedNetworkComponent::UAdvancedNetworkComponent()
{
    bReplicates = true;
    if (HasAuthority())
    {
        FOuterStruct Outer;
        Outer.InnerData.InnerArray = {10, 20, 30};
        Outer.OuterValue = 100;
        OuterArray.Add(Outer);
    }
}

void UAdvancedNetworkComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME(UAdvancedNetworkComponent, OuterArray);
}

void UAdvancedNetworkComponent::OnRep_OuterArray()
{
    // will not trigger changes for InnerData.InnerArray
}

void UAdvancedNetworkComponent::ModifyInnerArray(int32 OuterIndex, int32 InnerIndex, int32 NewValue)
{
    if (HasAuthority() && OuterArray.IsValidIndex(OuterIndex) && OuterArray[OuterIndex].InnerData.InnerArray.IsValidIndex(InnerIndex))
    {
        OuterArray[OuterIndex].InnerData.InnerArray[InnerIndex] = NewValue;
    }
}

void UAdvancedNetworkComponent::AddToInnerArray(int32 OuterIndex, int32 NewValue)
{
    if (HasAuthority() && OuterArray.IsValidIndex(OuterIndex))
    {
        OuterArray[OuterIndex].InnerData.InnerArray.Add(NewValue);
    }
}
nocturne quail
halcyon ore
#

Related to FastArray.
Am, I just missing something obvious?
How do I get the like "variable owner" in the fast array struct, for the add, remove, change?

fossil spoke
halcyon ore
#

The fast array has the pre remove, post add, and post change
But, how do I get those to the actor with the fast array, so I can do things when things change, without re-looping the entire array with the normal onrep

fossil spoke
#

Both the FastArraySerializer and the FastArraySerializerItem have that pattern.

#

They do vary slightly

#

So depends on which one you like more.

#

But something I tend to do if I need what you are asking for

#

Is I expose delegates for those functions on the FastArray

#

Which the Object that is using the FastArray would sub to

#
    /* Public interface for external systems to know when the container operations were performed. These will be called on Server and Client via their respective functions below. */
    DECLARE_MULTICAST_DELEGATE_OneParam(FOnAdded, const FSeat& /*AddedElement*/);
    FOnAdded OnAddedDelegate;

    DECLARE_MULTICAST_DELEGATE_OneParam(FOnChanged, const FSeat& /*ChangedElement*/);
    FOnChanged OnChangedDelegate;
#

For example

#

Which would be called respectively by

#
    /* These are called by the SerializerItem on both Client and Server when the container element is operated on. */
    void OnAdded(const FSeat& Seat);
    void OnChanged(const FSeat& Seat);
#

In turn called by

#
    void PostReplicatedAdd(const struct FSeatingArrangement& InArray);
    void PostReplicatedChange(const struct FSeatingArrangement& InArray);
#

On the FastArraySerializerItem in this case

halcyon ore
#

Ah, ok.
So, easier then I thought.

I tried GPT, cuz sometimes it has more then 2 IQ.
It suggested, my owning actor, fill each entry with a ref to the owner.

I was like...
Yeah, thats wrong.

fossil spoke
#

You could have the Array receive the OwningActor

#

Which would tightly couple them

#

And call functions directly on it

#

But I prefer the method I posted above

#

The AbilitySystem does that I think

#

Tightly couples their FastArrays to the ASC

#

But in their case that makes sense

#

Since their containers arent generally useful outside of use with the ASC

halcyon ore
#

Then, since your here, if you don't mind.

Am I also lost, on how Fast Array replication is handled on initial actor replication?
Normal case says it replicated all 10k entries all at once.
But, surely thats wrong, since it defeats a likely core purpose of the Fast Array?

If that is the case, and all 10k entries get replicated at once, then how do you handle manual batching, while also having the Fast Array handle delta changes?

fossil spoke
#

The entire Array state is sent initially.

#

It cannot be any other way

#

Since the Deltas need to operate on a known base

#

All Clients need to start from the base

#

This is true for regular arrays as well

halcyon ore
#

Fast Array seems a bit useless to me then?
Still gotta replicate 10k entries all at once.
So, if too much your just fucked?

How is that handled, since like you say, and logically they all need the same starting point/ current data, for deltas to work correctly

fossil spoke
#

They are far from useless

#

Putting aside their efficiency, just their structure is helpful for cross network operations.

#

Trying to figure out what element inside a regular array was changed by replication is tedious

#

FastArrays solve that

#

In the case of use large data sets

#

I would consider trying to think of ways to separate them into smaller groups of arrays

#

Instead of storing 10k elements in 1 array. Can you separate them out into 10 arrays of 1k elements

#

Thats just an example by the way.

halcyon ore
#

Wouldn't that still hit the limit?
You still learn of 10k entries at the end of the day.

fossil spoke
#

The limits apply to the individual container and Actor. So you would want to spread the load.

#

Maybe look up Network Managers

#

I believe there might be a pin in this channel

#

This problem is explored in a blog post somewhere

#

By someone on this server.

#

It might give you clues on how to adjust your use case

#

This link here

halcyon ore
#

Yeah, I had found that, all other stuff I could find for my use case/ thought was fast array.

fossil spoke
#

Network managers as described in this blog also use FastArrays

halcyon ore
#

Yeah, I had read over it.
But, perhaps I was miss-understanding something, cuz wouldn't it run into the same issue.
A Fast Array with a ton of entries in it.

fossil spoke
#

You might be missing the point of what its trying to build as a concept

#

The concept of a network manager is that you can have as many of them as you need

#

In order to spread the load of trying to handle large sets of data

halcyon ore
#

I thought I understood the network manager, as just an external source to handle the replication of like 3,000 replications, into 1 replication loop.

Rather then 3,000 replicated health components.
have 3,000 default non-replicated components.
and, 1 manager handles there 1-2 vars.

Given this commet from the discord:
Replicating several hundred moving Actors is doable, however, replicating several hundred moving Actors while you have several thousand non moving replicated Actors cluttering the NetBroadcastTick is not.

Cut down on the replicated components to give more direct replication to actual systems.

fossil spoke
#

Sure, but you can split them up however you like

#

You dont HAVE to have 1 Network Manager that does ALL health components

halcyon ore
#

Valid, and thats not something I considered/ thought of.
Given, most core UE systems are 1x stuff.

fossil spoke
#

What are you trying to replicate 10k elements for BTW?

#

What are you trying to achieve exactly?

halcyon ore
#

replicating the state of foliage on the map.
Just a basic cut down, not cut down.

fossil spoke
#

Yeah you probably dont want to do that.

#

There are better ways to do that

halcyon ore
#

Oh?

fossil spoke
#

Is the foliage runtime generated or baked?

halcyon ore
#

Planned to be baked from the normal editor foliage.

But, at the same time, runtime would be a nice addition, but not required. 😛

fossil spoke
#

Ok, the difference isnt huge.

#

We will do just baked

#

So with that, I mean, all instances of foliage have a predictable ID

#

Meaning its the same across all machines

#

So a tree on a hill, has the same ID for all clients for that same tree on that same hill.

#

You get that?

halcyon ore
#

Yes.
I was planning something similar.
But, tracking/ replication the state of each 1 tree is the cause for a 10k array. 😛

fossil spoke
#

So you are only tracking trees that got cut down?

halcyon ore
#

If they are cut down, or not cut down.
Since, they'll re-gen after some time.

fossil spoke
#

And you are expecting players to cut down 10k trees all at the same time?

#

Either way. This fits in perfectly with Network Managers TBH.

#

So

#

How you could handle it

#

Is you could make a Network Manager actor type for your Trees. That manager is responsible for just keeping track of the Tree IDs of those that have been cut down.

halcyon ore
fossil spoke
#

You can then place as many of these Network Managers around the world

#

Ideally you would probably have some sort of mechanism that would mean they understand which trees are within a volume around them

#

Or by distance or something

#

You need to then place enough of those managers around that all trees have a linked manager

#

So like, in my earlier suggestion, you could have 10 tree managers that each handle 1000 trees

#

This then gives you further leverage with dormancy and relevancy.

#

Does that help?

halcyon ore
#

That does.

I'm just glad I re-thought of a new method, for my original Fast Array usage.

Cuz, idk what I'd do there. 😛

fossil spoke
#

Keep in mind, you would be replicating an int32 for the Trees instance ID plus a bool or a uint8 (which can then handle further masked values) for every tree.

halcyon ore
#

As in?
I need to still be careful about length, or?

Cuz, yeah.
and, couldn't I also make the bool into a 0-1 bit. (to reduce replication size more)
or, does that not support replication by default?

pliant gull
#

hi, i am new here and im running into a problem and wondering if anyone might be able to help.

#

when i update a replicated variable on the client it does not actually update. im using a delegate to communicate from the player state to the userwidget. when i first start the game it tells me the delegate is bound and i get the variable showing correctly. however when i update the clients variable the OnRep funcitno is called however the delegate is no longer bound and i dont get an update. it works perfectly fine for the server

#

i tried calling the function directly (no delegate) from the playerstates onrep by GetPlayerController->GetHUD however the GetHUD for the client doesnt work. again the server works fine

#

i dont see how this could be an auth issue however i might just be completely wrong

#

i tried binding the function 10 seconds after the game starts. i am still able to get the initial variable set up for the widget but i dont get an update. the client says its not bound

exotic wasp
pliant gull
exotic wasp
#

don't use a rpc for that

pliant gull
#

what should i use instead

exotic wasp
#

or do you mean you're telling the server to change the value with a rpc?

pliant gull
#

yea

exotic wasp
#

oh that's fine

pliant gull
#

so like the server knows the variable is changed. the onrep is called

#

but my delegate is no longer bound?

exotic wasp
#

if something is getting unbound it's possible the relevancy of something changed

pliant gull
#

but if that were the case would both server player and client joining player have the issue

#

i also tried just straight calling the function on the widget from the playerstate. works fine for the server but again not able to for client

#

so i feel like for some reason the HUD is the issue

dark parcel
#

show your codes?

#

@pliant gull GetPlayerController->GetHUD

Where is this called? show all the relevant code.

pliant gull
pliant cypress
thin stratus
pliant cypress
#

loadoutComponent will handle inventory stuff

thin stratus
#

Like, you said you are lost on connecting Loadout to whatever your idea here is. In can tell you, without knowing much about your code and game and without sticking to your idea, how I would lay this out.

pliant cypress
#

such as Loadout struct and equipments etc

thin stratus
#

(if you want)

pliant cypress
#

it'd give me some insights and would help me really great

dark parcel
thin stratus
#

Design: Weapon + Mags + Inventory + Loadout

pliant gull
# dark parcel Show codes?

I'm not at my computer at the moment. but it should be this.

AMyPlayerController* PC = GetPlayerController();

AMyHud* MyHud = Cast<AMyHud>(PC->GetHud());

#

I'm realizing the issue

#

maybe I need to cast my player controller

#

?

dark parcel
#

need to know the actual GetController method

#

client only knows their controller, so as client, if you have someone's player state. GetController will return null.

pliant gull
#

well when I do if(PC) it returns true

dark parcel
#

even says in the description

dark parcel
#

there's no way your client knows other controller but their own local controller.

pliant gull
#

hmm right

dark parcel
#

So if you have something like
As Client
SomeoneElsePlayerState-> OnRep->GetController it will be null

#

though why do you need controller?

pliant gull
#

well my delegate is unbinding (don't know why) so I tried to instead reach the HUD by going through player controller

#

I'm sending an updated variable to a widget

dark parcel
#

Just make a method to get the HUD

pliant gull
#

where should that be located?

dark parcel
#

you can make a blueprint function library, a static method allow you to call it anywhere.

pliant gull
#

okay

#

I'll try that

#

I can't test now but Tomorrow I will try and let you know

dark parcel
#

Send all the relevant codes when you have the chance

#

someone else can probably help too

pliant gull
#

yep 👍

#

thank you

thin stratus
#

I don't think you should create some static function for this. It won't change anything. The Delegate shouldn't "unbind", so you'll have to share the code of this setup. Somewhere you are either doing something wrong or you expect something that is in fact not the case.

late surge
#

Do the CMC sync the direction of the movement or it's relative direction based on the character orientation? Meaning that if the client rotation and the server rotation is unsync. The client press move forward, do it cause desync?

exotic wasp
#

though if you've made your movement dependent on orientation, like if you move slower backwards or something, yeah it'll matter

exotic wasp
#

how do y'all handle ragdoll replication?

#

do you typically just avoid them having gameplay effects and have the client sim it with no replication?

#

what if it does effect gameplay, like for accessing loot? should that just be avoided?

grand kestrel
torn hull
#

Hey, I'm trying to get the voice chat volume from a VOIP talker, but the GetVoiceLevel() function doesn’t work on the local player. It only sends to other players (to avoid loopback I guess?). This function only works on the local player if voice loopback is enabled via a Cvar.

Does anybody have an idea how to capture and return the value that’s sent (or even that is captured via voip before sending?), so I can control a local player variable? I’ve been trying to go through VoiceConfig.cpp, but I haven’t found any answers yet.

torn hull
#

For now I'm using the AudioCapture component, but I have a problem with it because it's not really giving the same value you get from VOIP. Also, commands like voice.MicInputGain and voice.SilentThreshold don’t affect the AudioCapture component.

What I found is that using the voice.debug.PrintAmplitude cvar from VoiceCaptureWindows.cpp, gives exactly the value I want and works without voice loopback, but my current C++ knowledge doesn’t let me implement it in my own component yet

pliant gull
# thin stratus I don't think you should create some static function for this. It won't change a...

@dark parcel WidgetTest.h

    UFUNCTION()
    void OnMoneyUpdated(UMoneyComponent* NewMoney);

WidgetTest.cpp


#include "Widgets/MyUserWidgetTEST.h"
#include "GameModes/PlayerControllers/MyPlayerController.h"
#include "GameModes/PlayerStates/MyPlayerState.h"

void UMyUserWidgetTEST::NativeConstruct()
{
    Super::NativeConstruct();

    FTimerHandle TimerHandle;
    GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &UMyUserWidgetTEST::AttemptBinding, 6.0f, true);
}

void UMyUserWidgetTEST::OnMoneyUpdated(UMoneyComponent* NewMoney)
{
    if (CurrentBalanceText)
    {  
        CurrentBalanceText->SetText(FText::AsNumber(NewMoney->GetBalance()));
    }
}

void UMyUserWidgetTEST::AttemptBinding()
{
    if (PS == nullptr)
    {
        APlayerController* PlayerController = GetOwningPlayer();
        if (PlayerController)
        {
            PS = Cast<AMyPlayerState>(PlayerController->PlayerState);
        }
    }
     
    if (PS)
    {
        PS->OnMoneyUpdated.AddDynamic(this, &UMyUserWidgetTEST::OnMoneyUpdated);
        // PS->OnMoneyUpdated.AddDynamic(this, &UMyUserWidgetTEST::OnMoneyUpdated);
      
        if (PS->OnMoneyUpdated.IsBound())
        {
            OnMoneyUpdated(PS->Balance);
        }     
        GetWorld()->GetTimerManager().ClearAllTimersForObject(this);
    }
}
#

From the playerstate.h ``` DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMoneyUpdated, UMoneyComponent*, NewMoney);

UPROPERTY()
FOnMoneyUpdated OnMoneyUpdated;


From the playerstate.cpp
```void AMyPlayerState::OnRep_Balance(UMoneyComponent* balance)
{
   if (OnMoneyUpdated.IsBound())
   {
       UE_LOG(LogTemp, Warning, TEXT("= bound."));
       OnMoneyUpdated.Broadcast(balance);
   }
   else
   {
       UE_LOG(LogTemp, Warning, TEXT("OnBalanceChanged delegate is not bound."));
   }
}

void AMyPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
   Super::GetLifetimeReplicatedProps(OutLifetimeProps);
   DOREPLIFETIME(AMyPlayerState, Balance)
}```
#

idk if i needed to paste anything else but yea

#

i also tried having the OnRep function send the varible to my controller for the hud to access it from there but i get an exception thrown and stopped there

so basically the server client still has OnMoneyUpdated bound and can broadcast, the joining client can bind, because i can use it in the Attemptbind function in the widget but after that it unbinds

dark parcel
#

@pliant gull Have you ensure that the UMoneyComponent is set to replicate?

#

also another thing, how is the widget created? is there only one UMyUserWidgetTest PER machine, or is it PER PlayerState?

#

you should make sure that client actually bind on the attempt binding. Do a print string on the block and see where you fail.
Print string will show the machine that is executing the code.

pliant gull
#

like this?

dark parcel
#

Not only that

#

is OnRep called on client?

#
SetIsReplicatedByDefault(true);

have this on your ctor too

pliant gull
#

in the Character class?

dark parcel
#

no in the MoneyComponent class

pliant gull
#

okay

#

in constructor right

dark parcel
#

yeah

#

lets move to the most important part after. Debug if you are binding on client.

#

you will also need to check if OnRep is called on client.

pliant gull
#

its called

#

ive done that

#

the issue is the binding

dark parcel
#

How do you check it?

pliant gull
#

i used UE_LOG

#

and put them everywhere

dark parcel
#

does UE LOG come with the Net machine that execute it.

pliant gull
#

uh

dark parcel
#

I usually just use print string cuz it will says whos executing the code.

pliant gull
#

im not usre

#

i can try that

dark parcel
#

Try that first then we can move on to the binding

#

don't want to leave stones unturned.

pliant gull
#

👍

dark parcel
#

UKismetSystemLibrary::Printstring

pliant gull
#

lol thanks

pliant gull
dark parcel
#

afaik OnRep shouldn't even be called on Server

pliant gull
#

its possible ive set this up completely wrong

dark parcel
#

can you show me a print screen of the print string

pliant gull
#

from both client and server its called on server

dark parcel
#

That's on the OnRep?

pliant gull
#

yea

dark parcel
#

and you are playing as 2 players , set to Listen server?

pliant gull
#

yes

dark parcel
#

Oh wait, why are you OnRepping the component 0o

#

MoneyComponent can just be set to replicate.

#

What you should OnRep is the balance.

pliant gull
#

i dont understand

dark parcel
#

OnRep on money component should be called when the ptr changed. If you want to keep track of the member variable of the component, you will do that with the OnRep on the member property (in this case the balance)

#

UMoneyComponent is an ActorComponent?

pliant gull
#

yes

dark parcel
#

Yeah, you are just OnRepping the pointer to the Component

#

this is not even necessary if it's a replicated component.

#

Create a delegate when balance changes inside UMoneyComponent

#

OnRep_Balance -> Broadcast.

#

OnYour Player State Begin Play -> Get Player -> Get Money Component -> Bind to the delegate.

pliant gull
#

would having the moneycomponent on the player controller or character not defeat the point of replication?

dark parcel
#

Then just print string every time the delegate is fired. See how that goes.

dark parcel
#

Because all controllers exist on server.
But Client only know their own controller. So there's no way for Client 1 to know Client 2 money if the component is inside the controller.

#

If you don't want to attach the money component inside the player character (if you have to destroy it for what ever reason).
Then you can attach it to the player state.

#

since every machine have a copy of every player state

pliant gull
#

well my thought process was that if it was on the playerstate players need to go through the server to edit it. as in get permission

pliant gull
#

oh

dark parcel
#

Replication only work from server to client, and client can only communicate with server through Server RPC.

#

But you don't really need the client to have a say here.

#

Why do you want the client to say, Hey I just got 5000 gold?
Client can make request with server RPC.

#

server check and verivy. Then set the value if valid and replicate the changes.

#

lets focus on the current issue though.

#

Where is the MoneyComponent atm?

pliant gull
#

lol ok

#

its initialized in the playerstate

dark parcel
#

Is it attached to the player state?

pliant gull
#

with CreateDefaultSubobject

#

yea

dark parcel
#

Yeah, that's fine.

#

So now MoneyComponent have balance right?

pliant gull
#

yea

dark parcel
#

You want to create a delegate inside money component.
Which gets broadcasted on OnRep_Balance

pliant gull
#

when i start the game it displays the right balance

#

okay

dark parcel
#

call it BalanceUpdate or something along those line.

pliant gull
#

DECLARE_DYNAMIC_MULTICAST_DELEGATE

#

this is correct?

dark parcel
#

Yup, or one Param if you want to broadcast the value.

pliant gull
# dark parcel Yup, or one Param if you want to broadcast the value.

to bind the delegate from UMoneyComponent to the OnRep inside the playerstate i need access to the playerstate in the UMoneyComponent.cpp. how am i supposed to get the playercontroller to access the playerstate while in an actor component. or am i getting this all wrong

#

AMyPlayerController* PlayerController = Cast<AMyPlayerController>(Cast<AMyCharacter>(GetOwner())->GetController());

#

i did this

#

but i feel like this is just wrong

#

lol

#

any if it works it works

#

ill try

dark parcel
#

MoneyComponent just broadcast it's value.

#

the player state, widget or anything can listen to the broadcast.

#

Keep dependency one way.

dark parcel
#

For one the owner is a PlayerState, not a character. ALSO, in client this will be null (the controller for non owned player state).

pliant gull
dark parcel
#

yeah you probably hit exception.

pliant gull
#

yea after loading for 5 minute

dark parcel
#

Paste your money component code after you update it with OnRep on the balance and a delegate.

pliant gull
#

i have to redo cuz i misunderstood

#

just to confirm i should make the OnRep in the Moneycomponent

#

correct?

dark parcel
#

Correct, you OnRep the balance.

pliant gull
#
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "MoneyComponent.generated.h"


DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMoneyUpdated, UMoneyComponent*, NewMoney);

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class THEGAME_API UMoneyComponent : public UActorComponent
{
    GENERATED_BODY()
public:
    //UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
    UPROPERTY(ReplicateUsing = OnRep_Balance)
    int Balance = 100; 

protected:
    // Called when the game starts
    virtual void BeginPlay() override;

public:    
    // Sets default values for this component's properties
    UMoneyComponent();
    // Called every frame
    virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

    UFUNCTION()
    int GetBalance() const
    {
        return Balance;
    }

    UFUNCTION(BlueprintCallable)
    void AdjustBalance(const int& change)
    {
        Balance += change;
    }

    UFUNCTION(BlueprintCallable)
    bool CheckBalance(const int& change)
    {
        return (Balance >= change);
    }

    UFUNCTION(BlueprintCallable)
    bool Buy(const int& change)
    {
        if (CheckBalance(change))
        {
            Balance -= change;
            return true;
        }
        else
            return false;    
    }



    UFUNCTION()
    void OnRep_Balance(UMoneyComponent* currentbalance);

    UFUNCTION()
    FOnMoneyUpdated OnMoneyUpdated;


        
};
#

// Fill out your copyright notice in the Description page of Project Settings.


#include "Statlines/MoneyComponent.h"
#include "Kismet/KismetSystemLibrary.h"




// Sets default values for this component's properties
UMoneyComponent::UMoneyComponent()
{
    // Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
    // off to improve performance if you don't need them.
    PrimaryComponentTick.bCanEverTick = true;

    SetIsReplicatedByDefault(true);
    // ...
}


// Called when the game starts
void UMoneyComponent::BeginPlay()
{
    Super::BeginPlay();

    // ...
    
}


// Called every frame
void UMoneyComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

    // ...
}

void UMoneyComponent::OnRep_Balance(UMoneyComponent* currentbalance)
{

    OnMoneyUpdated.Broadcast(currentbalance);
}```
dark parcel
#

can you format it with cpp?

pliant gull
#

you mean just send you the file

dark parcel
#

add cpp after the triple quote

pliant gull
#

oh cool

#

yea

#

er

#

does that work

dark parcel
#
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "MoneyComponent.generated.h"


DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMoneyUpdated, UMoneyComponent*, NewMoney);

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class THEGAME_API UMoneyComponent : public UActorComponent
{
    GENERATED_BODY()
public:
    //UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
    UPROPERTY(ReplicateUsing = OnRep_Balance)
    int Balance = 100; 

protected:
    // Called when the game starts
    virtual void BeginPlay() override;

public:    
    // Sets default values for this component's properties
    UMoneyComponent();
    // Called every frame
    virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

    UFUNCTION()
    int GetBalance() const
    {
        return Balance;
    }

    UFUNCTION(BlueprintCallable)
    void AdjustBalance(const int& change)
    {
        Balance += change;
    }

    UFUNCTION(BlueprintCallable)
    bool CheckBalance(const int& change)
    {
        return (Balance >= change);
    }

    UFUNCTION(BlueprintCallable)
    bool Buy(const int& change)
    {
        if (CheckBalance(change))
        {
            Balance -= change;
            return true;
        }
        else
            return false;    
    }



    UFUNCTION()
    void OnRep_Balance(UMoneyComponent* currentbalance);

    UFUNCTION()
    FOnMoneyUpdated OnMoneyUpdated;


        
};
pliant gull
#

sorry

#

thanks

dark parcel
pliant gull
#

not good with discord

#

oh i got it

#

i updated the .cpp file aswell

dark parcel
#

Hmm, so you want to broadcast the component instead of just the value? I mean it should work, just not sure about this approach.

pliant gull
#

well i mean i can just add currentbalance->GetBalance();

#

i suppose you are right, better to just share the value

dark parcel
#

Well it would be more accurate to call it TheMoneyComponent instead of currentBalance but that aside, we can move on.

#

Nah it's fine, just keep the component.

#

you can just access the balance getter from the comp.

pliant gull
#

ok

#

yea

dark parcel
#

On your PlayerState Begin play, bind to that delegate.

#

so whenever your balance is updated, the player state will be informed.

pliant gull
#

what do i bind it to?

dark parcel
#

OnMoneyUpdated

pliant gull
#

sorry i mean what in the playerstate gets bound to the onmoneyupdated

dark parcel
#

to your custom Function

#

PlayerState BeginPlay -> Get Money Component -> Bind OnMoneyUpdate to some function.

#

Call that function PlayerMoneyUpdated

#

And in that function you can just do a print string.

#

print the balance of the mOneyComponent

#

PlayerMoneyUpdated (MoneyComp* PlayerMoneyComp)
{
print (PlayerMoneyComp->GetBalance())
}

#

at this stage, it should print both on server and client if the OnRep work.

thin stratus
#

Man, I would love to help, but that has been quite a lot of messages by now and I don't even know at what stage we are now.

#

I saw OnRep_MoneyComponent, which seems redundant.

pliant gull
#

like such?

pliant gull
thin stratus
#

Right, sure.

#

Can I ask a few questions, just for the design of this?

pliant gull
#

please

thin stratus
#
  • Who all needs to know about the Money of a given client value and that it changed?
    • Local client only?
    • All clients?
  • Where do you create the Widget that is supposed to display the value and update from the delegate?
pliant gull
#

so i suppoes everyone needs to know their own value

dark parcel
pliant gull
#

no

dark parcel
#

watt

pliant gull
#

well

#

i mean i just dont see the need for that to happen

#

maybe i am just missing something

dark parcel
#

when server change the money value, you should get a callback on every machine.

pliant gull
#

i got an exception thrown

#

for some rason

#

reason

dark parcel
#

show the callstack

pliant gull
dark parcel
#

Run the project with debugger.

pliant gull
#

do i not need super?

dark parcel
#

What is MoneyComponent derived from? Does Actor component even have begin play?

thin stratus
pliant gull
thin stratus
#

Since only the local player needs to know about the value and that it changed, place the Component onto the PlayerController.

Why is that better?

  1. Each player/client only has access to their own PlayerController. Other player's PlayerControllers aren't replicated to them. So you already, naturally, ensure others can't read the value and you aren't wasting bandwidth on replicating it to others that don't care to begin with.

  2. Since you create the Widget in an AHUD child class, which itself is created in the APlayerController, you aren't running into issues with order of initialization of Actors or their replication. Right now, you struggle with timing, which is why you also have that nasty looping timer in your Widget. Since the PlayerController spawns the AHUD locally and at that point the Component is also already valid, you can simply access the Component directly, without fearing that it hasn't replicated yet.

pliant gull
#

what would i do in regards to not allowing tempering of the values or is this not an actual problem

thin stratus
#

Note about the looping timer:

I understand why that instantly becomes the fallback when people notice that something isn't valid "yet", but seems to be valid "later". However, there isn't really a need for this, especially when you are using C++.

If you ever have a Widget that does in fact need to access the PlayerState, then you should fall back to delegates in Actors that you know are valid and that can tell you about the PlayerState becoming valid.

For the local player, their PlayerState will replicate potentially a bit later than the PlayerController, which is why you have the Timer. However, the PlayerState has an OnRep_PlayerState in the PlayerController (or rather in the AController class), which is virtual and can be overridden. So you can simply override that in your own PlayerController and broadcast a custom delegate. In your Widget you can then get the PlayerState and if it's invalid bind to that delegate. No need for the timer.

The only edge case here would be a ListenServer, as OnRep functions, in native code (so not your code but UEs) are often not called for Servers. So you would need to override a second function that is called for the server, such as InitPlayerState, and broadcast there too (after Super::).

This concept also works for Pawns/Characters/ for example. If you have a HealthComponent or so on your Pawn/Character, you can't be sure that it's valid when the Widgets are created from within your PlayerController or HUD.
You could add the Widget from within the Pawn/Character, but if you don't want that you'd basically require the same delegate setup.
Good news in that case is that AController has OnPossessedPawnChanged, so you don't even need to create your own.

For PlayerStates of non-local players, you usually want to simply ensure that he Widgets are also added when the PlayerStates become valid (e.g. through BeginPlay of the PlayerState).

thin stratus
#

It should have made very clear that 'automatic' replication always only works from Server to Clients and the only option for Clients to tell the Server something is to use a ServerRPC.

#

So placing the Component on PlayerController or PlayerState doesn't make a difference.

#

The thing that stops people from tampering with the value is YOU not giving them a ServeRPC that asks the Server blindly to change the value.

pliant gull
thin stratus
#

In theory, as a really vague rule of thumb, you only ever want to use a ServerRPC to tell the Server about something it can't possibly know.
And that "something" is almost always user input. Mouse, Keyboard, Gamepad and UI interactions. EVERYTHING ELSE, should never be ServerRPC'd. Shooting a gun? Send RPC when LMB is pressed. After that, Server can do the shooting and damaging.

#

So if you don't have an RPC that asks the Server to change Money to a new value, then they can also not temper with it.

#

They can locally change it, but that won't replicate to anyone.

#

One last word of advice: If you aren't planning on using Dedicated Servers for your game AND hosting those yourself, away from the players' reach (which is pretty expensive), then you shouldn't worry about cheating anyway, cause you can't prevent it.

pliant gull
#

okay well thank you very much for all the info

pliant gull
#

and if im putting things together properly the main reason to use the playerstate is for things shared with everyone? kinda?

thin stratus
#

Yes

#
Dedicated Server
- Client_1_PlayerController
- Client_1_PlayerState
- Client_1_Character
- Client_2_PlayerController
- Client_2_PlayerState
- Client_2_Character

Client_1
- Client_1_PlayerController
- Client_1_PlayerState
- Client_1_Character
- 
- Client_2_PlayerState
- Client_2_Character

Client_2
- 
- Client_1_PlayerState
- Client_1_Character
- Client_2_PlayerController
- Client_2_PlayerState
- Client_2_Character
pliant gull
#

so playerstates are actually known by other players?

thin stratus
#

Difference between PlayerState and Character (Pawn) would be that a client doesn't need to always have a Pawn and can potentially switch between different ones, respawn, etc. PlayerState remains one and the same for the entire session (until changing level and similar things).

pliant gull
#

hmm had no idea

thin stratus
#
Listen Server
- ListenServer_PlayerController
- ListenServer_PlayerState
- ListenServer_Character
- Client_1_PlayerController
- Client_1_PlayerState
- Client_1_Character
- Client_2_PlayerController
- Client_2_PlayerState
- Client_2_Character

Client_1
- 
- ListenServer_PlayerState
- ListenServer_Character
- Client_1_PlayerController
- Client_1_PlayerState
- Client_1_Character
- 
- Client_2_PlayerState
- Client_2_Character

Client_2
- 
- ListenServer_PlayerState
- ListenServer_Character
- 
- Client_1_PlayerState
- Client_1_Character
- Client_2_PlayerController
- Client_2_PlayerState
- Client_2_Character
#

Same thing for ListenServer. ListenServer is always a bit of an odd ball, cause the rules still apply all, but given the ListenServer is a Server after all, it does actually have access to all of the other Client's PlayerControllers, even though it's a player.

#

In the more traditional usage of Unreal Engine, that being Unreal Tournament for example, the PlayerState used to hold things like Kills, Deaths, Assists, while the Character held things like Health.

#

But you can read most of that in the Compendium, which was written to avoid repeating the basics :D so I will stop now.

pliant gull
#

lol

#

i will def read it

#

thank you for pointing it out to me

#

and sorry for my ignorance but thank you very much for everything

silent valley
lofty vale
#

how do you deal with large log files? is there any parser that you use to provide a useful summary instead of scrolling through endless lines?

torn hull
silent valley
pliant cypress
#
void ULoadoutComponent::OnRep_PrimaryWeapon()
{
    const USkeletalMeshSocket* HandSocket = Character->GetMesh()->GetSocketByName(FName("WeaponHandle"));
    if (HandSocket)
    {
        HandSocket->AttachActor(PrimaryWeapon, Character->GetMesh());
    }
}

void ULoadoutComponent::OnRep_SecondaryWeapon()
{
    const USkeletalMeshSocket* HandSocket = Character->GetMesh()->GetSocketByName(FName("WeaponHandle"));
    if (HandSocket)
    {
        HandSocket->AttachActor(SecondaryWeapon, Character->GetMesh());
    }
}

void ULoadoutComponent::SetWeapon(int32 index, AWeapon* NewWeapon)
{
    if (NewWeapon == nullptr) return;

    switch (index)
    {
    case 0:
        PrimaryWeapon = NewWeapon;
        const USkeletalMeshSocket* HandSocket = Character->GetMesh()->GetSocketByName(FName("WeaponHandle"));
        if (HandSocket)
        {
            HandSocket->AttachActor(PrimaryWeapon, Character->GetMesh());
        }
        break;
    case 1:
        SecondaryWeapon = NewWeapon;
        const USkeletalMeshSocket* HandSocket = Character->GetMesh()->GetSocketByName(FName("WeaponHandle"));
        if (HandSocket)
        {
            HandSocket->AttachActor(SecondaryWeapon, Character->GetMesh());
        }
        break;
    }


}
#

this is my loadout component

#

and i gotta bit confuse now

#
void UCombatComponent::EquipWeapon(AWeapon* WeaponToEquip)
{
    if (SoldierCharacter == nullptr || WeaponToEquip == nullptr || SoldierCharacter->GetLoadoutComponent())
    {
        return;
    }
    
    EquippedWeapon = WeaponToEquip;
    SoldierCharacter->GetLoadoutComponent()->SetWeapon(0, EquippedWeapon);
    HandleEquip();
    EquippedWeapon->SetOwner(SoldierCharacter);
    EquippedWeapon->SetHUDAmmo();
    if (Controller)
    {
        Controller->SetHUDCarriedMagazine(CarriedMagazine);
    }
}
#

this is my function on CombatComponent

#

called via Server

#

the thing i cannot comprehend is how do i store the weapon and do not crash like i don't have a weapon at begin and when i equip, it should be in my hand but if i have a weapon on hand (Equipped) then the other weapon should go to the back of my mesh (Secondary Weapon)

tawdry burrow
#

Hi,
I'm having a situation where an actor of a particular class is being possessed on the server, in the editor it shows as autonomous proxy, but PreNetReceive never gets called on a particular component, which it should when it receives the changed role from the server, but changing the class to a different one with the same base class, makes it work, that is PreNetReceive gets called and the component is able to react to the role change. The component is the same for both classes

#

I tried debugging Unreal source code but we only have a prebuilt version built in release

#

This also isn't my code

tawdry burrow
exotic wasp
#

i would say to just debug during PreNetReceive and see why its skipped but without a source build itll be trash

#

is this a bp/c++/both class?

tawdry burrow
#

I was trying to debug the actor channel itself, but I realised I can't trust the values I see

exotic wasp
#

yeah debugging any engine code without a source debug build is pointless

tawdry burrow
exotic wasp
#

is it a component with a similar or same name as another component added in bp or c++?

tawdry burrow
#

Nope

#

Definitely pretty distinct

exotic wasp
#

you could try copying the component list and looking at it in debug

#

also, does PreNetReceive get called when changing role? i thought it was just for properties

tawdry burrow
#

Well, in the case of the working actor class that's how it reacts to role change...I'm honestly pretty new

#

I didn't design any of this so I can't say if it's done right

#

I assume it is bc it's a fork of the network prediction plugin

little pumice
#

@haughty ingot hi, question about Use less RPCs
Does client send ServerRequestPickup() and ServerAddToInventory() from the same tick?
If not (i.e. server decides what ItemID to drop so ServerAddToInventory() will be called sequentially, after ClientConfirmPickup() response, then how does Transactional semantics in Command approach exactly works? Is it 2 FInventoryTransaction RPC calls from client to server?

exotic wasp
#

then the item replicates to the client as a replicated property

#

only 1 rpc

#

I'm pretty sure that article is advising against the design it first calls out

little pumice
#

I guess it's artificial example and there are might be real cases when you send a multiple related RPCs from the same tick. My question more about Command approach from the article and how it handles sequential scenarios

exotic wasp
#

so there is once action dependent on the previous, but they come from the same RPC

#

if you have dependent state it needs to be a single rpc, otherwise the order isn't guaranteed

#

he also shows an array that you could use for batching multiple transactions in order

#

so if you were to move 5 items in a row, make each move a transaction in an array. then send that array in the rpc

haughty ingot
#

Hi, sorry I just saw this.

I'm pretty sure that article is advising against the design it first calls out

That's precisely it, although there is a disclaimer at the top that reiterates it's purely an opinion, and there isn't a one-size fits all solution to anything. The purpose was to invite the reader to consider if they actually needed multiple RPCs to represent a flow of data, it may not work for every scenario and you very may well want RPCs to arrive sequentially (which brings a fight of it's own). The "transactional" aspect refers to treating the entire flow as a single logical operation from the caller's perspective. The server processes this command atomically and replies with the result state, not intermediate steps.

The article argues against reflexively creating separate RPCs for every micro-action (Pickup, Add, Remove, Stack, etc.) when you could model the caller's intent as a single command. Whether that works for your specific game depends on your latency tolerance and gameplay requirements.

Added note: this is also a comment section on the blog. Feel free to encapsulate the conversation there as well

azure zealot
#

I've been having trouble having a client Player controller from one pawn to another, I read up on some solutions and I'm not sure what I'm doing wrong. This block of code starts off an Advanced Input Action and it works to possess a camera pawn and works perfectly for the server, when testing from the client it is like the "possess" node gets skipped. What's my problem?
(Edit: the real custom event is run on server)

lost inlet
azure zealot
lost inlet
#

???

azure zealot
#

what

lost inlet
#

your response made very little sense. also input is not networked, so you will have to send something to the server (as the owning client) to trigger any of this

#

a pawn the player is possessing is an example of something the player owns

azure zealot
#

ah so i need to go to the client after input, then to the server?

lost inlet
#

I'm not sure why you'd need the former, you're either a client or a listen server host if you're running input

#

the input just needs to trigger a server RPC and that takes care of the rest

azure zealot
lost inlet
#

Well the whole left hand side isn't even connected to anything

#

Also you know the relevant player controller, you don't need to pass it in the RPC

azure zealot
dark parcel
#

the cut away code is making it very difficult or impossible to help.

azure zealot
#

ok let me get one of the full code

dark parcel
#

first of all, if the client is executing the code (pressing the input), that bp camera will only spawn on it's machine. The server will not get a copy as replication work from server to client, not vice versa.
So I'm not sure about the idea of getting Current Camera. It will be null anyway in the case of the client pressing the input.

#

a replicated actor will replicate only if spawned by server.

#

meaning every players will get a copy.

azure zealot
#

huh i dont know how i didnt catch that

#

one sec

dark parcel
#

I'm not sure what you are going to change though, what's the actual goal? why do you have to communicate the act of looking at a camera?

#

perhaps just spawn and execute the code locally.

azure zealot
#

this code is for placing and using a camera that you can look though anytime

azure zealot
dark parcel
#

Why do you have to RPC it?

#

why do client have to tell server, hey execute this function in your machine

azure zealot
#

i might not after replicating the spawning logic. also i have no idea bro i am winging this

#

ok my original code was fine the problem was that the actor wasn't replicated. THX @dark parcel

dark parcel
#

all g but must server replicate the camera? it's probably useless for other players.

#

you can just spawn locally and use set View Target with blend.

#

that will eliminate waiting for server too (delay). It sucks for player with considerable latency.

azure zealot
#

will the other players still be able to see and interact with it then?

dark parcel
#

Not if spawn locally, they will not get a copy

#

are you making a security camera or something

azure zealot
#

ok i will need it on server so others can shoot it and stuff.
yes.

dark parcel
#

well yeah, that have to be done on server then.

pliant gull
#

hey does anyone know why GetHUD() on the client playercontroller returns null

#

and if theres any way around it

barren elm
#

I've encountered a weird UE issue that's stumping me. Tapping into the hive mind here.

When you mark an actor AND its component to replicate, changing visibility of that component on a server replicates visibility to clients. All as expected. In my room fade this means that hiding an actor on the server affects all clients for replicated components. So I created a small proxy to multicast to clients to revert the visibility change when they see it. That all works.

Here's the problem. If you change the visibility on a client, that state is sent back to the server! I know, this seems wrong, but I've spent hours confirming my code. It totally brakes server authority and I don't know how to fix it. If I don't change visibility on the client the server is fine. Change it on the client and the server changes too!

thin stratus
thin stratus
#

None of it should allow the client to modify the server. And client/multicast RPCs to modify visibility, which is a state, is wrong to begin with.

lost inlet
#

I'd expect GetHUD() to return null for a remote client

#

the AHUD actor is spawned clientside by the player controller and is not replicated in any way

silver turtle
#

Hi all, let's say I have a ai-controlled Character that follows the player. Would it be correct to set its Owner to be the player Character (or the PlayerController) so a client can call server RPCs on it?

lost inlet
#

depending on the context that could be permissable, but it really depends what you want to do

#

not sure if that'll mess things up for the AI controller's possession

nova wasp
#

generally speaking it would only make sense to change the owner if the player had some unique control over them imo

#

what happens if another player needs to call the rpc? do they just keep swapping owners over and over?

#

my first guess would be to avoid that situation instead of gambling on the engine being okay with that personally

#

just make a new controller/playerstate component that describes to rpc with the npc as one of the params for the server rpc

lost inlet
#

yeah RTS style controls is what came to mind

nova wasp
#

yeah that would make sense

#

I'm honestly not even sure how live ownership changes work

#

I tend to just have stuff in the 3 regular owner actors

silver turtle
#

thank you!

fast yoke
#

How do i make montages be replicated for an NPC

#

like the server can see when an NPC plays a montage but not the clients

lament flax
#

Play Montage etc are local iirc

#

so you have to run it on all clients with a reped var or a multicast

fast yoke
#

ohh gotcha, thanks

barren elm
# thin stratus None of it should allow the client to modify the server. And client/multicast RP...

Some context. This is in a plugin that has no access to the design of the components hidden. The plugin fades out rooms and actor contents locally depending on camera position. On listen servers, we only want components hidden locally, but if the component is replicated we need to temporarily suppress replication of bHiddenInGame. Because we don't own that component, we hesitate to disable replication because we don't know side effects. So, my idea was to RPC to clients to locally revert the hide change. That works, but when I unset HiddenInGame on the client, that strangely undoes the hide on the server (only tested in PIE). I was not expecting state to be sent back to the server! I am fully aware how replication is supposed to work, and this breaks my understanding.

#

And yes I am being loose with actors and components here. When I say I hide an actor, I mean all the components in the actor. Other options considered were removing component from main render pass on server, but that does not work for Niagara and mesh render holes in world. I could add a invisible mask material for mesh, but I wanted shadows to be preserved and that breaks that. Visibility is best if I can get it to work.

halcyon ore
#

Why would visible not work in this case?

barren elm
#

Because visibility is replicated, and when I revert the replication on the client, that change strangely gets replicated back to the server. I know, that is not supposed to happen, but it is.

#

By visibility I mean either Visible, or HiddenInGame. They both behave strangely (in PIE at least).

halcyon ore
#

After looking.

I would say I got it mixed up, but sounds like your still having issues?
Cuz, I was gonna say bHiddenInGame doesn't have direct replication on the component.
But, I guess the actors replicate hidden must make that replicate to?

barren elm
#

The weird part is why changing visibility on the client affects the server. I've protected my code on the client with " if (HasAuthority()) return;" to triple ensure that it is the setting on the client that affects the server and verified in the debugger. Also double checked that NetMode == NM_Client. It's very strange.

thin stratus
#

btw, the bHiddenInGame stuff stops a given Actor from replicating at all, cause it won't be NetRelevant anymore. At least most of them won't.

barren elm
barren elm
#

The item in question is a weapon attached to a character.

crisp shard
#

before even asking , i know this issue is quite vague but i really don't know where else to ask atm

i have been testing my game in standalone with EOS for a long time now, and all of the sudden this morning, the server map will send the client back to the main menu immediately, and i have no idea why. i checked all the logs and most are basically empty excpet there are a few lines that i found semi suspcious, but i dont know much more, and im hoping someone else might have an answer as to where i can look

2025-10-22 15:53:06.641 UnrealEditor[7007:67207] [UE] [2025.10.22-19.53.06:641][134]LogNet: Warning: UNetConnection::Tick: Connection TIMED OUT. Closing connection.. Elapsed: 23.20, Real: 23.19, Good: 23.19, DriverTime: 23.20, Threshold: 20.00, [UNetConnection] RemoteAddr: 127.0.0.1:17777, Name: IpConnection_0, Driver: Name:PendingNetDriver Def:GameNetDriver NetDriverEIK_0, IsServer: NO, PC: NULL, Owner: NULL, UniqueId: INVALID 2025-10-22 15:53:06.641 UnrealEditor[7007:67207] [UE] [2025.10.22-19.53.06:641][134]LogNet: Error: UEngine::BroadcastNetworkFailure: FailureType = ConnectionTimeout, ErrorString = UNetConnection::Tick: Connection TIMED OUT. Closing connection.. Elapsed: 23.20, Real: 23.19, Good: 23.19, DriverTime: 23.20, Threshold: 20.00, [UNetConnection] RemoteAddr: 127.0.0.1:17777, Name: IpConnection_0, Driver: Name:PendingNetDriver Def:GameNetDriver NetDriverEIK_0, IsServer: NO, PC: NULL, Owner: NULL, UniqueId: INVALID, Driver = Name:PendingNetDriver Def:GameNetDriver NetDriverEIK_0 2025-10-22 15:53:06.649

#

and

#

2025-10-22 15:53:06.721 UnrealEditor[7007:67207] [UE] [2025.10.22-19.53.06:721][135]LogNet: Connection failed; returning to Entry 2025-10-22 15:53:06.721 UnrealEditor[7007:67207] [UE] [2025.10.22-19.53.06:721][135]LogLoad: LoadMap: /Game/LEVELS/MAIWORLDMAINMENU?closed 2025-10-22 15:53:06.721 UnrealEditor[7007:67207] [UE] [2025.10.22-19.53.06:721][135]LogWorld: BeginTearingDown for /Temp/Untitled_0

tardy fossil
#

did you manually set it to use port 17777 or is that a typo

crisp shard
#

so not sure what oculd cause a timeout / not being able to connect, but that's just weird to me, and then the second one is just also strange because of the /Temp/Untitled_0 as i don't have a map / level named this at all ?

crisp shard
#

i know it's typically 7777

#

not sure wher ei would have done 17777 tho

tardy fossil
#

the default is 7777 so it looks like a config typo or something

crisp shard
tame sapphire
#

Question: What/how would you recommend setting/storing a team and/or a loadout struct variables from a pre level lobbyto carry on to the gameplay lvl using the server? (Im currently using on swap player controller but feel like the player should ask the server what team and/or loadout they have not the player controller tell the server when it comes to respawning?)

#

I would assume GameInstance and then passing to player state for replication?

crisp shard
#

well actually, im noticing that it's still saying 17777 ?

#

maybe there's another place for that

#

2025-10-22 16:24:13.784 UnrealEditor[13144:117918] [UE] [2025.10.22-20.24.13:784][ 0]LogInit: Display: Starting Game. 2025-10-22 16:24:13.784 UnrealEditor[13144:117918] [UE] [2025.10.22-20.24.13:784][ 0]LogGlobalStatus: Browse Started Browse: "/Game/LEVELS/WORLD?Name=Player" 2025-10-22 16:24:13.784 UnrealEditor[13144:117918] [UE] [2025.10.22-20.24.13:784][ 0]LogNet: Browse: /Game/LEVELS/WORLD?Name=Player 2025-10-22 16:24:13.784 UnrealEditor[13144:117918] [UE] [2025.10.22-20.24.13:784][ 0]LogLoad: LoadMap: /Game/LEVELS/WORLD?Name=Player 2025-10-22 16:24:13.789 UnrealEditor[13144:117918] [UE] [2025.10.22-20.24.13:789][ 0]LogWorld: BeginTearingDown for /Temp/Untitled_0

the above is still the more concerning part tho, it just immediately destroys the world for some reason, and im not sure what it's trying to do ther, and aagin, dont have any level for /Temp/Untitled_0

crisp shard
#

it also works when i just play in viewport, but it also doesnt represent the game fully cause i can't get any info really from the server like a fake save game file so hard to actually test like that

barren elm
crisp shard
barren elm
crisp shard
compact harbor
#

Hey guys, i wanna have a manger actor, and in this manager i want to allow every client to call this manager's server functions. How can i do that? Maybe some quick workaround?

silent valley
compact harbor
tame sapphire
# tame sapphire Question: What/how would you recommend setting/storing a team and/or a loadout s...

Following on and as an example:
The lobby has a list of weapons you can select from (this could be not locked as in all players could choose the same weapon), The local player then selects the weapon they wish to use. This is then the only weapon they can spawn with when in the gameplay level.

Is it still select weapon in lobby, lobby updates game instance. Travel to new map then game instance updates player state to replicate the variable or should this be more server controlled (As I understand it the game instance is unique to each player so should only have the local players load-out/weapon/profile and isn't set up to be server authoritative? i.e. I think it should be more local player asks server what his load-out/weapon/profile is when in gameplay but in lobby he can tell the server?)

silent valley
# tame sapphire Following on and as an example: The lobby has a list of weapons you can select f...

Even the client 'owned' actors like PlayerController are server authoritative.
In the pre-match screen, the client sends a server RPC on the PC to the server to request a specific loadout.
Server validates this is OK, then stores the loadout in a replicated variable (on the PC, or maybe on the PlayerState, etc). Make sure these actors make it through server travel, of course.

When the client loads into the new map, query the replicated variables for the current loadout.

ocean swallow
#

I have a question about this Blueprint. It checks if the total of all players' possessions for a specific item is greater than a specified value. However, when a player calls the check event, AccEvent only counts the value sent by the client who pressed G, and it executes the same number of times as the total number of players. Values ​​sent by other players are not counted.

#

I can't understand what I did wrong 🙁

#

Additional Notes: The inventory system is functioning correctly and players' items are not mixed up.

tardy fossil
#

only a client that owns the actor can call a server RPC.. so when you call the multicast, all the clients that dont own the actor will have their server RPC not go through

#

are the players inventory different locally than the servers or something? you shouldnt need clients to send info back to the server like that

ocean swallow
#

Like using the get all actors of class method?

#

My idea is to only let the server collect the client's calculation results

#

@tardy fossil

#

So is it recommended to use nested loops?

quasi tide
#

It depends

#

Sometimes a nested loop is fine. Other times it could be done better.

#

But depending on the problem, you probably want to do it in C++ because loops are one of the worst areas for BP to do

tame sapphire
#

and weapon class gets passed in from ui

#

?

#

and player state is

haughty ingot
sour sequoia
exotic wasp
#

a fully bp array implementation would go so hard

#

i can already imagine calling add unique

pliant cypress
#
void UCombatComponent::Reload()
{
    if (CarriedMagazine > 0 && EquippedWeapon->GetMagAmmo() < EquippedWeapon->GetMaxMagAmmo() && CombatState != ECombatState::ECS_Reloading)
    {
        if (!SoldierCharacter->HasAuthority()) UE_LOG(LogTemp, Warning, TEXT("client reloading"));
        ServerReload();
    }
}

void UCombatComponent::ServerReload_Implementation()
{
    if (SoldierCharacter == nullptr && EquippedWeapon == nullptr) return;

    CombatState = ECombatState::ECS_Reloading;
    HandleReload();
}

void UCombatComponent::OnRep_CombatState()
{
    switch (CombatState)
    {
    case ECombatState::ECS_Unoccupied:
        HandleReload();
        break;
    case ECombatState::ECS_Reloading:
        if (bFireButtonPressed)
        {
            Fire();
        }
        break;
    }
}

void UCombatComponent::HandleReload()
{
    SoldierCharacter->PlayReloadAnimation();
}

void UCombatComponent::FinishReloading()
{
    if (SoldierCharacter == nullptr) return;
    if (SoldierCharacter->HasAuthority())
    {
        CombatState = ECombatState::ECS_Unoccupied;
        UpdateMagazineAmmo();
    }
    if (bFireButtonPressed)
    {
        Fire();
    }
}
#

animation and reloading is only happening on server

#

not on clients

#

also, finishreloading is called by AnimNotify in Montage

#

this is PlayReloadANimation

#
void ASoldierCharacter::PlayReloadAnimation()
{
    if (Combat == nullptr || Combat->EquippedWeapon == nullptr) return;

    UAnimInstance* AnimInstance;
    if (IsLocallyControlled())
    {
        AnimInstance = GetFPSMesh()->GetAnimInstance();
        if (AnimInstance && FP_ReloadWeaponMontage)
        {
            AnimInstance->Montage_Play(FP_ReloadWeaponMontage);
            FName SectionName;
            switch (Combat->EquippedWeapon->GetWeaponType())
            {
            case EWeaponType::EWT_AssultRifle:
                SectionName = FName("Rifle");
                break;
            }
            AnimInstance->Montage_JumpToSection(SectionName, FP_ReloadWeaponMontage);
        }
    }
    else
    {
        AnimInstance = GetMesh()->GetAnimInstance();
        if (AnimInstance && TP_ReloadWeaponMontage)
        {
            AnimInstance->Montage_Play(TP_ReloadWeaponMontage);
            FName SectionName;
            switch (Combat->EquippedWeapon->GetWeaponType())
            {
            case EWeaponType::EWT_AssultRifle:
                SectionName = FName("Rifle");
                break;
            }
            AnimInstance->Montage_JumpToSection(SectionName, TP_ReloadWeaponMontage);
        }
    }
}
#

i'm following TPS/FPS method for my FP game

crisp shard
#

for a "bank" that would act as a storage for each player separately, would i just use a simple contianer with an items array as like a component on game mode or game state? or player state? or character? lol

#

this data would be infrequently interacted with, so maybe i could use a savegame file , but im more curious where i should put this logic

tardy fossil
#

definitely not game mode as thats server only.. game state also doesn't seem like a good choice.. id say player state or character.. player state if you dont plan on having AI with inventories

crisp shard
#

nice that's actually a bit easier to visualize, i was only thinking somehting like gamestate/gamemode as they aren't the character but i suppose the reasoning was about the charcter not having direct access without the validation but yea a non-issue to worry about. i like player state

dark edge
crisp shard
dark edge
#

Component on playerstate will work

crisp shard
#

so don't really see it needing to replicate to anyone other than that owning lcient, but invenotry/storage is diffetn

dark edge
#

you can restrict it to only owning client

crisp shard
#

yea prob will that will give a nicer response time for the immediate pull up lol

viscid geyser
#

I'm interested in (at least beginning to start) learning client side prediction. I have a project where some of my player character scene components need to use absolute world location instead of local transforms and are updated to match the player's world location through blueprint. This works flawlessly as the host but all other clients experience jitter as the components slightly lag behind their position.

thin stratus
viscid geyser
#

Right, that part i understand. How would i begin creating a prediction blueprint for those world locations? I know at the very least i need to take into account linear and angular velocity as advised by a friend

thin stratus
#

What defines their "Absolute World Location"?

viscid geyser
#

Every scene component has a dropdown arrow next to location, when you open that there are 2 radio buttons 'local' and 'world'. When you switch it to world, location now displays as absolute location

thin stratus
#

What updates them? To what are they updated?

viscid geyser
#

'VInterp To' 'Get Actor Location'

thin stratus
#

Okay, so I don't think there is much about "client prediction" you would be doing here. I would advice ensuring that these Components are updated individually on all clients, so don't replicate their transform additionally. And then make them VInterpTo the MeshComponent and not the Actor (CapsuleComponent).

#

I don't really know what kind of components those are and you still haven't explained why you even do this.

#

It's difficult to help if you aren't answering the questions :P

viscid geyser
#

I'm very sorry I'm trying my best

#

I'm affecting 2 scene components separately and thus their children. The two affected parents are a second collision capsule and a spring arm. The reason I'm doing this is to have something similar to camera lag but only adding lag to the z location

#

Are there still other questions i haven't answered?

#

are there any screenshots I need to send?

thin stratus
# viscid geyser are there any screenshots I need to send?

No. So if they are "camera lag" related, I assume they are local client only? Or do other clients need to be aware of those? If they are local only, then I would continue to suggest simply VInterping to the MeshComponent and not having any additional replication for the transform going on. They MeshComp's Transform should already be smoothed.

viscid geyser
#

it is a similar effect to camera lag but it is not using that built in system at all. As for replication it did not work for clients at all unless I set up the blueprint that sets their world laction to start with a run on server event and multicast event

#

the basic use case of the system right now is the character uses two separate collision capsules, the built in one with the dimensions of a crouching character, and a second using this custom system to extend out from it to make up the remaining standing height. As the character steps up onto a ledge the top capsules X and Y are meant to stay 1 to 1 with the X and Y of the actor but the Z interpolates instead of snaps making the character appear as if it compresses when stepping up. The same principle is being applied to a spring arm and all cameras attached to it.
I'm handling this code in a custom actor component which has been added to the player blueprint alongside the existing CMC

#

the dimensions of either capsule never change, when crouching the top capsule just moves down into the bottom one. This was done so the player can run over very jagged terrain while the top have remains relatively level, similar to how car suspension works

#

the custom suspension component doesn't need the replication box enabled but the scene component its configured to affect does, like the capsule and spring arm

thin stratus
#

I assume that capsule is not supposed to affect the movement, or?

viscid geyser
#

in what way? the bottom capsule is still the player controller and does all of the built in cmc function like the Is Falling? check. The top portion is additional collision and I have a skeletal mesh parented to it because in the future I will handle crouch animations with IK instead of dedicated crouch animations.

thin stratus
#

Is that supposed to block the player's movement?

viscid geyser
#

yes

#

crouching would sink the "head" capsule into the main crouch height one allowing the player to crouch under

#

I can send two videos on why and how I'm doing it that way

thin stratus
#

Right... that probably ain't gonna work properly and also explains why you get problems with it. Everything that affects the Movement Simulation has to be built into the CMC or at least be based on already synced events.
A second capsule can work, but not if driven through Blueprints.

viscid geyser
#

right now everything is functional except for jitter on non-host clients. It still properly affects their movement, its just that the camera (using the same thing because of the affected spring arm) is nauseating for those other clients

thin stratus
#

except for jitter on non-host clients.
Then it's not functional :P

#

Also you are not taking into account corrections that aren't handled properly like this.

#

In theory you'd need to "record" the Z offset in SavedMoves to ensure the CMC properly recreates the state when replaying moves.

viscid geyser
#

that's why I'm seeking guidance

thin stratus
#

Well, are you able to use C++?

viscid geyser
#

I have minor experience outside of unreal but haven't used any in this project thus far

thin stratus
#

Hmpf. So, there is a lot going on inside the CMC. Almost all of the simulation, including the Prediction, the Correction, the Reconciliation and the Smoothing/Interpolation is all happening in C++ and heavily tied together.
It's all very much relying on the concept of the Client performing a move locally, saving all sorts of information about the move to a local buffer an sending the minimum of information via the CMCs ServerMove RPC to the Server. And the Server then performs the same move and compares the endlocation which the Client also sent via that RPC. If that location differs, it will correct the client, which will throw all older moves away, apply the server data as the truth and replay all currently still pending moves.

#

If you start messing this outside of that "loop", it won't be working properly, especially not in actual network conditions.

#

In 95% of cases, one needs to inherit from the CMC in C++ and add custom functionality the way the CMC needs it to work, so it's part of said "loop".

#

If you start doing things outside of the CMC, then there is no guaranteed anymore that the move the client did locally will be the same move the server does after the RPC.

viscid geyser
#

I'm going to sound really stupid but I can't just predict where the player will be?

thin stratus
#

And there is also no guarantee that the client can replay the moves after corrections.

thin stratus
viscid geyser
#

the simulation isn't breaking or desyncing right now

thin stratus
#

Are you testing with ping and potential package loss?

#

Cause it usually looks fine in PIE if you have that disabled.

#

Either way, you'd need to base the XY location of the additional stuff on the XY location of the player. The SimProxies probably don't even need this, as they just get the Transform replicated anyway. And if you update the additional capsule to match the other capsule every frame on Server and Client independently (no additional replication), then it would probably also match in the "live" world. You still might not have a fun time if you get corrections.

#

The Z interp stuff is a different story.

#

Cause there is no way you can guarantee that this is in sync.

viscid geyser
thin stratus
#

It wouldn't do that. It would just jitter like hell

viscid geyser
#

do what?

thin stratus
#

phase through stuff

viscid geyser
#

i just said it doesn't?

thin stratus
#

And I said it would also not do that.

#

As in, there is also no expectation that it would cause the player to phase through anything.

viscid geyser
#

wouldn't it if the two capsules desynced from each other?

thin stratus
#

You are still updating the location every frame after all. They won't really desync that much. The bigger problem is the Interpolation stuff you do isn't in sync.

#

Inside the CMC, the Server performs the Move with the DeltaTime that the Client used. The Client tells the Server the DeltaTime via the ServerRPC.

#

If you interpolate some float on Server and Client, they can have different FrameRates and will interpolate differently fast.

#

So when the Server moves, the second capsule might not be at the same location due to interpolating differently.

viscid geyser
thin stratus
#

All good.

viscid geyser
#

If my best course of action is to modify a default component with C++ at this point would I just be better off modifying camera lag to only lag for z movement?

thin stratus
#

Client performs Move X, sends result to Server. Server performs Move X.
Now the additional capsule isn't synced here, as you aren't doing the interpolation INSIDE the CMC.

thin stratus
#

Multiplayer is difficult. Client Predicted Movement in Multiplayer is one of the high end disciplines. Blueprints can do a lot, but this stuff usually has to be handled in C++. There are some things that work out of the box with Blueprints due to things naturally syncing up (like stepping onto a jump pad, as Server and Client will step on it during the same "Move"), but something as "core" as the collision is probably not gonna work well.

reef bison
#

im working on an open world game and im exploring ways to save, load, render and keep harvestable world objects (trees, rocks, ore deposits) synced for all clients. this is what i ended up with:

  • separate the world in chunks, each object is assigned to a chunk based on their location. everything goes into an array
  • when the world is loaded, use a server-side subsystem to calculate the necessary chunks around all players and create an actor on the server for each chunk (chunk actor)
  • each chunk actor will receive a list of objects to render, i implemented this with a FFastArraySerializer in the actor. once the list is received a dynamic ISM component is created for each type of mesh
  • this process repeats when the players move around the world, creating the necessary chunk actors

the entire thing relies on my understanding that the chunk actors created by the subsystem will correctly replicate to all clients, including the fast array. so if a chunk is modified (tree removed), this info first go to the subsystem, which will edit their version of the chunk actor and the fast array will replicate minimal data to update the clients

here is a video demonstrating how its working with a very small chunk size

#

my question is, is there a flaw or something wrong with my approach that i might be missing? am i reinventing something that already exists and would do the job way better?

haughty ingot
hollow raven
#

What is better for making simple shooter games in Low Poly? UE4 or 5?

reef bison
#

i did this because i read somewhere that spawning actors is very demanding and should be avoided if possible

#

now that you mentioned it, i didnt think about how i would implement dynamic objects yet ThinkO_O my proof of concept in the video is only for static meshes because i got worried about replicating millions of trees. i need to look into what i could do for actual separate actors (like a workbench or npcs)

#

i dont think i would be able to use a pool for that because they are all different classes

haughty ingot
#

The only thing I will say is that since you’re moving the position, I’m assuming the chunk actors are replicating movement. Also what it sounds like you’re doing is as the player moves around, you’re repopulating the fast array everytime the chunk is moved? (I assume you’re keeping the data that populates the chunk on disk or something similar), so it could hurt a bit if you’re essentially having to send the entire array back again everytime a chunk is loaded. You could also ditch the ISM for just using raw scene proxies but that’s a whole different topic.

reef bison
#

you’re repopulating the fast array everytime the chunk is moved?
yes, when you move the actor to a new chunk i need to change the position and re assign a complete different value to the array

haughty ingot
#

Honestly before making assumptions I would definitely profile with insights, you can profile networking directly on there. Rather then a stranger on the internet telling you what’s up without knowing your code 🙂

reef bison
#

technically the same chunk actors doesnt go from chunk x to chunk y, it work more like this:
actor is created to render chunk 0,0
player moves away
actor is moved to pool (no position change, array is set to [])
the subsystem needs a new chunk
actor is changed to render chunk 1,1 (position change, array is set to a new big list)

#

yeah for sure, i was mostly wondering if i was reinventing the wheel and a solution already existed for my use case, im still new to unreal so theres a lot to learn

#

i am gonna search about raw scene proxies, i havent seen this mentioned yet

haughty ingot
reef bison
#

yeah that might be the worst part of the solution, i am not sure how i could optimize it because the array is entirely different for each chunk

#

im gonna keep using it and if it starts lagging a log in the real map i will debug with the profiler

#

thanks for the comments bow

haughty ingot
#

There's also some default things in UE to consider like Replication Graph (and now Iris) if you feel like looking into them

reef bison
#

i didnt know about the replication graph, i will add it on my list to read later blobdetective

#

i read about iris a few weeks ago but it sounded a bit experimental so im afraid to use it and run into issues i might not be able to fix myself

fringe dove
#

for client recorded demos (not server recorded), what's the best approach for properties with skipowner, and for things like certain unicast rpcs to the main player controller that don't make it to the demo recording player controller (server doesn't know about it)?

#

most skip owner stuff I want to make sure to record is normally set locally, then set on server

halcyon ore
#

Hey all.
Timing question in regards to replication.

What might you suggest for a trap system, such as a swinging pendulum.
A simple like current anim time, that is inital only.
So, on inital replication the timing should match.
But, various factors would influence that.

Should I re-sync every couple seconds, or minutes?

#

I had thought about making this one instance, client driven, to feel more "correct"
Given its just a simple obstacle, and not some large fight that being able to god mode cheat in would affect much.

pliant cypress
halcyon ore
#

Seems, pretty obvious?
Server is setting the var "wrong" in this context.
ECS_Unoccupied is what calls HandlReload
But, your setting it to ECS_Reloading, which is used for Fire

#

Reload -> ServerReload -> CombatState = ECombatState::ECS_Reloading
But, it should be ECS_Unoccupied with the current setup.

haughty ingot
halcyon ore
#

Your right, it must not be obvious if no one else helped.
But, that is bad wording on my part.
I'm not used to helping in this discord.