Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

experiment: Unix Steam uses Steamworks.Net #1438

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 153 additions & 49 deletions src/XIVLauncher.Common.Unix/UnixSteam.cs
Original file line number Diff line number Diff line change
@@ -1,81 +1,185 @@
#nullable enable
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Steamworks;
using XIVLauncher.Common.PlatformAbstractions;

namespace XIVLauncher.Common.Unix
namespace XIVLauncher.Common.Unix;

/// <summary>
/// An implementation of ISteam for Unix endpoints. Borrows logic heavily from Facepunch's lib.
/// </summary>
public class UnixSteam : ISteam
{
public class UnixSteam : ISteam
private Callback<GamepadTextInputDismissed_t>? textDismissedCallback;

public void Initialize(uint appId)
{
public UnixSteam()
{
SteamUtils.OnGamepadTextInputDismissed += b => OnGamepadTextInputDismissed?.Invoke(b);
}
// workaround because SetEnvironmentVariable doesn't actually touch the process environment on unix
[System.Runtime.InteropServices.DllImport("c")]
static extern int setenv(string name, string value, int overwrite);

public void Initialize(uint appId)
{
// workaround because SetEnvironmentVariable doesn't actually touch the process environment on unix
[System.Runtime.InteropServices.DllImport("c")]
static extern int setenv(string name, string value, int overwrite);
setenv("SteamAppId", appId.ToString(), 1);
setenv("SteamGameId", appId.ToString(), 1);

setenv("SteamAppId", appId.ToString(), 1);
setenv("SteamGameId", appId.ToString(), 1);
this.IsValid = SteamAPI.Init();

SteamClient.Init(appId);
}
this.textDismissedCallback = Callback<GamepadTextInputDismissed_t>.Create(this.GamepadTextInputCallback);
}

public bool IsValid => SteamClient.IsValid;
public bool IsValid { get; private set; }

public bool BLoggedOn => SteamClient.IsLoggedOn;
public bool BLoggedOn => SteamUser.BLoggedOn();

public bool BOverlayNeedsPresent => SteamUtils.DoesOverlayNeedPresent;
public bool BOverlayNeedsPresent => SteamUtils.BOverlayNeedsPresent();

public void Shutdown()
{
SteamClient.Shutdown();
}
public void Shutdown()
{
this.textDismissedCallback?.Dispose();
SteamAPI.Shutdown();
this.IsValid = false;
}

public async Task<byte[]?> GetAuthSessionTicketAsync()
{
var ticket = await SteamUser.GetAuthSessionTicketAsync().ConfigureAwait(true);
return ticket?.Data;
}
public async Task<byte[]?> GetAuthSessionTicketAsync()
{
var result = EResult.k_EResultPending;
AuthTicket? ticket = null;
var stopwatch = Stopwatch.StartNew();

public bool IsAppInstalled(uint appId)
// We need to register our callback _before_ we can request our auth session ticket. Ignore the modified closure warning, this is what
// we expect after all.
using var cb = Callback<GetAuthSessionTicketResponse_t>.Create(t =>
{
return SteamApps.IsAppInstalled(appId);
}
// ReSharper disable AccessToModifiedClosure
if (ticket == null || t.m_hAuthTicket != ticket.Handle) return;

public string GetAppInstallDir(uint appId)
{
return SteamApps.AppInstallDir(appId);
}
result = t.m_eResult;
});

public bool ShowGamepadTextInput(bool password, bool multiline, string description, int maxChars, string existingText = "")
{
return SteamUtils.ShowGamepadTextInput(password ? GamepadTextInputMode.Password : GamepadTextInputMode.Normal, multiline ? GamepadTextInputLineMode.MultipleLines : GamepadTextInputLineMode.SingleLine, description, maxChars, existingText);
}
ticket = this.GetAuthSessionTicket();
if (ticket == null) return null;

public string GetEnteredGamepadText()
while (result == EResult.k_EResultPending)
{
return SteamUtils.GetEnteredGamepadText();
await Task.Delay(10);

if (stopwatch.Elapsed.TotalSeconds > 10)
{
ticket.Cancel();
return null;
}
}

public bool ShowFloatingGamepadTextInput(ISteam.EFloatingGamepadTextInputMode mode, int x, int y, int width, int height)
if (result == EResult.k_EResultOK)
{
// Facepunch.Steamworks doesn't have this...
return false;
return ticket.Data;
}

public bool IsRunningOnSteamDeck() => SteamUtils.IsRunningOnSteamDeck;
ticket.Cancel();
return null;
}

public bool IsAppInstalled(uint appId)
{
return SteamApps.BIsAppInstalled((AppId_t)appId);
}

public string GetAppInstallDir(uint appId)
{
SteamApps.GetAppInstallDir((AppId_t)appId, out var result, 1024);
return result;
}

public bool ShowGamepadTextInput(bool password, bool multiline, string description, int maxChars, string existingText = "")
{
return SteamUtils.ShowGamepadTextInput(
password ? EGamepadTextInputMode.k_EGamepadTextInputModePassword : EGamepadTextInputMode.k_EGamepadTextInputModeNormal,
multiline ? EGamepadTextInputLineMode.k_EGamepadTextInputLineModeMultipleLines : EGamepadTextInputLineMode.k_EGamepadTextInputLineModeSingleLine,
description,
(uint)maxChars,
existingText
);
}

