using Core.KV; using game.KV; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; using asap.core; using GameCore; using tysdk; using UnityEngine; using Newtonsoft.Json; using YiDun.Protector; #if UNITY_ANDROID using TapjoyUnity.Internal; #endif namespace game { // Failed login attempts may retry; explicit logout ends this service lifetime. public class KvUserService : IUserService { [Inject] public IConfig config { get; set; } [Inject] public ICustomServerMgr customServerMgr { get; set; } [Inject] public IPlayerSaveStore SaveStore { get; set; } // private readonly KvRuntimeSelection selection; private readonly CancellationTokenSource lifetime = new(); private readonly Dictionary profiles = new(); private readonly Dictionary levels = new(); private KvGameLoadCoordinator coordinator; private KvSessionOwner handle; private bool loading, closing, published; private KvSessionOwner failedHandle; private int mmTestValue; private Task closeTask; private long epoch; private string playerId; private uint internalId; public string CustomId { get; set; } public string UserId => KVHandoffReady ? playerId : null; public string ApiPlayerId => UserId; public uint InternalId => KVHandoffReady ? internalId : 0; public bool IsLogin => KVHandoffReady && playerId != null; public bool IsAuthorized => published && InternalId != 0 && KVHandoffReady; public CancellationToken LoginLifetime => lifetime.Token; public DateTime LastLoginTime { get; private set; } public DateTime LoginTime { get; private set; } public DateTime CreateTime { get; private set; } public string DisplayName { get; private set; } public string AvatarUrl { get; private set; } public string Language { get; private set; } public string ContinentCode { get; private set; } public Dictionary EAccoutUrl { get; protected set; } = new Dictionary(); internal bool KVHandoffReady => !closing && handle != null && coordinator != null && coordinator.TryGetCurrentGameHandle(epoch, out var current) && ReferenceEquals(current, handle); public KvUserService() { selection = SubBoostrap.RuntimeSelection; Application.quitting += OnApplicationQuitting; } protected virtual string NewPlayerName => $"Angler#{CustomId}"; protected virtual string NewPlayerAvatar => string.Empty; protected virtual void InitData(IDictionary data) { } public virtual async Task Login(Action progress, bool tyrLoginByToken = true) { if (loading || closing) { return ELoginStep.Api; } if (IsLogin) { progress?.Invoke(2); return ELoginStep.Done; } if (selection == null || !selection.IsKV || !selection.TryAdmitPlayer(CustomId, out var canonicalId)) { return ELoginStep.Api; } loading = true; var requestedId = CustomId; bool successEventStarted = false; IChatService chat = null; try { CustomServerMgr server; KvSaveStore store; KvGameLoadResult result; KvSessionOwner original; try { if (!await ReleaseFailedLoginAsync() || closing) { return ELoginStep.Api; } server = customServerMgr as CustomServerMgr; if (server == null || server.ApiBaseUri.AbsoluteUri != selection.Api.AbsoluteUri) { throw new InvalidOperationException("Formal KV transport required."); } coordinator = new KvGameLoadCoordinator( () => loading && !closing && !published && handle == null, server.BindKVForGame, server.ClearKVForGame); server.KVUnauthorized += OnUnauthorized; var api = selection.Api; var cacheRoot = Application.persistentDataPath; if (api == null || !api.IsAbsoluteUri || (api.Scheme != "http" && api.Scheme != "https") || !string.IsNullOrEmpty(api.UserInfo) || !string.IsNullOrEmpty(api.Query) || !string.IsNullOrEmpty(api.Fragment)) { throw new ArgumentException("Formal KV endpoint mismatch.", nameof(api)); } if (string.IsNullOrEmpty(cacheRoot)) { throw new ArgumentException("Controlled load configuration invalid."); } api = new Uri(api.AbsoluteUri.TrimEnd('/') + "/"); result = await coordinator.LoadForGameAsync(canonicalId, api, cacheRoot, LoginLifetime, () => !closing && CustomId == requestedId); if (result.Error != KVGameLoadError.None || closing || CustomId != requestedId || result.Account == null || result.Account.PlayerId != canonicalId || result.Account.Epoch != result.Epoch || !coordinator.TryGetCurrentGameHandle(result.Epoch, out original)) { await FailLoginAsync(); return ELoginStep.Api; } epoch = result.Epoch; handle = original; store = SaveStore as KvSaveStore; if (store == null) { throw new InvalidOperationException("KV save store required."); } store.Bind(original, LoginLifetime); } catch (Exception ex) { Debug.LogError("KV preparation failed: " + ex.GetType().Name); await FailLoginAsync(); return ELoginStep.Api; } await PrepareProfileAsync(result.Account); if (!KVHandoffReady || CustomId != requestedId) { await FailLoginAsync(); return ELoginStep.Api; } playerId = result.Account.PlayerId; internalId = result.Account.InternalId; ZZTimeHelper.AdjustTime(LoginTime); ZZTimeHelper.WathForcusChange(); if (!original.CompareDatas(ZZTimeHelper.UtcNow().Ticks).Success) { await FailLoginAsync(); return ELoginStep.Api; } var merged = original.ReadLocalSnapshot(); if (!merged.Success) { await FailLoginAsync(); return ELoginStep.Api; } KvMgr.Bind(store); InitializePlayerData(merged.Data.ToDictionary(pair => pair.Key, pair => pair.Value, StringComparer.Ordinal)); if (!KVHandoffReady || CustomId != requestedId) { await FailLoginAsync(); return ELoginStep.Api; } SetMMTestValue(); try { BindPromptServices(original); } catch (Exception ex) { Debug.LogError("KV prompt binding failed: " + ex.GetType().Name); await FailLoginAsync(); return ELoginStep.Api; } chat = GContext.container.Resolve(); chat.WaitingForLogin(); OnYinDunLogin(); if (!KVHandoffReady || CustomId != requestedId) { await FailLoginAsync(); return ELoginStep.Api; } server.EnableKVBusiness(epoch); published = true; successEventStarted = true; try { GContext.Publish(new EvtAPISvrLoginSuccess { playerId = playerId, internalId = internalId }); } catch (Exception ex) { // Original LoginApiServer catches subscriber errors and removes API authorization. Debug.LogError(ex); internalId = 0; published = false; server.DisableKVBusiness(); } if (closing) { return ELoginStep.Api; } InitTapJoy(); progress?.Invoke(2); return ELoginStep.Done; } catch (Exception ex) { if (successEventStarted) { throw; } Debug.LogError("KV login failed: " + ex.GetType().Name); try { (chat as IDisposable)?.Dispose(); } catch (Exception cleanupError) { Debug.LogError("KV chat cleanup failed: " + cleanupError); } try { await FailLoginAsync(); } catch (Exception cleanupError) { Debug.LogError("KV login cleanup failed: " + cleanupError); } throw; } finally { loading = false; } } private void InitializePlayerData(Dictionary data) { mmTestValue = 0; if (!data.ContainsKey("MMTest")) { if (config.Get("NewlyCreated", false)) { mmTestValue = -1; } else { SaveStore.Write("MMTest", "MMFailParam#0").RequireAccepted(); } } else { var parts = data["MMTest"].Split('#'); if (parts.Length > 1 && parts[0] == "MMFailParam") { int.TryParse(parts[1], out var value); mmTestValue = value; } else { mmTestValue = 0; SaveStore.Write("MMTest", "MMFailParam#0").RequireAccepted(); } } GContext.container.Resolve().LoadPlayerData(data); InitData(data); } private async Task PrepareProfileAsync(KvAccountDescriptor account) { if (account.InternalId == 0 || account.CreateTimeUtc == DateTime.MinValue || account.ServerTimeUtc == DateTime.MinValue) { throw new InvalidOperationException("Authenticated account metadata required."); } CreateTime = account.CreateTimeUtc; LoginTime = account.ServerTimeUtc; config.Set("NewlyCreated", account.NewlyCreated); var previous = Read("LastLoginTimeUtc"); LastLoginTime = ParseLastLoginTime(previous.Value, previous.Found.Value); DisplayName = Read("DisplayName").Value; AvatarUrl = Read("AvatarUrl").Value ?? string.Empty; Language = Read("Language").Value; var continent = Read("ContinentCode").Value; ContinentCode = new[] { "AF", "AN", "AS", "EU", "NA", "OC", "SA" }.Contains(continent) ? continent : null; if (string.IsNullOrEmpty(DisplayName)) { var name = await ChangeDisplayName(NewPlayerName); LoginLifetime.ThrowIfCancellationRequested(); if (!KVHandoffReady) { throw new InvalidOperationException("Stale KV profile."); } DisplayName = name; } if (string.IsNullOrEmpty(AvatarUrl)) { await UpdateAvatarUrl(NewPlayerAvatar); } await UpdateLanguage(GContext.container.Resolve().Lang); LoginLifetime.ThrowIfCancellationRequested(); } private async Task FailLoginAsync() { if (closing) { return; } if (closeTask != null) { await ReleaseFailedLoginAsync(); return; } failedHandle = handle; handle = null; playerId = null; internalId = 0; published = false; DisplayName = AvatarUrl = Language = ContinentCode = null; ClearPlayInfos(); if (customServerMgr is CustomServerMgr server) { server.KVUnauthorized -= OnUnauthorized; server.DisableKVBusiness(); } closeTask = coordinator?.Revoke() ?? Task.CompletedTask; await ReleaseFailedLoginAsync(); } private async Task ReleaseFailedLoginAsync() { if (closeTask == null) { return true; } var status = await (coordinator?.ObserveQuitAsync() ?? Task.FromResult(KVShutdownStatus.Ready)); if (closing || status != KVShutdownStatus.Ready || closeTask.Status != TaskStatus.RanToCompletion) { return false; } if (failedHandle != null) { KvMgr.Unbind(SaveStore as KvSaveStore); (SaveStore as KvSaveStore)?.ReleaseFailedBinding(failedHandle); (GContext.container.Resolve() as DefaultSettingService)?.ReleaseFailedPromptOwner(failedHandle); GContext.container.Resolve().ReleaseFailedPromptOwner(failedHandle); } failedHandle = null; closeTask = null; coordinator = null; epoch = 0; return true; } internal static DateTime ParseLastLoginTime(string value, bool found) { if (!found) { return DateTime.MinValue; } if (!DateTime.TryParseExact(value, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var time) || time == DateTime.MinValue || time.Kind == DateTimeKind.Unspecified) { throw new FormatException("Invalid LastLoginTimeUtc."); } return time.ToUniversalTime(); } private PlayerSaveReadResult Read(string key) { var result = SaveStore.Read(key); if (!result.Success) { throw new InvalidOperationException("KV profile read failed: " + result.Error); } return result; } private void BindPromptServices(KvSessionOwner original) { if (!KVHandoffReady) { throw new InvalidOperationException("Prompt binding is stale."); } var settings = GContext.container.Resolve() as DefaultSettingService; var notifications = GContext.container.Resolve() as LocalNotificationService; var club = GContext.container.Resolve(); if (settings == null || notifications == null || club == null) { throw new InvalidOperationException("Formal prompt services required."); } settings.PreflightFormalPromptOwner(original); club.PreflightFormalPromptOwner(original); notifications.PreflightFormalPromptSettings(settings); settings.BindFormalPromptOwner(original); club.BindFormalPromptOwner(original); notifications.BindFormalPromptSettings(settings); if (!KVHandoffReady) { throw new InvalidOperationException("Formal prompt binding changed."); } } private void OnUnauthorized(int version) { if (version == customServerMgr.SessionVersion) { Logout(); } } private void OnApplicationQuitting() { var original = SaveStore as KvSaveStore; try { KvMgr.Stop(original); if (original != null && original.IsCurrent) { _ = original.SyncDataAsync(); } } catch (Exception ex) { Debug.LogError("KV quit save failed: " + ex.GetType().Name); } finally { Logout(); } } public virtual void Logout() { ObserveClose(CloseKVForValidationAsync()); } private async void ObserveClose(Task task) { try { await task; } catch (Exception ex) { Debug.LogError("KV close failed: " + ex.GetType().Name); } } internal Task CloseKVForValidationAsync() { if (closing) { return closeTask ?? Task.CompletedTask; } closing = true; published = false; KvMgr.Unbind(SaveStore as KvSaveStore); (SaveStore as KvSaveStore)?.Dispose(); playerId = null; internalId = 0; handle = null; epoch = 0; DisplayName = AvatarUrl = Language = ContinentCode = null; ClearPlayInfos(); Application.quitting -= OnApplicationQuitting; if (customServerMgr is CustomServerMgr server) { server.KVUnauthorized -= OnUnauthorized; server.DisableKVBusiness(); } // Revoke first; a synchronous cancellation callback must already see invalid identity. closeTask = coordinator?.Revoke() ?? Task.CompletedTask; lifetime.Cancel(); return closeTask; } internal Task ObserveKVQuitAsync() => coordinator?.ObserveQuitAsync() ?? Task.FromResult(KVShutdownStatus.Ready); private async Task SaveProfile(string key, string value) { var original = handle; var store = SaveStore; if ((!IsLogin && !loading) || !KVHandoffReady || store == null) { return false; } try { var result = await store.SyncDataImmidiate(key, value); return ReferenceEquals(original, handle) && KVHandoffReady && ReferenceEquals(store, SaveStore) && store.IsCurrent && result; } catch (OperationCanceledException) { return false; } } public async Task ChangeDisplayName(string newName) { var originName = string.IsNullOrEmpty(DisplayName) ? NewPlayerName : DisplayName; if (!await SaveProfile("DisplayName", newName)) { return originName; } DisplayName = newName; GContext.Publish(new ChangeAvatarEvent()); UpdateProfileName(); return newName; } public async Task UpdateAvatarUrl(string url) { var originUrl = AvatarUrl; Debug.Log("UpdateAvatarUrlAsync originUrl:" + originUrl + " url " + url); if (string.IsNullOrEmpty(url)) { return originUrl; } if (url == originUrl) { return originUrl; } if (!await SaveProfile("AvatarUrl", url)) { return originUrl; } AvatarUrl = url; GContext.Publish(new ChangeAvatarEvent()); UpdateProfileAvatar(); return url; } public async Task UpdateLanguage(string code) { if (Language == code) { return Language; } if (await SaveProfile("Language", code.Replace('_', '-'))) { Language = code; } return Language; } public void GMMMT(int value) { mmTestValue = value; SaveStore.Write("MMTest", $"MMFailParam#{mmTestValue}").RequireAccepted(); SetMMTestValue(); } private void SetMMTestValue() { string ab = GContext.container.Resolve().Init(mmTestValue); GEvent.SetUserProp("MMTestValue", ab, true); Debug.Log($"UserId:{UserId} MMTestValue :0b{Convert.ToString(mmTestValue, 2)} MMTestValueString:{ab}"); } protected void AvatarToKV(TYSdkFacade.AccountInfo accountInfo) { if (accountInfo != null && (accountInfo.channel == EAccoutType.hwFacebook || accountInfo.channel == EAccoutType.hwGoogle) && !string.IsNullOrEmpty(accountInfo.avatar)) { if (EAccoutUrl == null) { EAccoutUrl = new Dictionary(); } EAccoutUrl[accountInfo.channel.ToString()] = accountInfo.avatar; SaveStore.Write("EAccoutUrl", JsonConvert.SerializeObject(EAccoutUrl)).RequireAccepted(); } } private void InitTapJoy() { #if UNITY_ANDROID var lastBalanceStr = Read("TapJoy_Diamond").Value ?? ""; Debug.Log($"InitTapJoy -> {lastBalanceStr}"); var lastBalance = int.TryParse(lastBalanceStr, out var temp) ? temp : 0; var tapjoyObject = GameObject.FindObjectOfType(); if (tapjoyObject && tapjoyObject.gameObject && !tapjoyObject.GetComponent()) { var tapjoyHelper = tapjoyObject.gameObject.AddComponent(); GameObject.DontDestroyOnLoad(tapjoyHelper); tapjoyHelper.Init(lastBalance, OnEarnCurrencyName, OnBalance); } return; void OnEarnCurrencyName(string currencyName, int amount) { Debug.Log($"OnEarnCurrencyName -> {currencyName} {amount}"); // var amount = (balance - lastBalance); var diamonds = new ItemData { id = 1005, count = amount }; diamonds.curCount = (ulong)GContext.container.Resolve().GetItemCount(diamonds.id); GContext.container.Resolve().AddItem(diamonds, null); GContext.Publish(new ShowData(new List { diamonds }, RewardType.NormalOne)); GContext.Publish(new ShowData()); #if AGG using (var e = GEvent.GameEvent("tapjoy")) { e.AddContent("item_id", diamonds.id) .AddContent("item_count", diamonds.count); } #endif } void OnBalance(string currencyName, int balance) { Debug.Log($"OnBalance -> {currencyName} {balance}"); // const string tapjoyKey = "TapJoy_Diamond"; // var lastBalanceStr = PlayFabMgr.Instance.GetLocalData(tapjoyKey) ?? ""; // var lastBalance = int.TryParse(lastBalanceStr, out var temp) ? temp : 0; { // PlayFabMgr.Instance.UpdateUserDataValue(tapjoyKey, balance.ToString()); // if (balance > lastBalance) // { // var amount = (balance - lastBalance); // var diamonds = new ItemData { id = 1005, count = amount }; // diamonds.curCount = (ulong)GContext.container.Resolve().GetItemCount(diamonds.id); // GContext.container.Resolve().AddItem(diamonds); // GContext.Publish(new ShowData(new List { diamonds }, RewardType.NormalOne)); // // GContext.Publish(new ShowData(diamonds)); // GContext.Publish(new ShowData()); // } } } #endif } private async void OnYinDunLogin() { // 开发模式下,先不启用了 if (config != null && (config.Get("DEBUG", "0") == "1" || config.Get("GM", "0") == "1")) { return; } var userService = GContext.container.Resolve(); var yiDunManager = GContext.container.Resolve(); var roleName = userService.DisplayName; var roleId = userService.UserId; var roleAccount = userService.CustomId; var versionData = new { GameVersion = Application.version, AssetVersion = config.Get(GConstant.K_AssetVersion, string.Empty), #if UNITY_ANDROID Channel = GConstant.Yidun_GooglePlayChannel #elif UNITY_IOS Channel = GConstant.Yidun_IOSChannel #endif }; Debug.Log($"JsonUtility.ToJson(versionData) => {JsonConvert.SerializeObject(versionData)}"); #if (UNITY_ANDROID || UNITY_IOS || UNITY_IPHONE) && !UNITY_EDITOR var businessId = GConstant.Yidun_AndroidBusinessId; #if UNITY_IOS businessId = GConstant.Yidun_IOSBusinessId; #endif yiDunManager.SetRoleInfo(businessId, roleId, roleName, roleAccount, null, -1, JsonConvert.SerializeObject(versionData)); yiDunManager.PostRiskCheck(); #endif } public double CreateTotalSeconds() => (ZZTimeHelper.UtcNow() - CreateTime).TotalSeconds; private sealed class BindConflictPreviewResponse { public int Code { get; set; } public bool Exists { get; set; } public bool HasArchive { get; set; } public string DisplayName { get; set; } public string AvatarUrl { get; set; } public int Level { get; set; } } public async Task GetBindConflictPreview(int sdkId) { try { string json = null; for (var attempt = 0; attempt < 4; attempt++) { json = await customServerMgr.AccountRequest("GetBindConflictPreview", new { targetUserId = sdkId.ToString() }); if (json != null) { break; } if (attempt < 3) { await Task.Delay(TimeSpan.FromSeconds(1 << attempt)); } } if (string.IsNullOrEmpty(json)) { return PlayFabTools.PFPlayerInfo.Empty(sdkId); } var response = JsonConvert.DeserializeObject(json); if (response == null || response.Code <= 0 || !response.Exists) { return PlayFabTools.PFPlayerInfo.Empty(sdkId); } if (!response.HasArchive) { return new PlayFabTools.PFPlayerInfo { SdkId = sdkId, PlayFabId = sdkId.ToString() }; } return new PlayFabTools.PFPlayerInfo { SdkId = sdkId, PlayFabId = sdkId.ToString(), DisplayName = response.DisplayName, Avatar = response.AvatarUrl, Level = response.Level }; } catch (Exception e) { Debug.LogWarning(e.Message); return PlayFabTools.PFPlayerInfo.Empty(sdkId); } } // The non-AGG channel has no account-link operation; never manufacture a successful binding. public virtual async Task<(int code, int userid)> LinkAccount(EAccoutType type) { await Awaiters.NextFrame; return (1, 0); } public virtual async Task GetLinkState(EAccoutType type) { await Awaiters.NextFrame; return false; } public string GetDefaultName(string id) => "Angler#" + (string.IsNullOrEmpty(id) ? string.Empty : id.Substring(0, Math.Min(5, id.Length))); public void SetPlayerLv(string id, int level) => levels[id] = id == UserId ? GContext.container.Resolve().lv : level; public int GetPlayerLv(string id) => levels.TryGetValue(id, out var level) ? level : 1; public async Task<(string, int)> GetPlayLv(string id) { var level = id == UserId && IsAuthorized ? (int)GContext.container.Resolve().lv : id != null && levels.TryGetValue(id, out var cachedLevel) ? cachedLevel : -1; if (level >= 0) { return (id, level); } if (id != UserId && !string.IsNullOrEmpty(id) && !levels.ContainsKey(id)) { var json = await customServerMgr.FriendRequest("SearchFriend", new SearchFriendReq { PlayFabId = id }); if (!string.IsNullOrEmpty(json)) { SearchFriendResp response = null; try { response = Newtonsoft.Json.JsonConvert.DeserializeObject(json); } catch (Newtonsoft.Json.JsonException ex) { Debug.LogError("KV player level response failed: " + ex.GetType().Name); } if (response != null && response.Code > 0 && response.KvProfile != null) { level = response.KvProfile.Level ?? 1; if (level >= 0) { levels[id] = level; return (id, level); } } } } throw new InvalidOperationException("Public player level has not been loaded."); } public PlayInfo GetPlayInfo(string id) { if (string.IsNullOrEmpty(id)) { return null; } if (!profiles.TryGetValue(id, out var profile)) { profile = new PlayInfo { name = GetDefaultName(id) }; profiles.Add(id, profile); SetPlayInfo(id, profile); } return profile; } private async void SetPlayInfo(string id, PlayInfo profile) { if (id == UserId) { profile.name = DisplayName; profile.avatarUrl = AvatarUrl; GContext.Publish(new GetClubPlayInfoEvent { id = id, name = profile.name, avatarUrl = profile.avatarUrl }); return; } var json = await customServerMgr.FriendRequest("SearchFriend", new SearchFriendReq { PlayFabId = id }); if (string.IsNullOrEmpty(json)) { return; } SearchFriendResp result; try { result = Newtonsoft.Json.JsonConvert.DeserializeObject(json); } catch (Newtonsoft.Json.JsonException ex) { Debug.LogError("KV player profile response failed: " + ex.GetType().Name); return; } if (result != null && result.Code > 0 && result.KvProfile != null) { if (!string.IsNullOrEmpty(result.KvProfile.DisplayName)) { profile.name = result.KvProfile.DisplayName; } profile.avatarUrl = result.KvProfile.AvatarUrl; GContext.Publish(new GetClubPlayInfoEvent { id = id, name = profile.name, avatarUrl = profile.avatarUrl }); } } public void SetPlayInfo(string id, string name, string avatarUrl) { profiles[id] = new PlayInfo { name = string.IsNullOrEmpty(name) ? GetDefaultName(id) : name, avatarUrl = avatarUrl }; } public void ClearPlayInfos() { profiles.Clear(); levels.Clear(); } private void UpdateProfileName() { if (!IsAuthorized) { return; } PlayerProfile playerProfile = new PlayerProfile(); playerProfile.DisplayName = DisplayName; LoginRequest loginRequest = new LoginRequest() { playerId = ApiPlayerId, playerProfile = playerProfile }; customServerMgr.AccountRequest("UpdateProfile", loginRequest); } private void UpdateProfileAvatar() { if (!IsAuthorized) { return; } PlayerProfile playerProfile = new PlayerProfile(); playerProfile.Avatar = AvatarUrl; LoginRequest loginRequest = new LoginRequest() { playerId = ApiPlayerId, playerProfile = playerProfile }; customServerMgr.AccountRequest("UpdateProfile", loginRequest); } public void UpdateProfile() { if (IsAuthorized) { customServerMgr.AccountRequest("UpdateProfile", new LoginRequest { playerId = ApiPlayerId, playerProfile = new PlayerProfile { Level = GContext.container.Resolve().lv, Avatar = AvatarUrl, DisplayName = DisplayName } }); } } public void UpdateProfileLv() { if (IsAuthorized) { customServerMgr.AccountRequest("UpdateProfile", new LoginRequest { playerId = ApiPlayerId, playerProfile = new PlayerProfile { Level = GContext.container.Resolve().lv } }); } } } }