Implement stage skipping

This commit is contained in:
Mikhail
2024-07-14 19:07:42 -04:00
parent 367cd64b8e
commit 5134ac187b
15 changed files with 558 additions and 49 deletions

View File

@@ -10,8 +10,8 @@ namespace ProtobufViewUtil
{ {
Console.WriteLine("Hello, World!"); Console.WriteLine("Hello, World!");
ResGetOutpostData s = new ResGetOutpostData(); StaticDataPackResponse s = new StaticDataPackResponse();
var inn = File.ReadAllBytes(@"C:\Users\Misha\Downloads\getoutpostdatach2done"); var inn = File.ReadAllBytes(@"C:\Users\Misha\Desktop\response");
s.MergeFrom(inn); s.MergeFrom(inn);
Console.WriteLine(s.ToString()); Console.WriteLine(s.ToString());
var outt = s.ToByteArray(); var outt = s.ToByteArray();

View File

@@ -25,6 +25,10 @@ If the game does not get past the title screen, open an issue and send %appdata%
Note that this was tested with the latest version (122.8.20c) Note that this was tested with the latest version (122.8.20c)
To access the admin panel, go to https://127.0.0.1/admin/ and sign in. Note that IsAdmin needs to be true for the user account. Note that this interface does not have anything yet.
To skip stages, a basic command line interface is implemented.
## Progress ## Progress
Stage, character, outpost and story information is saved and works, as well as player nickname. Stage, character, outpost and story information is saved and works, as well as player nickname.

View File

@@ -0,0 +1,22 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace nksrv.LobbyServer.Msgs.Arena
{
[PacketPath("/arena/special/showreward")]
public class ShowSpecialArenaReward : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqShowSpecialArenaReward>();
var response = new ResShowSpecialArenaReward();
// TODO
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,24 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace nksrv.LobbyServer.Msgs.FavoriteItem
{
[PacketPath("/favoriteitem/library")]
public class GetFavoriteItemLibrary : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqGetFavoriteItemLibrary>();
var response = new ResGetFavoriteItemLibrary();
var user = GetUser();
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,23 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace nksrv.LobbyServer.Msgs.FavoriteItem
{
[PacketPath("/favoriteitem/list")]
public class ListFavoriteItem : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqListFavoriteItem>();
var user = GetUser();
var response = new ResListFavoriteItem();
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,23 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace nksrv.LobbyServer.Msgs.FavoriteItem
{
[PacketPath("/favoriteitem/quest/list")]
public class ListFavoriteItemQuests : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqListFavoriteItemQuest>();
var user = GetUser();
var response = new ResListFavoriteItemQuest();
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,25 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace nksrv.LobbyServer.Msgs.Liberate
{
[PacketPath("/liberate/get")]
public class GetLiberateData : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqGetLiberateData>();
var user = GetUser();
var response = new ResGetLiberateData() { };
// TODO
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,25 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace nksrv.LobbyServer.Msgs.Lostsector
{
[PacketPath("/lostsector/get")]
public class GetLostSectorData : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqGetLostSectorData>();
var user = GetUser();
var response = new ResGetLostSectorData();
// TODO
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,25 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace nksrv.LobbyServer.Msgs.Sidestory
{
[PacketPath("/sidestory/list")]
public class ListSideStory : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqListSideStory>();
var user = GetUser();
var response = new ResListSideStory();
// TODO
await WriteDataAsync(response);
}
}
}

View File

@@ -24,56 +24,68 @@ namespace nksrv.LobbyServer.Msgs.Stage
// TODO: check if user has already cleared this stage // TODO: check if user has already cleared this stage
if (req.BattleResult == 1) if (req.BattleResult == 1)
{ {
var clearedStage = StaticDataParser.Instance.GetStageData(req.StageId); response = CompleteStage(user, req.StageId);
if (clearedStage == null) throw new Exception("cleared stage cannot be null");
if (user.FieldInfo.Count == 0)
{
user.FieldInfo.Add("0_" + clearedStage.chapter_mod, new FieldInfo() { });
}
DoQuestSpecificUserOperations(user, req.StageId);
var rewardData = StaticDataParser.Instance.GetRewardTableEntry(clearedStage.reward_id);
if (rewardData != null)
response.StageClearReward = RegisterRewardsForUser(user, rewardData);
else
Logger.Warn("rewardId is null for stage " + req.StageId);
if (clearedStage.stage_category == "Normal" || clearedStage.stage_category == "Boss" || clearedStage.stage_category == "Hard")
{
if (clearedStage.chapter_mod == "Hard")
{
user.LastHardStageCleared = req.StageId;
}
else if (clearedStage.chapter_mod == "Normal")
{
user.LastNormalStageCleared = req.StageId;
}
else
{
Logger.Warn("Unknown chapter mod " + clearedStage.chapter_mod);
}
}
else if (clearedStage.stage_category == "Extra")
{
}
else
{
Logger.Warn("Unknown stage category " + clearedStage.stage_category);
}
user.FieldInfo[(clearedStage.chapter_id - 1) + "_" + clearedStage.chapter_mod].CompletedStages.Add(new NetFieldStageData() { StageId = req.StageId });
JsonDb.Save();
} }
await WriteDataAsync(response); await WriteDataAsync(response);
} }
private NetRewardData RegisterRewardsForUser(Utils.User user, RewardTableRecord rewardData)
public static ResClearStage CompleteStage(Utils.User user, int StageId)
{
var response = new ResClearStage();
var clearedStage = StaticDataParser.Instance.GetStageData(StageId);
if (clearedStage == null) throw new Exception("cleared stage cannot be null");
if (user.FieldInfo.Count == 0)
{
user.FieldInfo.Add("0_" + clearedStage.chapter_mod, new FieldInfo() { });
}
DoQuestSpecificUserOperations(user, StageId);
var rewardData = StaticDataParser.Instance.GetRewardTableEntry(clearedStage.reward_id);
if (rewardData != null)
response.StageClearReward = RegisterRewardsForUser(user, rewardData);
else
Logger.Warn("rewardId is null for stage " + StageId);
if (clearedStage.stage_category == "Normal" || clearedStage.stage_category == "Boss" || clearedStage.stage_category == "Hard")
{
if (clearedStage.chapter_mod == "Hard")
{
user.LastHardStageCleared = StageId;
}
else if (clearedStage.chapter_mod == "Normal")
{
user.LastNormalStageCleared = StageId;
}
else
{
Logger.Warn("Unknown chapter mod " + clearedStage.chapter_mod);
}
}
else if (clearedStage.stage_category == "Extra")
{
}
else
{
Logger.Warn("Unknown stage category " + clearedStage.stage_category);
}
var key = (clearedStage.chapter_id - 1) + "_" + clearedStage.chapter_mod;
if (!user.FieldInfo.ContainsKey(key))
user.FieldInfo.Add(key, new FieldInfo());
user.FieldInfo[key].CompletedStages.Add(new NetFieldStageData() { StageId = StageId });
JsonDb.Save();
return response;
}
private static NetRewardData RegisterRewardsForUser(Utils.User user, RewardTableRecord rewardData)
{ {
NetRewardData ret = new(); NetRewardData ret = new();
if (rewardData.rewards == null) return ret; if (rewardData.rewards == null) return ret;

View File

@@ -21,6 +21,7 @@ using Swan;
using Google.Api; using Google.Api;
using nksrv.StaticInfo; using nksrv.StaticInfo;
using EmbedIO.WebApi; using EmbedIO.WebApi;
using nksrv.LobbyServer.Msgs.Stage;
namespace nksrv namespace nksrv
{ {
@@ -39,9 +40,195 @@ namespace nksrv
LobbyHandler.Init(); LobbyHandler.Init();
Logger.Info("Starting server"); Logger.Info("Starting server");
new Thread(() =>
{
var server = CreateWebServer();
server.RunAsync();
}).Start();
using var server = CreateWebServer(); // cli interface
await server.RunAsync();
ulong selectedUser = 0;
string prompt = "# ";
while (true)
{
Console.Write(prompt);
var input = Console.ReadLine();
var args = input.Split(' ');
if (string.IsNullOrEmpty(input) || string.IsNullOrWhiteSpace(input))
{
}
else if (input == "?" || input == "help")
{
Console.WriteLine("Nikke Private Server CLI interface");
Console.WriteLine();
Console.WriteLine("Commands:");
Console.WriteLine(" help - show this help");
Console.WriteLine(" ls /users - show all users");
Console.WriteLine(" cd (user id) - select user by id");
Console.WriteLine(" rmuser - delete selected user");
Console.WriteLine(" ban - ban selected user from game");
Console.WriteLine(" unban - unban selected user from game");
Console.WriteLine(" exit - exit server application");
Console.WriteLine(" completestage (chapter num)-(stage number) - complete selected stage and get rewards (and all previous ones). Example completestage 15-1. Note that the exact stage number cleared may not be exact.");
}
else if (input == "ls /users")
{
Console.WriteLine("Id,Username,Nickname");
foreach (var item in JsonDb.Instance.Users)
{
Console.WriteLine($"{item.ID},{item.Username},{item.Nickname}");
}
}
else if (input.StartsWith("cd"))
{
if (args.Length == 2)
{
if (ulong.TryParse(args[1], out ulong id))
{
// check if user id exists
var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == id);
if (user != null)
{
selectedUser = user.ID;
Console.WriteLine("Selected user: " + user.Username);
prompt = "/users/" + user.Username + "# ";
}
else
{
Console.WriteLine("User not found");
}
}
else
{
Console.WriteLine("Argument #1 should be a number");
Console.WriteLine("Usage: chroot (user id)");
}
}
else
{
Console.WriteLine("Incorrect number of arguments for chroot");
Console.WriteLine("Usage: chroot (user id)");
}
}
else if (input.StartsWith("rmuser"))
{
if (selectedUser == 0)
{
Console.WriteLine("No user selected");
}
else
{
var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser);
if (user == null)
{
Console.WriteLine("Selected user does not exist");
selectedUser = 0;
prompt = "# ";
}
else
{
Console.Write("Are you sure you want to delete user " + user.Username + "? (y/n) ");
var confirm = Console.ReadLine();
if (confirm == "y")
{
JsonDb.Instance.Users.Remove(user);
JsonDb.Save();
Console.WriteLine("User deleted");
selectedUser = 0;
prompt = "# ";
}
else
{
Console.WriteLine("User not deleted");
}
}
}
}
else if (input.StartsWith("completestage"))
{
if (selectedUser == 0)
{
Console.WriteLine("No user selected");
}
else
{
var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser);
if (user == null)
{
Console.WriteLine("Selected user does not exist");
selectedUser = 0;
prompt = "# ";
}
else
{
if (args.Length == 2)
{
var input2 = args[1];
try
{
var chapter = int.TryParse(input2.Split('-')[0], out int chapterNumber);
var stage = int.TryParse(input2.Split('-')[1], out int stageNumber);
if (chapter && stage)
{
for (int i = 0; i < chapterNumber + 1; i++)
{
var stages = StaticDataParser.Instance.GetStageIdsForChapter(i, true);
int target = 1;
foreach (var item in stages)
{
if (!user.IsStageCompleted(item, true))
{
Console.WriteLine("Completing stage " + item);
ClearStage.CompleteStage(user, item);
}
if (i == chapterNumber && target == stageNumber)
{
break;
}
target++;
}
}
}
else
{
Console.WriteLine("chapter and stage number must be a 32 bit integer");
}
}
catch (Exception ex)
{
Console.WriteLine("exception:" + ex.ToString());
}
}
else
{
Console.WriteLine("invalid argument length, must be 1");
}
}
}
}
else if (input == "exit")
{
Environment.Exit(0);
}
else if (input == "ban")
{
Console.WriteLine("Not implemented");
}
else if (input == "unban")
{
Console.WriteLine("Not implemented");
}
else
{
Console.WriteLine("Unknown command");
}
}
} }
private static WebServer CreateWebServer() private static WebServer CreateWebServer()
{ {

View File

@@ -2195,4 +2195,106 @@ message ReqAllClearEquipment {
message ResAllClearEquipment { message ResAllClearEquipment {
int64 Csn = 2; int64 Csn = 2;
repeated NetUserItemData Items = 3; repeated NetUserItemData Items = 3;
}
message NetFavoriteItemLibraryElement {
int32 Tid = 1;
int64 ReceivedAt = 2;
}
message ReqGetFavoriteItemLibrary {
}
message ResGetFavoriteItemLibrary {
repeated NetFavoriteItemLibraryElement FavoriteItemLibrary = 1;
}
message ReqListFavoriteItem {
}
message ResListFavoriteItem {
repeated NetUserFavoriteItemData FavoriteItems = 1;
}
message NetUserFavoriteItemQuestData {
int32 QuestId = 1;
bool Clear = 2;
bool Received = 3;
}
message ReqListFavoriteItemQuest {
}
message ResListFavoriteItemQuest {
repeated NetUserFavoriteItemQuestData FavoriteItemQuests = 1;
}
message NetUserLostSectorData {
int32 SectorId = 1;
int32 RewardCount = 2;
bool IsFinalReward = 3;
bool IsPlaying = 4;
bool IsOpen = 5;
int32 CurrentClearStageCount = 6;
int32 MaxClearStageCount = 7;
bool IsPerfectReward = 8;
}
message ReqGetLostSectorData {
}
message ResGetLostSectorData {
repeated NetUserLostSectorData LostSector = 2;
int32 LastEnterSectorId = 3;
repeated NetFieldStageData ClearedStages = 4;
}
message NetSideStoryStageData {
int32 SideStoryStageId = 1;
google.protobuf.Timestamp ClearedAt = 2;
}
message ReqListSideStory {}
message ResListSideStory {
repeated NetSideStoryStageData SideStoryStageDataList = 1;
}
enum LiberateMissionState {
LiberateMissionState_Running = 0;
LiberateMissionState_Rewarded = 1;
LiberateMissionState_Closed = 2;
}
message NetLiberateMissionData {
int64 Id = 1;
int32 MissionTid = 2;
int32 LiberateCharacterId = 3;
LiberateMissionState MissionState = 4;
int64 CreatedAt = 6;
int64 TriggerStartAt = 7;
int64 TriggerEndAt = 8;
optional int64 ReceivedAt = 9;
}
message NetLiberateData {
int32 CharacterId = 2;
int32 StepId = 3;
int32 ProgressPoint = 4;
repeated NetLiberateMissionData MissionData = 5;
int32 RewardedCount = 6;
bool IsCompleted = 7;
}
message ReqGetLiberateData {}
message ResGetLiberateData {
repeated int32 OpenLiberateTypeIdList = 2;
NetLiberateData LiberateData = 3;
}
message ReqShowSpecialArenaReward {}
message ResShowSpecialArenaReward {
NetRewardData reward = 4;
/*SpecialArenaContentsState SpecialArenaContentsState = 5;
NetSpecialArenaRewardHistory History = 6;*/
bool IsBan = 7;
NetArenaBanInfo BanInfo = 8;
} }

View File

@@ -26,6 +26,7 @@ namespace nksrv.StaticInfo
/// Can be Normal or Hard /// Can be Normal or Hard
/// </summary> /// </summary>
public string chapter_mod = ""; public string chapter_mod = "";
public string stage_type = "";
} }
public class RewardTableRecord public class RewardTableRecord
{ {

View File

@@ -448,5 +448,22 @@ namespace nksrv.StaticInfo
return null; return null;
} }
internal IEnumerable<int> GetStageIdsForChapter(int chapterNumber, bool normal)
{
string mod = normal ? "Normal" : "Hard";
foreach (JObject item in stageDataRecords)
{
CampaignStageRecord? data = JsonConvert.DeserializeObject<CampaignStageRecord>(item.ToString());
if (data == null) throw new Exception("failed to deserialize stage data");
int chVal = data.chapter_id - 1;
if (chapterNumber == chVal && data.chapter_mod == mod && data.stage_type == "Main")
{
yield return data.id;
}
}
}
} }
} }

View File

@@ -1,6 +1,7 @@
using ASodium; using ASodium;
using Newtonsoft.Json; using Newtonsoft.Json;
using nksrv.LobbyServer; using nksrv.LobbyServer;
using nksrv.LobbyServer.Msgs.Stage;
using nksrv.StaticInfo; using nksrv.StaticInfo;
using Swan.Logging; using Swan.Logging;
using System; using System;
@@ -135,6 +136,24 @@ namespace nksrv.Utils
return num; return num;
} }
public bool IsStageCompleted(int id, bool isNorm)
{
foreach (var item in FieldInfo)
{
if (item.Key.Contains("hard") && isNorm) continue;
if (item.Key.Contains("normal") && !isNorm) continue;
foreach (var s in item.Value.CompletedStages)
{
if (s.StageId == id)
{
return true;
}
}
}
return false;
}
} }
public class CoreInfo public class CoreInfo
{ {