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!");
ResGetOutpostData s = new ResGetOutpostData();
var inn = File.ReadAllBytes(@"C:\Users\Misha\Downloads\getoutpostdatach2done");
StaticDataPackResponse s = new StaticDataPackResponse();
var inn = File.ReadAllBytes(@"C:\Users\Misha\Desktop\response");
s.MergeFrom(inn);
Console.WriteLine(s.ToString());
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)
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
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,7 +24,17 @@ namespace nksrv.LobbyServer.Msgs.Stage
// TODO: check if user has already cleared this stage
if (req.BattleResult == 1)
{
var clearedStage = StaticDataParser.Instance.GetStageData(req.StageId);
response = CompleteStage(user, req.StageId);
}
await WriteDataAsync(response);
}
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");
@@ -33,24 +43,24 @@ namespace nksrv.LobbyServer.Msgs.Stage
user.FieldInfo.Add("0_" + clearedStage.chapter_mod, new FieldInfo() { });
}
DoQuestSpecificUserOperations(user, req.StageId);
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 " + req.StageId);
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 = req.StageId;
user.LastHardStageCleared = StageId;
}
else if (clearedStage.chapter_mod == "Normal")
{
user.LastNormalStageCleared = req.StageId;
user.LastNormalStageCleared = StageId;
}
else
{
@@ -66,14 +76,16 @@ namespace nksrv.LobbyServer.Msgs.Stage
Logger.Warn("Unknown stage category " + clearedStage.stage_category);
}
user.FieldInfo[(clearedStage.chapter_id - 1) + "_" + clearedStage.chapter_mod].CompletedStages.Add(new NetFieldStageData() { StageId = req.StageId });
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;
}
await WriteDataAsync(response);
}
private NetRewardData RegisterRewardsForUser(Utils.User user, RewardTableRecord rewardData)
private static NetRewardData RegisterRewardsForUser(Utils.User user, RewardTableRecord rewardData)
{
NetRewardData ret = new();
if (rewardData.rewards == null) return ret;

View File

@@ -21,6 +21,7 @@ using Swan;
using Google.Api;
using nksrv.StaticInfo;
using EmbedIO.WebApi;
using nksrv.LobbyServer.Msgs.Stage;
namespace nksrv
{
@@ -39,9 +40,195 @@ namespace nksrv
LobbyHandler.Init();
Logger.Info("Starting server");
new Thread(() =>
{
var server = CreateWebServer();
server.RunAsync();
}).Start();
using var server = CreateWebServer();
await server.RunAsync();
// cli interface
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()
{

View File

@@ -2196,3 +2196,105 @@ message ResAllClearEquipment {
int64 Csn = 2;
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
/// </summary>
public string chapter_mod = "";
public string stage_type = "";
}
public class RewardTableRecord
{

View File

@@ -448,5 +448,22 @@ namespace nksrv.StaticInfo
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 Newtonsoft.Json;
using nksrv.LobbyServer;
using nksrv.LobbyServer.Msgs.Stage;
using nksrv.StaticInfo;
using Swan.Logging;
using System;
@@ -135,6 +136,24 @@ namespace nksrv.Utils
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
{