feat(upserver): 保存M2客户端KV迁移至本人统计写检查点
保存独立KV用户服务、固定会话存档与原业务接线,以及C3d资料缓存和C3e本人统计写。 保留原PF/AGG路线、业务公式和时序;榜单读取、好友资料、公告、匹配及首场景闸仍待完成。 验证:ServiceHost两条件各33组646断言;Unity CLI Loop编译0错误269警告。 包含相关方案、评审裁决、测试及状态文档。未操作服务器、正式账号或打包。
This commit is contained in:
@@ -0,0 +1,303 @@
|
||||
using System.Text;
|
||||
using GameCore;
|
||||
using cfg;
|
||||
using asap.core;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
|
||||
namespace OfferStoryChain
|
||||
{
|
||||
public class Manager
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
#region RedPoint
|
||||
public bool DoNeedRedPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
var packIdList = GetPackIdList();
|
||||
if (_roundCount >= packIdList.Count)
|
||||
return false;
|
||||
var packList = GetPackList(packIdList[_roundCount]);
|
||||
if (_progress < 0 || _progress >= packList.Count)
|
||||
return false;
|
||||
return packList[_progress].IAPID == 0;
|
||||
}
|
||||
}
|
||||
|
||||
public const string RedPointKey = "offerstorychain";
|
||||
#endregion
|
||||
|
||||
|
||||
#region DataStorage
|
||||
private int _eventId, _progress, _vipOnTrigger, _roundCount;
|
||||
public int EventId => _eventId;
|
||||
public int Progress => _progress;
|
||||
public int VipLevel => _vipOnTrigger;
|
||||
public int RoundCount => _roundCount;
|
||||
public bool IsActive
|
||||
{
|
||||
get
|
||||
{
|
||||
(var startTime, var endTime) = FtUtils.GetEventTimeLimit(_eventId);
|
||||
var isWithinTime = ZZTimeHelper.UtcNow() >= startTime && ZZTimeHelper.UtcNow() <= endTime;
|
||||
var isOnGoing = _roundCount < GetMaxRoundCount();
|
||||
return isWithinTime && isOnGoing;
|
||||
}
|
||||
}
|
||||
|
||||
private const string PlayFabKey = "OfferStoryChain";
|
||||
private PlayerItemData _playerItemData => GContext.container.Resolve<PlayerItemData>();
|
||||
|
||||
public static void InitEvent(cfg.FishingEvent e)
|
||||
{
|
||||
var manager = GContext.container.Resolve<Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
GContext.container.Register<Manager>().AsSingleton();
|
||||
manager = GContext.container.Resolve<Manager>();
|
||||
}
|
||||
var loadRes = manager.Load();
|
||||
if (!loadRes || manager.EventId != e.ID)
|
||||
{
|
||||
manager.Init(e.ID);
|
||||
manager.Save();
|
||||
}
|
||||
UITypes.OfferStoryChainPanel.SetType(manager.GetUiAddressableUrl());
|
||||
}
|
||||
|
||||
public void Init(int eventId)
|
||||
{
|
||||
_eventId = eventId;
|
||||
_progress = 0;
|
||||
_redirectId = _tables.TbFishingEvent[_eventId].RedirectID;
|
||||
_vipOnTrigger = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
_roundCount = 0;
|
||||
}
|
||||
|
||||
public void InitTest()
|
||||
{
|
||||
_eventId = 114514;
|
||||
_progress = 0;
|
||||
_redirectId = 92101001;
|
||||
_vipOnTrigger = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
_roundCount = 0;
|
||||
}
|
||||
|
||||
public bool Load()
|
||||
{
|
||||
var s = saveStore.Read(PlayFabKey).ValueOrThrow();
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
Debug.LogWarning("[OfferStoryChain] No pf data found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var parts = s.Split(',');
|
||||
if (parts.Length != 4)
|
||||
{
|
||||
Debug.LogWarning($"[OfferStoryChain] String format error. Expect 4, but got {parts.Length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!int.TryParse(parts[0], out int eventId)
|
||||
|| !int.TryParse(parts[1], out int progress)
|
||||
|| !int.TryParse(parts[2], out int vipLevel)
|
||||
|| !int.TryParse(parts[3], out int roundCount))
|
||||
{
|
||||
Debug.LogWarning($"[OfferStoryChain] Parsing error.");
|
||||
return false;
|
||||
}
|
||||
|
||||
_eventId = eventId;
|
||||
_progress = progress;
|
||||
_vipOnTrigger = vipLevel;
|
||||
_redirectId = _tables.TbFishingEvent[_eventId].RedirectID;
|
||||
_roundCount = roundCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("{0},{1},{2},{3}", _eventId, _progress, _vipOnTrigger, _roundCount);
|
||||
saveStore.Write(PlayFabKey, sb.ToString()).RequireAccepted();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_progress = 0;
|
||||
_roundCount = 0;
|
||||
Save();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Table Content
|
||||
private readonly Tables _tables = GContext.container.Resolve<Tables>();
|
||||
private int _redirectId;
|
||||
private EventOfferStoryChain TableMain => _tables.TbEventOfferStoryChain[_redirectId];
|
||||
private TbEventPackManager TablePacks => _tables.TbEventPackManager;
|
||||
|
||||
private List<int> GetPackIdList()
|
||||
{
|
||||
var vip = Mathf.Clamp(_vipOnTrigger, 0, TableMain.VipOfferList.Count - 1);
|
||||
return TableMain.VipOfferList[vip];
|
||||
}
|
||||
|
||||
private List<Pack> GetPackList(int packId)
|
||||
{
|
||||
return TablePacks[packId].VIPPackList[0].Select(p => _tables.TbPack[p]).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Max progress in one round.
|
||||
/// Progress more than this will either trigger next round or end this game.
|
||||
/// -1 because the last pack is the final reward.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int GetMaxProgressInRound()
|
||||
{
|
||||
return GetPackList(GetPackIdList()[0]).Count - 1;
|
||||
}
|
||||
|
||||
private int GetMaxRoundCount()
|
||||
{
|
||||
var vip = Mathf.Clamp(_vipOnTrigger, 0, TableMain.VipOfferList.Count - 1);
|
||||
return TableMain.VipOfferList[vip].Count;
|
||||
}
|
||||
|
||||
public int GetFinalRewardDropId(int roundCount)
|
||||
{
|
||||
roundCount = Mathf.Clamp(roundCount, 0, GetMaxRoundCount() - 1);
|
||||
var packIdList = GetPackIdList();
|
||||
var packList = GetPackList(packIdList[roundCount]);
|
||||
return packList[^1].DropID;
|
||||
}
|
||||
|
||||
public ScrollerItemViewData[] GetScrollerSlotDataArray(int roundCount)
|
||||
{
|
||||
var packIdList = GetPackIdList();
|
||||
var packList = GetPackList(packIdList[roundCount]);
|
||||
return Enumerable.Range(0, GetMaxProgressInRound())
|
||||
.Select(i => new ScrollerItemViewData
|
||||
{
|
||||
Index = i,
|
||||
EventId = _eventId,
|
||||
DropId = packList[i].DropID,
|
||||
IapId = packList[i].IAPID,
|
||||
State = GetStateFromIndex(i)
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region System
|
||||
|
||||
private OfferStoryChain.ScrollerItemViewState GetStateFromIndex(int idx)
|
||||
{
|
||||
if (idx < _progress)
|
||||
return OfferStoryChain.ScrollerItemViewState.Bought;
|
||||
if (idx == _progress)
|
||||
return OfferStoryChain.ScrollerItemViewState.Available;
|
||||
return OfferStoryChain.ScrollerItemViewState.Locked;
|
||||
|
||||
}
|
||||
public IEventAggregator EventAggregator = new EventAggregator();
|
||||
|
||||
public MainPanelData GetMainPanelData()
|
||||
{
|
||||
var res = new MainPanelData();
|
||||
res.Progress = _progress;
|
||||
res.Reward = _playerItemData.GetItemDataByDropIdLureInflation(null, GetFinalRewardDropId(_roundCount))[0];
|
||||
res.ScrollerItemViewDataList = GetScrollerSlotDataArray(_roundCount);
|
||||
res.SpineBaseName = TableMain.Spine;
|
||||
return res;
|
||||
}
|
||||
|
||||
public List<ItemData> GrantReward(int dropId)
|
||||
{
|
||||
var rewards = _playerItemData.AddItemByDropLureInflation(null, dropId, null);
|
||||
MoveToNextSlotAndTryGrantFinalReward();
|
||||
return rewards;
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task<(bool res, List<ItemData> rewards)> GrantReward(int dropId, IAPItemList iapItemList, int eventId)
|
||||
{
|
||||
var rewards = _playerItemData.GetItemDataByDropIdLureInflation(null, dropId);
|
||||
var shopBuyTypeData = new ShopBuyTypeData { type = ShopBuyType.EventPack, ID = eventId };
|
||||
bool res = await GContext.container.Resolve<PlayerShopData>()
|
||||
.OnBuy(dropId, shopBuyTypeData, iapItemList, rewards);
|
||||
return (res, rewards);
|
||||
}
|
||||
|
||||
public void MoveToNextSlotAndTryGrantFinalReward()
|
||||
{
|
||||
Debug.Log($"[OfferStoryChain] Move to next slot. Current Progress: {_progress}");
|
||||
_progress++;
|
||||
if (_progress >= GetMaxProgressInRound())
|
||||
{
|
||||
var finalRewardDropId = GetFinalRewardDropId(_roundCount);
|
||||
_playerItemData.AddItemByDropLureInflation(null, finalRewardDropId, null, false);
|
||||
_progress = 0;
|
||||
_roundCount++;
|
||||
}
|
||||
Save();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI Data
|
||||
public TimerContext GetTimerData()
|
||||
{
|
||||
var res = new TimerContext();
|
||||
(res.StartTime, res.ExpiryTime) = FtUtils.GetEventTimeLimit(_eventId);
|
||||
return res;
|
||||
}
|
||||
|
||||
public string GetEntrancIconUrl()
|
||||
{
|
||||
return TableMain.Icon;
|
||||
}
|
||||
|
||||
public string GetUiAddressableUrl()
|
||||
{
|
||||
return TableMain.Addressable;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class ScrollerItemViewData
|
||||
{
|
||||
// public ItemData[] ItemDataList { get; set; }
|
||||
// public string TextPrice { get; set; }
|
||||
public int Index { get; set; }
|
||||
public int EventId { get; set; }
|
||||
public int DropId { get; set; }
|
||||
public int IapId { get; set; }
|
||||
// public string TextDiscount { get; set; }
|
||||
public ScrollerItemViewState State { get; set; }
|
||||
}
|
||||
|
||||
public enum ScrollerItemViewState
|
||||
{
|
||||
Bought,
|
||||
Locked,
|
||||
Available
|
||||
}
|
||||
|
||||
public class MainPanelData
|
||||
{
|
||||
public int Progress { get; set; }
|
||||
public ItemData Reward { get; set; }
|
||||
public ScrollerItemViewData[] ScrollerItemViewDataList { get; set; }
|
||||
public string SpineBaseName { get; set; }
|
||||
// public System.DateTime StartTime { get; set; }
|
||||
// public System.DateTime EndTime { get; set; }
|
||||
}
|
||||
|
||||
public class OfferStoryChainCloseEvent { }
|
||||
}
|
||||
Reference in New Issue
Block a user