Code/NetTransaction.cs
using System;
using System.Threading.Tasks;
/// <summary>
/// Optimistic client prediction with host reconciliation. Apply a change instantly on the
/// client, send the request, rollback if the host rejects. Makes interactions feel zero-latency.
/// </summary>
/// <remarks>
/// <code>
/// await NetTransaction.Execute(
/// predict: () => { money -= 100; UpdateHUD(); },
/// request: new BuyReq { ItemId = "sword" },
/// rollback: () => { money += 100; ShowError("Failed"); }
/// );
/// </code>
/// </remarks>
public static class NetTransaction
{
/// <summary>Response structs implement this so NetTransaction knows if the host accepted or rejected.</summary>
public interface INetResponse
{
bool Success { get; }
}
/// <summary>
/// Execute a predicted transaction. Predict runs immediately. If the host's response
/// has <c>Success == false</c>, rollback fires.
/// </summary>
public static async Task<TResponse> Execute<TRequest, TResponse>(
Action predict,
TRequest request,
Action rollback
) where TResponse : INetResponse
{
predict?.Invoke();
var response = await NetRequest.Ask<TRequest, TResponse>( request );
if ( response == null || !response.Success )
rollback?.Invoke();
return response;
}
/// <summary>
/// Execute with a custom success check instead of INetResponse.
/// </summary>
public static async Task<TResponse> Execute<TRequest, TResponse>(
Action predict,
TRequest request,
Func<TResponse, bool> shouldRollback,
Action rollback
)
{
predict?.Invoke();
var response = await NetRequest.Ask<TRequest, TResponse>( request );
if ( shouldRollback != null && shouldRollback( response ) )
rollback?.Invoke();
return response;
}
}