How to compare two FUniqueNetId properly?

I’m working on a party system. Since I don’t want to create a widget for the local player, I compare unique ids.
The issue is that sometimes the == operator returns false for the same UniqueId. Here’s the code that compares the unique ids.



// PlayerState is of class ALobbyBeaconPlayerState, the UniqueId is a replicated variable.
// Both the PlayerState and the UniqueId is replicated and valid (not null)

TSharedPtr<const FUniqueNetId> LocalPlayerUniqueId = GetGameInstance()->GetPrimaryPlayerUniqueId();
TSharedPtr<const FUniqueNetId> InPlayerUniqueId = PlayerState->UniqueId.GetUniqueNetId();

// This returns false on client side, even though they are the same unique ids (according to logs)
bool bIsLocalPlayer = LocalPlayerUniqueId == InPlayerUniqueId;

// Temp logs
UE_LOG(LogTemp, Warning, TEXT("MyPlayerUniqueId = %s"), *LocalPlayerUniqueId->ToDebugString());
UE_LOG(LogTemp, Warning, TEXT("IncomingUniqueId = %s"), *InPlayerUniqueId->ToDebugString());
UE_LOG(LogTemp, Warning, TEXT("bIsLocalPlayer = %s"), (bIsLocalPlayer ? TEXT("TRUE") : TEXT("FALSE")));




// Relevant logs, client side

// Player 1
LogTemp: Warning: MyPlayerUniqueId = DESKTOP-ODR6QKS-A60BEDB543B30859CE0958A6F8111521
LogTemp: Warning: IncomingUniqueId = DESKTOP-ODR6QKS-834ADD144BEB1CCDD30DB1B941AFD920
LogTemp: Warning: bIsLocalPlayer = FALSE

// Player 2 <-- This is the local player
// The debug string is the same, yet the compare returned false
LogTemp: Warning: MyPlayerUniqueId = DESKTOP-ODR6QKS-A60BEDB543B30859CE0958A6F8111521
LogTemp: Warning: IncomingUniqueId = DESKTOP-ODR6QKS-A60BEDB543B30859CE0958A6F8111521
LogTemp: Warning: bIsLocalPlayer = FALSE


I guess I could compare the string versions as a workaround, but still, I think I’m missing something here. Could it be an issue that the PlayerState’s UniqueId is a FUniqueNetIdRepl, and I’m getting the actual UniqueNetId via UniqueId.GetUniqueNetId()?

Thanks in advance!

have you replicated, So the client can see its real value.


bool bIsLocalPlayer

@gamepainters It doesn’t have to replicate, it’s checked locally

I’m trying to figure out what you are making. Is this a Lobby for players to gather in?
Who is the second player? <person joining from the net? or bot?
This is running on the client? and not server to client?

LocalPlayerUniqueId and InPlayerUniqueId are objects of the class template TSharedPtr<const FUniqueNetId>. You’re comparing those objects rather than the FUniqueNetId structs they’re pointng to. Moreover, even if you were trying to determine if they’re the same, the assignement operator is the only operator overloaded not the equality operator. You’re probably going to get unexpected behaviour by using the equality operator to compare the two. Is there a reason you aren’t using the Compare() member function?



TSharedPtr<const FUniqueNetId> LocalPlayerUniqueIdPtr = GetGameInstance()->GetPrimaryPlayerUniqueId();
TSharedPtr<const FUniqueNetId> InPlayerUniqueIdPtr = PlayerState->UniqueId.GetUniqueNetId();

bool bIsSamePlayerId = LocalPlayerUniqueIdPtr.Get()->Compare(InPlayerUniqueIdPtr.Get());

// If you must compare with the equaily operator
bool bIsSamePlayerId = LocalPlayerUniqueIdPtr.Get() == InPlayerUniqueIdPtr.Get();


@Tulpuh Hey, thanks for the help. This looks like the proper solutions, I’ll try it out later. I didn’t use the Compare function because I didn’t know it existed (:

@Tulpuh It turns out, the Compare function is protected, so I can’t use it. I’ve also tried comparing the actual FUniqueNetId (via the SharedPtr.Get), but as expected, it made no difference. I’m starting to think that this is a bug. Something happens with the unique id during replication maybe?

I dont think it’s a replication issue because the debug strings do show that they have different/same IDs. I hadn’t noticed that it was protected. Every OSS likely needs to implement their own Compare in their derived id class. I took a look at the definition of FUniqueNetId and it turns out that the comparison operators are overloaded, but they use the Compare function.


/**
* Abstraction of a profile service online Id
* The class is meant to be opaque
*/
class FUniqueNetId : public TSharedFromThis<FUniqueNetId>
{
protected:

    /** Only constructible by derived type */
    FUniqueNetId() = default;

    FUniqueNetId(const FUniqueNetId& Src) = default;
    FUniqueNetId& operator=(const FUniqueNetId& Src) = default;

    virtual bool Compare(const FUniqueNetId& Other) const
    {
        return (GetSize() == Other.GetSize()) &&
        (FMemory::Memcmp(GetBytes(), Other.GetBytes(), GetSize()) == 0);
    }

public:

    virtual ~FUniqueNetId() = default;

    /**
    * Comparison operator
    */
    friend bool operator==(const FUniqueNetId& Lhs, const FUniqueNetId& Rhs)
    {
        return Lhs.Compare(Rhs);
    }

    friend bool operator!=(const FUniqueNetId& Lhs, const FUniqueNetId& Rhs)
    {
        return !Lhs.Compare(Rhs);
    }

    /**
    * Get the raw byte representation of this opaque data
    * This data is platform dependent and shouldn't be manipulated directly
    *
    * @return byte array of size GetSize()
    */
    virtual const uint8* GetBytes() const = 0;
...]
}


The base Compare function itself uses GetBytes and Memcmp to compare the IDs. You could define a modified Compare function that takes the two FUniqueNetIds and does the same Memcmp as shown above. This is assuming GetBytes and GetSize are returning the correct values for each ID, which might not be the case given == is not returning the correct results. At this point, you probably should just use the string functions as you mentioned in your original post. Unfortunately, the documentation does mention not doing this since they’re platform dependent. For a simple comparison between IDs on the same machine you should be fine though.

I’ve gone back to refactor the code. As it turns out, if I don’t call the GetUniqueNetId() function on the FUniqueNetIdRepl, it compares properly. Weird.



bool bIsLocalPlayer = GetGameInstance()->GetPrimaryPlayerUniqueId() == OwningPlayerState->UniqueId;


The Compare method is protected, you must use the equality/inequality operators.

friend bool operator==(const FUniqueNetId& Lhs, const FUniqueNetId& Rhs)
{
	return Lhs.Compare(Rhs);
}

friend bool operator!=(const FUniqueNetId& Lhs, const FUniqueNetId& Rhs)
{
	return !Lhs.Compare(Rhs);
}

Those operators acts on references. So you have to do something like this:

const FUniqueNetId& LocalPlayerUniqueIdRef = *(GetGameInstance()->GetPrimaryPlayerUniqueId().Get());
const FUniqueNetId& InPlayerUniqueIdRef = *(PlayerState->UniqueId.GetUniqueNetId().Get());

bool bIsSamePlayerId = LocalPlayerUniqueIdRef == InPlayerUniqueIdRef;
1 Like