Skip to content

Commit

Permalink
2.17.1896
Browse files Browse the repository at this point in the history
* SubmitNewModFileForMod now supports platform information for games with CrossPlatform Modfiles enabled
* FModioModInfo now exposes mod visibility
* FModioFilterParams can now sort by DownloadsTotal
* Deferred unsubscriptions are now treated as successes
* New VerifyUserAuthenticationAsync method for verifying authentication state on the server
* EnableModManagement now reports failure reasons to the log
* ErrorCodeMatches method should now be correctly exposed to Blueprint
  • Loading branch information
stephenwhittle committed Jun 27, 2022
1 parent aa9d6d7 commit 69c167e
Show file tree
Hide file tree
Showing 46 changed files with 1,306 additions and 196 deletions.
52 changes: 52 additions & 0 deletions Config/Localization/Modio.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
[CommonSettings]
SourcePath=Plugins/Modio/Content/UI/Localization
DestinationPath=Plugins/Modio/Content/UI/Localization
ManifestName=Modio.manifest
ArchiveName=Modio.archive
PortableObjectName=Modio.po
ResourceName=Modio.locres
NativeCulture=en
CulturesToGenerate=en

;Gather text from source code
[GatherTextStep0]
CommandletClass=GatherTextFromSource
SearchDirectoryPaths=Plugins/Modio/Source/ModioUI
FileNameFilters=*.cpp
FileNameFilters=*.h
FileNameFilters=*.c
FileNameFilters=*.inl
FileNameFilters=*.mm
FileNameFilters=*.ini
FileNameFilters=*.csv
ShouldGatherFromEditorOnlyData=false

