Compare commits

...

7 commits

33 changed files with 1714 additions and 6 deletions

View file

@ -5,7 +5,7 @@ DefaultGraphicsPerformance=Maximum
AppliedDefaultGraphicsPerformance=Maximum
[/Script/EngineSettings.GameMapsSettings]
EditorStartupMap=/Game/0_Project/Maps/TestTile.TestTile
EditorStartupMap=/Game/0_Project/Maps/MainMap.MainMap
LocalMapOptions=
TransitionMap=None
bUseSplitscreen=True
@ -14,7 +14,7 @@ ThreePlayerSplitscreenLayout=FavorTop
FourPlayerSplitscreenLayout=Grid
bOffsetPlayerGamepadIds=False
GameInstanceClass=/Game/0_Project/Core/Engine/MyGameInstance.MyGameInstance_C
GameDefaultMap=/Game/0_Project/Maps/TestTile.TestTile
GameDefaultMap=/Game/0_Project/Maps/MainMap.MainMap
ServerDefaultMap=/Engine/Maps/Entry.Entry
GlobalDefaultGameMode=/Game/0_Project/Core/Engine/My_GameMode.My_GameMode_C
GlobalDefaultServerGameMode=None
@ -31,13 +31,39 @@ r.DefaultFeature.AutoExposure=False
[/Script/NavigationSystem.RecastNavMesh]
bDoFullyAsyncNavDataGathering=False
RuntimeGeneration=Dynamic
DrawOffset=10.000000
DrawOffset=1.000000
bFixedTilePoolSize=False
bSortNavigationAreasByCost=False
CellSize=5.000000
CellHeight=2.000000
AgentRadius=10.000000
AgentHeight=12.000000
bForceRebuildOnLoad=True
bAutoDestroyWhenNoNavigation=False
ObservedPathsTickInterval=0.100000
TilePoolSize=2048
AgentMaxSlope=14.841908
AgentMaxStepHeight=5.000000
MinRegionArea=9.000000
MergeRegionSize=50.000000
bDrawPolyEdges=True
MaxSimplificationError=1.500000
TileSizeUU=300.000000
[/Script/NavigationSystem.NavigationSystemV1]
bSpawnNavDataInNavBoundsLevel=False
bGenerateNavigationOnlyAroundNavigationInvokers=False
DirtyAreasUpdateFreq=2.000000
DefaultAgentName=None
CrowdManagerClass=/Script/AIModule.CrowdManager
bAutoCreateNavigationData=True
bSpawnNavDataInNavBoundsLevel=True
bAllowClientSideNavigation=False
bShouldDiscardSubLevelNavData=True
bTickWhilePaused=True
bInitialBuildingLocked=False
bSkipAgentHeightCheckWhenPickingNavData=False
DataGatheringMode=Instant
bGenerateNavigationOnlyAroundNavigationInvokers=False
ActiveTilesUpdateInterval=1.000000
+SupportedAgents=(Name="Default",Color=(B=255,G=251,R=181,A=164),DefaultQueryExtent=(X=50.000000,Y=50.000000,Z=250.000000),NavDataClass=/Script/NavigationSystem.RecastNavMesh,AgentRadius=10.000000,AgentHeight=12.000000,AgentStepHeight=-1.000000,NavWalkingSearchHeightScale=0.500000,PreferredNavData=None,bCanCrouch=False,bCanJump=False,bCanWalk=True,bCanSwim=False,bCanFly=False)
SupportedAgentsMask=(bSupportsAgent0=True,bSupportsAgent1=True,bSupportsAgent2=True,bSupportsAgent3=True,bSupportsAgent4=True,bSupportsAgent5=True,bSupportsAgent6=True,bSupportsAgent7=True,bSupportsAgent8=True,bSupportsAgent9=True,bSupportsAgent10=True,bSupportsAgent11=True,bSupportsAgent12=True,bSupportsAgent13=True,bSupportsAgent14=True,bSupportsAgent15=True)
DirtyAreasUpdateFreq=0.100000

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,7 @@
{
"BuildId": "13144385",
"Modules":
{
"GameJoltPlugin": "UE4Editor-GameJoltPlugin.dll"
}
}

View file

