Feedback via Discord in UE4

I show you how to allow your players to send feedback directly to your discord.

Sept. 26, 2020, 7 p.m.

 

You can just download the class here if you're lazy. mbpfl.h and mbpfl.cpp

 

  1. Create a BlueprintFunctionLibrary class in C++ and add these includes and defines.
    #if PLATFORM_WINDOWS
    #include "Windows/WindowsPlatformMisc.h"
    #elif PLATFORM_LINUX
    #include "Linux/LinuxPlatformMisc.h"
    #elif PLATFORM_MAC
    #include "Mac/MacPlatformMisc.h"
    #endif
    
    #include "GameFramework/GameUserSettings.h"
    
    #define BUG_REPORT_WEBHOOK_URL "YOURWEBHOOKURLHERE"
    #define MYGAME_VERSION "v1.0-pre-alpha"
    #define DEFAULT_ICON_URL "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png"

     

  2. Add the following enums and structs to the header.
    UENUM(BlueprintType)
    enum class EBugReportCategory : uint8
    {
    	Map,
    	Weapon,
    	Other
    };
    
    UENUM(BlueprintType)
    enum class EBugReportPriority : uint8
    {
    	Crash,
    	High,
    	Med,
    	Low
    };
    
    USTRUCT()
    struct FDiscordMessageField
    {
    	GENERATED_BODY()
    
    	UPROPERTY()
    	FString name;
    	UPROPERTY()
    	FString value;
    	UPROPERTY()
    	bool binline;
    };
    
    USTRUCT()
    struct FDiscordMessageEmbedAuthor
    {
    	GENERATED_BODY()
    
    	UPROPERTY()
    	FString name;
    	UPROPERTY()
    	FString url;
    	UPROPERTY()
    	FString icon_url;
    
    	FDiscordMessageEmbedAuthor() : url("https://trdwll.com"), icon_url(DEFAULT_ICON_URL) {}
    };
    
    USTRUCT()
    struct FDiscordMessageEmbedFooter
    {
    	GENERATED_BODY()
    
    	UPROPERTY()
    	FString text;
    	UPROPERTY()
    	FString icon_url;
    
    	FDiscordMessageEmbedFooter() : icon_url(DEFAULT_ICON_URL) {}
    };
    
    USTRUCT()
    struct FDiscordMessageEmbed
    {
    	GENERATED_BODY()
    
    	UPROPERTY()
    	FString title;
    	UPROPERTY()
    	FString description;
    	UPROPERTY()
    	uint32 color;
    	UPROPERTY()
    	TArray<FDiscordMessageField> fields;
    	UPROPERTY()
    	FDiscordMessageEmbedAuthor author;
    	UPROPERTY()
    	FDiscordMessageEmbedFooter footer;
    	UPROPERTY()
    	FString timestamp;
    
    	FDiscordMessageEmbed() : color(720640), timestamp(FDateTime::Now().ToIso8601()) {}
    
    	uint32 GetColor(const EBugReportPriority Priority)
    	{
    		switch (Priority)
    		{
    		case EBugReportPriority::Crash: return 16711680;
    		case EBugReportPriority::High: return 12389388;
    		case EBugReportPriority::Med: return 16742912;
    		default:
    		case EBugReportPriority::Low: return 720640;
    		}
    	}
    };
    
    USTRUCT()
    struct FDiscordMessage
    {
    	GENERATED_BODY()
    
    	UPROPERTY()
    	FString username;
    	UPROPERTY()
    	FString avatar_url;
    	UPROPERTY()
    	FString content;
    	UPROPERTY()
    	TArray<FDiscordMessageEmbed> embeds;
    
    	FDiscordMessage() : avatar_url(DEFAULT_ICON_URL) {}
    };
    
    USTRUCT(BlueprintType)
    struct FBugReportUserData
    {
    	GENERATED_BODY()
    
    	UPROPERTY(BlueprintReadWrite)
    	FString Author;
    	UPROPERTY(BlueprintReadWrite)
    	FVector Location;
    	UPROPERTY(BlueprintReadWrite)
    	FString MapName;
    	UPROPERTY(BlueprintReadWrite)
    	FString Message;
    };
  3. Add the following to your header file.
    	// Thanks to Rama for this method
    	// GetEnumValueAsString<EEnumType>("EEnumType", Value);
    	template<typename TEnum>
    	static FString GetEnumValueAsString(const FString& Name, TEnum Value)
    	{
    		const UEnum* enumPtr = FindObject<UEnum>(ANY_PACKAGE, *Name, true);
    		if (!enumPtr)
    		{
    			return FString("Invalid");
    		}
    		return enumPtr->GetNameByValue((int64)Value).ToString();
    	}
    
    	UFUNCTION(BlueprintCallable, Category = "Discord")
    	static void SubmitBugReport(const EBugReportCategory Category, const EBugReportPriority Priority, const FBugReportUserData& AuthorData, FString& ReportID, bool bIncludeSpecs = true);
    
    	/**
    	 * Gets the number of RAM on the system
    	 */
    	UFUNCTION(BlueprintPure)
    	static FORCEINLINE int32 GetTotalRAM()
    	{
    		const FPlatformMemoryConstants& MemoryConstants = FPlatformMemory::GetConstants();
    		return MemoryConstants.TotalPhysicalGB;
    	}
    
    	UFUNCTION(BlueprintPure)
    	static FString GetGameResolution() { return *FString::Printf(TEXT("%dx%d"), UGameUserSettings::GetGameUserSettings()->GetScreenResolution().X, UGameUserSettings::GetGameUserSettings()->GetScreenResolution().Y); }
    
    	/**
    	 * Gets the CPU Brand Name information.
    	 */
    	UFUNCTION(BlueprintPure)
    	static FString GetCPUBrandName()
    	{
    #if PLATFORM_WINDOWS
    		return FWindowsPlatformMisc::GetCPUBrand();
    #elif PLATFORM_LINUX
    		return FLinuxPlatformMisc::GetCPUBrand();
    #elif PLATFORM_MAC
    		return FMacPlatformMisc::GetCPUBrand();
    #endif
    	}
    
    	/**
    	 * Gets the CPU Vendor Name information.
    	 */
    	UFUNCTION(BlueprintPure)
    	static FString GetCPUVendorName()
    	{
    #if PLATFORM_WINDOWS
    		return FWindowsPlatformMisc::GetCPUVendor();
    #elif PLATFORM_LINUX
    		return FLinuxPlatformMisc::GetCPUVendor();
    #elif PLATFORM_MAC
    		return FMacPlatformMisc::GetCPUVendor();
    #endif
    	}
    
    	/**
    	 * Gets the GPU Brand Name information.
    	 */
    	UFUNCTION(BlueprintPure)
    	static FString GetGPUBrandName()
    	{
    #if PLATFORM_WINDOWS
    		return FWindowsPlatformMisc::GetPrimaryGPUBrand();
    #elif PLATFORM_LINUX
    		return FLinuxPlatformMisc::GetPrimaryGPUBrand();
    #elif PLATFORM_MAC
    		return FMacPlatformMisc::GetPrimaryGPUBrand();
    #endif
    	}
    
    	/**
    	 * Gets the GPU Driver information.
    	 */
    	UFUNCTION(BlueprintPure)
    	static FORCEINLINE FString GetGPUDriverInfo() { return GRHIAdapterUserDriverVersion; }

     

  4. In your BPFL source file add includes.

  5. #include "Http.h"
    #include "Interfaces/IHttpRequest.h"
    #include "JsonObjectConverter.h"
  6. The implementation for SubmitBugReport.
    void UMBPFL::SubmitBugReport(const EBugReportCategory Category, const EBugReportPriority Priority, const FBugReportUserData& AuthorData, FString& ReportID, bool bIncludeSpecs)
    {
    	// Just some quick checks to prevent spamming
    	if (AuthorData.Author == "" || AuthorData.MapName == "" || AuthorData.Message == "")
    	{
    		return;
    	}
    
    	if (FHttpModule* http = &FHttpModule::Get())
    	{
    		TSharedRef<IHttpRequest> request = http->CreateRequest();
    		request->SetVerb("POST");
    		request->SetHeader("Content-Type", "application/json");
    		request->SetURL(BUG_REPORT_WEBHOOK_URL);
    
    		ReportID = FGuid::NewGuid().ToString();
    
    		// Create the embed for the report
    		FDiscordMessageEmbed embed;
    		embed.title = GetEnumValueAsString<EBugReportCategory>("EBugReportCategory", Category);
    		embed.author.icon_url = "";
    		embed.author.name = AuthorData.Author;
    		embed.description = AuthorData.Message;
    		embed.color = embed.GetColor(Priority);
    		embed.fields.Add({ "Priority", GetEnumValueAsString<EBugReportPriority>("EBugReportPriority", Priority) });
    		embed.fields.Add({ "Location", AuthorData.Location.ToString(), true });
    		embed.fields.Add({ "Map", AuthorData.MapName, true });
    		embed.footer.text = FString::Printf(TEXT("MYGame %s | %s"), *FString(MYGAME_VERSION), *ReportID);
    
    		FDiscordMessage data;
    		data.username = "Bug Reporter";
    		data.embeds.Add(embed);
    
    		// Don't show "private" information unless the player wants to
    		if (bIncludeSpecs)
    		{
    			// Create another embed for the users pc specs
    			FDiscordMessageEmbed embed2;
    			embed2.title = FString::Printf(TEXT("%s PC Specs"), *AuthorData.Author);
    			embed2.color = embed.GetColor(Priority);
    			embed2.fields.Add({ "Resolution", GetGameResolution(), true });
    			embed2.fields.Add({ "RAM", FString::Printf(TEXT("%dGB"), GetTotalRAM()), true });
    			embed2.fields.Add({ "CPU", GetCPUBrandName(), true });
    			embed2.fields.Add({ "GPU", GetGPUBrandName(), true });
    			embed2.fields.Add({ "GPU Driver", GetGPUDriverInfo(), true });
    			data.embeds.Add(embed2);
    		}
    
    		FString OutputString;
    		FJsonObjectConverter::UStructToJsonObjectString(data, OutputString);
    		OutputString = OutputString.Replace(TEXT("binline"), TEXT("inline"));
    
    		request->SetContentAsString(OutputString);
    		request->ProcessRequest();
    	}
    }
  7. Add "Http", "Json", "JsonUtilities", "RHI" to your Project.Build.cs under PublicDependencyModuleNames.
  8. Head on over to your discord server Server Settings -> Integrations -> Create Webhook. Then replace the URL for BUG_REPORT_WEBHOOK_URL with the one it gives you.
 
 
until next time