Skip to content

Commit

Permalink
* Lot of refactoring.
Browse files Browse the repository at this point in the history
* Added support for elapsed timers and logo.
* New static method to destroy discord object instance.
  • Loading branch information
ryanjon2040 committed Jun 18, 2020
1 parent cfd36b7 commit 4910004
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 21 deletions.
89 changes: 76 additions & 13 deletions Source/DiscordUE4/Private/DiscordObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,62 @@

#include "DiscordObject.h"
#include "../discord-files/discord.h"
#include "UObject/Class.h"

DEFINE_LOG_CATEGORY_STATIC(LogDiscord, Log, All)

#define LogDisplay(Param1) UE_LOG(LogDiscord, Display, TEXT("%s"), *FString(Param1))
#define LogError(Param1) UE_LOG(LogDiscord, Error, TEXT("%s"), *FString(Param1))

discord::Core* core{};
discord::Activity activity{};

UDiscordObject* UDiscordObject::DiscordObjectInstance = nullptr;

void UDiscordObject::BeginDestroy()

UDiscordObject::UDiscordObject()
{
Super::BeginDestroy();
if (DiscordObjectInstance)
{
DiscordObjectInstance->ConditionalBeginDestroy();
DiscordObjectInstance = nullptr;
LogDisplay("Discord object destroyed.");
}
bCanTick = bTimerStarted = false;
}

UDiscordObject* UDiscordObject::GetOrCreateDiscordObject(FString InClientID, const bool bRequireDiscordRunning /*= false*/)
UDiscordObject* UDiscordObject::GetOrCreateDiscordObject(FString InClientID, const bool bRequireDiscordRunning /*= false*/, const bool bStartElapsedTimer /*= true*/)
{
if (DiscordObjectInstance == nullptr)
{
DiscordObjectInstance = NewObject<UDiscordObject>();
DiscordObjectInstance->Internal_CreateDiscordObject(InClientID, bRequireDiscordRunning);
DiscordObjectInstance->AddToRoot();
DiscordObjectInstance->Internal_CreateDiscordObject(InClientID, bRequireDiscordRunning, bStartElapsedTimer);
}

return DiscordObjectInstance;
}

void UDiscordObject::DestroyDiscordObject()
{
if (DiscordObjectInstance)
{
DiscordObjectInstance->bCanTick = false;
DiscordObjectInstance->StopDiscordTimer();
DiscordObjectInstance->RemoveFromRoot();
DiscordObjectInstance->ConditionalBeginDestroy();
DiscordObjectInstance = nullptr;
LogDisplay("Discord object destroyed.");
}
}

const FString UDiscordObject::GetDiscordResultString(EDiscordReturnResult InDiscordResult)
{
return UEnum::GetDisplayValueAsText(InDiscordResult).ToString();
}

void UDiscordObject::SetState(FString InNewState)
{
activity.SetState(TCHAR_TO_UTF8(*InNewState));
core->ActivityManager().UpdateActivity(activity, [](discord::Result result)
{
uint8 ResultByte = (uint8)result;
DiscordObjectInstance->OnStateSet.Broadcast(static_cast<EDiscordReturnResult>(ResultByte));
LogDisplay(FString::Printf(TEXT("State Set Result: %s"), *GetDiscordResultString(static_cast<EDiscordReturnResult>(ResultByte))));
});
}

Expand All @@ -52,18 +69,64 @@ void UDiscordObject::SetDetails(FString InNewDetails)
{
uint8 ResultByte = (uint8)result;
DiscordObjectInstance->OnDetailsSet.Broadcast(static_cast<EDiscordReturnResult>(ResultByte));
LogDisplay(FString::Printf(TEXT("Details Set Result: %s"), *GetDiscordResultString(static_cast<EDiscordReturnResult>(ResultByte))));
});
}

void UDiscordObject::Internal_CreateDiscordObject(const FString& InClientID, const bool bRequireDiscordRunning)
void UDiscordObject::SetLargeImage(const FString InKeyName)
{
activity.GetAssets().SetLargeImage(TCHAR_TO_UTF8(*InKeyName));
core->ActivityManager().UpdateActivity(activity, [](discord::Result result)
{
uint8 ResultByte = (uint8)result;
DiscordObjectInstance->OnLargeImageSet.Broadcast(static_cast<EDiscordReturnResult>(ResultByte));
LogDisplay(FString::Printf(TEXT("Large Image Set Result: %s"), *GetDiscordResultString(static_cast<EDiscordReturnResult>(ResultByte))));
});
}

void UDiscordObject::StartDiscordTimer()
{
activity.GetTimestamps().SetStart(FDateTime::UtcNow().ToUnixTimestamp());
core->ActivityManager().UpdateActivity(activity, [](discord::Result result)
{
uint8 ResultByte = (uint8)result;
DiscordObjectInstance->OnTimerStart.Broadcast(static_cast<EDiscordReturnResult>(ResultByte));
LogDisplay(FString::Printf(TEXT("Timer Start Result: %s"), *GetDiscordResultString(static_cast<EDiscordReturnResult>(ResultByte))));
});
}

void UDiscordObject::StopDiscordTimer()
{
DiscordObjectInstance->bTimerStarted = false;
activity.GetTimestamps().SetEnd(FDateTime::UtcNow().ToUnixTimestamp());
core->ActivityManager().UpdateActivity(activity, [](discord::Result result)
{
uint8 ResultByte = (uint8)result;
DiscordObjectInstance->OnTimerEnd.Broadcast(static_cast<EDiscordReturnResult>(ResultByte));
LogDisplay(FString::Printf(TEXT("Timer End Result: %s"), *GetDiscordResultString(static_cast<EDiscordReturnResult>(ResultByte))));
});
}

