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,499 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
public class MatchGameData
|
||||
{
|
||||
public int eventID;
|
||||
public int index;
|
||||
public int vip;
|
||||
public float inflation;
|
||||
public int seed;
|
||||
public List<int> isOpenedList = new List<int>();
|
||||
//连续翻开不同的次数
|
||||
public int openCount;
|
||||
//兔子交换记录,-1 表示无交换
|
||||
public int swapFrom = -1;
|
||||
public int swapTo = -1;
|
||||
public int swapFrom2 = -1;
|
||||
public int swapTo2 = -1;
|
||||
public int tokenConsumed;
|
||||
}
|
||||
|
||||
public enum FlipResult
|
||||
{
|
||||
// InsufficientTokens,
|
||||
FirstHatFlipped,
|
||||
NoMatch,
|
||||
Match,
|
||||
}
|
||||
|
||||
|
||||
public class EventMatchGameManager
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public string EventMatchGameDataKey => "EventMatchGameDataKey";
|
||||
public int ItemSubType => 3;
|
||||
public int ItemType => 6;
|
||||
int EventType => 4;
|
||||
int EventSubType => 13;
|
||||
Tables tables;
|
||||
FishingEventData fishingEventData;
|
||||
TbFishingEvent tbFishingEvent;
|
||||
TbFishingEventCycleItem tbCycleItem;
|
||||
TbEventMatchInit tbEventMatchInit;
|
||||
public EventMatchGameManager()
|
||||
{
|
||||
tables = GContext.container.Resolve<Tables>();
|
||||
fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
tbFishingEvent = tables.TbFishingEvent;
|
||||
tbCycleItem = tables.TbFishingEventCycleItem;
|
||||
tbEventMatchInit = tables.TbEventMatchInit;
|
||||
}
|
||||
|
||||
public MatchGameData GetEventData()
|
||||
{
|
||||
var matchGameData = new MatchGameData();
|
||||
|
||||
string data = saveStore.Read(EventMatchGameDataKey).ValueOrThrow();
|
||||
if (data != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
matchGameData = Newtonsoft.Json.JsonConvert.DeserializeObject<MatchGameData>(data) ?? throw new Newtonsoft.Json.JsonSerializationException("Null MatchGameData save.");
|
||||
if (matchGameData.vip > 10)
|
||||
{
|
||||
matchGameData.vip = 10;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return matchGameData;
|
||||
}
|
||||
|
||||
public MatchGameData CheckEventData()
|
||||
{
|
||||
MatchGameData matchGameData = GetEventData();
|
||||
FishingEvent tr = GetEvent();
|
||||
if (tr == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (matchGameData.eventID != tr.ID)
|
||||
{
|
||||
FishingEventCycleItem cycleItem = tbCycleItem.GetOrDefault(tr.RedirectID);
|
||||
matchGameData = new MatchGameData();
|
||||
matchGameData.eventID = tr.ID;
|
||||
matchGameData.inflation = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
matchGameData.seed = new System.Random().Next();
|
||||
matchGameData.index = 0;
|
||||
matchGameData.vip = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
if (matchGameData.vip > 10)
|
||||
{
|
||||
matchGameData.vip = 10;
|
||||
}
|
||||
SetTokens(tr.ID, cycleItem.WelcomeGift);
|
||||
SaveData(matchGameData);
|
||||
}
|
||||
return matchGameData;
|
||||
}
|
||||
|
||||
public bool CheckOpen()
|
||||
{
|
||||
var matchGameData = CheckEventData();
|
||||
if (matchGameData == null || matchGameData.eventID == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FishingEvent fishingEvent = tables.TbFishingEvent.GetOrDefault(matchGameData.eventID);
|
||||
if (fishingEvent == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LimitedTime limitedTime = fishingEvent.TimeDefinition as LimitedTime;
|
||||
var startTime = DateTime.Parse(limitedTime.StartTime);
|
||||
var endTime = DateTime.Parse(limitedTime.EndTime);
|
||||
var curTime = ZZTimeHelper.UtcNow();
|
||||
if (curTime < startTime || curTime > endTime)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FishingEvent GetEvent()
|
||||
{
|
||||
return fishingEventData.GetEventByTypeAndSubType(EventType, EventSubType);
|
||||
}
|
||||
|
||||
#region Tokens
|
||||
public void AddTokens(int subType, int count)
|
||||
{
|
||||
if (subType == ItemSubType)
|
||||
{
|
||||
FishingEvent tr = GetEvent();
|
||||
if (tr != null)
|
||||
{
|
||||
int tokens = GetTokenCount(tr.ID);
|
||||
tokens += count;
|
||||
SetTokens(tr.ID, tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
void SetTokens(int eventID, int count)
|
||||
{
|
||||
fishingEventData.SaveTransitionData(eventID, count);
|
||||
}
|
||||
|
||||
public void RemoveTokens(int eventID, int count)
|
||||
{
|
||||
int tokens = GetTokenCount(eventID);
|
||||
tokens -= count;
|
||||
SetTokens(eventID, tokens);
|
||||
}
|
||||
|
||||
public int GetTokenCount()
|
||||
{
|
||||
FishingEvent tr = GetEvent();
|
||||
if (tr != null)
|
||||
{
|
||||
return GetTokenCount(tr.ID);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public int GetTokenCount(int eventID)
|
||||
{
|
||||
return fishingEventData.GetTransitionDataCount(eventID);
|
||||
}
|
||||
#endregion Tokens
|
||||
|
||||
public FlipResult AddProgress(int eventID, MatchGameData data, List<int> eventSpecialItemList, int opened, List<int> board, out List<int> openPos)
|
||||
{
|
||||
openPos = new List<int>();
|
||||
FlipResult idOpened = FlipResult.FirstHatFlipped;
|
||||
RemoveTokens(eventID, 1);
|
||||
data.tokenConsumed++;
|
||||
int count = data.isOpenedList.Count;
|
||||
if (count % 2 == 0)
|
||||
{
|
||||
data.isOpenedList.Add(opened);
|
||||
openPos.Add(opened);
|
||||
}
|
||||
else
|
||||
{
|
||||
//做对比
|
||||
int curOpened = data.isOpenedList[count - 1];
|
||||
//获取 curOpened 的数据,对比 opened 的数据
|
||||
int curId = board[curOpened];
|
||||
int nextId = board[opened];
|
||||
openPos.Add(curOpened);
|
||||
openPos.Add(opened);
|
||||
|
||||
#if AGG
|
||||
EventMatchInit matchInit = GetMatchInit(eventID);
|
||||
if (matchInit != null)
|
||||
{
|
||||
EventMatchStage matchStage = GetMatchStage(data, matchInit);
|
||||
int countR1 = matchInit.StageListRound1.Count;
|
||||
int roundId = data.index < countR1 ? 1 : (data.index - countR1) / matchInit.StageListRound2.Count + 2;
|
||||
using (var e = GEvent.GameEvent("event_match"))
|
||||
{
|
||||
e.AddContent("event_id", eventID)
|
||||
.AddContent("round_id", roundId)
|
||||
.AddContent("stage_id", matchStage.ID)
|
||||
.AddContent("item_id1", curId)
|
||||
.AddContent("item_id2", nextId);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (curId == nextId)
|
||||
{
|
||||
//成功
|
||||
data.isOpenedList.Add(opened);
|
||||
data.openCount = 0;
|
||||
idOpened = FlipResult.Match;
|
||||
if (eventSpecialItemList.Contains(curId))
|
||||
{
|
||||
var spOpenPos = GetSpItemOpen(data, board);
|
||||
if (spOpenPos.Count > 0)
|
||||
{
|
||||
data.isOpenedList.AddRange(spOpenPos);
|
||||
openPos.AddRange(spOpenPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//失败
|
||||
data.isOpenedList.RemoveAt(count - 1);
|
||||
data.openCount++;
|
||||
idOpened = FlipResult.NoMatch;
|
||||
if (data.openCount >= 4)
|
||||
{
|
||||
var tipsPos = GetTipsItemOpen(data, board);
|
||||
if (tipsPos.Count > 0)
|
||||
{
|
||||
openPos.AddRange(tipsPos);
|
||||
}
|
||||
data.openCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
GameDebug.Log("[EventMatch] ItemID " + string.Join(',', board) + " " + string.Join(',', data.isOpenedList));
|
||||
SaveData(data);
|
||||
return idOpened;
|
||||
}
|
||||
List<int> GetSpItemOpen(MatchGameData data, List<int> board)
|
||||
{
|
||||
List<int> openPos = new List<int>();
|
||||
Dictionary<int, int> boardDic = new Dictionary<int, int>();
|
||||
|
||||
for (int i = 0; i < board.Count; i++)
|
||||
{
|
||||
if (!data.isOpenedList.Contains(i))
|
||||
{
|
||||
boardDic.Add(i, board[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (boardDic.Count <= 4)
|
||||
{
|
||||
openPos.AddRange(boardDic.Keys);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Random random = new System.Random();
|
||||
// 执行两次随机选择
|
||||
SelectAndRemoveGroup(boardDic, openPos, random);
|
||||
SelectAndRemoveGroup(boardDic, openPos, random);
|
||||
}
|
||||
return openPos;
|
||||
}
|
||||
|
||||
List<int> GetTipsItemOpen(MatchGameData data, List<int> board)
|
||||
{
|
||||
List<int> openPos = new List<int>();
|
||||
Dictionary<int, int> dict = new Dictionary<int, int>();
|
||||
for (int i = 0; i < board.Count; i++)
|
||||
{
|
||||
if (!data.isOpenedList.Contains(i))
|
||||
{
|
||||
dict.Add(i, board[i]);
|
||||
}
|
||||
}
|
||||
System.Random random = new System.Random();
|
||||
var keys = dict.Keys.ToList();
|
||||
int key = keys[random.Next(keys.Count)];
|
||||
int value = dict[key];
|
||||
// 找到所有具有相同值的键
|
||||
return dict.Where(kv => kv.Value == value).Select(kv => kv.Key).ToList();
|
||||
}
|
||||
|
||||
void SelectAndRemoveGroup(Dictionary<int, int> dict, List<int> result, System.Random random)
|
||||
{
|
||||
var keys = dict.Keys.ToList();
|
||||
int key = keys[random.Next(keys.Count)];
|
||||
int value = dict[key];
|
||||
|
||||
// 找到所有具有相同值的键
|
||||
var group = dict.Where(kv => kv.Value == value).Select(kv => kv.Key).ToList();
|
||||
|
||||
// 添加到结果列表
|
||||
result.AddRange(group);
|
||||
|
||||
// 从字典中移除已选中的组
|
||||
foreach (int k in group)
|
||||
{
|
||||
dict.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public int NextStage()
|
||||
{
|
||||
MatchGameData data = GetEventData();
|
||||
EventMatchInit eventMatchInit = GetMatchInit(data.eventID);
|
||||
|
||||
EventMatchStage stage = GetMatchStage(data, eventMatchInit);
|
||||
if (data.isOpenedList.Count == stage.ItemCount)
|
||||
{
|
||||
#if AGG
|
||||
int countR1 = eventMatchInit.StageListRound1.Count;
|
||||
int roundId = data.index < countR1 ? 1 : (data.index - countR1) / eventMatchInit.StageListRound2.Count + 2;
|
||||
using (var e = GEvent.GameEvent("event_match_complete"))
|
||||
{
|
||||
e.AddContent("event_id", data.eventID)
|
||||
.AddContent("round_id", roundId)
|
||||
.AddContent("stage_id", stage.ID)
|
||||
.AddContent("item_consume", data.tokenConsumed);
|
||||
}
|
||||
#endif
|
||||
//领奖
|
||||
ChangeSource changeSource = new ChangeSource(data.eventID, "Event_Minigame1", "Event_Minigame1_Match", "StageReward");
|
||||
List<ItemData> itemData = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(data.inflation, stage.DropId, changeSource);
|
||||
|
||||
//下一关
|
||||
data.index++;
|
||||
data.seed = new System.Random().Next();
|
||||
data.isOpenedList.Clear();
|
||||
data.swapFrom = -1;
|
||||
data.swapTo = -1;
|
||||
data.swapFrom2 = -1;
|
||||
data.swapTo2 = -1;
|
||||
data.tokenConsumed = 0;
|
||||
SaveData(data);
|
||||
if (stage.ID == eventMatchInit.StageListRound1[^1] || stage.ID == eventMatchInit.StageListRound2[^1])
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public List<int> GetBoardData(MatchGameData data)
|
||||
{
|
||||
EventMatchInit eventMatchInit = GetMatchInit(data.eventID);
|
||||
|
||||
EventMatchStage stage = GetMatchStage(data, eventMatchInit);
|
||||
int count = stage.ItemCount / 2;
|
||||
|
||||
List<EventMatchItem> eventMatchItems = tables.TbEventMatchItem.DataList;
|
||||
List<int> ids = new List<int>(eventMatchInit.EventItemList);
|
||||
List<int> itemList = new List<int>(stage.ItemList);
|
||||
List<int> itemID = new List<int>();
|
||||
int id;
|
||||
if (itemList.Count > 0)
|
||||
{
|
||||
count -= itemList.Count;
|
||||
for (int i = 0; i < itemList.Count; i++)
|
||||
{
|
||||
id = eventMatchInit.EventItemList[itemList[i] - 1];
|
||||
ids.Remove(id);
|
||||
EventMatchItem eventMatchItem = tables.TbEventMatchItem.GetOrDefault(id);
|
||||
if (eventMatchItem != null)
|
||||
{
|
||||
itemID.Add(id);
|
||||
itemID.Add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
System.Random random = new System.Random(data.seed);
|
||||
if (count > 0)
|
||||
{
|
||||
int index;
|
||||
double spRoll = random.NextDouble();
|
||||
if (spRoll < stage.SpItemProb)
|
||||
{
|
||||
var spItemIDs = eventMatchInit.EventSpecialItemList;
|
||||
if (spItemIDs.Count > 0)
|
||||
{
|
||||
count--;
|
||||
index = random.Next(spItemIDs.Count);
|
||||
itemID.Add(spItemIDs[index]);
|
||||
itemID.Add(spItemIDs[index]);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
index = random.Next(ids.Count);
|
||||
id = ids[index];
|
||||
ids.Remove(id);
|
||||
|
||||
itemID.Add(id);
|
||||
itemID.Add(id);
|
||||
}
|
||||
}
|
||||
//itemID 打乱顺序
|
||||
if (data.index == 0)
|
||||
{
|
||||
itemID[2] = itemID[0];
|
||||
itemID[1] = itemID[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
itemID = ConvertTools.Shuffle(itemID, random);
|
||||
}
|
||||
Debug.Log("[EventMatch] ItemID " + string.Join(',', itemID));
|
||||
return itemID;
|
||||
}
|
||||
|
||||
public async void ShowFestPack(int eventID)
|
||||
{
|
||||
try
|
||||
{
|
||||
var init = GetMatchInit(eventID);
|
||||
if (init == null)
|
||||
return;
|
||||
var packData = new EventNeoNormalPackData
|
||||
{
|
||||
EventId = eventID,
|
||||
PackId = init.PackId,
|
||||
// Panel = init.PackPanel
|
||||
};
|
||||
await EventNeoNormalPackPanel.TryShowAsync(packData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
}
|
||||
public EventMatchInit GetMatchInit(int eventID)
|
||||
{
|
||||
try
|
||||
{
|
||||
FishingEvent fishingEvent = tbFishingEvent.GetOrDefault(eventID);
|
||||
FishingEventCycleItem cycleItem = tbCycleItem.GetOrDefault(fishingEvent.RedirectID);
|
||||
return tbEventMatchInit[cycleItem.RedirectID];
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public EventMatchInit GetMatchInitRedirectID(int redirectID)
|
||||
{
|
||||
try
|
||||
{
|
||||
FishingEventCycleItem cycleItem = tbCycleItem.GetOrDefault(redirectID);
|
||||
return tbEventMatchInit[cycleItem.RedirectID];
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public EventMatchStage GetMatchStage(MatchGameData data, EventMatchInit eventMatchInit)
|
||||
{
|
||||
int stageID;
|
||||
int countRound1 = eventMatchInit.StageListRound1.Count;
|
||||
if (data.index < countRound1)
|
||||
{
|
||||
stageID = eventMatchInit.StageListRound1[data.index];
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = data.index - countRound1;
|
||||
index %= eventMatchInit.StageListRound2.Count;
|
||||
stageID = eventMatchInit.StageListRound2[index];
|
||||
}
|
||||
return tables.TbEventMatchStage.GetOrDefault(stageID);
|
||||
}
|
||||
|
||||
public void SaveData(MatchGameData matchGameData)
|
||||
{
|
||||
saveStore.Write(EventMatchGameDataKey, JsonMapper.ToJson(matchGameData)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user