public string GetEnteredGamepadText()
{
var length = SteamUtils.GetEnteredGamepadTextLength();
SteamUtils.GetEnteredGamepadTextInput(out var result, length);

return result;
}

public bool ShowFloatingGamepadTextInput(ISteam.EFloatingGamepadTextInputMode mode, int x, int y, int width, int height)
{
return SteamUtils.ShowFloatingGamepadTextInput((EFloatingGamepadTextInputMode)mode, x, y, width, height);
}

public bool DismissFloatingGamepadTextInput()
{
return SteamUtils.DismissFloatingGamepadTextInput();
}

public bool IsRunningOnSteamDeck()
{
return SteamUtils.IsSteamRunningOnSteamDeck();
}

public uint GetServerRealTime()
{
return SteamUtils.GetServerRealTime();
}

public void ActivateGameOverlayToWebPage(string url, bool modal = false)
{
var mode = modal
? EActivateGameOverlayToWebPageMode.k_EActivateGameOverlayToWebPageMode_Modal
: EActivateGameOverlayToWebPageMode.k_EActivateGameOverlayToWebPageMode_Default;

SteamFriends.ActivateGameOverlayToWebPage(url, mode);
}

public event Action<bool>? OnGamepadTextInputDismissed;

private void GamepadTextInputCallback(GamepadTextInputDismissed_t cb)
{
this.OnGamepadTextInputDismissed?.Invoke(cb.m_bSubmitted);
}

private AuthTicket? GetAuthSessionTicket()
{
var buffer = new byte[1024];
var ticket = SteamUser.GetAuthSessionTicket(buffer, buffer.Length, out var ticketLength);

if (ticket == HAuthTicket.Invalid) return null;

return new AuthTicket
{
Data = buffer.Take((int)ticketLength).ToArray(),
Handle = ticket
};
}

public uint GetServerRealTime() => (uint)((DateTimeOffset)SteamUtils.SteamServerTime).ToUnixTimeSeconds();
private class AuthTicket : IDisposable
{
public byte[]? Data { get; set; }
public HAuthTicket Handle { get; set; }

public void ActivateGameOverlayToWebPage(string url, bool modal = false)
public void Cancel()
{
SteamFriends.OpenWebOverlay(url, modal);
if (this.Handle != HAuthTicket.Invalid)
{
SteamUser.CancelAuthTicket(this.Handle);
}

this.Handle = HAuthTicket.Invalid;
this.Data = null;
}

public event Action<bool> OnGamepadTextInputDismissed;
public void Dispose()
{
this.Cancel();
}
}
}
}
6 changes: 3 additions & 3 deletions src/XIVLauncher.Common.Unix/XIVLauncher.Common.Unix.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<Description>Shared XIVLauncher platform-specific implementations for Unix-like systems.</Description>
<VersionPrefix>1.0.0</VersionPrefix>
<Nullable>disable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<PropertyGroup>
Expand Down Expand Up @@ -43,7 +44,6 @@
</ItemGroup>

<ItemGroup>
<!-- Custom steamworks, based on the chippy branch of Facepunch.Steamworks -->
<PackageReference Include="goaaats.Steamworks" Version="2.3.4" />
<PackageReference Include="Steamworks.NET" Version="20.1.0" />
</ItemGroup>
</Project>
</Project>
8 changes: 7 additions & 1 deletion src/XIVLauncher.Common.Windows/WindowsSteam.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,12 @@ public bool ShowFloatingGamepadTextInput(ISteam.EFloatingGamepadTextInputMode mo
return false;
}

public bool DismissFloatingGamepadTextInput()
{
// Facepunch.Steamworks doesn't have this...
return false;
}

public bool IsRunningOnSteamDeck() => SteamUtils.IsRunningOnSteamDeck;

public uint GetServerRealTime() => (uint)((DateTimeOffset)SteamUtils.SteamServerTime).ToUnixTimeSeconds();
Expand All @@ -172,4 +178,4 @@ public void ActivateGameOverlayToWebPage(string url, bool modal = false)

public event Action<bool> OnGamepadTextInputDismissed;
}
}
}
3 changes: 2 additions & 1 deletion src/XIVLauncher.Common/PlatformAbstractions/ISteam.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public interface ISteam
bool ShowGamepadTextInput(bool password, bool multiline, string description, int maxChars, string existingText = "");
string GetEnteredGamepadText();
bool ShowFloatingGamepadTextInput(EFloatingGamepadTextInputMode mode, int x, int y, int width, int height);
bool DismissFloatingGamepadTextInput();
bool IsRunningOnSteamDeck();
uint GetServerRealTime();
public void ActivateGameOverlayToWebPage(string url, bool modal = false);
Expand All @@ -29,4 +30,4 @@ enum EFloatingGamepadTextInputMode
}

event Action<bool> OnGamepadTextInputDismissed;
}
}