[GatherTextStep1]
CommandletClass=GatherTextFromAssets
IncludePathFilters=Plugins/Modio/Content/*
ExcludePathFilters=Plugins/Modio/Content/UI/Localization/*
PackageFileNameFilters=*.uasset
PackageFileNameFilters=*.umap


;Write Manifest
[GatherTextStep2]
CommandletClass=GenerateGatherManifest

;Write Archives
[GatherTextStep3]
CommandletClass=GenerateGatherArchive

;Import localized PO files
[GatherTextStep4]
CommandletClass=InternationalizationExport
bImportLoc=true

;Write Localized Text Resource
[GatherTextStep5]
CommandletClass=GenerateTextLocalizationResource

;Export PO files
[GatherTextStep6]
CommandletClass=InternationalizationExport
bExportLoc=true
534 changes: 483 additions & 51 deletions Doc/documentation.html

Large diffs are not rendered by default.

129 changes: 125 additions & 4 deletions Doc/getting-started.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ NOTE: The plugin event loop, any internal event handlers, and all callbacks you

==== Non-blocking, asynchronous interface

The plugin communicates with the mod.io servers, the filesystem on the device you're using, and platform-provided services for authentication. All of these may not return results immediately, so many functions provided by the ModioSubsystem are non-blocking and asynchronous.
The plugin communicates with the mod.io servers, the filesystem on the device you're using, and platform-provided services for authentication. All of these may not return results immediately, so many functions provided by the ModioSubsystem are non-blocking and asynchronous. For example, the initialization function returns immediately. However, your game should consider the ModioSybsystem as initialized only when the init callback executes.

NOTE: All async methods in the public API end with the suffix `Async`.

Expand Down Expand Up @@ -110,9 +110,8 @@ The plugin stores mods in a game-specific directory in the following directory b

[stripes=odd,frame=none,cols="1,^1,^1"]
|===
|Windows | Linux | OSX

|`${FolderID_Public}/mod.io` | TBD |TBD
| Windows | Linux | OSX
|`${FolderID_Public}/mod.io` | `${USER_HOME}/mod.io` | `${USER_HOME}/Library/Application Support/mod.io`
|===

However, this value can be overridden in one of two ways:
Expand Down Expand Up @@ -156,6 +155,7 @@ image::img/initasync_getoptions.png[]
.Notes
* The error-handling in this sample has been omitted. See <<Plugin quick-start: Error Handling>> for more information on error handling.
* To fully initialize the SDK, you must receive confirmation from the callback. Consider that most functions return after invocation, nonetheless, their effects are only visible in their callback function
====

Expand Down Expand Up @@ -348,6 +348,45 @@ Here's what steps 1 and 2 might look like in Blueprint:

image::img/authenticate_user_external.png[]

Note that the SDK will automatically URL encode parameters (such as the auth token) when making the request.

===== Steam Authentication Example

In order to use the Steam authentication functionality, you must add your games https://partner.steamgames.com/apps/sdkauth[Encrypted App Ticket Key] from Steamworks. On your games profile on mod.io, go to Edit > Options and add the key. You can then call <<K2_AuthenticateUserExternalAsync>> and provide the users Encrypted App Ticket as the Auth Token. Note that the Auth Token must be Base64 encoded when passed

Below is a sample Blueprint method that will get the users current Encrypted App Ticket that you can use in your Authentication request. Add this to a BlueprintLibrary in your games codebase.

.C++ Example
[%collapsible]
====
[source]
----
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnGetTicket, int32, LocalUserNum, FString, TokenData);
UFUNCTION(BlueprintCallable)
static void GetSteamAuthTicket(int32 LocalUserNum, FOnGetTicket Callback)
{
//Get the steam subsystem
FOnlineSubsystemSteam* SteamSubsystem = static_cast<FOnlineSubsystemSteam*>(IOnlineSubsystem::Get());
//Add a handler to the subsystem for when the ticket has been retrieved
SteamSubsystem->GetEncryptedAppTicketInterface()->OnEncryptedAppTicketResultDelegate.AddLambda(
[LocalUserNum, OnComplete = Callback](bool bEncryptedDataAvailable, int32 ResultCode) {
TArray<uint8> TokenData;
if (bEncryptedDataAvailable)
{
//If the ticket was retrieved successfully, get its data
SteamSubsystem->GetEncryptedAppTicketInterface()->GetEncryptedAppTicket(TokenData);
}
//Call the user callback with the base64-encoded ticket, ready for submission via AuthenticateUserExternalAsync
OnComplete.ExecuteIfBound(LocalUserNum, FBase64::Encode(TokenData));
});
//Begin the actual async request for the ticket, which will invoke the above lambda when it completes
SteamSubsystem->GetEncryptedAppTicketInterface()->RequestEncryptedAppTicket(nullptr, 0);
}
----
Note that if you are on 4.27 or above, Epic provides a helper method in OnlineIdentityInterface::GetLinkedAccountAuthToken that will get the current account's auth token without having to take a direct dependency on FOnlineSubsystemSteam. Ensure that the token is Base64 encoded when being passed to <<K2_AuthenticateUserExternalAsync>>.
====

==== Token Lifetime & Re-Authentication

By default, tokens issued via email token exchange have a lifetime of 1 year. You can verify that a user has been successfully authenticated by using <<QueryUserProfile>>. A success and non-null result indicates that a user has been authenticated.
Expand Down Expand Up @@ -713,3 +752,85 @@ image::img/get_last_validation_error.png[]

'''

==== Submitting a new mod

In order to submit a mod, you have to first create a mod handle using <<K2_GetModCreationHandle>> and use that handle when calling <<K2_SubmitNewModAsync>>

.Blueprint Example
[%collapsible]
====
image::img/submit_new_mod.png[]
====

.C++ Example
[%collapsible]
====
[source]
----
void UModioManager::SubmitNewMod()
{
if (GEngine->GetEngineSubsystem<UModioSubsystem>())
{
FModioModCreationHandle Handle = GEngine->GetEngineSubsystem<UModioSubsystem>()->GetModCreationHandle();
FModioCreateModParams Params;
Params.Name = TEXT("My Awesome Mod");
Params.Description = TEXT("This is an amazing mod");
Params.PathToLogoFile = TEXT("C:\\temp\\image.png");
GEngine->GetEngineSubsystem<UModioSubsystem>()->SubmitNewModAsync(Handle, Params, FOnSubmitNewModDelegateFast::CreateUObject(this, &UModioManager::OnSubmitNewModCallback));
}
}
void UModioManager::OnSubmitNewModCallback(FModioErrorCode ErrorCode, TOptional<FModioModID> ModId)
{
if (ErrorCode == false)
{
// Mod was submitted successfully. Use ModId to submit some files to it.
}
}
----
====


==== Submitting a file for a mod

Once you have successfully submitted a mod, you can then submit a file for that mod using <<K2_SubmitNewModFileForMod>>. When you submit a file, you pass a <<ModioCreateModFileParams>> containing the directory of the files that you want to submit. The plugin will then compress this folder into a zip file and upload it as the active version of the mod. Note that there is no callback for this method; you'll get notified of the completed upload by the Mod Management callbacks.

.Blueprint Example
[%collapsible]
====
As an example, after the callback for submitting a mod has completed, you can get the Mod Id to use for file submission.
image::img/submit_new_mod_file.png[]
====

.C++ Example
[%collapsible]
====
[source]
----
void UModioManager::SubmitNewModFile(FModioModID ModId)
{
if (GEngine->GetEngineSubsystem<UModioSubsystem>())
{
FModioCreateModFileParams Params;
Params.PathToModRootDirectory = TEXT("C:\\temp\\mod_folder");
GEngine->GetEngineSubsystem<UModioSubsystem>()->SubmitNewModFileForMod(ModId, Params);
}
}
----
====
Binary file added Doc/img/nd_img_Add.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Doc/img/nd_img_BreakToComponents.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Doc/img/nd_img_Divide.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Doc/img/nd_img_EqualTo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Doc/img/nd_img_GreaterThan.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Doc/img/nd_img_K2_ArchiveModAsync.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Doc/img/nd_img_K2_EnableModManagement.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Doc/img/nd_img_LessThan.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Doc/img/nd_img_MakeFromComponents.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Doc/img/nd_img_NotEqualTo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Doc/img/nd_img_Subtract.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ The settings have the following parameters:

== Further reading

To begin using the Plugin, either from Blueprint or from C++, please read our link:doc/getting-started.adoc[Getting Started Guide] for a detailed explanation of initialization and usage.
To begin using the Plugin, either from Blueprint or from C++, please read our link:Doc/getting-started.adoc[Getting Started Guide] for a detailed explanation of initialization and usage.

* link:Doc/getting-started.adoc#plugin-quick-start-initialization-and-teardown[SDK initialization and event loop]
* link:Doc/getting-started.adoc#plugin-quick-start-user-authentication[Authentication]
Expand Down
2 changes: 1 addition & 1 deletion Source/Modio/Modio.Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ private void AddCommonHeaderPaths(string GeneratedHeaderPath)
PublicIncludePaths.AddRange(new string[] {
Path.Combine(GeneratedHeaderPath, "Public")
});

ConditionalAddModuleDirectory(new DirectoryReference(Path.Combine(GeneratedHeaderPath, "Public")));
// Add common private includes from the Native SDK
PrivateIncludePaths.AddRange(new string[]
{
Expand Down
5 changes: 5 additions & 0 deletions Source/Modio/Private/Internal/Convert/CreateModFileParams.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,10 @@ FORCEINLINE Modio::CreateModFileParams ToModio(const FModioCreateModFileParams&
Out.Changelog = ToModioOptional<std::string>(In.Changelog);
Out.bSetAsActive = ToModioOptional<bool>(In.bSetAsActiveRelease);
Out.MetadataBlob = ToModioOptional<std::string>(In.MetadataBlob);
if (In.ModfilePlatforms.IsSet())
{
Out.Platforms = ToModio<Modio::ModfilePlatform, EModioModfilePlatform>(In.ModfilePlatforms.GetValue());
}

return Out;
}
2 changes: 2 additions & 0 deletions Source/Modio/Private/Internal/Convert/FilterParams.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ FORCEINLINE Modio::FilterParams::SortFieldType ToModio(EModioSortFieldType Envir
return Modio::FilterParams::SortFieldType::DateMarkedLive;
case EModioSortFieldType::DateUpdated:
return Modio::FilterParams::SortFieldType::DateUpdated;
case EModioSortFieldType::DownloadsTotal:
return Modio::FilterParams::SortFieldType::DownloadsTotal;
}

return Modio::FilterParams::SortFieldType::ID;
Expand Down
9 changes: 9 additions & 0 deletions Source/Modio/Private/Internal/Convert/ModCollectionEntry.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ FModioModCollectionEntry ToUnreal(const Modio::ModCollectionEntry& In)
Out.ModID = ToUnreal(In.GetID());
Out.ModPath = ToUnreal(In.GetPath());
Out.ModProfile = ToUnreal(In.GetModProfile());
Out.SizeOnDisk = FModioUnsigned64(0);
if (In.GetSizeOnDisk())
{
if (*In.GetSizeOnDisk())
{
Out.SizeOnDisk = ToUnreal(*In.GetSizeOnDisk());
}
}

return Out;
}

2 changes: 2 additions & 0 deletions Source/Modio/Private/Internal/Convert/ModInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ FORCEINLINE FModioModInfo ToUnreal(const Modio::ModInfo& In)
Out.ProfileDateUpdated = ToUnrealDateTime(In.ProfileDateUpdated);
Out.ProfileDateLive = ToUnrealDateTime(In.ProfileDateLive);
Out.ProfileMaturityOption = ToUnreal<EModioMaturityFlags, Modio::MaturityOption>(In.ProfileMaturityOption);
Out.bVisible = In.bVisible;

Out.MetadataBlob = FString(In.MetadataBlob.c_str()); // Converting verbatim rather than via TCHAR as ToUnreal does

if (In.FileInfo.has_value())
Expand Down
11 changes: 6 additions & 5 deletions Source/Modio/Private/Internal/Convert/ModProgressInfo.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*
/*
* Copyright (C) 2021 mod.io Pty Ltd. <https://mod.io>
*
*
* This file is part of the mod.io UE4 Plugin.
*
* Distributed under the MIT License. (See accompanying file LICENSE or
*
* Distributed under the MIT License. (See accompanying file LICENSE or
* view online at <https://github.com/modio/modio-ue4/blob/main/LICENSE>)
*
*
*/