@ -0,0 +1,23 @@
{
"FileVersion": 3,
"Version": 14,
"VersionName": "1.8",
"FriendlyName": "GameJolt Plugin",
"Description": " This plugin allows you to communicate with the GameJolt-Servers to use Scoreboards, Sessions, Cloud-Data-Storage and more.",
"Category": "GameJolt",
"CreatedBy": "FreezerNick",
"CreatedByURL": "https://gamejolt.com/@FreezerNick",
"DocsURL": "https://gitlab.com/f2p-entertainment/plugins/ue4/gj/ue4-gamejoltapi/-/wikis/home",
"MarketplaceURL": "",
"SupportURL": "mailto:incoming+f2p-entertainment-plugins-ue4-gj-ue4-gamejoltapi-5318268-issue-@incoming.gitlab.com",
"EngineVersion": "4.25.0",
"CanContainContent": false,
"Installed": true,
"Modules": [
{
"Name": "GameJoltPlugin",
"Type": "Runtime",
"LoadingPhase": "Default"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View file

@ -0,0 +1,717 @@
#pragma once
#include "CoreMinimal.h"
#include "Interfaces/IHttpRequest.h"
#include "Serialization/JsonSerializer.h"
#include "Serialization/JsonWriter.h"
#include "Dom/JsonValue.h"
#include "UEGameJoltAPI.generated.h"
/* Represents all possible requests */
UENUM(BlueprintType)
enum class EGameJoltComponentEnum : uint8
{
GJ_USER_AUTH UMETA(DisplayName = "Authorize User"),
GJ_USER_AUTOLOGIN UMETA(DisplayName = "Automatic Login"),
GJ_USER_FETCH UMETA(DisplayName = "Fetch Current User"),
GJ_USERS_FETCH UMETA(DisplayName = "Fetch Users"),
GJ_USER_FRIENDLIST UMETA(DisplayName = "Fetch Friendlist"),
GJ_SESSION_OPEN UMETA(DisplayName = "Open Session"),
GJ_SESSION_PING UMETA(DisplayName = "Ping Session"),
GJ_SESSION_CLOSE UMETA(DisplayName = "Close Session"),
GJ_SESSION_CHECK UMETA(DisplayName = "Check Session"),
GJ_TROPHIES_FETCH UMETA(DisplayName = "Fetch Trophies"),
GJ_TROPHIES_ADD UMETA(DisplayName = "Reward Trophy"),
GJ_TROHIES_REMOVE UMETA(DisplayName = "Remove Rewarded Trophy"),
GJ_SCORES_FETCH UMETA(DisplayName = "Fetch Scores"),
GJ_SCORES_ADD UMETA(DisplayName = "Add Score"),
GJ_SCORES_TABLE UMETA(DisplayName = "Fetch Tables"),
GJ_SCORES_RANK UMETA(DisplayName = "Fetch Rank of Highscore"),
GJ_DATASTORE_FETCH UMETA(DisplayName = "Fetch Data"),
GJ_DATASTORE_SET UMETA(DisplayName = "Set Data"),
GJ_DATASTORE_UPDATE UMETA(DisplayName = "Update Data"),
GJ_DATASTORE_REMOVE UMETA(DisplayName = "Fetch Keys"),
GJ_OTHER UMETA(DisplayName = "Other"),
GJ_TIME UMETA(DisplayName = "Fetch Server Time")
};
/* Represents the possible selections for "Fetch Trophies" (all, achieved, unachieved) */
UENUM(BlueprintType)
enum class EGameJoltAchievedTrophies : uint8
{
GJ_ACHIEVEDTROPHY_BLANK UMETA(DisplayName = "All Trophies"),
GJ_ACHIEVEDTROPHY_USER UMETA(DisplayName = "User Achieved Trophies"),
GJ_ACHIEVEDTROPHY_GAME UMETA(DisplayName = "Unachieved Trophies")
};
/** Represents the possible values for the status of a session
* https://gamejolt.com/game-api/doc/sessions/ping
*/
UENUM(BlueprintType)
enum class ESessionStatus : uint8
{
Active,
Idle
};
UENUM(BlueprintType)
enum class EDataStore : uint8
{
Global,
User
};
UENUM(BlueprintType)
enum class EDataOperation : uint8
{
add UMETA(DisplayName = "Add"),
substract UMETA(DisplayName = "Substract"),
multiply UMETA(DisplayName = "Multiply"),
divide UMETA(DisplayName = "Divide"),
append UMETA(DisplayName = "Append"),
prepend UMETA(DisplayName = "Prepend")
};
/* Contains all available information about a user */
USTRUCT(BlueprintType)
struct FUserInfo
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "User ID")
int32 S_User_ID;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "User type")
FString User_Type;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Username")
FString User_Name;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "User Avatar")
FString User_AvatarURL;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "User Signed up")
FString Signed_up;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "User Last Logged in")
FString Last_Logged_in;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "User Status")
FString status;
};
/* Contains all information about a trophy */
USTRUCT(BlueprintType)
struct FTrophyInfo
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trophy ID")
int32 Trophy_ID;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trophy's Name")
FString Name;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trophy's Description")
FString Description;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trophy's Difficulty")
FString Difficulty;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trophy's Image URL")
FString image_url;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Achieved Time")
FString achieved;
};
/* Contains all information about an entry in a scoreboard */
USTRUCT(BlueprintType)
struct FScoreInfo
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString ScoreString;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 ScoreSort;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString ExtraData;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString UserName;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 UserID;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString Guest;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString UnixTimestamp;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
struct FDateTime TimeStamp;
FScoreInfo()
{
TimeStamp = FDateTime::Now();
ScoreSort = 0;
UserID = 0;
}
};
/* Contains all information about a scoreboard */
USTRUCT(BlueprintType)
struct FScoreTableInfo
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Scoreboard Table ID")
int32 Id;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Scoreboard Table Name")
FString Name;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Scoreboard Table Description")
FString Description;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Scoreboard Table Primary")
FString Primary;
};
/* Generates a delegate for the OnGetResult event */
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnGetResult);
/* Generates a delegate for the OnFailed event */
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnFailed);
#pragma region Specific Delegate Declaration
/* Authorize User */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnUserAuthorized, bool, bIsLoggedIn);
/* Automatic Login */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAutoLogin, bool, bIsLoggedIn);
/* Get Current User Info */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnUserFetched, FUserInfo, CurrentUserInfo);
/* Get User Info*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnUsersFetched, const TArray<FUserInfo>&, UserInfo);
/* Get Friendlist */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFriendlistFetched, const TArray<int32>&, Friendlist);
/* Open Session */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSessionOpened, bool, bIsSessionOpen);
/* Ping Session */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSessionPinged, bool, bIsSessionStillOpen);
/* Close Session */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSessionClosed, bool, bIsSessionClosed);
/* Check Session */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSessionChecked, bool, bIsSessionStillOpen);
/* Fetch Trophies */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTrophiesFetched, TArray<FTrophyInfo>, Trophies);
/* Remove Trophy */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTrophyRemoved, bool, bWasRemoved);
/* Add Score */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnScoreAdded, bool, bWasScoreAdded);
/* Fetch Scoreboard */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnScoreboardFetched, const TArray<FScoreInfo>&, Scores);
/* Fetch Scoreboard Table */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnScoreboardTableFetched, TArray<FScoreTableInfo>, ScoreboardTable);
/* Fetch High-Score Rank */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRankFetched, int32, Rank);
/* Fetch Time */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTimeFetched, struct FDateTime, ServerTime);
#pragma endregion
/**
* Class to use the GameJoltAPI
* Is also internally used by an UUEGameJoltAPI instance as a carrier for response data
*/
UCLASS(BlueprintType, Blueprintable)
class GAMEJOLTPLUGIN_API UUEGameJoltAPI : public UObject
{
GENERATED_UCLASS_BODY()
private:
/**
* Callback for IHttpRequest::OnProcessRequestComplete()
* @param Request HTTP request pointer
* @param Response Response pointer
* @param bWasSuccessful Whether the request was successful or not
*/
void OnReady(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
/* Reset Data*/
void Reset();
void WriteObject(TSharedRef<TJsonWriter<TCHAR>> writer, FString key, FJsonValue* value);
public:
UObject* contextObject;
/* Prevents crashes in Get-Functions */
UPROPERTY(Transient)
class UWorld* World;
/* Allows usage of the World-Property */
virtual class UWorld* GetWorld() const override;
/* The username of the guest profile */
UPROPERTY(BlueprintReadWrite, Category = "GameJolt|User")
FString Guest_username;
/* Whether a user is currently logged in. Treated as a guest if false */
UPROPERTY(BlueprintReadOnly, Category = "GameJolt|User")
bool bIsLoggedIn;
/* An enum representing the last request send. Local 'Get' nodes don't count */
UPROPERTY(BlueprintReadWrite, Category = "GameJolt")
EGameJoltComponentEnum LastActionPerformed;
/* The actual field data */
TSharedPtr<FJsonObject> Data;
/* Contains the actual page content, as a string */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "GameJolt|Request")
FString Content;
/* Event which triggers when the content has been retrieved */
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events")
FOnGetResult OnGetResult;
/* Event which triggers when the request failed */
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events")
FOnFailed OnFailed;
#pragma region Specific Events
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnUserAuthorized OnUserAuthorized;
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnAutoLogin OnAutoLogin;
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnUserFetched OnUserFetched;
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnUsersFetched OnUsersFetched;
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnFriendlistFetched OnFriendlistFetched;
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnSessionOpened OnSessionOpened;
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnSessionPinged OnSessionPinged;
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnSessionClosed OnSessionClosed;
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnSessionChecked OnSessionChecked;
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnTrophiesFetched OnTrophiesFetched;
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnTrophyRemoved OnTrophyRemoved;
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnScoreAdded OnScoreAdded;
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnScoreboardFetched OnScoreboardFetched;
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnScoreboardTableFetched OnScoreboardTableFetched;
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnRankFetched OnRankFetched;
UPROPERTY(BlueprintAssignable, Category = "GameJolt|Events|Specific")
FOnTimeFetched OnTimeFetched;
#pragma endregion
/* Creates new data from the input string */
UFUNCTION(BlueprintCallable, meta = (DisplayName = "From String"), Category = "GameJolt|Request")
void FromString(const FString& dataString);
/**
* Creates a new instance of the UUEGameJoltAPI class, for use in Blueprint graphs.
* @param WorldContextObject The current context (default to self / this)
* @return A pointer to the newly created post data
**/
UFUNCTION(BlueprintPure, meta = (DisplayName = "Create GameJolt API Data", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "GameJolt")
static UUEGameJoltAPI* Create(UObject* WorldContextObject);
/* GameID */
UPROPERTY(BlueprintReadOnly, meta = (DisplayName = "Your Game ID"), Category = "GameJolt")
int32 Game_ID;
/* Private Key */
UPROPERTY(BlueprintReadOnly, meta = (DisplayName = "Your Game Private Key"), Category = "GameJolt")
FString Game_PrivateKey;
/* Username */
UPROPERTY(BlueprintReadOnly, meta = (DisplayName = "Players Username"), Category = "GameJolt|User")
FString UserName;
private:
/* Token */
UPROPERTY()
FString UserToken;
public:
/* Properties for HTTP-Request*/
UPROPERTY(BlueprintReadWrite, meta = (DisplayName = "GameJolt API Server"), Category = "GameJolt|Request")
FString GJAPI_SERVER;
UPROPERTY(BlueprintReadWrite, meta = (DisplayName = "GameJolt API Root"), Category = "GameJolt|Request")
FString GJAPI_ROOT;
UPROPERTY(BlueprintReadWrite, meta = (DisplayName = "GameJolt API Version"), Category = "GameJolt|Request")
FString GJAPI_VERSION;
/* End of Properties */
/* Public Functions */
/**
* Sets information needed for all requests
* You can find these values in the GameJolt API section of your game's dashboard
* @param PrivateKey The private key of your game
* @param GameID The id of your game
* @param AutoLogin Whether to check for passed credentials by the GameJolt client or not
* @return Whether the .gj-crendential file was found or not. Also false if AutoLogin is false
**/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Init", AdvancedDisplay=2), Category = "GameJolt")
bool Init(const int32 GameID, const FString PrivateKey, const bool AutoLogin);
private:
void AutoLogin(const FString Username, const FString Token);
#pragma region Session
/**
* Opens a session. You'll have to ping it manually with a timer
* @return True if the request succeded, false if not
**/
UFUNCTION(BlueprintCallable, meta = (DislayName = "Open Session"), Category = "GameJolt|Sessions")
bool OpenSession();
/**
* Pings the Session. Every 30 to 60 seconds is good.
* @param SessionStatus The status of the session. Can be "Active" or "Idle"
* @return True if the request succeded, false if not
**/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Ping Session"), Category = "GameJolt|Sessions")
bool PingSession(ESessionStatus SessionStatus);
/**
* Closes the session
* @return True if the request succeded, false if not
**/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Close Session"), Category = "GameJolt|Sessions")
bool CloseSession();
/**
* Fetches the current session status
* @return True if the request succeded, false if not
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Fetch Session Status"), Category = "GameJolt|Sessions")
bool CheckSession();
/**
* Gets the current session status
* @return Whether the session is open or not. Also false if any error occurred
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Get Session Status"), Category = "GameJolt|Sessions")
bool GetSessionStatus();
#pragma endregion
/**
* Gets the time of the GameJolt servers
* @return True if the request succeded, false if not
**/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Fetch Server Time"), Category = "GameJolt|Misc")
bool FetchServerTime();
/**
* Puts the requested server time in a readable format
* UUEGameJoltAPI::FetchServerTime has to be called before this function
* @return The server time in a FDateTime struct
*/
UFUNCTION(BlueprintPure, meta = (DisplayName = "Read Server Time"), Category = "GameJolt|Misc")
struct FDateTime ReadServerTime();
#pragma region User
/**
* Sends a request to authentificate the user
* Call UUEGameJoltAPI::isUserAuthorize / Is User Login to check whether the authorization was successful or not
* @param Name The username - case insensitive
* @param Token The token - case insensitive
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Login"), Category = "GameJolt|User")
void Login(const FString Name, const FString Token);
/**
* Checks if the authentification was succesful
* @return True if the user could be logged in, false if not
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Is User Login"), Category = "GameJolt|User")
bool isUserAuthorize();
/* Resets user related properties */
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Logoff User"), Category = "GameJolt|User")
void LogOffUser();
/**
* Gets information about the current user
* @return True if it the request succeded and false if it failed
**/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Fetch Current User Info"), Category = "GameJolt|User")
bool FetchUser();
/**
* Fetches an array of users
* @param Users An array (int32) representing the user ids
* @return True if the request succeded, false if not
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Fetch Users"), Category = "GameJolt|User")
bool FetchUsers(const TArray<int32> Users);
/**
* Gets a single or an array of users and puts them in an array of FUserInfo structs
* @return An array with the FUserInfo structs
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Get User Info"), Category = "GameJolt|User")
TArray<FUserInfo> GetUserInfo();
/**
* Fetches the friendlist of the current user
* @return True if the request could be send, false if not
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Fetch Friendlist"), Category = "GameJolt|User")
bool FetchFriendlist();
/**
* Returns the fetched friendlist
* @warning Call FetchFriendlist first
* @return The user ids of all friends
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Get Friendlist"), Category = "GameJolt|User")
TArray<int32> GetFriendlist();
#pragma endregion
#pragma region Trophies
/**
* Awards the current user a trophy
* @return True if the request succeded, false if not
**/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Reward Trophies"), Category = "GameJolt|Trophies")
bool RewardTrophy(const int32 Trophy_ID);
/**
* Gets information for all trophies
* This is meant for the use in Blueprints
* It's just a wrapper around FetchTrophies with an empty TArray as an parameter
* You can call UUEGameJoltAPI::FetchTrophies directly
**/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Fetch All Trophies"), Category = "GameJolt|Trophies")
void FetchAllTrophies(const EGameJoltAchievedTrophies AchievedType);
/**
* Gets information for the selected trophies
* @param AchievedType Whether only achieved, unachieved or all trophies should be fetched
* @param Tropies_ID An array of trophy IDs. An empty array will return all trophies
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Fetch Trophies"), Category = "GameJolt|Trophies")
void FetchTrophies(const EGameJoltAchievedTrophies AchievedType, const TArray<int32> Trophy_IDs);
/**
* Gets the trophy information from the fetched trophies
* @return Array of FTrophyInfo structs for all fetched trophies
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Get Trophies"), Category = "GameJolt|Trophies")
TArray<FTrophyInfo> GetTrophies();
/**
* Unachieved the specified trophy for the curernt user
* @param Trophy_ID The ID of the trophy to be unachieved
* @return Whether the request could be send or not
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Remove Rewarded Trophy"), Category = "GameJolt|Trophies")
bool RemoveRewardedTrophy(const int32 Trophy_ID);
/**
* Checks the success of a trophy removal
* @return Whether the trophy was successfuly remove or not
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Check Trophy Removal Status"), Category = "GameJolt|Trophies")
bool GetTrophyRemovalStatus();
#pragma endregion
#pragma region Scores
/**
* Returns a list of scores either for a user or globally for a game
* @param ScoreLimit The amount of scores you want to fetch. Default is 10, maximum is 100
* @param Table_id The ID of the score table
* @param BetterThan Fetch only scores better than this score sort value
* @param WorseThan Fetch only scores worse than this score sort value
* @return True if the request succeded, false if not
**/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Fetch Scoreboard"), Category = "GameJolt|Scoreboard")
bool FetchScoreboard(const int32 ScoreLimit, const int32 Table_id, const int32 BetterThan, const int32 WorseThan);
/**
* Gets the list of scores fetched with FetchScoreboard
* @return An array of FScoreInfo structs for all entries
**/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Get Scoreboard"), Category = "GameJolt|Scoreboard")
TArray<FScoreInfo> GetScoreboard();
/**
* Adds an entry to a scoreboard
* @param UserScore A String value associated with the score. Example: "234 Jumps".
* @param UserScore_Sort An integer sorting value associated with the score. All sorting will work off of this number. Example: "234".
* @param GuestUser The guest's name. Leave blank if you're storing for a user.
* @param extra_data If there's any extra data you would like to store (as a string), you can use this variable. This data is never shown to the user.
* @param table_id The id of the high score table that you want to submit to. If left blank the score will be submitted to the primary high score table.
* @return True if the request succeded, false if not
**/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Add Score to Scoreboard"), Category = "GameJolt|Scoreboard")
bool AddScore(const FString UserScore, const int32 UserScore_Sort, const FString GuestUser, const FString extra_data, const int32 table_id);
/**
* Returns a list of high score tables for a game.
* @return True if it the request succeded and false if it failed
**/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Fetch Scoreboard Table"), Category = "GameJolt|Scoreboard")
bool FetchScoreboardTable();
/**
* Gets a list of high score tables for a game and puts them in an array of FScoreTableInfo structs
* @return A array of FScoreTableInfo structs
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Get Scoreboard Table"), Category = "GameJolt|Scoreboard")
TArray<FScoreTableInfo> GetScoreboardTable();
/**
* Fetches the rank of the specified score
* Use "Get Rank of Score" / GetRank or the OnGetRank delegate to read the results
* @param Score The numeric score value to look for
* @param TableID The ID of the scoreboard to search. '0' means primary table
* @return Whether the request could be send successfully or not
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Fetch Rank of Score"), Category = "GameJolt|Scoreboard")
bool FetchRank(const int32 Score, const int32 TableID);
/**
* Gets the rank of a highscore from the response data
*
* If the score is not represented by any rank on the score table, the request will return the rank that is closest to the requested score.
*
* @warning Make sure to call "Fetch Rank of Score" / FetchRank before this
* @return The rank of the score
*/
UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Rank of Score"), Category = "GameJolt|Scoreboard")
int32 GetRank();
#pragma endregion
#pragma region Data-Store
/**
* Either posts data for a new key or changes data for an existing one.
* @param Type Whether to store the key/value pair for all users (global) or for the current user (user)
* @param Key The key/label for the data
* @param Data The actual data to store
*/
UFUNCTION(BlueprintCallable)
void SetData(EDataStore Type, const FString Key, const FString Data);
/**
* Tries to fetch the data stored under the specified key
* @param Type Whether to fetch a global key/value pair or a key/value pair stored for the current user
* @param Key The key/label of the data
*/
UFUNCTION(BlueprintCallable)
void FetchData(EDataStore Type, FString Key);
/**
* Updates already stored data
* @param Type Whether to update a global key/value pair or a key/value pair stored of the current user
* @param Key The key of the data to update
* @param Operation The operation that should be performed on the data
* @param Value The value for the selected operation
*/
UFUNCTION(BlueprintCallable)
void UpdateData(EDataStore Type, const FString Key, EDataOperation Operation, const FString Value);
/**
* Deletes the data stored under the specified key
* @param Type Whether to remove a global key/value pair or a key/value pair stored for the current user
* @param Key The key of the data to remove
*/
UFUNCTION(BlueprintCallable)
void RemoveData(EDataStore Type, const FString Key);
/**
* Gets the fetched data and converts them to a string or an integer (if possible)
* @param Success Whether the data was found
* @param DataAsString The fetched data as a string
* @param DataAsInt The fetched data as an integer (0 if conversion was not possible)
*/
UFUNCTION(BlueprintCallable)
void GetData(bool& Success, FString& DataAsString, int32& DataAsInt);
#pragma endregion
#pragma region Utility
/* Sends Request */
UFUNCTION(Blueprintcallable, meta = (Displayname = " Send Request"), Category = "GameJolt|Request|Advanced")
bool SendRequest(const FString& output, FString url, bool bAppendUserInfo = true);
/** Gets nested post data from the object with the specified key
* @param key The key of the post data value
* @return The value as an UUEGameJoltAPI object reference / pointer
*/
UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Data Field", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "GameJolt|Request|Advanced")
UUEGameJoltAPI* GetObject(const FString& key);
/** Gets a string from the object with the specified key
* @param key The key of the string value
* @return The value as a string
*/
UFUNCTION(BlueprintPure, meta = (DisplayName = "Get String Field"), Category = "GameJolt|Request|Advanced")
FString GetString(const FString& key) const;
/** Gets a bool from the object with the specified key
* @param key The key of the bool value
* @return The value as a bool
*/
UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Bool Field"), Category = "GameJolt|Request|Advanced")
bool GetBool(const FString& key) const;
/** Gets an integer from the object with the specified key
* @param key The key of the integer value
* @return The value as an integer
*/
UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Int Field"), Category = "GameJolt|Request|Advanced")
int32 GetInt(const FString& key) const;
/**
* Gets a string array of all keys from the post data
* @return An array with all keys
*/
UFUNCTION(Blueprintpure, meta = (Displayname = "Get Object Keys", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "GameJolt|Request|Advanced")
TArray<FString> GetObjectKeys(UObject* WorldContextObject);
/**
* Gets an array fromt the post data
* @param key The key of the array
* @return The array assigned to the key
**/
UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Object Array Field", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "GameJolt|Request|Advanced")
TArray<UUEGameJoltAPI*> GetObjectArray(UObject* WorldContextObject, const FString& key);
#pragma endregion
};

View file

@ -0,0 +1,40 @@
using UnrealBuildTool;
using System.IO;
namespace UnrealBuildTool.Rules
{
public class GameJoltPlugin : ModuleRules
{
public GameJoltPlugin(ReadOnlyTargetRules Target) : base (Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Classes"));
PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "Private"));
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"HTTP",
"CoreUObject",
"Engine",
"Json",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Engine",
"Core",
"CoreUObject",
"HTTP",
"JSON",
}
);
}
}
}

