begin trigger implementation

This commit is contained in:
Mikhail
2024-12-26 11:57:05 -05:00
parent 506a17cf54
commit 5ad06556d8
10 changed files with 297 additions and 55 deletions

View File

@@ -172,6 +172,27 @@ namespace EpinelPS.Database
};
}
}
public class Trigger
{
public TriggerType Type;
public long Id;
public long CreatedAt;
public int ConditionId;
public int Value;
public NetTrigger ToNet()
{
return new()
{
ConditionId = ConditionId,
CreatedAt = CreatedAt,
Seq = Id,
Trigger = (int)Type,
UserValue = Value
};
}
}
public class User
{
// User info
@@ -249,11 +270,29 @@ namespace EpinelPS.Database
public List<Badge> Badges = [];
public List<NetUserAttractiveData> BondInfo = [];
public List<Trigger> Triggers = [];
public int LastTriggerId = 1;
// Event data
public Dictionary<int, EventData> EventInfo = new();
public MogMinigameInfo MogInfo = new();
public Trigger AddTrigger(TriggerType type, int value, int conditionId = 0)
{
Trigger t = new()
{
Id = LastTriggerId++,
Type = type,
ConditionId = conditionId,
CreatedAt = DateTime.Now.Ticks,
Value = value
};
Triggers.Add(t);
return t;
}
public Badge AddBadge(BadgeContents type, string location)
{
// generate unique badge SEQ

View File

@@ -50,12 +50,12 @@ namespace EpinelPS.StaticInfo
public Dictionary<int, EventManager> eventManagers = new Dictionary<int, EventManager>();
public Dictionary<int, LiveWallpaperRecord> lwptablemgrs = new Dictionary<int, LiveWallpaperRecord>(); // Fixed initialization
public Dictionary<int, AlbumResourceRecord> albumResourceRecords = new Dictionary<int, AlbumResourceRecord>();
public Dictionary<int, UserFrameTableRecord> userFrameTable = new Dictionary<int, UserFrameTableRecord>();
public Dictionary<int, ArchiveRecordManagerRecord> archiveRecordManagerTable = new Dictionary<int, ArchiveRecordManagerRecord>();
public Dictionary<int, ArchiveEventStoryRecord> archiveEventStoryRecords = new Dictionary<int, ArchiveEventStoryRecord>();
public Dictionary<int, ArchiveEventQuestRecord> archiveEventQuestRecords = new Dictionary<int, ArchiveEventQuestRecord>();
public Dictionary<int, ArchiveEventDungeonStageRecord> archiveEventDungeonStageRecords = new Dictionary<int, ArchiveEventDungeonStageRecord>();
public Dictionary<int, UserTitleRecord> userTitleRecords = new Dictionary<int, UserTitleRecord>();
public Dictionary<int, UserFrameTableRecord> userFrameTable = new Dictionary<int, UserFrameTableRecord>();
public Dictionary<int, ArchiveRecordManagerRecord> archiveRecordManagerTable = new Dictionary<int, ArchiveRecordManagerRecord>();
public Dictionary<int, ArchiveEventStoryRecord> archiveEventStoryRecords = new Dictionary<int, ArchiveEventStoryRecord>();
public Dictionary<int, ArchiveEventQuestRecord> archiveEventQuestRecords = new Dictionary<int, ArchiveEventQuestRecord>();
public Dictionary<int, ArchiveEventDungeonStageRecord> archiveEventDungeonStageRecords = new Dictionary<int, ArchiveEventDungeonStageRecord>();
public Dictionary<int, UserTitleRecord> userTitleRecords = new Dictionary<int, UserTitleRecord>();
public Dictionary<int, ArchiveMessengerConditionRecord> archiveMessengerConditionRecords = [];
public Dictionary<int, CharacterStatRecord> characterStatTable = [];
public Dictionary<int, SkillInfoRecord> skillInfoTable = [];
@@ -107,7 +107,7 @@ namespace EpinelPS.StaticInfo
// Initialize Jukebox data dictionaries
jukeboxListDataRecords = [];
jukeboxThemeDataRecords = [];
archiveMessengerConditionRecords = [];
archiveMessengerConditionRecords = [];
var rawBytes = File.ReadAllBytes(filePath);
Sha256Hash = SHA256.HashData(rawBytes);
@@ -409,12 +409,12 @@ namespace EpinelPS.StaticInfo
{
OutpostBattle.Add(obj.id, obj);
}
var archiveRecordManagerTableData = await LoadZip<ArchiveRecordManagerTable>("ArchiveRecordManagerTable.json", progress);
foreach (var obj in archiveRecordManagerTableData.records)
{
archiveRecordManagerTable.Add(obj.id, obj);
}
var archiveRecordManagerTableData = await LoadZip<ArchiveRecordManagerTable>("ArchiveRecordManagerTable.json", progress);
foreach (var obj in archiveRecordManagerTableData.records)
{
archiveRecordManagerTable.Add(obj.id, obj);
}
var gachaTypeTable = await LoadZip<GachaTypeTable>("GachaTypeTable.json", progress);
@@ -439,38 +439,38 @@ namespace EpinelPS.StaticInfo
{
lwptablemgrs.Add(obj.id, obj); // Use obj.id as the key and obj (the LiveWallpaperRecord) as the value
}
var userFrameData = await LoadZip<UserFrameTable>("UserFrameTable.json", progress);
foreach (var record in userFrameData.records)
{
userFrameTable[record.id] = record;
}
// Load and parse ArchiveEventDungeonStageTable.json
var archiveEventDungeonStageData = await LoadZip<ArchiveEventDungeonStageTable>("ArchiveEventDungeonStageTable.json", progress);
foreach (var obj in archiveEventDungeonStageData.records)
{
archiveEventDungeonStageRecords.Add(obj.id, obj);
}
var userTitleTable = await LoadZip<UserTitleTable>("UserTitleTable.json", progress);
foreach (var obj in userTitleTable.records)
{
userTitleRecords.Add(obj.id, obj);
}
// Load and parse ArchiveEventStoryTable.json
var archiveEventStoryTable = await LoadZip<ArchiveEventStoryTable>("ArchiveEventStoryTable.json", progress);
foreach (var obj in archiveEventStoryTable.records)
{
archiveEventStoryRecords.Add(obj.id, obj);
}
var userFrameData = await LoadZip<UserFrameTable>("UserFrameTable.json", progress);
foreach (var record in userFrameData.records)
{
userFrameTable[record.id] = record;
}
// Load and parse ArchiveEventDungeonStageTable.json
var archiveEventDungeonStageData = await LoadZip<ArchiveEventDungeonStageTable>("ArchiveEventDungeonStageTable.json", progress);
foreach (var obj in archiveEventDungeonStageData.records)
{
archiveEventDungeonStageRecords.Add(obj.id, obj);
}
// Load and parse ArchiveEventQuestTable.json
var archiveEventQuestTable = await LoadZip<ArchiveEventQuestTable>("ArchiveEventQuestTable.json", progress);
foreach (var obj in archiveEventQuestTable.records)
{
archiveEventQuestRecords.Add(obj.id, obj);
}
var userTitleTable = await LoadZip<UserTitleTable>("UserTitleTable.json", progress);
foreach (var obj in userTitleTable.records)
{
userTitleRecords.Add(obj.id, obj);
}
// Load and parse ArchiveEventStoryTable.json
var archiveEventStoryTable = await LoadZip<ArchiveEventStoryTable>("ArchiveEventStoryTable.json", progress);
foreach (var obj in archiveEventStoryTable.records)
{
archiveEventStoryRecords.Add(obj.id, obj);
}
// Load and parse ArchiveEventQuestTable.json
var archiveEventQuestTable = await LoadZip<ArchiveEventQuestTable>("ArchiveEventQuestTable.json", progress);
foreach (var obj in archiveEventQuestTable.records)
{
archiveEventQuestRecords.Add(obj.id, obj);
}
// LOAD ARCHIVE MESSENGER CONDITION TABLE
var archiveMessengerConditionTable = await LoadZip<ArchiveMessengerConditionTable>("ArchiveMessengerConditionTable.json", progress);
foreach (var obj in archiveMessengerConditionTable.records)
@@ -763,4 +763,151 @@ namespace EpinelPS.StaticInfo
}
public enum TriggerType
{
None = 0,
UserLevel = 1,
CampaignClear = 2,
ChapterClear = 3,
CampaignStart = 4,
TowerAllStart = 5,
TowerBasicClear = 6,
CharacterLevel = 7,
CharacterGrade = 8,
CharacterCore = 9,
CharacterAttractiveLevel = 10,
MainShopBuy = 11,
ShopGuildBuy = 12,
GachaCharacter = 13,
OutpostBattleReward = 14,
OutpostFastBattleReward = 15,
PointRewardDaily = 16,
PointRewardWeekly = 17,
ObtainCharacter = 18,
OutpostBuilding = 19,
SendFriendShipPoint = 20,
SendDispatch = 21,
MainQuestClear = 22,
ObtainJukeboxTheme = 23,
SubQuestClear = 24,
CampaignGroupClear = 25,
NpcTalk = 26,
TowerElysionClear = 27,
TowerMissilisClear = 28,
TowerTetraClear = 29,
TowerOverspecClear = 30,
AchieveRanking1st = 31,
AchieveRanking5th = 32,
AchieveRanking10th = 33,
EventPoint = 34,
HardChapterClear = 35,
ObtainCharacterSSR = 36,
GachaCompany = 37,
ObtainCharacterNew = 38,
WinArena = 39,
SpecialArenaTier = 40,
PointRewardAchievement = 41,
ObtainCharacterPilgrim = 42,
ShopDisassembleBuy = 43,
CharacterCounsel = 44,
CharacterAttractivePresent = 45,
ObtainEquipItemT3T4 = 46,
ObtainEquipItemT5T6 = 47,
ObtainEquipItemT7T8 = 48,
ObtainEquipItemT9 = 49,
PointRewardEvent = 50,
MissionClearEvent = 51,
ObtainMemorialItem = 52,
LostSectorClear = 53,
ObtainHarmonyCube = 54,
HarmonyCubeLevel = 55,
CooperationEventClear = 56,
SynchroDeviceSlot = 57,
ObtainEquipItemALL = 58,
EquipItemLevel = 59,
CharacterSkillLevel = 60,
PickupGachaCharacter = 61,
ObtainSilverMileage = 62,
ObtainGoldMileage = 63,
SendDispatchGrade = 64,
OutpostBattleBoxLevel = 65,
GetFriendShipPoint = 66,
MessageClear = 67,
RecycleResearchLevel = 68,
GachaPremium = 69,
CharacterLevelUpCount = 70,
CharacterGradeUpCount = 71,
CharacterLevelMax = 72,
CharacterGradeMax = 73,
HarmonyCubeLevelMax = 74,
CharacterSkillLevelMax = 75,
CharacterAttractiveLevelMax = 76,
EquipItemLevelMax = 77,
ObtainEquipItemT2 = 78,
FieldObjectCollection = 79,
SimulationRoomStart = 80,
InterceptStart = 81,
EquipItemLevelCount = 82,
SimulationRoomClear = 83,
InterceptClear = 84,
DailyEventClear = 85,
EventStageClear = 86,
ObtainEventCurrencyMaterialMiraclesnow = 87,
EventDungeonStageClear = 88,
EventSortOutClear = 89,
EventSortOutPointMax = 90,
EquipItemLevelUpCount = 91,
CharacterSkillLevelUpCount = 92,
FirstPaidGacha_Legecy = 93,
ObtainCharacterCostume = 94,
SyncroDeviceLevelMax = 95,
ObtainEventCurrencyMaterial = 96,
SimulationRoomClearWithoutCondition = 97,
EventTextAdventureClear = 98,
RookieArenaPlayCount = 99,
EventDicePlayCount = 100,
EventBBQTycoonDailyRewardCheck = 101,
EventBBQTycoonHighScore = 102,
ChampionArenaGambleWinAll = 103,
ChampionArenaGambleLoseAll = 104,
EventMiniGameCe002PlayCheck = 105,
EventMiniGameNKSPlayCheck = 106,
EventSnowfallOasisDailyRewardCheck = 107,
EventMiniGameCe003RewardCheck = 108,
EventTowerDefensePlayCheck = 109,
EventPlaySodaPlayCheck = 110,
EventIslandAdventureFishingPlayCheck = 111,
MiniGameDDCompleteDive = 112,
MiniGameDDCompleteSushi = 113,
MiniGameDDTotalGold = 114,
MiniGameDDSushiPreTurnover = 115,
MiniGameDDTotalFish = 116,
MiniGameDDPerUnderwaterEncounter = 117,
MiniGameDDUnlockEmployeeCount = 118,
MiniGameDDUnlockNikkeCount = 119,
MiniGameDDGunLevel = 120,
MiniGameDDIDiverLevel = 121,
MiniGameDDUnlockSushiCount = 122,
MiniGameDDSushiLevel = 123,
MiniGameDDSushiCookScore = 124,
MiniGameDDSushiPreTurnoverTotalMax = 125,
MiniGameDDSushiLevelTotalMax = 126,
MiniGameDDAllAchievement = 127,
ComebackPollComplete = 128,
AliceAccessAttractiveScenario = 129,
AliceEquipCollectionItemLevel = 130,
AliceEquipCollectionItemSR = 131,
AliceEquipItemOverload = 132,
AliceSkill1Level = 133,
AliceSkillBurstLevel = 134,
InterceptNormalClearWithCondition = 135,
InterceptSpecialClearWithCondition = 136,
SimulationRoomClearCount1Only = 137,
TacticAcademyFinish9_4 = 138,
EventMiniGameCe004RewardCheck = 139,
EventMVGPlayCheck = 140,
EventDDRRewardCheck = 141
}
}

