增加steam

This commit is contained in:
yaoyanwei
2025-08-11 15:00:21 +08:00
parent 8fda4168b3
commit 65ee6c6eeb
288 changed files with 34032 additions and 27 deletions

View File

@@ -152,6 +152,25 @@ namespace TcgEngine
onLogin?.Invoke(login_res);
return login_res;
}
public async Task<LoginResponse> SteamLogin(string email, string username, string password)
{
Logout(); //Disconnect
LoginRequest data = new LoginRequest();
data.email = email;
data.username = username;
data.password = password;
string url = ServerURL + "/auth/steam";
string json = ApiTool.ToJson(data);
WebResponse res = await SendPostRequest(url, json);
LoginResponse login_res = GetLoginRes(res);
AfterLogin(login_res);
onLogin?.Invoke(login_res);
return login_res;
}
public async Task<LoginResponse> RefreshLogin()
{

View File

@@ -1,7 +1,15 @@
using System.Collections;
using System.Collections.Generic;
#if UNITY_STANDALONE_WIN || UNITY_STANDALONE_LINUX || UNITY_STANDALONE_OSX
#define STEAMWORKS_ENABLED
#endif
#if STEAMWORKS_ENABLED
using Steamworks;
#endif
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using TcgEngine; // 添加TcgEngine命名空间引用以访问SteamCurrencyManager
namespace TcgEngine.UI
{
@@ -36,12 +44,141 @@ namespace TcgEngine.UI
private bool clicked = false;
private static LoginMenu instance;
#if STEAMWORKS_ENABLED
private Callback<GameOverlayActivated_t> m_GameOverlayActivated;
private Callback<UserStatsReceived_t> m_UserStatsReceived;
private Callback<UserStatsStored_t> m_UserStatsStored;
private Callback<SteamServersConnected_t> m_SteamServersConnected;
private Callback<SteamServersDisconnected_t> m_SteamServersDisconnected;
private Callback<ValidateAuthTicketResponse_t> m_ValidateAuthTicketResponse;
#endif
void Awake()
{
instance = this;
#if STEAMWORKS_ENABLED
// 确保SteamManager存在
if (SteamManager.Initialized)
{
// 注册 Steam 回调
m_GameOverlayActivated = Callback<GameOverlayActivated_t>.Create(OnGameOverlayActivated);
m_UserStatsReceived = Callback<UserStatsReceived_t>.Create(OnUserStatsReceived);
m_UserStatsStored = Callback<UserStatsStored_t>.Create(OnUserStatsStored);
m_SteamServersConnected = Callback<SteamServersConnected_t>.Create(OnSteamServersConnected);
m_SteamServersDisconnected = Callback<SteamServersDisconnected_t>.Create(OnSteamServersDisconnected);
m_ValidateAuthTicketResponse = Callback<ValidateAuthTicketResponse_t>.Create(OnValidateAuthTicketResponse);
Debug.Log("Steam initialized through SteamManager");
}
// 初始化Steam货币系统
if (SteamCurrencyManager.instance == null)
{
GameObject steamCurrencyGO = new GameObject("SteamCurrencyManager");
SteamCurrencyManager currencyManager = steamCurrencyGO.AddComponent<SteamCurrencyManager>();
currencyManager.InitializeSteamCurrency();
}
else
{
SteamCurrencyManager.instance.InitializeSteamCurrency();
}
#endif
}
#if STEAMWORKS_ENABLED
private void OnGameOverlayActivated(GameOverlayActivated_t pCallback)
{
if (pCallback.m_bActive != 0)
{
Debug.Log("Steam overlay now active");
}
else
{
Debug.Log("Steam overlay now inactive");
}
}
private void OnUserStatsReceived(UserStatsReceived_t pCallback)
{
if ((ulong)pCallback.m_nGameID == SteamUtils.GetAppID().m_AppId && pCallback.m_eResult == EResult.k_EResultOK)
{
Debug.Log("Received stats and achievements from Steam");
}
else
{
Debug.Log("Failed to receive stats and achievements from Steam");
}
}
private void OnUserStatsStored(UserStatsStored_t pCallback)
{
if (pCallback.m_eResult == EResult.k_EResultOK)
{
Debug.Log("Stored stats and achievements to Steam");
}
else
{
Debug.Log("Failed to store stats and achievements to Steam");
}
}
private void OnSteamServersConnected(SteamServersConnected_t pCallback)
{
Debug.Log("Connected to Steam servers");
}
private void OnSteamServersDisconnected(SteamServersDisconnected_t pCallback)
{
Debug.Log("Disconnected from Steam servers");
}
private void OnValidateAuthTicketResponse(ValidateAuthTicketResponse_t pCallback)
{
Debug.Log("Auth ticket response received");
}
public void UnlockAchievement(string achievementId)
{
if (SteamManager.Initialized)
{
bool ret = SteamUserStats.SetAchievement(achievementId);
if (ret)
{
SteamUserStats.StoreStats();
Debug.Log("Unlocked achievement: " + achievementId);
}
}
}
public void UpdateStat(string statName, int value)
{
if (SteamManager.Initialized)
{
bool ret = SteamUserStats.SetStat(statName, value);
if (ret)
{
SteamUserStats.StoreStats();
Debug.Log($"Updated stat {statName} to {value}");
}
}
}
public string GetSteamUserName()
{
if (SteamManager.Initialized && SteamUser.GetSteamID().IsValid())
{
return SteamFriends.GetPersonaName();
}
return "";
}
public ulong GetSteamID()
{
if (SteamManager.Initialized && SteamUser.GetSteamID().IsValid())
{
return SteamUser.GetSteamID().m_SteamID;
}
return 0;
}
#endif
private void Start()
{
AudioTool.Get().PlayMusic("music", music);
@@ -117,6 +254,27 @@ namespace TcgEngine.UI
{
SceneNav.GoTo("Menu");
}
#if STEAMWORKS_ENABLED
else if (SteamManager.Initialized && SteamUser.GetSteamID().IsValid())
{
string steamID = SteamUser.GetSteamID().ToString();
string personaName = SteamFriends.GetPersonaName();
Debug.Log("Steam User: " + personaName+"("+steamID+")");
bool success2 = await Authenticator.Get().SteamLogin(personaName+"@shenynet.com", personaName, steamID+"@shenynet.com");
if (success2)
{
Debug.Log("Steam User Success: " + personaName);
}
else
{
Debug.Log("Steam User Failure: " + Authenticator.Get().GetError());
}
// 请求当前用户的统计信息
SteamUserStats.RequestUserStats(SteamUser.GetSteamID());
SceneNav.GoTo("Menu");
}
#endif
else
{
login_panel.Show();

View File

@@ -274,6 +274,22 @@ namespace TcgEngine.UI
{
SettingsPanel.Get().Show();
}
public void OnClickRecharge()
{
#if STEAMWORKS_ENABLED
if (SteamCurrencyManager.instance != null && SteamCurrencyManager.instance.IsInitialized())
{
SteamCurrencyManager.instance.PurchaseGoldPackage(100, 99);
}
else
{
Debug.LogError("Steam currency system not initialized");
}
#else
Debug.LogWarning("Steamworks not enabled");
#endif
}
public void FadeToScene(string scene)
{

View File

@@ -30,12 +30,20 @@ namespace TcgEngine
public virtual async Task<bool> Login(string username, string token)
{
return await Login(username); //Some authenticator dont define this function
await Task.Yield(); //Do nothing
return false;
}
public virtual async Task<bool> SteamLogin(string email,string username, string password)
{
await Task.Yield(); //Do nothing
return false;
}
public virtual async Task<bool> RefreshLogin()
{
return await Login(username); //Same as Login if not defined
await Task.Yield(); //Do nothing
return false;
}
//Bypass login system by just assigning your own values, for testing
@@ -48,7 +56,8 @@ namespace TcgEngine
public virtual async Task<bool> Register(string username, string email, string token)
{
return await Login(username, token); //Some authenticator dont define this function
await Task.Yield(); //Do nothing
return false;
}
public virtual async Task<UserData> LoadUserData()

View File

@@ -31,6 +31,19 @@ namespace TcgEngine
}
return res.success;
}
public override async Task<bool> SteamLogin(string email, string username, string password)
{
LoginResponse res = await Client.SteamLogin(email, username, password);
if (res.success)
{
this.logged_in = true;
this.user_id = res.id;
this.username = res.username;
permission = res.permission_level;
}
return res.success;
}
public override async Task<bool> RefreshLogin()
{

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c7f63ee07a6748debcf60e005d35793a
timeCreated: 1754619478

View File

@@ -0,0 +1,343 @@
#if UNITY_STANDALONE_WIN || UNITY_STANDALONE_LINUX || UNITY_STANDALONE_OSX
#define STEAMWORKS_ENABLED
#endif
#if STEAMWORKS_ENABLED
using Steamworks;
#endif
using UnityEngine;
namespace TcgEngine
{
/// <summary>
/// Can send requests and receive responses
/// </summary>
public class SteamCurrencyManager : MonoBehaviour
{
#if STEAMWORKS_ENABLED
private bool m_bInitialized = false;
private Callback<SteamInventoryResultReady_t> m_SteamInventoryResultReady;
private Callback<SteamInventoryFullUpdate_t> m_SteamInventoryFullUpdate;
private Callback<SteamInventoryDefinitionUpdate_t> m_SteamInventoryDefinitionUpdate;
public static SteamCurrencyManager instance;
void Awake()
{
if (instance == null)
{
instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
return;
}
}
public void InitializeSteamCurrency()
{
// 使用SteamManager来检查Steam是否已初始化
if (!SteamManager.Initialized)
{
Debug.LogError("Steam not initialized through SteamManager");
return;
}
m_bInitialized = true;
// 注册回调
m_SteamInventoryResultReady = Callback<SteamInventoryResultReady_t>.Create(OnSteamInventoryResultReady);
m_SteamInventoryFullUpdate = Callback<SteamInventoryFullUpdate_t>.Create(OnSteamInventoryFullUpdate);
m_SteamInventoryDefinitionUpdate = Callback<SteamInventoryDefinitionUpdate_t>.Create(OnSteamInventoryDefinitionUpdate);
Debug.Log("Steam Currency Manager initialized");
}
// 获取用户库存
public void GetUserInventory()
{
if (!m_bInitialized || !SteamManager.Initialized) return;
SteamInventoryResult_t resultHandle;
if (SteamInventory.GetAllItems(out resultHandle))
{
Debug.Log("Getting user inventory...");
}
else
{
Debug.LogError("Failed to get user inventory");
}
}
// 购买金币包
public void PurchaseGoldPackage(int goldAmount, int priceInCents)
{
if (!m_bInitialized || !SteamManager.Initialized)
{
Debug.LogError("Steam not initialized");
return;
}
// 启动购买流程
SteamItemDef_t[] itemDefs = new SteamItemDef_t[] { new SteamItemDef_t(GetItemDefIDForGold(goldAmount)) };
uint[] quantities = new uint[] { 1 };
SteamAPICall_t callHandle = SteamInventory.StartPurchase(itemDefs, quantities, 1);
if (callHandle != SteamAPICall_t.Invalid)
{
Debug.Log("Purchase request sent");
}
else
{
Debug.LogError("Failed to start purchase");
}
}
// 根据金币数量获取对应的物品ID
private int GetItemDefIDForGold(int goldAmount)
{
// 这些ID需要在Steam后台配置
switch (goldAmount)
{
case 100: return 1001; // 100金币包
case 500: return 1002; // 500金币包
case 1000: return 1003; // 1000金币包
case 2000: return 1004; // 2000金币包
default: return 1001; // 默认100金币包
}
}
// 购买结果回调
private void OnPurchaseResult(EResult result, ulong orderID)
{
if (result == EResult.k_EResultOK)
{
Debug.Log($"Purchase initiated successfully. Order ID: {orderID}");
// 显示购买确认UI
ShowPurchaseConfirmation(orderID);
}
else
{
Debug.LogError($"Purchase failed with result: {result}");
// 显示错误信息
ShowPurchaseError(result);
}
}
// 处理库存结果就绪
private void OnSteamInventoryResultReady(SteamInventoryResultReady_t callback)
{
Debug.Log($"Inventory result ready: {callback.m_result}");
if (callback.m_result == EResult.k_EResultOK)
{
ProcessInventoryResult(callback.m_handle);
}
else
{
Debug.LogError($"Inventory result failed: {callback.m_result}");
}
// 清理结果句柄
SteamInventory.DestroyResult(callback.m_handle);
}
// 处理完整库存更新
private void OnSteamInventoryFullUpdate(SteamInventoryFullUpdate_t callback)
{
Debug.Log("Full inventory update received");
ProcessInventoryResult(callback.m_handle);
SteamInventory.DestroyResult(callback.m_handle);
}
// 处理物品定义更新
private void OnSteamInventoryDefinitionUpdate(SteamInventoryDefinitionUpdate_t callback)
{
Debug.Log("Inventory definitions updated");
}
// 处理库存结果
private void ProcessInventoryResult(SteamInventoryResult_t resultHandle)
{
if (!m_bInitialized || !SteamManager.Initialized) return;
uint itemCount = 0;
// 获取物品数量
if (!SteamInventory.GetItemsByID(out resultHandle, null, itemCount))
{
Debug.LogError("Failed to get item count");
return;
}
if (itemCount == 0)
{
Debug.Log("Inventory is empty");
return;
}
// 获取物品详情
SteamItemInstanceID_t[] itemIDs = new SteamItemInstanceID_t[itemCount];
SteamItemDef_t[] itemDefs = new SteamItemDef_t[itemCount];
uint[] quantities = new uint[itemCount];
if (SteamInventory.GetItemsByID(out resultHandle, itemIDs, itemCount))
{
// 先获取物品的数量信息
SteamInventory.GetItemsWithPrices(out itemDefs, out quantities, itemCount);
for (int i = 0; i < itemCount; i++)
{
// 获取物品属性
string goldAmount = "";
uint goldLength = 32;
SteamInventory.GetItemDefinitionProperty(itemDefs[i], "gold_amount", out goldAmount, ref goldLength);
Debug.Log($"Item: {itemDefs[i].m_SteamItemDef}, Quantity: {quantities[i]}, Gold: {goldAmount}");
// 如果是金币包,添加到用户账户
if (!string.IsNullOrEmpty(goldAmount))
{
int goldToAdd;
if (int.TryParse(goldAmount, out goldToAdd))
{
AddGoldToUser(goldToAdd);
// 消费物品(标记为已使用)
ConsumeItem(itemIDs[i], 1);
}
}
}
}
}
// 添加金币到用户账户
private async void AddGoldToUser(int amount)
{
// 获取当前用户数据
UserData userData = Authenticator.Get().UserData;
if (userData != null)
{
userData.coins += amount;
Debug.Log($"Added {amount} gold to user. Total: {userData.coins}");
// 保存用户数据
await Authenticator.Get().SaveUserData();
// 更新UI
UpdateGoldUI(userData.coins);
// 解锁相关成就
CheckGoldAchievements(userData.coins);
}
}
// 消费物品(标记为已使用)
private void ConsumeItem(SteamItemInstanceID_t itemID, uint quantity)
{
if (!m_bInitialized || !SteamManager.Initialized) return;
SteamInventoryResult_t resultHandle;
if (SteamInventory.ConsumeItem(out resultHandle, itemID, quantity))
{
Debug.Log($"Consumed item {itemID} x{quantity}");
}
}
// 更新金币UI
private void UpdateGoldUI(int newGoldAmount)
{
// 这里可以调用您的UI更新方法
// 例如MainMenu.Get().RefreshUserData();
}
// 检查金币相关成就
private void CheckGoldAchievements(int totalGold)
{
if (!m_bInitialized || !SteamManager.Initialized) return;
if (totalGold >= 1000)
{
// 解锁"首次充值"成就
SteamUserStats.SetAchievement("ACH_FIRST_PURCHASE");
SteamUserStats.StoreStats();
}
if (totalGold >= 10000)
{
// 解锁"大富豪"成就
SteamUserStats.SetAchievement("ACH_BIG_SPENDER");
SteamUserStats.StoreStats();
}
}
// 显示购买确认
private void ShowPurchaseConfirmation(ulong orderID)
{
// 在这里显示购买确认UI
Debug.Log($"Purchase confirmed. Order: {orderID}");
}
// 显示购买错误
private void ShowPurchaseError(EResult result)
{
// 在这里显示错误信息UI
Debug.LogError($"Purchase error: {result}");
}
// 更新Steam回调
private void Update()
{
// SteamAPI.RunCallbacks()现在由SteamManager处理不需要在这里调用
}
// 清理
private void OnDestroy()
{
// Steam API清理现在由SteamManager处理
// 不需要在这里调用SteamAPI.Shutdown()
m_bInitialized = false;
}
public bool IsInitialized()
{
return m_bInitialized;
}
#else
public static SteamCurrencyManager instance;
void Awake()
{
// 在不支持Steamworks的平台上创建一个空实例
if (instance == null)
{
instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
return;
}
}
public void InitializeSteamCurrency()
{
Debug.LogWarning("SteamCurrencyManager is not available on this platform");
}
public bool IsInitialized()
{
return false;
}
// 非Steam平台的空实现
public void GetUserInventory() { }
public void PurchaseGoldPackage(int goldAmount, int priceInCents) { }
public bool IsInitialized() { return false; }
private void Update() { }
private void OnDestroy() { }
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 59ba142d069f4f52bbf4170d4c42b887
timeCreated: 1754619554

View File

@@ -0,0 +1,216 @@
// The SteamManager is designed to work with Steamworks.NET
// This file is released into the public domain.
// Where that dedication is not recognized you are granted a perpetual,
// irrevocable license to copy and modify this file as you see fit.
//
// Version: 1.0.12
#if !(UNITY_STANDALONE_WIN || UNITY_STANDALONE_LINUX || UNITY_STANDALONE_OSX || STEAMWORKS_WIN || STEAMWORKS_LIN_OSX)
#define STEAMWORKS_ENABLED
#endif
using UnityEngine;
#if !STEAMWORKS_ENABLED
using System.Collections;
using Steamworks;
#endif
namespace TcgEngine
{
//
// The SteamManager provides a base implementation of Steamworks.NET on which you can build upon.
// It handles the basics of starting up and shutting down the SteamAPI for use.
//
[DisallowMultipleComponent]
public class SteamManager : MonoBehaviour
{
#if !STEAMWORKS_ENABLED
protected static bool s_EverInitialized = false;
protected static SteamManager s_instance;
protected static SteamManager Instance
{
get
{
if (s_instance == null)
{
return new GameObject("SteamManager").AddComponent<SteamManager>();
}
else
{
return s_instance;
}
}
}
protected bool m_bInitialized = false;
public static bool Initialized
{
get { return Instance.m_bInitialized; }
}
protected SteamAPIWarningMessageHook_t m_SteamAPIWarningMessageHook;
[AOT.MonoPInvokeCallback(typeof(SteamAPIWarningMessageHook_t))]
protected static void SteamAPIDebugTextHook(int nSeverity, System.Text.StringBuilder pchDebugText)
{
Debug.LogWarning(pchDebugText);
}
#if UNITY_2019_3_OR_NEWER
// In case of disabled Domain Reload, reset static members before entering Play Mode.
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void InitOnPlayMode()
{
s_EverInitialized = false;
s_instance = null;
}
#endif
protected virtual void Awake()
{
// Only one instance of SteamManager at a time!
if (s_instance != null)
{
Destroy(gameObject);
return;
}
s_instance = this;
if (s_EverInitialized)
{
// This is almost always an error.
// The most common case where this happens is when SteamManager gets destroyed because of Application.Quit(),
// and then some Steamworks code in some other OnDestroy gets called afterwards, creating a new SteamManager.
// You should never call Steamworks functions in OnDestroy, always prefer OnDisable if possible.
throw new System.Exception("Tried to Initialize the SteamAPI twice in one session!");
}
// We want our SteamManager Instance to persist across scenes.
DontDestroyOnLoad(gameObject);
if (!Packsize.Test())
{
Debug.LogError(
"[Steamworks.NET] Packsize Test returned false, the wrong version of Steamworks.NET is being run in this platform.",
this);
}
if (!DllCheck.Test())
{
Debug.LogError(
"[Steamworks.NET] DllCheck Test returned false, One or more of the Steamworks binaries seems to be the wrong version.",
this);
}
try
{
// If Steam is not running or the game wasn't started through Steam, SteamAPI_RestartAppIfNecessary starts the
// Steam client and also launches this game again if the User owns it. This can act as a rudimentary form of DRM.
// Once you get a Steam AppID assigned by Valve, you need to replace AppId_t.Invalid with it and
// remove steam_appid.txt from the game depot. eg: "(AppId_t)480" or "new AppId_t(480)".
// See the Valve documentation for more information: https://partner.steamgames.com/doc/sdk/api#initialization_and_shutdown
if (SteamAPI.RestartAppIfNecessary(AppId_t.Invalid))
{
Application.Quit();
return;
}
}
catch (System.DllNotFoundException e)
{
// We catch this exception here, as it will be the first occurrence of it.
Debug.LogError(
"[Steamworks.NET] Could not load [lib]steam_api.dll/so/dylib. It's likely not in the correct location. Refer to the README for more details.\n" +
e, this);
Application.Quit();
return;
}
// Initializes the Steamworks API.
// If this returns false then this indicates one of the following conditions:
// [*] The Steam client isn't running. A running Steam client is required to provide implementations of the various Steamworks interfaces.
// [*] The Steam client couldn't determine the App ID of game. If you're running your application from the executable or debugger directly then you must have a [code-inline]steam_appid.txt[/code-inline] in your game directory next to the executable, with your app ID in it and nothing else. Steam will look for this file in the current working directory. If you are running your executable from a different directory you may need to relocate the [code-inline]steam_appid.txt[/code-inline] file.
// [*] Your application is not running under the same OS user context as the Steam client, such as a different user or administration access level.
// [*] Ensure that you own a license for the App ID on the currently active Steam account. Your game must show up in your Steam library.
// [*] Your App ID is not completely set up, i.e. in Release State: Unavailable, or it's missing default packages.
// Valve's documentation for this is located here:
// https://partner.steamgames.com/doc/sdk/api#initialization_and_shutdown
m_bInitialized = SteamAPI.Init();
if (!m_bInitialized)
{
Debug.LogError(
"[Steamworks.NET] SteamAPI_Init() failed. Refer to Valve's documentation or the comment above this line for more information.",
this);
return;
}
s_EverInitialized = true;
}
// This should only ever get called on first load and after an Assembly reload, You should never Disable the Steamworks Manager yourself.
protected virtual void OnEnable()
{
if (s_instance == null)
{
s_instance = this;
}
if (!m_bInitialized)
{
return;
}
if (m_SteamAPIWarningMessageHook == null)
{
// Set up our callback to receive warning messages from Steam.
// You must launch with "-debug_steamapi" in the launch args to receive warnings.
m_SteamAPIWarningMessageHook = new SteamAPIWarningMessageHook_t(SteamAPIDebugTextHook);
SteamClient.SetWarningMessageHook(m_SteamAPIWarningMessageHook);
}
}
// OnApplicationQuit gets called too early to shutdown the SteamAPI.
// Because the SteamManager should be persistent and never disabled or destroyed we can shutdown the SteamAPI here.
// Thus it is not recommended to perform any Steamworks work in other OnDestroy functions as the order of execution can not be garenteed upon Shutdown. Prefer OnDisable().
protected virtual void OnDestroy()
{
if (s_instance != this)
{
return;
}
s_instance = null;
if (!m_bInitialized)
{
return;
}
SteamAPI.Shutdown();
}
protected virtual void Update()
{
if (!m_bInitialized)
{
return;
}
// Run Steam client callbacks
SteamAPI.RunCallbacks();
}
#else
public static bool Initialized {
get {
return false;
}
}
#endif // !STEAMWORKS_ENABLED
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 784bef9815a848bb98bf3ba2514d04e9
timeCreated: 1754620014