#pragma once
Expand All @@ -18,6 +18,7 @@ FORCEINLINE FModioModProgressInfo ToUnreal(const Modio::ModProgressInfo& In)
FModioModProgressInfo Out;
Out.TotalDownloadSize = ToUnreal(In.TotalDownloadSize);
Out.CurrentlyDownloadedBytes = ToUnreal(In.CurrentlyDownloadedBytes);
Out.CurrentlyExtractedBytes = ToUnreal(In.CurrentlyExtractedBytes);
Out.TotalExtractedSizeOnDisk = ToUnreal(In.TotalExtractedSizeOnDisk);
Out.ID = ToUnreal(In.ID);
return Out;
Expand Down
41 changes: 37 additions & 4 deletions Source/Modio/Private/Internal/ModioConvert.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "Internal/ModioConvert.h"
#include "Types/ModioUnsigned64.h"

std::string ToModio(const FString& String)
{
Expand Down Expand Up @@ -47,19 +48,21 @@ FString ToUnreal(const std::string& String)
return UTF8_TO_TCHAR(String.c_str());
}



FString ToUnreal(const Modio::filesystem::path& Path)
{
return UTF8_TO_TCHAR(Path.generic_u8string().c_str());
}

FModioUnsigned64 ToUnreal(const Modio::FileSize& In)
{
return FModioUnsigned64(In);
}

