Feedback via Discord in UE4

Sept. 26, 2020, 7 p.m.

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


 

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