I’ve upgraded the project from version 4.26.2 → 4.27.1
The same code does not work anymore. Has the timer manager implementation changed ?
I have a class FKPlayerManager working with a simple recurring timer
The header file
#pragma once
#include "KLocalPlayer.h"
#if !UE_SERVER
/**
* @brief Information about the player managed by the FKPlayerManager
*/
struct FKPlayerInfo
{
bool bConnected;
int32 Level;
int32 Elo;
FKPlayerInfo(const bool InConnected = false, const int32 InLevel = 0, const int32 InElo = 0)
{
bConnected = InConnected;
Level = InLevel;
Elo = InElo;
}
};
/**
* The player manager is in charge of discovering if player is online,
* invite player in a group,
*/
class FKPlayerManager : public TSharedFromThis<FKPlayerManager>
{
public:
/** constructor. */
FKPlayerManager(TWeakObjectPtr<UKLocalPlayer> InLocalPlayer);
/** destructor. */
virtual ~FKPlayerManager();
/**
* @return the global instance of the FKPlayerManager.
*/
static TSharedPtr<FKPlayerManager> Get()
{
return FKPlayerManager::Instance;
}
/**
* @brief Initializes the server manager with a local player.
*
* @param InLocalPlayer The local player.
*/
static void Initialize(TWeakObjectPtr<UKLocalPlayer> InLocalPlayer)
{
UE_LOG(LogKSGMMessage, Verbose, TEXT("FKPlayerManager::Initialize"));
if (FKPlayerManager::Instance.IsValid())
{
FKPlayerManager::Instance.Reset();
}
FKPlayerManager::Instance = MakeShareable(new FKPlayerManager(InLocalPlayer));
};
/**
* @brief Shutdown the server manager.
*/
static void Shutdown()
{
UE_LOG(LogKSGMMessage, Verbose, TEXT("FKPlayerManager::Shutdown"));
if (FKPlayerManager::Instance.IsValid())
{
FKPlayerManager::Instance->Cleanup();
FKPlayerManager::Instance.Reset();
}
};
/**
* @brief Is the player's connected.
* @param InUniqueID Unique identifier for the player
* @returns True if connected, false if not.
*/
bool IsPlayerConnected(const FString InUniqueID);
/**
* @brief Gets the player's level.
* @param InUniqueID Unique identifier for the player
* @returns The level if connected, 0 if not.
*/
int32 GetPlayerLevel(const FString InUniqueID);
/**
* @brief Gets the player's ELO rating.
* @param InUniqueID Unique identifier for the player
* @returns The ELO if connected, -1 if not.
*/
int32 GetPlayerElo(const FString InUniqueID);
/**
* @brief Player exist?
* @param InUniqueID Unique identifier for the player.
* @returns True if it exists, false if not.
*/
bool PlayerExists(const FString InUniqueID) const;
/**
* @brief Gets all players' information array.
* @return The array reference.
*/
TMap<FString, struct FKPlayerInfo>& GetAllPlayers();
/**
* @brief Refresh the connection status of all players contained within the map.
*/
void RefreshConnectionStatus();
/**
* @brief Starts refresh connection status loop.
* @param InRecTime (Optional) The recurring time to refresh the connection status of all players.
*/
void StartRefreshConnectionStatus(float InRecTime = 15.0f);
/**
* @brief Stops refresh connection status loop.
*/
void StopRefreshConnectionStatus();
protected:
void Cleanup();
// Adds a player information
void AddPlayer(const FString InUniqueID);
/**
* @brief The call-back to known the result of a player connected.
* @param InUniqueID Unique identifier for the in.
* @param bConnected True if connected.
* @param InLevel The level of the player. 0 if not connected
* @param InElo The ELO of the player. -1 if not connected
*/
void OnIsPlayerConnectedComplete(const FString InUniqueID, bool bConnected, int32 InLevel, int32 InElo);
private:
/** @brief The Player Owner. */
TWeakObjectPtr<UKLocalPlayer> LocalPlayer;
/** A shared pointer to the global instance of the server manager. */
static TSharedPtr<FKPlayerManager> Instance;
/** @brief A map of players.
* The key is the unique player ID
*/
TMap<FString, struct FKPlayerInfo> MapPlayers;
/** @brief The timer handle of the refresh connection status. */
FTimerHandle RefreshConnectionStatusHandle;
};
inline TMap<FString, struct FKPlayerInfo>& FKPlayerManager::GetAllPlayers()
{
return MapPlayers;
}
inline bool FKPlayerManager::PlayerExists(const FString InUniqueID) const
{
return MapPlayers.Contains(InUniqueID.ToLower());
}
/** @remark we add to unique id in lower case to avoid problem when
* with some functions it is return in lower case
*/
inline void FKPlayerManager::AddPlayer(const FString InUniqueID)
{
MapPlayers.Add(InUniqueID.ToLower(), FKPlayerInfo(false));
}
#endif // End of !UE_SERVER
And the source file
#include "KPlayerManager.h"
#include "KBasePlayerController.h"
#include "KLocalPlayerHelpers.h"
#include "TimerManager.h"
#if !UE_SERVER
TSharedPtr<FKPlayerManager> FKPlayerManager::Instance = nullptr;
FKPlayerManager::FKPlayerManager(TWeakObjectPtr<UKLocalPlayer> InLocalPlayer)
{
LocalPlayer = InLocalPlayer;
}
FKPlayerManager::~FKPlayerManager()
{
LocalPlayer = nullptr;
}
void FKPlayerManager::Cleanup()
{
UE_LOG(LogKSGMMessage, Verbose, TEXT("FKPlayerManager::Cleanup"));
if (LocalPlayer != nullptr && LocalPlayer.IsValid())
{
if (LocalPlayer->GetWorld())
LocalPlayer->GetWorldTimerManager().ClearTimer(RefreshConnectionStatusHandle);
}
MapPlayers.Empty();
LocalPlayer = nullptr;
}
bool FKPlayerManager::IsPlayerConnected(const FString InUniqueID)
{
UE_LOG(LogKSGMMessage, Verbose, TEXT("FKPlayerManager::IsPlayerConnected InUniqueID=%s"), *InUniqueID);
// REMOVE IT WHEN WORKING AGAIN.
// To check why it is not working
LocalPlayer->GetWorldTimerManager().ListTimers();
if (PlayerExists(InUniqueID))
return MapPlayers[InUniqueID].bConnected;
// Not found, create it
AddPlayer(InUniqueID);
return false;
}
int32 FKPlayerManager::GetPlayerLevel(const FString InUniqueID)
{
if (PlayerExists(InUniqueID))
return MapPlayers[InUniqueID].Level;
// Not found, create it
AddPlayer(InUniqueID);
return 0;
}
int32 FKPlayerManager::GetPlayerElo(const FString InUniqueID)
{
if (PlayerExists(InUniqueID))
return MapPlayers[InUniqueID].Elo;
// Not found, create it
AddPlayer(InUniqueID);
return -1;
}
void FKPlayerManager::StopRefreshConnectionStatus()
{
UE_LOG(LogKSGMMessage, Verbose, TEXT("FKPlayerManager::StopRefreshConnectionStatus"));
if (LocalPlayer != nullptr && LocalPlayer.IsValid())
{
if (LocalPlayer->GetWorldTimerManager().IsTimerActive(RefreshConnectionStatusHandle))
LocalPlayer->GetWorldTimerManager().ClearTimer(RefreshConnectionStatusHandle);
}
}
void FKPlayerManager::StartRefreshConnectionStatus(float InRecTime /*= 15.0f*/)
{
UE_LOG(LogKSGMMessage, Verbose, TEXT("FKPlayerManager::StartRefreshConnectionStatus InRecTime=%f"), InRecTime);
if (LocalPlayer != nullptr && LocalPlayer.IsValid())
{
if (LocalPlayer->GetWorldTimerManager().IsTimerActive(RefreshConnectionStatusHandle))
LocalPlayer->GetWorldTimerManager().ClearTimer(RefreshConnectionStatusHandle);
LocalPlayer->GetWorldTimerManager().SetTimer(RefreshConnectionStatusHandle, FTimerDelegate::CreateRaw(this, &FKPlayerManager::RefreshConnectionStatus), InRecTime, true, 1.0f);
}
}
void FKPlayerManager::RefreshConnectionStatus()
{
UE_LOG(LogKSGMMessage, Verbose, TEXT("FKPlayerManager::RefreshConnectionStatus"));
if (AKBasePlayerController* PC = GetBasePlayerController(LocalPlayer.Get()))
{
for (auto& Elem : MapPlayers)
{
PC->IsPlayerConnected(Elem.Key, FKBindableEventStringBoolInt2::CreateSP(this, &FKPlayerManager::OnIsPlayerConnectedComplete));
}
}
}
void FKPlayerManager::OnIsPlayerConnectedComplete(const FString InUniqueID, bool bConnected, int32 InLevel, int32 InElo)
{
UE_LOG(LogKSGMMessage, Verbose, TEXT("FKPlayerManager::OnIsPlayerConnectedComplete - InUniqueID=%s bConnected=%i InLevel=%i InElo=%i"), *InUniqueID, bConnected, InLevel, InElo);
if (PlayerExists(InUniqueID))
{
MapPlayers[InUniqueID.ToLower()].bConnected = bConnected;
MapPlayers[InUniqueID.ToLower()].Level = InLevel;
MapPlayers[InUniqueID.ToLower()].Elo = InElo;
}
}
#endif // End of !UE_SERVER
In version 4.26.2, the timer was calling the function RefreshConnectionStatus()
In version 4.27.1 the timer just call the function the first time, and then never.
[2021.11.10-10.15.03:031][625]LogKSGMMessage: Verbose: FKPlayerManager::StartRefreshConnectionStatus InRecTime=15.000000
[2021.11.10-10.15.03:848][937]LogKSGMMessage: Verbose: FKPlayerManager::RefreshConnectionStatus
If I check the log, I can see the function is binded with the timer, but the ExpireTime never changed. It stays at ExpireTime=16.066159
[2021.11.10-10.20.54:205][102]LogEngine: ------- 1 Active Timers -------
[2021.11.10-10.20.54:744][102]TimerData 000001FCB55E0100 : bLoop=true, bRequiresDelegate=true, Status=1, Rate=15.000000, ExpireTime=16.066159, Delegate=DELEGATE,NO OBJ,vtbl: 00007FF657BEF510 func: 0x7ff651634000 FKPlayerManager::RefreshConnectionStatus() [D:\KadeoGames\UDK\KSGM\Source\KSGM\Online\KPlayerManager.cpp:105]
even after 1 minute and the callback never called
[2021.11.10-10.21.40:166][629]LogEngine: ------- 1 Active Timers -------
[2021.11.10-10.21.40:166][629]TimerData 000001FCB55E0100 : bLoop=true, bRequiresDelegate=true, Status=1, Rate=15.000000, ExpireTime=16.066159, Delegate=DELEGATE,NO OBJ,vtbl: 00007FF657BEF510 func: 0x7ff651634000 FKPlayerManager::RefreshConnectionStatus() [D:\KadeoGames\UDK\KSGM\Source\KSGM\Online\KPlayerManager.cpp:105]
Something should have changed.
Have a nice day.
D.