View file

@ -0,0 +1,16 @@
#include "GameJoltPluginModule.h"
DEFINE_LOG_CATEGORY(GJAPI);
void GameJoltPlugin::StartupModule()
{
}
void GameJoltPlugin::ShutdownModule()
{
}
IMPLEMENT_MODULE(FDefaultGameModuleImpl, GameJoltPlugin);

View file

@ -0,0 +1,17 @@
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
DECLARE_LOG_CATEGORY_EXTERN(GJAPI, Log, All);
class GAMEJOLTPLUGIN_API GameJoltPlugin : public IModuleInterface
{
private:
public:
GameJoltPlugin();
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View file

@ -0,0 +1,862 @@
#include "UEGameJoltAPI.h"
#include "Engine/Engine.h"
#include "HttpModule.h"
#include "Interfaces/IHttpResponse.h"
#include "Dom/JsonObject.h"
#include "Serialization/JsonReader.h"
#include "GameJoltPluginModule.h"
#include "Misc/DateTime.h"
#include "Engine/World.h"
#include "Misc/Paths.h"
#include "Misc/FileHelper.h"
/* Constructor */
UUEGameJoltAPI::UUEGameJoltAPI(const class FObjectInitializer& PCIP) : Super(PCIP)
{
Reset();
bIsLoggedIn = false;
GJAPI_SERVER = "api.gamejolt.com";
GJAPI_ROOT = "/api/game/";
GJAPI_VERSION = "v1_2";
Game_ID = 0;
Game_PrivateKey = "";
LastActionPerformed = EGameJoltComponentEnum::GJ_USER_AUTH;
}
/* Prevents crashes within 'Get...' functions */
UWorld* UUEGameJoltAPI::GetWorld() const
{
return World;
}
/* Sets information needed for all requests */
bool UUEGameJoltAPI::Init(const int32 GameID, const FString PrivateKey, const bool AutoLogin = false)
{
Game_ID = GameID;
Game_PrivateKey = PrivateKey;
if(!AutoLogin)
{
UE_LOG(GJAPI, Log, TEXT("Autologin is turned off!"));
return false;
}
if(!FPaths::FileExists(FPaths::Combine(FPaths::ProjectDir(), TEXT(".gj-credentials"))))
return false;
TArray<FString> strings;
FFileHelper::LoadFileToStringArray(strings, *FPaths::Combine(FPaths::ProjectDir(), TEXT(".gj-credentials")));
this->AutoLogin(strings[1], strings[2]);
return true;
}
void UUEGameJoltAPI::AutoLogin(const FString Name, const FString Token)
{
FString output;
UserName = Name;
UserToken = Token;
LastActionPerformed = EGameJoltComponentEnum::GJ_USER_AUTOLOGIN;
SendRequest(output, "/users/auth/?");
}
/* Gets the time of the GameJolt servers */
bool UUEGameJoltAPI::FetchServerTime()
{
FString output;
LastActionPerformed = EGameJoltComponentEnum::GJ_TIME;
return SendRequest(output, "/time/?", false);
}
/* Puts the requested server time in a readable format */
FDateTime UUEGameJoltAPI::ReadServerTime()
{
UUEGameJoltAPI* responseField = NULL;
responseField = GetObject("response");
if (responseField == NULL)
{
UE_LOG(GJAPI, Error, TEXT("responseField Return Null"));
return FDateTime();
}
if(!responseField->GetBool("success"))
{
UE_LOG(GJAPI, Error, TEXT("Can't read time: Request failed!"));
if(responseField->GetString("message") != "")
{
UE_LOG(GJAPI, Error, TEXT("Error message: %s"), *responseField->GetString("message"));
}
return FDateTime();
}
int32 Year = responseField->GetInt("year");
int32 Month = responseField->GetInt("month");
int32 Day = responseField->GetInt("day");
int32 Hour = responseField->GetInt("hour");
int32 Minute = responseField->GetInt("minute");
int32 Second = responseField->GetInt("second");
return FDateTime(Year, Month, Day, Hour, Minute, Second);
}
/* Creates a new instance of the UUEGameJoltAPI class, for use in Blueprint graphs. */
UUEGameJoltAPI* UUEGameJoltAPI::Create(UObject* WorldContextObject) {
// Get the world object from the context
UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject);
// Construct the object and return it
UUEGameJoltAPI* fieldData = NewObject<UUEGameJoltAPI>((UUEGameJoltAPI*)GetTransientPackage(), UUEGameJoltAPI::StaticClass());
fieldData->contextObject = WorldContextObject;
fieldData->World = World;
return fieldData;
}
/* Sends a request to authentificate the user */
void UUEGameJoltAPI::Login(const FString name, const FString token)
{
FString output;
FString GameIDString = FString::FromInt(Game_ID);
LastActionPerformed = EGameJoltComponentEnum::GJ_USER_AUTH;
UserName = name;
UserToken = token;
SendRequest(output, "/users/auth/?");
}
/* Checks if the authentification was succesful */
bool UUEGameJoltAPI::isUserAuthorize()
{
bool outAuthorize;
UUEGameJoltAPI* responseField = NULL;
responseField = GetObject("response");
if (responseField == NULL)
{
UE_LOG(GJAPI, Error, TEXT("responseField Return Null"));
return false;
}
outAuthorize = responseField->GetBool("success");
if (!outAuthorize)
{
bIsLoggedIn = false;
UE_LOG(GJAPI, Error, TEXT("Couldn't authenticate user. Message: %s"), *responseField->GetString("message"));
return false;
}
bIsLoggedIn = true;
return true;
}
/* Gets information the current user */
bool UUEGameJoltAPI::FetchUser()
{
bool ret = false;
FString output;
LastActionPerformed = EGameJoltComponentEnum::GJ_USER_FETCH;
ret = SendRequest(output, "/users/?username=" + UserName, false);
if (!ret)
{
UE_LOG(GJAPI, Error, TEXT("Could not fetch user."));
return false;
}
return true;
}
/* Fetches an array of users */
bool UUEGameJoltAPI::FetchUsers(const TArray<int32> Users)
{
FString output;
LastActionPerformed = EGameJoltComponentEnum::GJ_USERS_FETCH;
FString UserIDs = "";
for(const int32 UserID : Users)
UserIDs.Append(FString::FromInt(UserID) + ",");
return SendRequest(output, "/users/?user_id=" + UserIDs, false);
}
/* Fetches the friendlist of the current user */
bool UUEGameJoltAPI::FetchFriendlist()
{
FString output;
LastActionPerformed = EGameJoltComponentEnum::GJ_USER_FRIENDLIST;
return SendRequest(output, "/friends/?");
}
/* Gets the friendlist */
TArray<int32> UUEGameJoltAPI::GetFriendlist()
{
TArray<UUEGameJoltAPI*> returnArray = GetObject("response")->GetObjectArray(GetObject("response"), "friends");
TArray<int32> returnIDs;
for(int i = 0; i < returnArray.Num(); i++)
returnIDs.Add(returnArray[i]->GetInt("friend_id"));
return returnIDs;
}
/* Resets user related properties */
void UUEGameJoltAPI::LogOffUser()
{
bIsLoggedIn = false;
UserName = "";
UserToken = "";
}
/* Opens a session */
bool UUEGameJoltAPI::OpenSession()
{
FString output;
FString GameIDString;
GameIDString = FString::FromInt(Game_ID);
LastActionPerformed = EGameJoltComponentEnum::GJ_SESSION_OPEN;
return SendRequest(output, "/sessions/open/?");
}
/* Pings the session */
bool UUEGameJoltAPI::PingSession(ESessionStatus SessionStatus)
{
FString output;
FString SessionString = SessionStatus == ESessionStatus::Active ? FString("active") : FString("idle");
FString GameIDString = FString::FromInt(Game_ID);
LastActionPerformed = EGameJoltComponentEnum::GJ_SESSION_PING;
return SendRequest(output, "/sessions/ping/?status=" + SessionString);
}
/* Closes the session */
bool UUEGameJoltAPI::CloseSession()
{
FString output;
FString GameIDString;
GameIDString = FString::FromInt(Game_ID);
LastActionPerformed = EGameJoltComponentEnum::GJ_SESSION_CLOSE;
return SendRequest(output, "/sessions/close/?");
}
/* Fetches the session status */
bool UUEGameJoltAPI::CheckSession()
{
FString output;
LastActionPerformed = EGameJoltComponentEnum::GJ_SESSION_CHECK;
return SendRequest(output, "/sessions/check/?");
}
/* Gets the session status */
bool UUEGameJoltAPI::GetSessionStatus()
{
UUEGameJoltAPI* Response = GetObject("response");
if(!Response)
{
UE_LOG(GJAPI, Error, TEXT("Response invalid in GetSessionStatus. Was ist called to early?"));
return false;
}
return Response->GetBool("success");
}
/* Gets an array of users and puts them in an array of FUserInfo structs */
TArray<FUserInfo> UUEGameJoltAPI::GetUserInfo()
{
TArray<UUEGameJoltAPI*> returnArray = GetObject("response")->GetObjectArray(GetObject("response"), "users");
TArray<FUserInfo> returnUserInfo;
FUserInfo tempUser;
for (int i = 0; i< returnArray.Num(); i++)
{
tempUser.S_User_ID = returnArray[i]->GetInt("id");
tempUser.User_Name = returnArray[i]->GetString("username");
tempUser.User_Type = returnArray[i]->GetString("type");
tempUser.User_AvatarURL = returnArray[i]->GetString("avatar_url");
tempUser.Signed_up = returnArray[i]->GetString("signed_up");
tempUser.Last_Logged_in = returnArray[i]->GetString("last_logged_in");
tempUser.status = returnArray[i]->GetString("status");
returnUserInfo.Add(tempUser);
}
return returnUserInfo;
}
/* Awards the current user a trophy */
bool UUEGameJoltAPI::RewardTrophy(const int32 Trophy_ID)
{
bool ret = true;
FString output;
FString GameIDString;
FString TrophyIDString;
GameIDString = FString::FromInt(Game_ID);
if (!bIsLoggedIn)
{
UE_LOG(GJAPI, Error, TEXT("User is not logged in"));
return false;
}
TrophyIDString = FString::FromInt(Trophy_ID);
LastActionPerformed = EGameJoltComponentEnum::GJ_TROPHIES_ADD;
ret = SendRequest(output, "/trophies/add-achieved/?trophy_id=" + TrophyIDString);
return true;
}
/* Gets information for all trophies */
void UUEGameJoltAPI::FetchAllTrophies(const EGameJoltAchievedTrophies AchievedType)
{
TArray<int32> Trophies;
FetchTrophies(AchievedType, Trophies);
}
/* Gets information for the selected trophies */
void UUEGameJoltAPI::FetchTrophies(const EGameJoltAchievedTrophies AchievedType, const TArray<int32> Trophy_IDs)
{
TArray<FTrophyInfo> returnTrophies;
bool ret = true;
FString output;
FString TrophyIDString;
FString AchievedString;
if (!bIsLoggedIn)
{
UE_LOG(GJAPI, Error, TEXT("User is not logged in!"));
return;
}
LastActionPerformed = EGameJoltComponentEnum::GJ_TROPHIES_FETCH;
if(AchievedType == EGameJoltAchievedTrophies::GJ_ACHIEVEDTROPHY_GAME){
AchievedString ="false";
}
else
{
AchievedString = "true";
}
for (int32 i = 0; i < Trophy_IDs.Num(); i++){
TrophyIDString += FString::FromInt(Trophy_IDs[i]);
if (i != Trophy_IDs.Num()-1)
{
TrophyIDString += TEXT(",");
}
}
if (AchievedType == EGameJoltAchievedTrophies::GJ_ACHIEVEDTROPHY_BLANK)//if We Want to get all trophies
{
ret = SendRequest(output, "/trophies/?" + (Trophy_IDs.Num() > 0 ? "&trophy_id=" : "" + TrophyIDString));
}
else //if We Want to get what trophies the User achieved have Not Achieved
{
ret = SendRequest(output, "/trophies/?achieved=" + AchievedString +
(Trophy_IDs.Num() > 0 ? "&trophy_id=" : "" + TrophyIDString));
}
if (!ret)
{
UE_LOG(GJAPI, Error, TEXT("Could not fetch trophies."));
return;
}
return;
}
/* Gets the trophy information from the fetched trophies */
TArray<FTrophyInfo> UUEGameJoltAPI::GetTrophies()
{
TArray<FTrophyInfo> returnTrophy;
TArray<UUEGameJoltAPI*> returnArray = GetObject("response")->GetObjectArray(GetObject("response"), "trophies");
FTrophyInfo tempTrophies;
for (int i = 0; i< returnArray.Num(); i++)
{
tempTrophies.Trophy_ID = returnArray[i]->GetInt("id");
tempTrophies.Name = returnArray[i]->GetString("title");
tempTrophies.Description = returnArray[i]->GetString("description");
tempTrophies.Difficulty = returnArray[i]->GetString("difficulty");
tempTrophies.image_url = returnArray[i]->GetString("image_url");
tempTrophies.achieved = returnArray[i]->GetString("achieved");
returnTrophy.Add(tempTrophies);
}
return returnTrophy;
}
/* Unachieves a trophy */
bool UUEGameJoltAPI::RemoveRewardedTrophy(const int32 Trophy_ID)
{
FString output;
LastActionPerformed = EGameJoltComponentEnum::GJ_TROHIES_REMOVE;
return SendRequest(output, "/trophies/remove-achieved/?trophy_id=" + FString::FromInt(Trophy_ID));
}
/* Checks if the trophy removel was successful */
bool UUEGameJoltAPI::GetTrophyRemovalStatus()
{
UUEGameJoltAPI* Response = GetObject("response");
if(!Response)
{
UE_LOG(GJAPI, Error, TEXT("Response invalid in GetTrophyRemovalStatus. Was ist called to early?"));
return false;
}
return Response->GetBool("success");
}
/* Returns a list of scores either for a user or globally for a game */
bool UUEGameJoltAPI::FetchScoreboard(const int32 ScoreLimit, const int32 Table_id, const int32 BetterThan, const int32 WorseThan)
{
TArray<FTrophyInfo> returnTrophies;
bool ret = true;
FString output;
FString GameIDString;
FString TableIDString;
FString ScoreLimitString;
GameIDString = FString::FromInt(Game_ID);
TableIDString = FString::FromInt(Table_id);
ScoreLimitString = FString::FromInt(ScoreLimit);
LastActionPerformed = EGameJoltComponentEnum::GJ_SCORES_FETCH;
ret = SendRequest(output, TEXT("/scores/?game_id=") + GameIDString +
(!UserName.IsEmpty() || !bIsLoggedIn ? "&username=" : "") + UserName +
(bIsLoggedIn ? "&user_token=" : "") + UserToken +
(ScoreLimit > 0 ? "&limit=" + ScoreLimitString : "") +
(Table_id > 0 ? "&table_id=" + TableIDString : "") +
(BetterThan > 0 ? "&better_than=" + FString::FromInt(BetterThan) : "") +
(WorseThan > 0 ? "&worse_than=" + FString::FromInt(WorseThan) : ""));
if (!ret)
{
UE_LOG(GJAPI, Error, TEXT("Could not fetch scoreboard."));
return false;
}
return true;
}
/* Gets the list of scores fetched with FetchScoreboard */
TArray<FScoreInfo> UUEGameJoltAPI::GetScoreboard()
{
TArray<FScoreInfo> returnScoreInfo;
TArray<UUEGameJoltAPI*> returnArray = GetObject("response")->GetObjectArray(GetObject("response"), "scores");
FScoreInfo tempScore;
for (int i = 0; i < returnArray.Num(); i++)
{
tempScore.ScoreSort = returnArray[i]->GetInt("sort");
tempScore.ScoreString = returnArray[i]->GetString("score");
tempScore.ExtraData = returnArray[i]->GetString("extra_data");
tempScore.UserName = returnArray[i]->GetString("user");
tempScore.UserID = returnArray[i]->GetInt("user_id");
tempScore.Guest = returnArray[i]->GetString("guest");
tempScore.UnixTimestamp = returnArray[i]->GetString("stored");
tempScore.TimeStamp = FDateTime::FromUnixTimestamp(returnArray[i]->GetInt("stored"));
returnScoreInfo.Add(tempScore);
}
return returnScoreInfo;
}
/* Adds an entry to a scoreboard */
bool UUEGameJoltAPI::AddScore(const FString UserScore, const int32 UserScore_Sort, const FString GuestUser, const FString extra_data, const int32 table_id)
{
bool ret = true;
FString output;
FString GameIDString;
FString TableIDString;
GameIDString = FString::FromInt(Game_ID);
TableIDString = FString::FromInt(table_id);
LastActionPerformed = EGameJoltComponentEnum::GJ_SCORES_ADD;
ret = SendRequest(output, "/scores/add/?score=" + UserScore +
TEXT("&sort=") + FString::FromInt(UserScore_Sort) +
(!UserName.IsEmpty() || bIsLoggedIn ? "&username=" : "") + UserName +
(bIsLoggedIn ? "&user_token=" : "") + UserToken +
(!bIsLoggedIn ? "&guest=" : "") + GuestUser +
(!extra_data.IsEmpty() ? "&extra_data=" : "") + extra_data +
(table_id > 0 ? "&table_id=" : "") + (table_id > 0 ? TableIDString : ""));
if (!ret)
{
UE_LOG(GJAPI, Error, TEXT("Failed to add user's score"));
return false;
}
return true;
}
/* Fetches all scoreboard tables */
bool UUEGameJoltAPI::FetchScoreboardTable()
{
bool ret = true;
FString output;
FString GameIDString;
GameIDString = FString::FromInt(Game_ID);
LastActionPerformed = EGameJoltComponentEnum::GJ_SCORES_TABLE;
ret = SendRequest(output, "/scores/tables/?");
if (!ret)
{
UE_LOG(GJAPI, Error, TEXT("Could not fetch scoreboard table"));
return false;
}
return true;
}
/* Creates an array of FScoreTableInfo structs for all scoreboards of the game */
TArray<FScoreTableInfo> UUEGameJoltAPI::GetScoreboardTable()
{
TArray<FScoreTableInfo> returnTableinfo;
FScoreTableInfo tempTable;
TArray<UUEGameJoltAPI*> returnArray = GetObject("response")->GetObjectArray(GetObject("response"), "tables");
for (int i = 0; i < returnArray.Num(); i++)
{
tempTable.Id = returnArray[i]->GetInt("id");
tempTable.Name = returnArray[i]->GetString("name");
tempTable.Description = returnArray[i]->GetString("description");
tempTable.Primary = returnArray[i]->GetString("primary");
returnTableinfo.Add(tempTable);
}
return returnTableinfo;
}
/* Fetches the rank of a highscore */
bool UUEGameJoltAPI::FetchRank(const int32 Score, const int32 TableID = 0)
{
LastActionPerformed = EGameJoltComponentEnum::GJ_SCORES_RANK;
FString output;
return SendRequest(output, "/scores/get-rank/?sort=" + FString::FromInt(Score) + ((TableID != 0) ? ("&table_id=" + FString::FromInt(TableID)) : ""));
}
/* Gets the rank of a highscore from the response */
int32 UUEGameJoltAPI::GetRank()
{
UUEGameJoltAPI* response = GetObject("response");
if(!response)
{
UE_LOG(GJAPI, Error, TEXT("Response in GetRank is invalid! Was it called to early? LastActionPerformed is %s"), *UEnum::GetValueAsString<EGameJoltComponentEnum>(LastActionPerformed));
return 0;
}
return GetObject("response")->GetInt("rank");
}
#pragma region Data-Store
void UUEGameJoltAPI::SetData(EDataStore Type, FString key, FString data)
{
FString output;
LastActionPerformed = EGameJoltComponentEnum::GJ_DATASTORE_SET;
SendRequest(output, "/data-store/set/?key=" + key + "&data=" + data, Type == EDataStore::User);
}
void UUEGameJoltAPI::FetchData(EDataStore Type, FString key)
{
FString output;
LastActionPerformed = EGameJoltComponentEnum::GJ_DATASTORE_FETCH;
SendRequest(output, "/data-store/?key=" + key, Type == EDataStore::User);
}
void UUEGameJoltAPI::UpdateData(EDataStore Type, FString key, EDataOperation Operation, FString value)
{
FString output;
LastActionPerformed = EGameJoltComponentEnum::GJ_DATASTORE_UPDATE;
SendRequest(output, "/data-store/update/?key=" + key + "&value=" + value + "&operation=" + UEnum::GetValueAsString<EDataOperation>(Operation).RightChop(16), Type == EDataStore::User);
}
void UUEGameJoltAPI::RemoveData(EDataStore Type, FString key)
{
FString output;
LastActionPerformed = EGameJoltComponentEnum::GJ_DATASTORE_REMOVE;
SendRequest(output, "/data-store/remove/?key=" + key, Type == EDataStore::User);
}
void UUEGameJoltAPI::GetData(bool& Success, FString& DataAsString, int32& DataAsInt)
{
DataAsString = "";
DataAsInt = 0;
UUEGameJoltAPI* response = GetObject("response");
if(!response)
{
Success = false;
return;
}
Success = response->GetBool("success");
if(!Success)
return;
DataAsString = response->GetString("data");
DataAsInt = response->GetInt("data");
}
#pragma endregion
/* Gets nested post data from the object with the specified key */
UUEGameJoltAPI* UUEGameJoltAPI::GetObject(const FString& key)
{
UUEGameJoltAPI* fieldObj = NULL;
// Try to get the object field from the data
const TSharedPtr<FJsonObject> *outPtr;
if (!Data->TryGetObjectField(*key, outPtr)) {
// Throw an error and return NULL when the key could not be found
UE_LOG(GJAPI, Error, TEXT("Entry '%s' not found in the field data!"), *key);
return NULL;
}
// Create a new field data object and assign the data
fieldObj = UUEGameJoltAPI::Create(contextObject);
fieldObj->Data = *outPtr;
// Return the newly created object
return fieldObj;
}
/* Gets a string field */
FString UUEGameJoltAPI::GetString(const FString& key) const
{
FString outString;
if (!Data->TryGetStringField(*key, outString))
{
UE_LOG(GJAPI, Error, TEXT("Entry '%s' not found in the field data!"), *key);
return "";
}
return outString;
}
/* Gets a bool field */
bool UUEGameJoltAPI::GetBool(const FString& key)const
{
bool outBool;
if (!Data->TryGetBoolField(*key,outBool))
{
UE_LOG(GJAPI, Error, TEXT("Entry '%s' not found in the field data!"), *key);
return false;
}
return outBool;
}
/* Gets an integer field */
int32 UUEGameJoltAPI::GetInt(const FString& key) const
{
int32 outInt;
if (!Data->TryGetNumberField(*key, outInt))
{
UE_LOG(GJAPI, Error, TEXT("Entry '%s' not found in the field data!"), *key);
return 0;
}
return outInt;
}
/* Gets a string array of all keys from the post data */
TArray<FString> UUEGameJoltAPI::GetObjectKeys(UObject* WorldContextObject)
{
TArray<FString> stringArray;
for (auto currJsonValue = Data->Values.CreateConstIterator(); currJsonValue; ++currJsonValue) {
stringArray.Add((*currJsonValue).Key);
}
// Return the array, will be empty if unsuccessful
return stringArray;
}
/* Gets an array of post data */
TArray<UUEGameJoltAPI*> UUEGameJoltAPI::GetObjectArray(UObject* WorldContextObject, const FString& key)
{
TArray<UUEGameJoltAPI*> objectArray;
// Try to fetch and assign the array to the array pointer
const TArray<TSharedPtr<FJsonValue>> *arrayPtr;
if (Data->TryGetArrayField(*key, arrayPtr)) {
// Iterate through the input array and create new post data objects for every entry and add them to the objectArray
for (int32 i = 0; i < arrayPtr->Num(); i++) {
UUEGameJoltAPI* pageData = Create(WorldContextObject);
pageData->Data = (*arrayPtr)[i]->AsObject();
objectArray.Add(pageData);
}
}
else {
// Throw an error, since the value with the supplied key could not be found
UE_LOG(GJAPI, Error, TEXT("Array entry '%s' not found in the field data!"), *key);
}
// Return the array, will be empty if unsuccessful
return objectArray;
}
/* Sends a request */
bool UUEGameJoltAPI::SendRequest(const FString& output, FString url, bool bAppendUserInfo)
{
if (Game_PrivateKey == TEXT(""))
{
UE_LOG(GJAPI, Error, TEXT("You must put in your game's private key before you can use any of the API functions."));
return false;
}
if(Game_ID == 0)
{
UE_LOG(GJAPI, Error, TEXT("You must put in your game's ID before you can use any of the API functions"));
return false;
}
FString outStr;
TSharedRef<TJsonWriter<TCHAR>> JsonWriter = TJsonWriterFactory<TCHAR>::Create(&outStr);
//Start writing the response
WriteObject(JsonWriter, "", new FJsonValueObject(Data));
JsonWriter->Close();
//Create URL First
url = TEXT("https://") + GJAPI_SERVER + GJAPI_ROOT + GJAPI_VERSION + url + "&game_id=" + FString::FromInt(Game_ID);
if(bAppendUserInfo)
url += "&username=" + UserName + "&user_token=" + UserToken;
FString signature(FMD5::HashAnsiString(*(url + Game_PrivateKey)));
url += TEXT("&signature=") + signature;
UE_LOG(GJAPI, Log, TEXT("%s"), *url);
TSharedRef<IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest();
HttpRequest->SetVerb("POST");
HttpRequest->SetURL(url);
HttpRequest->SetHeader("Content-Type", "application/json");
HttpRequest->SetContentAsString(output);
HttpRequest->OnProcessRequestComplete().BindUObject(this, &UUEGameJoltAPI::OnReady);
HttpRequest->ProcessRequest();
return true;
}
/* Writes data */
void UUEGameJoltAPI::WriteObject(TSharedRef<TJsonWriter<TCHAR>> writer, FString key, FJsonValue* value) {
if (value->Type == EJson::String) {
// Write simple string entry, don't a key when it isn't set
if (key.Len() > 0) {
writer->WriteValue(key, value->AsString());
}
else {
writer->WriteValue(value->AsString());
}
}
else if (value->Type == EJson::Object) {
// Write object entry
if (key.Len() > 0) {
writer->WriteObjectStart(key);
}
else {
writer->WriteObjectStart();
}
// Loop through all the values in the object data
TSharedPtr<FJsonObject> objectData = value->AsObject();
for (auto objectValue = objectData->Values.CreateIterator(); objectValue; ++objectValue) {
// Using recursion to write the key and value to the writer
WriteObject(writer, objectValue.Key(), objectValue.Value().Get());
}
writer->WriteObjectEnd();
}
else if (value->Type == EJson::Array) {
// Process array entry
writer->WriteArrayStart(key);
TArray<TSharedPtr<FJsonValue>> objectArray = value->AsArray();
for (int32 i = 0; i < objectArray.Num(); i++) {
// Use recursion with an empty key to process all the values in the array
WriteObject(writer, "", objectArray[i].Get());
}
writer->WriteArrayEnd();
}
}
/* Creates data from a string */
void UUEGameJoltAPI::FromString(const FString& dataString) {
TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(dataString);
// Deserialize the JSON data
bool isDeserialized = FJsonSerializer::Deserialize(JsonReader, Data);
if (!isDeserialized || !Data.IsValid()) {
UE_LOG(GJAPI, Error, TEXT("JSON data is invalid! Input:\n'%s'"), *dataString);
return;
}
// Assign the request content
Content = dataString;
}
/* Callback for IHttpRequest::OnProcessRequestComplete() */
void UUEGameJoltAPI::OnReady(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) {
if (!bWasSuccessful) {
UE_LOG(GJAPI, Warning, TEXT("Response was invalid! Please check the URL."));
// Broadcast the failed event
OnFailed.Broadcast();
return;
}
// Process the string
FromString(Response->GetContentAsString());
if(!GetObject("response") || (GetObject("response")->GetBool("success") == false && LastActionPerformed != EGameJoltComponentEnum::GJ_SESSION_CHECK))
{
OnFailed.Broadcast();
return;
}
switch(LastActionPerformed)
{
case EGameJoltComponentEnum::GJ_USER_AUTH:
OnUserAuthorized.Broadcast(isUserAuthorize());
break;
case EGameJoltComponentEnum::GJ_USER_AUTOLOGIN:
OnAutoLogin.Broadcast(isUserAuthorize());
break;
case EGameJoltComponentEnum::GJ_USER_FETCH:
OnUserFetched.Broadcast(GetUserInfo()[0]);
break;
case EGameJoltComponentEnum::GJ_USERS_FETCH:
OnUsersFetched.Broadcast(GetUserInfo());
break;
case EGameJoltComponentEnum::GJ_USER_FRIENDLIST:
OnFriendlistFetched.Broadcast(GetFriendlist());
break;
case EGameJoltComponentEnum::GJ_SESSION_OPEN:
OnSessionOpened.Broadcast(GetSessionStatus());
break;
case EGameJoltComponentEnum::GJ_SESSION_PING:
OnSessionPinged.Broadcast(GetSessionStatus());
break;
case EGameJoltComponentEnum::GJ_SESSION_CLOSE:
OnSessionClosed.Broadcast(GetSessionStatus());
break;
case EGameJoltComponentEnum::GJ_SESSION_CHECK:
OnSessionChecked.Broadcast(GetSessionStatus());
break;
case EGameJoltComponentEnum::GJ_TROPHIES_FETCH:
OnTrophiesFetched.Broadcast(GetTrophies());
break;
case EGameJoltComponentEnum::GJ_TROHIES_REMOVE:
OnTrophyRemoved.Broadcast(GetTrophyRemovalStatus());
break;
case EGameJoltComponentEnum::GJ_SCORES_ADD:
OnScoreAdded.Broadcast(GetObject("response")->GetBool("success"));
break;
case EGameJoltComponentEnum::GJ_SCORES_FETCH:
OnScoreboardFetched.Broadcast(GetScoreboard());
break;
case EGameJoltComponentEnum::GJ_SCORES_TABLE:
OnScoreboardTableFetched.Broadcast(GetScoreboardTable());
break;
case EGameJoltComponentEnum::GJ_SCORES_RANK:
OnRankFetched.Broadcast(GetRank());
break;
case EGameJoltComponentEnum::GJ_TIME:
OnTimeFetched.Broadcast(ReadServerTime());
break;
}
// Broadcast the result event
OnGetResult.Broadcast();
return;
}
/* Resets the saved data */
void UUEGameJoltAPI::Reset()
{
if (Data.IsValid())
Data.Reset();
// Created a new JSON Object
Data = MakeShareable(new FJsonObject());
}