Skip to content

Instantly share code, notes, and snippets.

@sunnamed434
Last active June 5, 2024 11:50
Show Gist options
  • Save sunnamed434/e0311ae4996dcd9a221c43706d0ee568 to your computer and use it in GitHub Desktop.
Save sunnamed434/e0311ae4996dcd9a221c43706d0ee568 to your computer and use it in GitHub Desktop.
Usage example for new thing in Unturned SteamPlayer.OnSteamAuthTicketForWebApiReceived
// Sharing this because I didn't found a use case for it for UnturnedGuard, so instead of removing it in trash I decided to share it with everyone, feel free to use that
// You will also need to integrate UniTask in you plugin, but you can remove it and make it different ways, doesn't matter (you can check openmod how it work)
// Warning!: No Help/Support/Guarantees that it work or something. I'd recommend to just use it as learning thing, to understand how this new feature work actually. But yeah if you want to criticize my shit code then why not
// AsyncHelper is the same thing as Task.Run() (you can check openmod how it work)
using RocketLogger = Rocket.Core.Logging.Logger;
using UnityTime = UnityEngine.Time;
public delegate void SteamAuthTicketReceiveFail(ulong steamId, SteamAuthTicketFailReason reason);
public delegate void SteamAuthTicketReceiveSuccess(ulong steamId, Player player, string receivedIdentity, string originalIdentity, byte[] ticket);
public enum SteamAuthTicketFailReason
{
TicketNotReceivedAfterDelay,
PlayerDisconnected,
}
public interface ISteamAuthTicket
{
void Request(
Player player,
SteamAuthTicketReceiveFail failCallback, SteamAuthTicketReceiveSuccess successCallback,
CancellationToken cancellationToken);
}
public class SteamAuthTicket : IDisposable, ISteamAuthTicket
{
private static readonly TimeSpan TicketReceiveCheckTime = TimeSpan.FromMinutes(3);
private readonly string _steamAuthTicketIdentity;
private readonly ConcurrentDictionary<ulong, TicketRequest> _requestedTickets;
private readonly ConcurrentDictionary<ulong, TicketReceive> _receivedTickets;
public SteamAuthTicket()
{
_steamAuthTicketIdentity = CreateSteamAuthIdentity();
_requestedTickets = [];
_receivedTickets = [];
Provider.onServerDisconnected += OnPlayerDisconnected;
SteamPlayer.OnSteamAuthTicketForWebApiReceived += OnSteamAuthTicketReceived;
}
public void Request(
Player player,
SteamAuthTicketReceiveFail failCallback,
SteamAuthTicketReceiveSuccess successCallback,
CancellationToken cancellationToken)
{
var steamId = player.GetSteamId();
var uSteamId = steamId.m_SteamID;
if (_requestedTickets.ContainsKey(uSteamId))
{
return;
}
var identity = _steamAuthTicketIdentity;
player.channel.owner.RequestSteamAuthTicketForWebApi(identity);
var realtime = UnityTime.realtimeSinceStartup;
_requestedTickets[uSteamId] = new TicketRequest(failCallback, successCallback, identity, realtime);
RequestInternal(player, uSteamId, identity, realtime, cancellationToken);
}
public void Dispose()
{
Provider.onServerDisconnected -= OnPlayerDisconnected;
SteamPlayer.OnSteamAuthTicketForWebApiReceived -= OnSteamAuthTicketReceived;
}
private void RequestInternal(Player player, ulong uSteamId, string identity, float realtime, CancellationToken cancellationToken)
{
AsyncHelper.Schedule(nameof(RequestInternal), async () =>
{
async UniTask FireFailCallbackTask(ulong steamId, SteamAuthTicketFailReason reason, SteamAuthTicketReceiveFail callback)
{
await UniTask.SwitchToMainThread();
callback.Invoke(steamId, reason);
}
async UniTask FireSuccessCallbackTask(ulong steamId, Player player, string receivedIdentity, string originalIdentity, byte[] ticket, SteamAuthTicketReceiveSuccess callback)
{
await UniTask.SwitchToMainThread();
callback.Invoke(steamId, player, receivedIdentity, originalIdentity, ticket);
}
await Task.Delay(TicketReceiveCheckTime, cancellationToken);
if (_requestedTickets.TryGetValue(uSteamId, out var requestedTicket) == false)
{
return;
}
if (requestedTicket.IsSameTicket(realtime) == false)
{
return;
}
if (_receivedTickets.TryGetValue(uSteamId, out var receivedTicket) == false)
{
try
{
await FireFailCallbackTask(uSteamId, SteamAuthTicketFailReason.TicketNotReceivedAfterDelay, requestedTicket.FailCallback).AsTask();
}
catch (Exception ex)
{
RocketLogger.LogException(ex, $"An error occured while executing {nameof(FireFailCallbackTask)}");
}
_requestedTickets.TryRemove(uSteamId, out _);
return;
}
try
{
await FireSuccessCallbackTask(uSteamId, player, receivedTicket.Identity, identity, receivedTicket.Ticket, requestedTicket.SuccessCallback).AsTask();
}
catch (Exception ex)
{
RocketLogger.LogException(ex, $"An error occured while executing {nameof(FireSuccessCallbackTask)}");
}
finally
{
_requestedTickets.TryRemove(uSteamId, out _);
_receivedTickets.TryRemove(uSteamId, out _);
}
});
}
private void OnSteamAuthTicketReceived(SteamPlayer steamPlayer, string identity, byte[] ticket)
{
if (string.IsNullOrWhiteSpace(identity))
{
return;
}
var steamId = steamPlayer.playerID.steamID.m_SteamID;
if (_requestedTickets.TryGetValue(steamId, out var ticketRequest) == false)
{
return;
}
if (ticketRequest.Identity != identity)
{
return;
}
_receivedTickets[steamId] = new TicketReceive(identity, ticket);
}
private void OnPlayerDisconnected(CSteamID steamId)
{
if (_requestedTickets.TryGetValue(steamId.m_SteamID, out var requestedTicket) == false)
{
return;
}
try
{
requestedTicket.FailCallback(steamId.m_SteamID, SteamAuthTicketFailReason.PlayerDisconnected);
}
catch (Exception ex)
{
RocketLogger.LogException(ex, $"An error occured while firing {nameof(requestedTicket.FailCallback)}");
}
finally
{
_requestedTickets.TryRemove(steamId.m_SteamID, out _);
}
}
/// <summary>
/// Make identity unique every time. Against cheaters/players which could understand that this server has this thing on the server
/// </summary>
private static string CreateSteamAuthIdentity()
{
return Guid
.NewGuid()
.ToString("N")[..4];
}
private class TicketRequest
{
public TicketRequest(
SteamAuthTicketReceiveFail failCallback, SteamAuthTicketReceiveSuccess successCallback,
string identity, float realtime)
{
FailCallback = failCallback;
SuccessCallback = successCallback;
Identity = identity;
Realtime = realtime;
}
public SteamAuthTicketReceiveFail FailCallback { get; }
public SteamAuthTicketReceiveSuccess SuccessCallback { get; }
public string Identity { get; }
public float Realtime { get; }
public bool IsSameTicket(float requestRealtime)
{
return Realtime == requestRealtime;
}
}
private class TicketReceive
{
public TicketReceive(string identity, byte[] ticket)
{
Identity = identity;
Ticket = ticket;
}
public string Identity { get; }
public byte[] Ticket { get; }
}
}
// Usage:
Provider.onServerConnected += OnPlayerConnected;
var steamAuthTicket = new SteamAuthTicket(); // don't forget to dispose it somewhere
private void OnPlayerConnected(CSteamID steamId)
{
steamAuthTicket.Request(steamId.m_SteamID, OnSteamAuthTicketReceiveFail, OnSteamAuthTicketReceiveSuccess);
}
private void OnSteamAuthTicketReceiveFail(ulong steamId, SteamAuthTicketFailReason reason)
{
// ticket is not received
}
private void OnSteamAuthTicketReceiveSuccess(ulong steamId, Player player, string receivedIdentity, string originalIdentity, byte[] ticket)
{
// ticked received
}
@sunnamed434
Copy link
Author

@rube200 sure, so what do you think about improving of the dispose?

@rube200
Copy link

rube200 commented Jun 4, 2024

Clear lists at least and maybe(not sure) call fail for every remaining request on it

@sunnamed434
Copy link
Author

Oh yeah nice idea

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment