mirror of
https://github.com/Facepunch/Facepunch.Steamworks.git
synced 2025-04-12 12:30:05 +03:00
300 lines
8.8 KiB
C#
300 lines
8.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
namespace Facepunch.Steamworks
|
|
{
|
|
public partial class Client : IDisposable
|
|
{
|
|
Inventory _inv;
|
|
|
|
public Inventory Inventory
|
|
{
|
|
get
|
|
{
|
|
if ( _inv == null )
|
|
_inv = new Inventory( this );
|
|
|
|
return _inv;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class Inventory
|
|
{
|
|
/// <summary>
|
|
/// Called when the items are first retrieved, and when they change
|
|
/// </summary>
|
|
public Action OnUpdate;
|
|
|
|
internal Client client;
|
|
private int updateRequest = -1;
|
|
|
|
internal Inventory( Client c )
|
|
{
|
|
client = c;
|
|
|
|
LoadItemDefinitions();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Call this at least every two minutes, every frame doesn't hurt.
|
|
/// You should call it when you consider it active play time.
|
|
/// IE - your player is alive, and playing.
|
|
/// Don't stress on it too much tho cuz it's super hijackable anyway.
|
|
/// </summary>
|
|
public void PlaytimeHeartbeat()
|
|
{
|
|
client.native.inventory.SendItemDropHeartbeat();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Call this to retrieve the items.
|
|
/// Note that if this has already been called it won't
|
|
/// trigger a call to OnUpdate unless the items have changed
|
|
/// </summary>
|
|
public void Refresh()
|
|
{
|
|
// Pending
|
|
if ( updateRequest != -1 )
|
|
return;
|
|
|
|
if ( !client.native.inventory.GetAllItems( ref updateRequest ) )
|
|
{
|
|
Console.WriteLine( "GetAllItems failed!?" );
|
|
}
|
|
}
|
|
|
|
public Definition CreateDefinition( int id )
|
|
{
|
|
return new Definition( id )
|
|
{
|
|
client = client
|
|
};
|
|
}
|
|
|
|
internal void LoadItemDefinitions()
|
|
{
|
|
//
|
|
// Make sure item definitions are loaded, because we're going to be using them.
|
|
//
|
|
client.native.inventory.LoadItemDefinitions();
|
|
|
|
int[] ids;
|
|
if ( !client.native.inventory.GetItemDefinitionIDs( out ids ) )
|
|
return;
|
|
|
|
Definitions = ids.Select( x =>
|
|
{
|
|
var d = CreateDefinition( x );
|
|
d.SetupCommonProperties();
|
|
return d;
|
|
|
|
} ).ToArray();
|
|
}
|
|
|
|
internal T DefinitionProperty<T>( int i, string name )
|
|
{
|
|
string val = string.Empty;
|
|
client.native.inventory.GetItemDefinitionProperty( i, name, out val );
|
|
return (T) Convert.ChangeType( val, typeof( T) );
|
|
}
|
|
|
|
internal void DestroyResult()
|
|
{
|
|
if ( updateRequest == -1 ) return;
|
|
|
|
client.native.inventory.DestroyResult( updateRequest );
|
|
updateRequest = -1;
|
|
}
|
|
|
|
|
|
internal void Update()
|
|
{
|
|
UpdateRequest();
|
|
}
|
|
|
|
private void UpdateRequest()
|
|
{
|
|
if ( updateRequest == -1 )
|
|
return;
|
|
|
|
var status = (Valve.Steamworks.EResult) client.native.inventory.GetResultStatus( updateRequest );
|
|
|
|
if ( status == Valve.Steamworks.EResult.k_EResultPending )
|
|
return;
|
|
|
|
if ( status == Valve.Steamworks.EResult.k_EResultOK || status == Valve.Steamworks.EResult.k_EResultExpired )
|
|
{
|
|
RetrieveInventory();
|
|
return;
|
|
}
|
|
|
|
// Some other error
|
|
// Lets just retry.
|
|
DestroyResult();
|
|
Refresh();
|
|
}
|
|
|
|
/// <summary>
|
|
/// A list of items owned by this user. You should call Refresh() before trying to access this,
|
|
/// and then wait until it's non null or listen to OnUpdate to find out immediately when it's populated.
|
|
/// </summary>
|
|
public Item[] Items;
|
|
|
|
/// <summary>
|
|
/// You can send this data to a server, or another player who can then deserialize it
|
|
/// and get a verified list of items.
|
|
/// </summary>
|
|
public byte[] SerializedItems;
|
|
|
|
/// <summary>
|
|
/// Serialized data exprires after an hour. This is the time the value in SerializedItems will expire.
|
|
/// </summary>
|
|
public DateTime SerializedExpireTime;
|
|
|
|
private unsafe void RetrieveInventory()
|
|
{
|
|
Valve.Steamworks.SteamItemDetails_t[] items = null;
|
|
client.native.inventory.GetResultItems( updateRequest, out items );
|
|
|
|
Items = items.Select( x =>
|
|
{
|
|
return new Item()
|
|
{
|
|
client = client,
|
|
Quantity = x.m_unQuantity,
|
|
Id = x.m_itemId,
|
|
DefinitionId = x.m_iDefinition,
|
|
TradeLocked = ( (int)x.m_unFlags & (int)Valve.Steamworks.ESteamItemFlags.k_ESteamItemNoTrade ) != 0,
|
|
Definition = Definitions.FirstOrDefault( y => y.Id == x.m_iDefinition )
|
|
};
|
|
} ).ToArray();
|
|
|
|
//
|
|
// Get a serialized version
|
|
//
|
|
uint size = 0;
|
|
client.native.inventory.SerializeResult( updateRequest, IntPtr.Zero, ref size );
|
|
SerializedItems = new byte[size];
|
|
|
|
fixed( byte* b = SerializedItems )
|
|
{
|
|
client.native.inventory.SerializeResult( updateRequest, (IntPtr) b, ref size );
|
|
}
|
|
|
|
SerializedExpireTime = DateTime.Now.Add( TimeSpan.FromMinutes( 60 ) );
|
|
|
|
//
|
|
// Finished with this result, don't call it again
|
|
//
|
|
DestroyResult();
|
|
|
|
//
|
|
// Tell everyone we've got new items!
|
|
//
|
|
OnUpdate?.Invoke();
|
|
}
|
|
|
|
/// <summary>
|
|
/// An item in your inventory.
|
|
/// </summary>
|
|
public class Item
|
|
{
|
|
internal Client client;
|
|
|
|
public ulong Id;
|
|
public int Quantity;
|
|
|
|
public int DefinitionId;
|
|
public Definition Definition;
|
|
|
|
public bool TradeLocked;
|
|
}
|
|
|
|
/// <summary>
|
|
/// A list of items defined for this app.
|
|
/// This should be immediately populated and available.
|
|
/// </summary>
|
|
public Definition[] Definitions;
|
|
|
|
|
|
/// <summary>
|
|
/// An item definition. This describes an item in your Steam inventory, but is
|
|
/// not unique to that item. For example, this might be a tshirt, but you might be able to own
|
|
/// multiple tshirts.
|
|
/// </summary>
|
|
public class Definition
|
|
{
|
|
internal Client client;
|
|
|
|
public int Id;
|
|
public string Name;
|
|
public string Description;
|
|
|
|
public DateTime Created;
|
|
public DateTime Modified;
|
|
|
|
internal Definition( int id )
|
|
{
|
|
Id = id;
|
|
}
|
|
|
|
public T GetProperty<T>( string name )
|
|
{
|
|
string val = string.Empty;
|
|
|
|
if ( !client.native.inventory.GetItemDefinitionProperty( Id, name, out val ) )
|
|
return default( T );
|
|
|
|
try
|
|
{
|
|
return (T)Convert.ChangeType( val, typeof( T ) );
|
|
}
|
|
catch( System.Exception )
|
|
{
|
|
return default( T );
|
|
}
|
|
}
|
|
|
|
internal void SetupCommonProperties()
|
|
{
|
|
Name = GetProperty<string>( "name" );
|
|
Description = GetProperty<string>( "description" );
|
|
Created = GetProperty<DateTime>( "timestamp" );
|
|
Modified = GetProperty<DateTime>( "modified" );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Trigger an item drop. Call this when it's a good time to award
|
|
/// an item drop to a player. This won't automatically result in giving
|
|
/// an item to a player. Just call it every minute or so, or on launch.
|
|
/// ItemDefinition is usually a generator
|
|
/// </summary>
|
|
public void TriggerItemDrop()
|
|
{
|
|
int result = 0;
|
|
client.native.inventory.TriggerItemDrop( ref result, Id );
|
|
client.native.inventory.DestroyResult( result );
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Utility, given a "1;VLV250" string, convert it to a 2.5
|
|
/// </summary>
|
|
public static float PriceCategoryToFloat( string price )
|
|
{
|
|
price = price.Replace( "1;VLV", "" );
|
|
|
|
int iPrice = 0;
|
|
if ( !int.TryParse( price, out iPrice ) )
|
|
return 0.0f;
|
|
|
|
return int.Parse( price ) / 100.0f;
|
|
}
|
|
|
|
}
|
|
}
|