Skip to content

Commit 6adfe5a

Browse files
authored
Merge pull request #20 from modio/v2025.2
Syncing v2025.2
2 parents 589445d + f5f829b commit 6adfe5a

32 files changed

+279
-70
lines changed

Additional Packages.meta

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
2.92 KB
Binary file not shown.

Additional Packages/modio-gog.unitypackage.meta

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Experimental/modio-ui.unitypackage

3.47 KB
Binary file not shown.

Platform/GOG.meta

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Platform/Oculus/ModioPlatformExampleOculus.cs

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ void Awake()
1414
{
1515
// Oculus takes a fair bit of time to initialize and perform the entitlement check. By calling this
1616
// we're telling the plugin to wait until it's ready.
17+
// You MUST call ModioPlatform.SetInitializingFailed if you are later prevented from setting the ModioPlatform
1718
ModioPlatform.SetInitializing();
1819

1920
// Ensure you have your game's App Id set up in the Oculus Settings object provided by the Meta Platform SDK
@@ -24,6 +25,7 @@ void OnInitialize(Message<PlatformInitialize> message)
2425
{
2526
if (message.IsError)
2627
{
28+
ModioPlatform.SetInitializingFailed();
2729
Logger.Log(LogLevel.Error, $"initializing Oculus Platform: {message.GetError().Message}");
2830
return;
2931
}
@@ -37,6 +39,7 @@ void OnEntitled(Message message)
3739
{
3840
if (message.IsError)
3941
{
42+
ModioPlatform.SetInitializingFailed();
4043
Logger.Log(LogLevel.Error, $"Error checking Oculus Platform entitlement: {message.GetError().Message}");
4144
return;
4245
}

Platform/Oculus/ModioPlatformOculus.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,12 @@ public Task<Result> OpenCheckoutFlow(PortalSku sku)
178178
{
179179
if (message.IsError)
180180
{
181+
if (message.GetError().Code == 0)
182+
taskCompletionSource.SetResult(ResultBuilder.Create(ResultCode.Internal_OperationCancelled));
183+
181184
Logger.Log(LogLevel.Error, $"{message.GetError().Message}");
182185

183-
taskCompletionSource.SetResult(ResultBuilder.Create(ResultCode.Internal_OperationCancelled));
186+
taskCompletionSource.SetResult(ResultBuilder.Create(ResultCode.Unknown));
184187
return;
185188
}
186189

Platform/Steam/Facepunch/ModioPlatformFacepunch.cs

+38
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,44 @@ public override void OpenWebPage(string url)
8484
SteamFriends.OpenWebOverlay(url);
8585
#else
8686
base.OpenWebPage(url);
87+
#endif
88+
}
89+
90+
public override bool TryOpenVirtualKeyboard(
91+
string title,
92+
string text,
93+
string placeholder,
94+
ModioVirtualKeyboardType virtualKeyboardType,
95+
int characterLimit,
96+
bool multiline,
97+
Action<string> onClose)
98+
{
99+
#if UNITY_FACEPUNCH
100+
// This will not work in desktop mode of Steam Deck.
101+
// Facepunch does not support Steam deck API calls yet
102+
// TODO: If the above changes, re-implement to check if we're steam deck
103+
if (!SteamUtils.IsSteamInBigPictureMode) return false;
104+
105+
SteamUtils.OnGamepadTextInputDismissed += OnGamepadTextInputClose;
106+
107+
return SteamUtils.ShowGamepadTextInput(
108+
GamepadTextInputMode.Normal,
109+
multiline ? GamepadTextInputLineMode.MultipleLines : GamepadTextInputLineMode.SingleLine,
110+
title,
111+
characterLimit,
112+
placeholder
113+
);
114+
115+
void OnGamepadTextInputClose(bool isSubmitted)
116+
{
117+
if (!isSubmitted) return;
118+
119+
SteamUtils.OnGamepadTextInputDismissed -= OnGamepadTextInputClose;
120+
121+
onClose.Invoke(SteamUtils.GetEnteredGamepadText());
122+
}
123+
#else
124+
return false;
87125
#endif
88126
}
89127
}

Platform/Steam/Steamworks/ModioPlatformSteamworks.cs

+48
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ public class ModioPlatformSteamworks : ModioPlatform, IModioSsoPlatform
2020
private string betaStoreHomePage => $"https://store.steampowered.com/app/{appId}/modio/?beta=1";
2121
private string betaStoreInventoryPage => $"https://store.steampowered.com/itemstore/{appId}/browse/?filter=all";
2222

23+
#if UNITY_STEAMWORKS && !DISABLESTEAMWORKS
24+
Callback<GamepadTextInputDismissed_t> _virtualKeyboardCallback;
25+
#endif
26+
2327
public static void SetAsPlatform(uint appId)
2428
{
2529
ModioPlatformSteamworks.appId = appId;
@@ -97,6 +101,50 @@ public override void OpenWebPage(string url)
97101
SteamFriends.ActivateGameOverlayToWebPage(this.betaStoreHomePage, EActivateGameOverlayToWebPageMode.k_EActivateGameOverlayToWebPageMode_Modal);
98102
#else
99103
base.OpenWebPage(url);
104+
#endif
105+
}
106+
107+
public override bool TryOpenVirtualKeyboard(
108+
string title,
109+
string text,
110+
string placeholder,
111+
ModioVirtualKeyboardType virtualKeyboardType,
112+
int characterLimit,
113+
bool multiline,
114+
Action<string> onClose)
115+
{
116+
#if UNITY_STEAMWORKS && !DISABLESTEAMWORKS
117+
if (!SteamUtils.IsSteamRunningOnSteamDeck() || !SteamUtils.IsSteamInBigPictureMode())
118+
return false;
119+
120+
_virtualKeyboardCallback = Callback<GamepadTextInputDismissed_t>.Create(OnGamepadTextInputClose);
121+
122+
return SteamUtils.ShowGamepadTextInput(
123+
EGamepadTextInputMode.k_EGamepadTextInputModeNormal,
124+
multiline ? EGamepadTextInputLineMode.k_EGamepadTextInputLineModeMultipleLines : EGamepadTextInputLineMode.k_EGamepadTextInputLineModeSingleLine,
125+
title,
126+
(uint)characterLimit,
127+
placeholder
128+
);
129+
130+
void OnGamepadTextInputClose(GamepadTextInputDismissed_t result)
131+
{
132+
if (result.m_bSubmitted)
133+
{
134+
uint textLength = SteamUtils.GetEnteredGamepadTextLength();
135+
bool success = SteamUtils.GetEnteredGamepadTextInput(out string enteredText, textLength);
136+
137+
if (!success)
138+
Logger.Log(LogLevel.Warning, $"Failed to retrieve virtual keyboard text");
139+
else
140+
onClose.Invoke(enteredText);
141+
}
142+
143+
_virtualKeyboardCallback.Dispose();
144+
_virtualKeyboardCallback = null;
145+
}
146+
#else
147+
return false;
100148
#endif
101149
}
102150
}

Platform/SystemIO/ModIO.Implementation.Platform/PlatformConfiguration_SystemIO.cs

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ internal static partial class PlatformConfiguration
2020
#elif UNITY_STANDALONE_LINUX
2121
/// <summary>Holds the value for the platform header value to use in requests.</summary>
2222
public static RestApiPlatform RESTAPI_HEADER = RestApiPlatform.Linux;
23+
#elif MODIO_OCULUS
24+
/// <summary>Holds the value for the platform header value to use in requests.</summary>
25+
public static RestApiPlatform RESTAPI_HEADER = RestApiPlatform.Oculus;
2326
#elif UNITY_ANDROID
2427
/// <summary>Holds the value for the platform header value to use in requests.</summary>
2528
public static RestApiPlatform RESTAPI_HEADER = RestApiPlatform.Android;

README.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ sidebar_position: 0
77
---
88

99
<a href="https://mod.io"><img src="https://mod.io/images/branding/modio-logo-bluewhite.svg" alt="mod.io" width="360" align="right"/></a>
10-
# mod.io Unity Plugin v2024.11
11-
[![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/modio/modio-unity/blob/master/LICENSE)
10+
# mod.io Unity Plugin v2025.2
11+
[![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/modio/modio-unity/blob/master/Documentation/LICENSE.md)
1212
[![Discord](https://img.shields.io/discord/389039439487434752.svg?label=Discord&logo=discord&color=7289DA&labelColor=2C2F33)](https://discord.mod.io)
1313
[![Master docs](https://img.shields.io/badge/docs-master-green.svg)](https://docs.mod.io/unity/)
14-
[![Unity 3D](https://img.shields.io/badge/Unity-2020.3+-lightgrey.svg)](https://unity3d.com)
14+
[![Unity 3D](https://img.shields.io/badge/Unity-2021.3+-lightgrey.svg)](https://unity3d.com)
1515

1616
Welcome to the mod.io Unity Engine plugin [repository](https://github.com/modio/modio-unity)!
1717

@@ -23,9 +23,9 @@ mod.io enables game developers of all sizes to integrate user-generated content
2323
- Founded by the [ModDB.com](https://moddb.com) team, with over two decades of experience in the UGC space
2424
- Constantly evolving - we continue to work alongside our partners to iterate and improve our plugin support
2525

26-
The mod.io Unity Engine plugin is the simplest and fastest way to integrate UGC into your Unity **2020.3+** game. It handles all of the common tasks, allowing game developers to quickly and easily implement a solution that enables players to access and discover user-generated content for their games.
26+
The mod.io Unity Engine plugin is the simplest and fastest way to integrate UGC into your Unity **2021.3+** game. It handles all of the common tasks, allowing game developers to quickly and easily implement a solution that enables players to access and discover user-generated content for their games.
2727

28-
A custom built [ready-made UI](#browser-ui) for mod discovery is included, along with installation and collection management, and a full-featured [C# interface](#getting-started) which connects to the [mod.io REST API](https://docs.mod.io).
28+
A custom-built [ready-made UI](#browser-ui) for mod discovery is included, along with installation and collection management, and a full-featured [C# interface](#getting-started) which connects to the [mod.io REST API](https://docs.mod.io).
2929
> [!WARNING]
3030
> The Browser UI is scheduled for deprecation, and may not receive updates.
3131
> This is to be replaced with the [Component UI / Template UI](#component-ui)
@@ -87,7 +87,7 @@ The first thing you'll need to do is [create a game profile](https://mod.io/g/ad
8787
4. Use the *Insert URL* buttons to set the `server URL` depending on where you created your game profile earlier.
8888

8989
> [!WARNING]
90-
> Deselect the config file before entering Play mode. A known Unity bug can cause the Editor to crash in Unity 2019-2021.
90+
> Deselect the config file before entering Play mode. A known Unity bug can cause the Editor to crash.
9191
9292
Your setup is now complete. The following sections will guide you through getting your mod.io integration up and running quickly.
9393

Runtime/ModIO.Implementation/Classes/ModCollectionManager.cs

-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66
using ModIO.Implementation.API.Requests;
77
using Runtime.Enums;
88

9-
#if UNITY_GAMECORE
10-
using Unity.GameCore;
11-
#endif
12-
139
namespace ModIO.Implementation
1410
{
1511
/// <summary>

Runtime/ModIO.Implementation/Classes/ModIOUnityImplementation.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3470,7 +3470,7 @@ public static async Task<ResultAnd<Entitlement[]>> SyncEntitlements()
34703470

34713471
config = API.Requests.SyncEntitlements.OculusRequest(userId.value, device);
34723472
requestTask = WebRequestManager.Request<SyncEntitlements.ResponseSchema>(config);
3473-
#elif UNITY_STANDALONE && !UNITY_EDITOR
3473+
#elif UNITY_FACEPUNCH || UNITY_STEAMWORKS
34743474
config = API.Requests.SyncEntitlements.SteamRequest();
34753475
requestTask = WebRequestManager.Request<SyncEntitlements.ResponseSchema>(config);
34763476
#elif ((UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR) && MODIO_MOBILE_IAP

Runtime/ModIO.Implementation/Classes/ModIOVersion.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ internal struct ModIOVersion : System.IComparable<ModIOVersion>
55
{
66
// ---------[ Singleton ]---------
77
/// <summary>Singleton instance for current version.</summary>
8-
public static readonly ModIOVersion Current = new ModIOVersion(2024, 11, 0, "");
8+
public static readonly ModIOVersion Current = new ModIOVersion(2025, 2, 0, "");
99

1010
// ---------[ Fields ]---------
1111
/// <summary>Main Version number.</summary>

Runtime/ModIO.Implementation/Classes/ModManagement.cs

+76-29
Original file line numberDiff line numberDiff line change
@@ -428,27 +428,19 @@ static async Task<Result> PerformOperation_Download(ModManagementJob job)
428428
long modId = job.modEntry.modObject.id;
429429
long fileId = job.modEntry.modObject.modfile.id;
430430

431-
bool hasEnoughSpace;
432-
433-
// Consoles separate storage into temporary & persistent, so we check them separately
434-
if (Application.isConsolePlatform)
435-
{
436-
hasEnoughSpace =
437-
await DataStorage.temp.IsThereEnoughDiskSpaceFor(job.modEntry.modObject.modfile.filesize)
438-
&& await DataStorage.persistent.IsThereEnoughDiskSpaceFor(job.modEntry.modObject.modfile.filesize_uncompressed);
439-
}
440-
else
441-
{
442-
// On other platforms, these locations refer to the same drive
443-
long totalRequiredSpace = job.modEntry.modObject.modfile.filesize + job.modEntry.modObject.modfile.filesize_uncompressed;
444-
hasEnoughSpace = await DataStorage.persistent.IsThereEnoughDiskSpaceFor(totalRequiredSpace);
445-
}
431+
bool hasEnoughSpace = await HasEnoughSpace(job.modEntry.modObject.modfile.filesize,
432+
job.modEntry.modObject.modfile.filesize_uncompressed);
446433

447434
if(!hasEnoughSpace)
448435
{
449436
Logger.Log(LogLevel.Error, $"INSUFFICIENT STORAGE FOR DOWNLOAD [{modId}_{fileId}]");
450437
notEnoughStorageMods.Add((ModId)modId);
451-
return ResultBuilder.Create(ResultCode.IO_InsufficientStorage);
438+
439+
Result insufficientStorageResult = ResultBuilder.Create(ResultCode.IO_InsufficientStorage);
440+
InvokeModManagementDelegate((ModId)modId,
441+
ModManagementEventType.DownloadFailed,
442+
insufficientStorageResult);
443+
return insufficientStorageResult;
452444
}
453445

454446
Logger.Log(LogLevel.Message, $"DOWNLOADING MODFILE[{modId}_{fileId}]");
@@ -572,6 +564,25 @@ await DataStorage.temp.IsThereEnoughDiskSpaceFor(job.modEntry.modObject.modfile.
572564

573565
return result;
574566
}
567+
static async Task<bool> HasEnoughSpace(long tempBytes, long persistentBytes)
568+
{
569+
// Consoles separate storage into temporary & persistent, so we check them separately
570+
bool hasEnoughSpace;
571+
if (Application.isConsolePlatform)
572+
{
573+
hasEnoughSpace =
574+
await DataStorage.temp.IsThereEnoughDiskSpaceFor(tempBytes)
575+
&& await DataStorage.persistent.IsThereEnoughDiskSpaceFor(persistentBytes);
576+
}
577+
else
578+
{
579+
// On other platforms, these locations refer to the same drive
580+
long totalRequiredSpace = tempBytes + persistentBytes;
581+
hasEnoughSpace = await DataStorage.persistent.IsThereEnoughDiskSpaceFor(totalRequiredSpace);
582+
}
583+
584+
return hasEnoughSpace;
585+
}
575586

576587
static Result DownloadCleanup(Result result, long modId, long fileId)
577588
{
@@ -753,28 +764,21 @@ public static SubscribedModStatus GetModCollectionEntrysSubscribedModStatus(
753764
{
754765
if(DataStorage.TryGetInstallationDirectory(modId, currentFileId,
755766
out string _))
756-
{
757767
return SubscribedModStatus.WaitingToUninstall;
758-
}
768+
return SubscribedModStatus.None;
759769
}
760-
else if(DataStorage.TryGetInstallationDirectory(modId, currentFileId,
761-
out string _))
770+
if(DataStorage.TryGetInstallationDirectory(modId, currentFileId,
771+
out string _))
762772
{
763773
if(currentFileId != fileId)
764-
{
765774
return SubscribedModStatus.WaitingToUpdate;
766-
}
775+
return SubscribedModStatus.Installed;
767776
}
768-
else if(DataStorage.TryGetModfileArchive(modId, fileId, out string _))
777+
if(DataStorage.TryGetModfileArchive(modId, fileId, out string _))
769778
{
770779
return SubscribedModStatus.WaitingToInstall;
771780
}
772-
else
773-
{
774-
return SubscribedModStatus.WaitingToDownload;
775-
}
776-
777-
return SubscribedModStatus.Installed;
781+
return SubscribedModStatus.WaitingToDownload;
778782
}
779783

780784
static async Task<ModManagementJob> GetNextModManagementJob()
@@ -1011,5 +1015,48 @@ public static void RetryFailedDownload(ModId modid)
10111015
taintedMods.Remove(modid);
10121016
WakeUp();
10131017
}
1018+
public static async Task<Result> DoesHaveSpaceForMod(ModId modId)
1019+
{
1020+
long spaceRequired = 0;
1021+
long tempSpaceRequired = 0;
1022+
1023+
foreach ((ModId key, ModCollectionEntry modCollectionEntry) in ModCollectionManager.Registry.mods)
1024+
{
1025+
if (currentJob != null && currentJob.modEntry.modObject.id == key.id)
1026+
{
1027+
float progressRemaining = 1;
1028+
1029+
if(currentJob.progressHandle != null)
1030+
progressRemaining = (1 - currentJob.progressHandle.Progress);
1031+
1032+
if (currentJob.type == ModManagementOperationType.Download)
1033+
{
1034+
var currentSize = currentJob.modEntry.currentModfile.filesize;
1035+
tempSpaceRequired += (long)(progressRemaining * currentSize);
1036+
}
1037+
else
1038+
{
1039+
var currentSize = currentJob.modEntry.currentModfile.filesize_uncompressed;
1040+
spaceRequired += (long)(progressRemaining * currentSize);
1041+
}
1042+
continue;
1043+
}
1044+
1045+
var modStatus = GetModCollectionEntrysSubscribedModStatus(modCollectionEntry);
1046+
if (modStatus is SubscribedModStatus.None or SubscribedModStatus.Installed or SubscribedModStatus.ProblemOccurred)
1047+
continue;
1048+
1049+
if (modStatus is SubscribedModStatus.WaitingToDownload or SubscribedModStatus.Downloading)
1050+
tempSpaceRequired += currentJob.modEntry.currentModfile.filesize;
1051+
if (modStatus is SubscribedModStatus.WaitingToUninstall)
1052+
spaceRequired -= currentJob.modEntry.currentModfile.filesize_uncompressed;
1053+
else
1054+
spaceRequired += currentJob.modEntry.currentModfile.filesize_uncompressed;
1055+
}
1056+
bool hasEnoughSpace = await HasEnoughSpace(tempSpaceRequired, spaceRequired);
1057+
if(!hasEnoughSpace)
1058+
return ResultBuilder.Create(ResultCode.IO_InsufficientStorage);
1059+
return ResultBuilder.Success;
1060+
}
10141061
}
10151062
}

0 commit comments

Comments
 (0)