View File

@@ -107,13 +107,13 @@ namespace EpinelPS.LobbyServer
}
else
{
var bin = await PacketDecryption.DecryptOrReturnContentAsync(ctx);
// return grpc IMessage from byte array with type T
T msg = new();
Console.WriteLine("Reading " + msg.GetType().Name);
var bin = await PacketDecryption.DecryptOrReturnContentAsync(ctx);
msg.MergeFrom(bin.Contents);
Console.WriteLine("Reading " + msg.GetType().Name);
PrintMessage(msg);
Console.WriteLine();

View File

@@ -0,0 +1,18 @@
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Mission
{
[PacketPath("/mission/obtain/daily")]
public class ObtainDaily : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqObtainDailyMissionReward>();
var response = new ResObtainDailyMissionReward();
// TODO
await WriteDataAsync(response);
}
}
}

View File

@@ -7,7 +7,7 @@ namespace EpinelPS.LobbyServer.Mission.Rewards
{
protected override async Task HandleAsync()
{
var req = ReadData<ReqGetWeeklyRewardedData>();
var req = await ReadData<ReqGetWeeklyRewardedData>();
// TODO: implement
var response = new ResGetWeeklyRewardedData();

View File

@@ -7,7 +7,7 @@ namespace EpinelPS.LobbyServer.Jukebox
{
protected override async Task HandleAsync()
{
var req = ReadData<ReqRecordJukeboxPlayHistory>();
var req = await ReadData<ReqRecordJukeboxPlayHistory>();
var response = new ResRecordJukeboxPlayHistory();
await WriteDataAsync(response);

View File

@@ -147,9 +147,11 @@ namespace EpinelPS.LobbyServer.Stage
private static void DoQuestSpecificUserOperations(Database.User user, int clearedStageId)
{
var quest = GameData.Instance.GetMainQuestForStageClearCondition(clearedStageId);
if (quest != null)
user.SetQuest(quest.id, false);
var quest = GameData.Instance.GetMainQuestForStageClearCondition(clearedStageId) ?? throw new Exception("cannot find quest from cleared stage id");
user.SetQuest(quest.id, false);
user.AddTrigger(TriggerType.CampaignClear, 1, clearedStageId);
user.AddTrigger(TriggerType.MainQuestClear, 1, quest.id);
// TODO: Is this the right place to add default characters?
// Stage 1-4 BOSS

View File

@@ -8,8 +8,11 @@ namespace EpinelPS.LobbyServer.Stage
protected override async Task HandleAsync()
{
var req = await ReadData<ReqEnterStage>();
var user = GetUser();
var response = new ResEnterStage();
user.AddTrigger(StaticInfo.TriggerType.CampaignStart, 1);
await WriteDataAsync(response);
}

View File

@@ -14,8 +14,10 @@ namespace EpinelPS.LobbyServer.Trigger
Console.WriteLine("Complete quest: " + req.Tid);
user.SetQuest(req.Tid, false);
var completedQuest = GameData.Instance.GetMainQuestByTableId(req.Tid);
if (completedQuest == null) throw new Exception("Quest not found");
var completedQuest = GameData.Instance.GetMainQuestByTableId(req.Tid) ?? throw new Exception("Quest not found");
user.AddTrigger(TriggerType.CampaignClear, 1, completedQuest.condition_id);
user.AddTrigger(TriggerType.MainQuestClear, 1, completedQuest.id);
JsonDb.Save();
var response = new ResFinMainQuest();

View File

@@ -1,4 +1,5 @@
using EpinelPS.Utils;
using EpinelPS.Database;
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Trigger
{
@@ -7,9 +8,39 @@ namespace EpinelPS.LobbyServer.Trigger
{
protected override async Task HandleAsync()
{
var req = ReadData<ReqSyncTrigger>();
var req = await ReadData<ReqSyncTrigger>();
var user = GetUser();
// This request is responsible for fetching a log for
// daily, weekly, challenge mission completion.
// This endpoint also returns the entire "history" for the account when
// Seq = 0, which the client does when it is started for the first time, or when
// the "Clear Cache" option is invoked.
// When Seq = 0, the server limits the responses to 2000 items,
// and HasRemainData is set to true.
// TODO: Is it necessary to store the entire account history each time a stage
// is cleared, why does the official server do this?
var response = new ResSyncTrigger();
Console.WriteLine("needs " + req.Seq);
// Look for triggers past that amount
var newTriggers = user.Triggers.Where(x => x.Id > req.Seq).ToArray();
// Return all triggers
int triggerCount = 0;
foreach (var item in newTriggers)
{
triggerCount++;
response.Triggers.Add(item.ToNet());
if (triggerCount >= 2000)
{
response.HasRemainData = true;
break;
}
}
await WriteDataAsync(response);
}
}