#define STEAM
using System;
using UnityEngine;
using ManagedSteam;
using ManagedSteam.Exceptions;
using ManagedSteam.CallbackStructures;
using ManagedSteam.SteamTypes;
using ManagedSteam.Utility;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.IO;
public class Steamworks : MonoBehaviour
{
static Steamworks instance;
static Steam steam { get; set; }
/// <summary>
/// The ID of this game.
/// </summary>
static internal GameID gameID;
/// <summary>
/// Whether the Steam API is available.
/// </summary>
static public bool isActive { get { return steam != null; } }
/// <summary>
/// Steam ID is a fairly long number.
/// </summary>
static public string steamID { get { return steam != null ? steam.User.GetSteamID().ToString() : (mID ?? SystemInfo.deviceUniqueIdentifier); } }
/// <summary>
/// Get the Steam username.
/// </summary>
static public string username
{
get
{
if (steam == null) return mUsername;
return steam.Friends.GetFriendPersonaName(steam.User.GetSteamID());
}
}
static string mID = null;
static string mUsername = "Guest";
void Awake ()
{
// Makes sure that only one instance of this object is in use at a time
if (instance == null)
{
instance = this;
bool error = false;
try
{
Steam.RestartAppIfNecessary(<your App's Steam ID>);
try
{
steam = Steam.Initialize();
}
catch (AlreadyLoadedException e)
{
Debug.LogError("The native dll is already loaded, this should not happen if ReleaseManagedResources is used and Steam.Initialize() is only called once.");
Debug.LogError(e.Message, this);
error = true;
}
catch (SteamInitializeFailedException e)
{
Debug.LogError("Could not initialize the native Steamworks API. This is usually caused by a missing steam_appid.txt file or if the Steam client is not running.");
Debug.LogError(e.Message, this);
error = true;
}
catch (SteamInterfaceInitializeFailedException e)
{
Debug.LogError("Could not initialize the wanted versions of the Steamworks API. Make sure that you have the correct Steamworks SDK version. See the documentation for more info.");
Debug.LogError(e.Message, this);
error = true;
}
catch (DllNotFoundException e)
{
Debug.LogError("Could not load a dll file. Make sure that the steam_api.dll/libsteam_api.dylib file is placed at the correct location. See the documentation for more info.");
Debug.LogError(e.Message, this);
error = true;
}
}
catch (Exception)
{
error = true;
}
if (!error)
{
gameID = new GameID(steam.AppID.AsUInt64);
RegisterListeners();
}
}
}
/// <summary>
/// Open the specified URL.
/// </summary>
static public void OpenURL (string url)
{
#if UNITY_EDITOR
Application.OpenURL(url);
#else
if (!string.IsNullOrEmpty(url))
{
if (steam != null) steam.Friends.ActivateGameOverlayToWebPage(url);
else Application.OpenURL(url);
}
#endif
}
/// <summary>
/// Return the specified player's Steam name, or
'null' if the player
is not on the friends list
. /// </summary>
static public string GetFriendName (string steamID)
{
if (isActive)
{
if (mFriends.Count == 0)
{
IFriends fr = steam.Friends;
int count = fr.GetFriendCount(FriendFlags.All);
for (int i = 0; i < count; ++i)
{
SteamID sid = fr.GetFriendByIndex(i, FriendFlags.All);
mFriends.Add(sid.ToString(), fr.GetFriendPersonaName(sid));
}
}
string fn;
if (mFriends.TryGetValue(steamID, out fn)) return fn;
}
return null;
}
/// <summary>
/// Increment the specified stat.
/// </summary>
static internal int IncrementStat (string name, bool sync = true)
{
int val = GetInt(name) + 1;
SetInt(name, val, sync);
return val;
}
/// <summary>
/// Get the specified integer-based stat.
/// </summary>
static internal int GetInt (string name, int val = 0)
{
#if STEAM
if (steam != null)
{
int i = 0;
if (steam.Stats.GetStat(name, out i)) return i;
}
#endif
return PlayerPrefs.GetInt(name, val);
}
/// <summary>
/// Get the specified float-based stat.
/// </summary>
static internal float GetFloat (string name, float val = 0f)
{
#if STEAM
if (steam != null)
{
float f = 0f;
if (steam.Stats.GetStat(name, out f)) return f;
}
#endif
return PlayerPrefs.GetFloat(name, val);
}
/// <summary>
/// Set the specified stat.
/// </summary>
static internal void SetInt (string name, int val, bool sync = true)
{
PlayerPrefs.SetInt(name, val);
#if STEAM
if (steam != null && steam.Stats.SetStat(name, val) && sync)
steam.Stats.StoreStats();
#endif
}
/// <summary>
/// Set the specified stat.
/// </summary>
static internal void SetFloat (string name, float val, bool sync = true)
{
PlayerPrefs.SetFloat(name, val);
#if STEAM
if (steam != null && steam.Stats.SetStat(name, val) && sync)
steam.Stats.StoreStats();
#endif
}
/// <summary>
/// Unlock the specified achievement.
/// </summary>
static internal bool UnlockAchievement (string name, bool sync = true)
{
#if STEAM
if (steam != null)
{
if (GetAchievement(name)) return false;
if (steam.Stats.SetAchievement(name))
{
if (sync) steam.Stats.StoreStats();
#if UNITY_EDITOR
Debug.Log("Unlocked " + name);
#endif
return true;
}
#if UNITY_EDITOR
else Debug.Log("Unable to unlock " + name);
#endif
}
#endif
return false;
}
/// <summary>
/// Update the specified achievement progress.
/// </summary>
static internal void ShowAchievementProgress (string name, int current, int max, bool sync = true)
{
#if STEAM
if (steam != null)
{
if (GetAchievement(name)) return;
if (steam.Stats.IndicateAchievementProgress(name, (uint)current, (uint)max))
{
if (sync) steam.Stats.StoreStats();
}
}
#endif
}
/// <summary>
/// Unlock the specified achievement.
/// </summary>
static internal void ClearAchievement (string name)
{
#if STEAM
if (steam != null)
{
steam.Stats.ClearAchievement(name);
steam.Stats.StoreStats();
}
#endif
}
/// <summary>
/// Immediately sync all player stats and achievements.
/// </summary>
static internal void SyncStatsAndAchievements ()
{
#if STEAM
if (steam != null) steam.Stats.StoreStats();
#endif
}
/// <summary>
/// Unlock the specified achievement.
/// </summary>
static internal bool GetAchievement (string name)
{
#if STEAM
if (steam != null)
{
StatsGetAchievementResult result = steam.Stats.GetAchievement(name);
return (result.result && result.sender);
}
#endif
return false;
}
/// <summary>
/// Show the Steam Overlay.
/// </summary>
static internal void ShowOverlay ()
{
#if STEAM
steam.Friends.ActivateGameOverlay(OverlayDialog.Friends);
#endif
}
/// <summary>
/// Save the specified file to the Steam cloud.
/// </summary>
static internal bool SaveFile (string filename, byte[] bytes)
{
#if STEAM
if (steam == null) return false;
if (bytes != null && bytes.Length > 0)
{
IntPtr ptr = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, ptr, bytes.Length);
bool retVal = steam.Cloud.Write(filename, ptr, bytes.Length);
Marshal.FreeHGlobal(ptr);
return retVal;
}
else
{
steam.Cloud.Delete(filename);
return true;
}
#else
return false;
#endif
}
/// <summary>
/// Whether the specified file is present or not.
/// </summary>
static internal bool HasFile (string filename)
{
#if STEAM
string[] list = GetFiles();
if (list != null) foreach (string s in list) if (s == filename) return true;
#endif
return false;
}
/// <summary>
/// Read the specified file from the Steam cloud.
/// </summary>
static internal byte[] ReadFile (string filename)
{
byte[] bytes = null;
#if STEAM
if (steam == null) return bytes;
int maxBytes = steam.Cloud.GetSize(filename);
if (maxBytes == 0) return bytes;
IntPtr ptr = Marshal.AllocHGlobal(maxBytes);
int bytesRead = steam.Cloud.Read(filename, ptr, maxBytes);
if (bytesRead > 0)
{
bytes
= new byte[bytesRead
]; Marshal.Copy(ptr, bytes, 0, bytesRead);
}
Marshal.FreeHGlobal(ptr);
#endif
return bytes;
}
/// <summary>
/// Delete the specified file from the Steam cloud.
/// </summary>
static internal void DeleteFile (string filename)
{
#if STEAM
if (steam != null) steam.Cloud.Delete(filename);
#endif
}
/// <summary>
/// Get the list of saved files from Steam.
/// </summary>
static internal string[] GetFiles ()
{
#if STEAM
if (steam == null || steam.Cloud == null) return null;
int count = steam.Cloud.GetFileCount();
if (count == 0) return null;
string[] filenames
= new string[count
];
for (int i = 0; i < count; ++i)
{
CloudGetFileNameAndSizeResult result = steam.Cloud.GetFileNameAndSize(i);
filenames[i] = result.result;
}
return filenames;
#else
return null;
#endif
}
/// <summary>
/// Command-line argument used to auto-connect an external server.
/// </summary>
static internal string autoConnectString
{
get
{
#if STEAM
try
{
bool connectStringIsNext = false;
string[] args = System.Environment.GetCommandLineArgs();
for (int i = 0; i < args.Length; ++i)
{
string val = args[i];
if (connectStringIsNext) return val;
else if (val == "+connect") connectStringIsNext = true;
}
}
catch (System.Exception) {}
#endif
return null;
}
}
/// <summary>
/// Set the game status. Friends will be able to right-click and see this status, then possibly join the game.
/// </summary>
static internal void SetPublicStatus (string status, System.Net.IPEndPoint address = null)
{
#if STEAM && !UNITY_EDITOR
if (steam != null)
{
steam.Friends.SetRichPresence("status", status);
if (address != null) steam.Friends.SetRichPresence("connect", "+connect " + address);
else steam.Friends.SetRichPresence("connect", "");
}
#endif
}
void RegisterListeners ()
{
steam.ExceptionThrown += ExceptionThrown;
steam.Friends.GameOverlayActivated += OverlayToggle;
steam.Friends.GameServerChangeRequested += OnServerChange;
steam.Friends.GameRichPresenceJoinRequested += OnJoinRequest;
steam.Stats.UserStatsReceived += UserStatsReceived;
steam.Stats.UserStatsStored += UserStatsStored;
steam.Stats.RequestCurrentStats();
}
void UnregisterListeners ()
{
steam.ExceptionThrown -= ExceptionThrown;
steam.Friends.GameOverlayActivated -= OverlayToggle;
steam.Friends.GameServerChangeRequested -= OnServerChange;
steam.Friends.GameRichPresenceJoinRequested -= OnJoinRequest;
steam.Stats.UserStatsReceived -= UserStatsReceived;
steam.Stats.UserStatsStored -= UserStatsStored;
}
void UserStatsReceived (UserStatsReceived value)
{
#if UNITY_EDITOR
if (value.GameID == gameID)
{
if (value.Result == ManagedSteam.SteamTypes.Result.OK)
UnityEngine.Debug.Log("Stats downloaded");
}
#endif
}
void UserStatsStored (UserStatsStored value)
{
#if UNITY_EDITOR
if (value.GameID == gameID)
{
if (value.Result != ManagedSteam.SteamTypes.Result.OK)
{
UnityEngine.Debug.Log("Stats saved to the server successfully.");
}
}
#endif
}
void OverlayToggle (GameOverlayActivated value)
{
// This method is called when the game overlay is hidden or shown
// NOTE: The overlay may not work when a game is run in the Unity editor
// Build the game and "publish" it to a local content server and then start the game via the
// steam client to make the overlay work.
if (value.Active) UIGameWindow.PauseGame();
}
void OnServerChange (GameServerChangeRequested value)
{
NGUIDebug.Log("OnServerChange: " + value.Server);
}
void OnJoinRequest (GameRichPresenceJoinRequested req)
{
Debug.Log("Join request: " + req.Connect + "\n" + req.SteamIDFriend);
}
void ExceptionThrown (Exception e)
{
// This method is called when an exception have been thrown from native code.
// We print the exception so we can see what went wrong.
NGUIDebug.Log(e.GetType().Name + ": " + e.Message + "\n" + e.StackTrace);
}
void Update () { if (steam != null) steam.Update(); }
void OnDestroy ()
{
if (instance == this)
{
instance = null;
Cleanup();
}
}
void OnApplicationQuit () { Cleanup(); }
void Cleanup ()
{
if (steam != null)
{
UnregisterListeners();
if (Application.isEditor)
{
// Only release managed handles if we run from inside the editor. This enables us
// to use the library again without restarting the editor.
steam.ReleaseManagedResources();
}
else
{
// We are running from a standalone build. Shutdown the library completely
steam.Shutdown();
}
steam = null;
}
}
}