void UDiscordObject::Internal_CreateDiscordObject(const FString& InClientID, const bool bRequireDiscordRunning, const bool bStartElapsedTimer)
{
discord::Result result = discord::Core::Create(FCString::Atoi64(*InClientID), DiscordCreateFlags_Default, &core);
discord::Result result = discord::Core::Create(FCString::Atoi64(*InClientID), bRequireDiscordRunning ? DiscordCreateFlags_Default : DiscordCreateFlags_NoRequireDiscord, &core);
DiscordObjectInstance->bCanTick = true;
LogDisplay("Discord object created.");

if (bStartElapsedTimer)
{
DiscordObjectInstance->StartDiscordTimer();
}
}

void UDiscordObject::Tick(float DeltaTime)
{
::core->RunCallbacks();
if (bCanTick)
{
::core->RunCallbacks();
}
}


#undef LogDisplay
#undef LogError
61 changes: 53 additions & 8 deletions Source/DiscordUE4/Public/DiscordObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ enum class EDiscordReturnResult : uint8
TransactionAborted = 43,
};

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnStateSet, EDiscordReturnResult, StateSetResult);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnDetailsSet, EDiscordReturnResult, DetailsSetResult);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnDiscordResult, EDiscordReturnResult, StateSetResult);

UCLASS(NotBlueprintable, BlueprintType)
class DISCORDUE4_API UDiscordObject : public UObject, public FTickableGameObject
Expand All @@ -68,26 +67,55 @@ class DISCORDUE4_API UDiscordObject : public UObject, public FTickableGameObject

static UDiscordObject* DiscordObjectInstance;

uint8 bCanTick : 1;
uint8 bTimerStarted : 1;

public:

void BeginDestroy() override;
UDiscordObject();

UPROPERTY(BlueprintAssignable, Category = "Discord|Delegates")
FOnDiscordResult OnStateSet;

UPROPERTY(BlueprintAssignable, Category = "Discord|Delegates")
FOnDiscordResult OnDetailsSet;

UPROPERTY(BlueprintAssignable, Category = "Discord|Delegates")
FOnStateSet OnStateSet;
FOnDiscordResult OnLargeImageSet;

UPROPERTY(BlueprintAssignable, Category = "Discord|Delegates")
FOnStateSet OnDetailsSet;
FOnDiscordResult OnTimerStart;

UPROPERTY(BlueprintAssignable, Category = "Discord|Delegates")
FOnDiscordResult OnTimerEnd;

/**
* public static UDiscordObject::GetOrCreateDiscordObject
* Creates or get Discord Object Instance.
* @See Make sure you setup your app as per this documentation https://discord.com/developers/docs/game-sdk/sdk-starter-guide
* @param InClientID [FString] The client ID of your application after creating it in https://discord.com/developers/
* @param bRequireDiscordRunning [const bool] If false, the game will close, Discord will re-open, and will try and relaunch your game. IMPORTANT NOTE: Editor will crash if this is true and discord is NOT running.
* @param bStartElapsedTimer [const bool] If true, rich presence will show elapsed time. You can manually start time stamps by calling Start/Stop Discord Timer.
* @returns [UDiscordObject*] Discord object instance.
**/
UFUNCTION(BlueprintCallable, Category = "Discord")
static UDiscordObject* GetOrCreateDiscordObject(FString InClientID, const bool bRequireDiscordRunning = false);
static UDiscordObject* GetOrCreateDiscordObject(FString InClientID, const bool bRequireDiscordRunning = false, const bool bStartElapsedTimer = true);

/**
* public static UDiscordObject::DestroyDiscordObject
* Destroys the static Discord Object Instance.
**/
UFUNCTION(BlueprintCallable, Category = "Discord")
static void DestroyDiscordObject();

/**
* public static UDiscordObject::GetDiscordResultString
* Returns a string representation of given discord result. Can be used with Discord delegates. @See OnStateSet, OnDetailsSet etc.
* @param InDiscordResult [EDiscordReturnResult] Result to check for.
* @return [const FString] Human readable string representation of the given result.
**/
UFUNCTION(BlueprintPure, Category = "Discord")
static const FString GetDiscordResultString(EDiscordReturnResult InDiscordResult);

/**
* public UDiscordObject::SetState
Expand All @@ -107,15 +135,32 @@ class DISCORDUE4_API UDiscordObject : public UObject, public FTickableGameObject
UFUNCTION(BlueprintCallable, Category = "Discord")
void SetDetails(FString InNewDetails);

UFUNCTION(BlueprintCallable, Category = "Discord")
void SetLargeImage(const FString InKeyName);

/**
* public UDiscordObject::StartDiscordTimer
* Starts elapsed timer in Rich Presence.
**/
UFUNCTION(BlueprintCallable, Category = "Discord")
void StartDiscordTimer();

/**
* public UDiscordObject::StopDiscordTimer
* Stops elapsed timer in Rich Presence.
**/
UFUNCTION(BlueprintCallable, Category = "Discord")
void StopDiscordTimer();

private:

void Internal_CreateDiscordObject(const FString& InClientID, const bool bRequireDiscordRunning);
void Internal_CreateDiscordObject(const FString& InClientID, const bool bRequireDiscordRunning, const bool bStartElapsedTimer);

public:

void Tick(float DeltaTime) override;

bool IsTickable() const override { return true; }
bool IsTickable() const override { return bCanTick; }
bool IsTickableInEditor() const override { return false; }
bool IsTickableWhenPaused() const override { return true; }
TStatId GetStatId() const override { return TStatId(); }
Expand Down

0 comments on commit 4910004

Please sign in to comment.