FDateTime ToUnrealDateTime(std::int64_t UnixTimestamp)
{
return FDateTime::FromUnixTimestamp(UnixTimestamp);
}


Modio::ApiKey ToModio(const FModioApiKey& In)
{
return Modio::ApiKey(TCHAR_TO_UTF8(*In.ToString()));
Expand Down Expand Up @@ -93,7 +96,6 @@ Modio::UserID ToModio(const FModioUserID& In)
return Modio::UserID(In.UserID);
}


Modio::LogLevel ToModio(EModioLogLevel UnrealLogLevel)
{
switch (UnrealLogLevel)
Expand Down Expand Up @@ -174,6 +176,37 @@ Modio::Environment ToModio(EModioEnvironment Environment)
return Modio::Environment::Test;
}

Modio::ModfilePlatform ToModio(EModioModfilePlatform Platform)
{
switch (Platform)
{
case EModioModfilePlatform::Android:
return Modio::ModfilePlatform::Android;
case EModioModfilePlatform::iOS:
return Modio::ModfilePlatform::iOS;
case EModioModfilePlatform::Linux:
return Modio::ModfilePlatform::Linux;
case EModioModfilePlatform::Mac:
return Modio::ModfilePlatform::Mac;
case EModioModfilePlatform::Oculus:
return Modio::ModfilePlatform::Oculus;
case EModioModfilePlatform::PS4:
return Modio::ModfilePlatform::PS4;
case EModioModfilePlatform::PS5:
return Modio::ModfilePlatform::PS5;
case EModioModfilePlatform::Switch:
return Modio::ModfilePlatform::Switch;
case EModioModfilePlatform::Windows:
return Modio::ModfilePlatform::Windows;
case EModioModfilePlatform::XboxOne:
return Modio::ModfilePlatform::XboxOne;
case EModioModfilePlatform::XboxSeriesX:
return Modio::ModfilePlatform::XboxSeriesX;
}
checkf(false, TEXT("Unhandled value in ToModio(EModioModfilePlatform Platform)"));
return Modio::ModfilePlatform::Windows;
}

Modio::Portal ToModio(EModioPortal Portal)
{
switch (Portal)
Expand Down
Loading

0 comments on commit 69c167e

Please sign in to comment.