48 Commits

Author SHA1 Message Date
Mikhail
01a0e70ae3 release v0.1.4 2024-08-08 10:24:45 -04:00
Mikhail
eee538f97f Update game version to 123.8.10M 2024-08-08 10:14:13 -04:00
Mikhail
e2a0712889 Fix missing ssl cert 2024-07-31 14:12:10 -04:00
Mikhail
42b17682b8 Synchro device 2024-07-31 13:47:16 -04:00
Mikhail
fc0e2801ed BuildBuilding 2024-07-31 10:23:32 -04:00
Mikhail
10fc4c3941 implement campaign lost relic 2024-07-31 10:18:12 -04:00
Mikhail
a1c910f444 Use pregenerated proto file 2024-07-31 09:19:30 -04:00
Mikhail
f41cb1533d Update dotnet-desktop.yml 2024-07-30 15:06:50 -04:00
Mikhail
c1dc421e16 Update gameconfig.json 2024-07-30 13:10:22 -04:00
Mikhail
8a9447dc09 update 2024-07-30 13:09:33 -04:00
Mikhail
1c29003eaa Update GetSentryParams.cs 2024-07-30 13:07:55 -04:00
Mikhail
239c4f293f Rename project to EpinelPS 2024-07-30 13:04:25 -04:00
Mikhail
0990f46266 remove unneeded projects 2024-07-30 12:55:45 -04:00
Mikhail
6fb6c7b1ee SetProfileData 2024-07-30 10:35:42 -04:00
Mikhail
467a20170f implement SetProfileTeam & GetProfile, outpost bug fix 2024-07-29 14:34:02 -04:00
Mikhail
8157d5ea3c Update StaticDataParser.cs 2024-07-29 10:07:27 -04:00
Mikhail
0ebc235a93 improve performance and ram usage 2024-07-29 10:06:42 -04:00
SELEKCJONER
6f8497e60b Add intercept support (#14)
* Add files via upload

---------

Co-authored-by: Mikhail <mishakeys20@gmail.com>
2024-07-29 09:47:01 -04:00
Mikhail
a139373320 fix team saving for interception 2024-07-27 20:41:47 -04:00
Mikhail
8467a5fce9 Implement level reset, bug fixes 2024-07-27 17:06:19 -04:00
Mikhail
e185b7d87e Forgot to update changelog in launcher 2024-07-26 15:53:39 -04:00
Mikhail
35221356f8 Update ClearStage.cs 2024-07-26 15:29:24 -04:00
Mikhail
766a8605b7 add GetInterceptData 2024-07-26 14:43:59 -04:00
Mikhail
9d203958c3 Fix main mission obtain reward 2024-07-26 14:41:09 -04:00
Mikhail
78659f9c6e Equipment and main quest bug fixes 2024-07-26 14:12:30 -04:00
Mikhail
cce8179e8d implement ban, add static data progress bar, allow changing server name 2024-07-26 12:49:11 -04:00
Mikhail
8ba6d69ade only show 1 server 2024-07-26 11:02:26 -04:00
Mikhail
9e331c487a update event data 2024-07-26 09:32:12 -04:00
Mikhail
e83dcdbfa8 Implement side story + move jsondb class 2024-07-25 15:50:51 -04:00
Mikhail
8ea17003e6 reduce route config 2024-07-25 12:45:47 -04:00
Mikhail
643d3a5c7c Update to game version 123.8.11 2024-07-25 08:54:10 -04:00
Mikhail
49df23798f Update JupiterAuthLogin.cs 2024-07-24 10:21:03 -04:00
Mikhail
a8b13f8539 updates work in launcher now 2024-07-24 10:11:07 -04:00
Mikhail
dea8d98db4 add additional messages 2024-07-20 11:02:19 -04:00
Mikhail
22d19cf38b format code 2024-07-19 10:53:02 -04:00
Mikhail
1670142226 implement useronlinestatelog 2024-07-18 13:15:02 -04:00
Mikhail
6ed393714f Fix XP, outpost level working, completestages also completes scenarios now 2024-07-18 11:29:40 -04:00
Mikhail
fcf9396b86 Update ExecGacha.cs 2024-07-17 18:13:16 -04:00
Mikhail
10f0641e77 add warning if auth token is unknown 2024-07-17 09:35:00 -04:00
Mikhail
9cf99754d6 misc changes 2024-07-16 20:27:56 -04:00
Mikhail
7a1f361d25 Update README.md 2024-07-16 08:56:37 -04:00
Mikhail
db70b5068e implement tactic academy 2024-07-15 14:05:30 -04:00
Mikhail
005ad80095 properly implement character level up
currency is now subtracted
2024-07-15 12:18:29 -04:00
Mikhail
15778751af Decrease database size 2024-07-15 11:30:18 -04:00
Mikhail
645697f713 check if character level is valid 2024-07-15 11:15:50 -04:00
Mikhail
56c526cec1 Begin moving things out of allmsgs.proto 2024-07-15 11:13:02 -04:00
Mikhail
702995bcfd improve error handling 2024-07-15 10:43:36 -04:00
Mikhail
181d1433cf Update dotnet-desktop.yml 2024-07-15 10:33:17 -04:00
235 changed files with 437383 additions and 4739 deletions

3
.gitattributes vendored
View File

@@ -1,3 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
cert.sh eol=lf
* text=auto

View File

@@ -39,10 +39,10 @@ jobs:
run: dotnet publish ServerSelector.Desktop
- name: Publish Server
run: dotnet publish nksrv
run: dotnet publish EpinelPS
- name: Copy to output
run: echo ${{ github.workspace }} && md ${{ github.workspace }}/out/ && xcopy "${{ github.workspace }}\ServerSelector.Desktop\bin\Release\net8.0\win-x64\publish\" "${{ github.workspace }}\out\" && xcopy "${{ github.workspace }}\nksrv\bin\Release\net8.0\win-x64\publish\" "${{ github.workspace }}\out\" && copy "${{ github.workspace }}\ServerSelector.Desktop\sodium.dll" "${{ github.workspace }}\out\sodium.dll"
run: echo ${{ github.workspace }} && md ${{ github.workspace }}/out/ && xcopy /s /e "${{ github.workspace }}\ServerSelector.Desktop\bin\Release\net8.0\win-x64\publish\" "${{ github.workspace }}\out\" && xcopy /s /e "${{ github.workspace }}\EpinelPS\bin\Release\net8.0\win-x64\publish\" "${{ github.workspace }}\out\" && copy "${{ github.workspace }}\ServerSelector.Desktop\sodium.dll" "${{ github.workspace }}\out\sodium.dll"
- name: Upload build artifacts
uses: actions/upload-artifact@v4

6
.gitignore vendored
View File

@@ -454,6 +454,8 @@ $RECYCLE.BIN/
!.vscode/extensions.json
# Allow pregenerated ssl cert
!nksrv/site.pfx
!EpinelPS/site.pfx
!ServerSelector/myCA.pfx
!ServerSelector/sodium.dll
!ServerSelector/sodium.dll
EpinelPS/Protos/allmsgs.proto

View File

@@ -1,10 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -1,57 +0,0 @@
using System.IO;
using System.IO.Compression;
namespace DataFixupUtil
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
foreach(var arg in Directory.GetFiles("C:\\NIKKE\\NIKKE\\Game"))
{
var fileName = Path.GetFileName(arg);
if (fileName.StartsWith("input"))
{
byte[] FileContents = File.ReadAllBytes(arg);
using MemoryStream ms = new MemoryStream(FileContents);
File.WriteAllBytes("fullPkt-decr", ms.ToArray());
var unkVal1 = ms.ReadByte();
var pktLen = ms.ReadByte() & 0x1f;
var seqNumB = ms.ReadByte();
var seqNum = seqNumB;
if (seqNumB >= 24)
{
var b = ms.ReadByte();
seqNum = BitConverter.ToUInt16(new byte[] { (byte)b, (byte)seqNumB }, 0);
// todo support uint32
}
var startPos = (int)ms.Position;
var contents = FileContents.Skip(startPos).ToArray();
if (contents.Length > 2 && contents[0] == 0x1f && contents[1] == 0x8b)
{
// gzip compression is used
using Stream csStream = new GZipStream(new MemoryStream(contents), CompressionMode.Decompress);
using MemoryStream decoded = new MemoryStream();
csStream.CopyTo(decoded);
contents = decoded.ToArray();
File.WriteAllBytes(arg, contents);
}
else
{
File.WriteAllBytes(arg, contents);
}
}
}
}
}
}

View File

@@ -3,11 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.34928.147
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "nksrv", "nksrv\nksrv.csproj", "{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataFixupUtil", "DataFixupUtil\DataFixupUtil.csproj", "{13124DFB-448B-4F4F-A479-537EE067D836}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProtobufViewUtil", "ProtobufViewUtil\ProtobufViewUtil.csproj", "{FDEDD0D6-9C02-4E58-8110-04E8D5639881}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EpinelPS", "EpinelPS\EpinelPS.csproj", "{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServerSelector", "ServerSelector\ServerSelector.csproj", "{EC613C24-8A35-42E8-92C1-9A8431F74F58}"
EndProject
@@ -55,54 +51,6 @@ Global
{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}.ReleaseDLL|x64.Build.0 = Release|Any CPU
{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}.ReleaseDLL|x86.ActiveCfg = Release|Any CPU
{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}.ReleaseDLL|x86.Build.0 = Release|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.Debug|x64.ActiveCfg = Debug|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.Debug|x64.Build.0 = Debug|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.Debug|x86.ActiveCfg = Debug|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.Debug|x86.Build.0 = Debug|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.DebugDLL|Any CPU.ActiveCfg = Debug|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.DebugDLL|Any CPU.Build.0 = Debug|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.DebugDLL|x64.ActiveCfg = Debug|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.DebugDLL|x64.Build.0 = Debug|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.DebugDLL|x86.ActiveCfg = Debug|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.DebugDLL|x86.Build.0 = Debug|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.Release|Any CPU.Build.0 = Release|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.Release|x64.ActiveCfg = Release|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.Release|x64.Build.0 = Release|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.Release|x86.ActiveCfg = Release|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.Release|x86.Build.0 = Release|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.ReleaseDLL|Any CPU.ActiveCfg = Release|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.ReleaseDLL|Any CPU.Build.0 = Release|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.ReleaseDLL|x64.ActiveCfg = Release|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.ReleaseDLL|x64.Build.0 = Release|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.ReleaseDLL|x86.ActiveCfg = Release|Any CPU
{13124DFB-448B-4F4F-A479-537EE067D836}.ReleaseDLL|x86.Build.0 = Release|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.Debug|x64.ActiveCfg = Debug|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.Debug|x64.Build.0 = Debug|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.Debug|x86.ActiveCfg = Debug|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.Debug|x86.Build.0 = Debug|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.DebugDLL|Any CPU.ActiveCfg = Debug|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.DebugDLL|Any CPU.Build.0 = Debug|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.DebugDLL|x64.ActiveCfg = Debug|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.DebugDLL|x64.Build.0 = Debug|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.DebugDLL|x86.ActiveCfg = Debug|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.DebugDLL|x86.Build.0 = Debug|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.Release|Any CPU.Build.0 = Release|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.Release|x64.ActiveCfg = Release|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.Release|x64.Build.0 = Release|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.Release|x86.ActiveCfg = Release|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.Release|x86.Build.0 = Release|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.ReleaseDLL|Any CPU.ActiveCfg = Release|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.ReleaseDLL|Any CPU.Build.0 = Release|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.ReleaseDLL|x64.ActiveCfg = Release|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.ReleaseDLL|x64.Build.0 = Release|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.ReleaseDLL|x86.ActiveCfg = Release|Any CPU
{FDEDD0D6-9C02-4E58-8110-04E8D5639881}.ReleaseDLL|x86.Build.0 = Release|Any CPU
{EC613C24-8A35-42E8-92C1-9A8431F74F58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EC613C24-8A35-42E8-92C1-9A8431F74F58}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EC613C24-8A35-42E8-92C1-9A8431F74F58}.Debug|x64.ActiveCfg = Debug|Any CPU

View File

@@ -1,12 +1,11 @@
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
using nksrv.Utils;
using System;
using EpinelPS.Database;
using System.Security.Cryptography;
using System.Text;
namespace nksrv
namespace EpinelPS
{
public class AdminApiController : WebApiController
{

408
EpinelPS/Database/JsonDb.cs Normal file
View File

@@ -0,0 +1,408 @@
using Newtonsoft.Json;
using EpinelPS.LobbyServer;
using EpinelPS.StaticInfo;
using EpinelPS.Utils;
using Swan.Logging;
using Google.Protobuf.WellKnownTypes;
using static Google.Rpc.Context.AttributeContext.Types;
namespace EpinelPS.Database
{
public class AccessToken
{
public string Token = "";
public long ExpirationTime;
public ulong UserID;
}
public class FieldInfo
{
public List<NetFieldStageData> CompletedStages = [];
public List<NetFieldObject> CompletedObjects = [];
}
public class FieldInfoNew
{
public List<int> CompletedStages = [];
public List<NetFieldObject> CompletedObjects = [];
}
public class Character
{
public int Csn = 0;
public int Tid = 0;
public int CostumeId = 0;
public int Level = 1;
public int UltimateLevel = 1;
public int Skill1Lvl = 1;
public int Skill2Lvl = 1;
public int Grade = 0;
}
public class MainQuestData
{
public int TableId = 0;
public bool IsReceieved = false;
}
public class UserPointData
{
public int UserLevel = 1;
public int ExperiencePoint = 0;
}
public class ItemData
{
public int ItemType;
public long Csn;
public int Count;
public int Level;
public int Exp;
public int Position;
public int Corp;
public long Isn;
}
public class EventData
{
public List<string> CompletedScenarios = new();
}
public class SynchroSlot
{
/// <summary>
/// Index of slot, 1 based
/// </summary>
public int Slot;
/// <summary>
/// Character CSN
/// </summary>
public long CharacterSerialNumber;
/// <summary>
/// Time when slot cooldown expires
/// </summary>
public long AvailableAt;
}
public class User
{
// User info
public string Username = "";
public string Password = "";
public string PlayerName = "";
public ulong ID;
public long RegisterTime;
public int LastNormalStageCleared;
public int LastHardStageCleared;
public string Nickname = "SomePlayer";
public int ProfileIconId = 39900;
public bool ProfileIconIsPrism = false;
public int ProfileFrame = 1;
public bool IsAdmin = false;
public bool IsBanned = false;
public DateTime BanStart;
public DateTime BanEnd;
public int BanId = 0;
// Game data
public List<string> CompletedScenarios = [];
public Dictionary<string, FieldInfo> FieldInfo = []; // here for backwards compatibility
public Dictionary<string, FieldInfoNew> FieldInfoNew = [];
public Dictionary<string, string> MapJson = [];
public Dictionary<CurrencyType, long> Currency = new() {
{ CurrencyType.ContentStamina, 2 }
};
public List<SynchroSlot> SynchroSlots = new List<SynchroSlot>();
public bool SynchroDeviceUpgraded = false;
public int SynchroDeviceLevel = 200;
public List<ItemData> Items = new();
public List<Character> Characters = [];
public NetWholeUserTeamData RepresentationTeamData = new();
public Dictionary<int, ClearedTutorialData> ClearedTutorialData = [];
public NetWallpaperData[] WallpaperList = [];
public Dictionary<int, NetUserTeamData> UserTeams = new Dictionary<int, NetUserTeamData>();
public Dictionary<int, bool> MainQuestData = new();
public int InfraCoreExp = 0;
public int InfraCoreLvl = 1;
public UserPointData userPointData = new();
public DateTime LastLogin = DateTime.UtcNow;
public DateTime BattleTime = DateTime.UtcNow;
public NetOutpostBattleLevel OutpostBattleLevel = new() { Level = 1 };
public int GachaTutorialPlayCount = 0;
public List<int> CompletedTacticAcademyLessons = [];
public List<int> CompletedSideStoryStages = new();
public List<int> Memorial = new();
public List<int> JukeboxBgm = new();
// Event data
public Dictionary<int, EventData> EventInfo = new();
public void SetQuest(int tid, bool recievedReward)
{
if (MainQuestData.ContainsKey(tid))
{
MainQuestData[tid] = recievedReward;
return;
}
else
{
MainQuestData.Add(tid, recievedReward);
}
}
public int GenerateUniqueItemId()
{
var num = Rng.RandomId();
while (Items.Any(x => x.Isn == num))
{
num = Rng.RandomId();
}
return num;
}
public int GenerateUniqueCharacterId()
{
var num = Rng.RandomId();
while (Characters.Any(x => x.Csn == num))
{
num = Rng.RandomId();
}
return num;
}
public bool IsStageCompleted(int id, bool isNorm)
{
foreach (var item in FieldInfoNew)
{
if (item.Key.Contains("hard") && isNorm) continue;
if (item.Key.Contains("normal") && !isNorm) continue;
if (item.Value.CompletedStages.Contains(id))
{
return true;
}
}
return false;
}
public long GetCurrencyVal(CurrencyType type)
{
if (Currency.ContainsKey(type))
return Currency[type];
else
{
Currency.Add(type, 0);
return 0;
}
}
public void AddCurrency(CurrencyType type, long val)
{
if (Currency.ContainsKey(type)) Currency[type] += val;
else Currency.Add(type, val);
}
public bool SubtractCurrency(CurrencyType type, long val)
{
if (Currency.ContainsKey(type)) Currency[type] -= val;
else return false;
if (Currency[type] < 0)
{
Currency[type] += val;
return false;
}
return true;
}
public bool CanSubtractCurrency(CurrencyType type, long val)
{
if (Currency.ContainsKey(type))
{
if (Currency[type] >= val) return true;
else return false;
}
else
{
if (val == 0) return true;
else return false;
}
}
public bool HasCharacter(int c)
{
return Characters.Any(x => x.Tid == c);
}
public Character? GetCharacterBySerialNumber(long value)
{
return Characters.Where(x => x.Csn == value).FirstOrDefault();
}
internal bool GetSynchro(long csn)
{
return SynchroSlots.Where(x => x.CharacterSerialNumber == csn).Count() >= 1;
}
internal int GetCharacterLevel(int csn)
{
var c = GetCharacterBySerialNumber(csn);
if (c == null) throw new Exception("failed to lookup character");
return GetCharacterLevel(csn, c.Level);
}
internal int GetCharacterLevel(int csn, int characterLevel)
{
foreach (var item in SynchroSlots)
{
if (item.CharacterSerialNumber == csn)
{
return GetSynchroLevel();
}
}
return characterLevel;
}
internal int GetSynchroLevel()
{
if (SynchroDeviceUpgraded)
return SynchroDeviceLevel;
var highestLevelCharacters = Characters.OrderByDescending(x => x.Level).Take(5).ToList();
if (highestLevelCharacters.Count > 0)
{
return highestLevelCharacters.Last().Level;
}
else
{
return 1;
}
}
}
public class CoreInfo
{
public int DbVersion = 3;
public List<User> Users = [];
public List<AccessToken> LauncherAccessTokens = [];
public Dictionary<string, GameClientInfo> GameClientTokens = [];
public string ServerName = "<color=\"green\">Private Server</color>";
}
internal class JsonDb
{
public static CoreInfo Instance { get; internal set; }
// Note: change this in sodium
public static byte[] ServerPrivateKey = Convert.FromBase64String("FSUY8Ohd942n5LWAfxn6slK3YGwc8OqmyJoJup9nNos=");
public static byte[] ServerPublicKey = Convert.FromBase64String("04hFDd1e/BOEF2h4b0MdkX2h6W5REeqyW+0r9+eSeh0=");
static JsonDb()
{
if (!File.Exists(AppDomain.CurrentDomain.BaseDirectory + "/db.json"))
{
"users: warning: configuration not found, writing default data".Warn();
Instance = new CoreInfo();
Save();
}
var j = JsonConvert.DeserializeObject<CoreInfo>(File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + "/db.json"));
if (j != null)
{
Instance = j;
if (Instance.DbVersion == 0)
{
Instance.DbVersion = 1;
// In older versions, field info key used chapter number, but now difficultly is appened.
Console.WriteLine("Starting database update...");
foreach (var user in Instance.Users)
{
foreach (var f in user.FieldInfoNew.ToList())
{
var isNumeric = int.TryParse(f.Key, out int n);
if (isNumeric)
{
var val = f.Value;
user.FieldInfoNew.Remove(f.Key);
user.FieldInfoNew.Add(n + "_Normal", val);
}
}
}
Console.WriteLine("Database update completed");
}
else if (Instance.DbVersion == 1)
{
Console.WriteLine("Starting database update...");
// there was a bug where equipment position was not saved, so remove all items from each characters
Instance.DbVersion = 2;
foreach (var user in Instance.Users)
{
foreach (var f in user.Items.ToList())
{
f.Csn = 0;
}
}
Console.WriteLine("Database update completed");
}
else if (Instance.DbVersion == 2)
{
Console.WriteLine("Starting database update...");
// I used to use a class for FieldInfo cleared stages, but now int list is used
Instance.DbVersion = 3;
foreach (var user in Instance.Users)
{
foreach (var f in user.FieldInfo)
{
var newField = new FieldInfoNew();
foreach (var stage in f.Value.CompletedStages)
{
newField.CompletedStages.Add(stage.StageId);
}
user.FieldInfoNew.Add(f.Key, newField);
}
user.FieldInfo.Clear();
}
Console.WriteLine("Database update completed");
}
Save();
ValidateDb();
"Loaded db".Info();
}
else
{
throw new Exception("Failed to read configuration json file");
}
}
private static void ValidateDb()
{
// check if character level is valid
foreach (var item in Instance.Users)
{
foreach (var c in item.Characters)
{
if (c.Level > 1000)
{
$"Warning: Character level for character {c.Tid} cannot be above 1000, setting to 1000".Warn();
c.Level = 1000;
}
}
}
}
public static User? GetUser(ulong id)
{
return Instance.Users.Where(x => x.ID == id).FirstOrDefault();
}
public static void Save()
{
if (Instance != null)
{
File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + "/db.json", JsonConvert.SerializeObject(Instance, Formatting.Indented));
}
}
}
}

View File

@@ -33,6 +33,9 @@
<None Update="gameconfig.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="gameversion.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="site.pfx">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>

View File

@@ -1,38 +1,18 @@
using nksrv.Utils;
using Swan.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using Newtonsoft.Json.Linq;
using Swan.Parsers;
using ICSharpCode.SharpZipLib.Zip;
using Newtonsoft.Json;
using System.Drawing;
using Newtonsoft.Json.Linq;
using EpinelPS.Utils;
using Swan.Logging;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Cryptography;
namespace nksrv.StaticInfo
namespace EpinelPS.StaticInfo
{
/// <summary>
/// "Static data" which is what the game calls it, contains data such as map info, characters, quests, rewards and a lot more.
/// </summary>
public class StaticDataParser
public class GameData
{
// These fields were extracted from the game.
public static byte[] PresharedKey = [0xCB, 0xC2, 0x1C, 0x6F, 0xF3, 0xF5, 0x07, 0xF5, 0x05, 0xBA, 0xCA, 0xD4, 0x98, 0x28, 0x84, 0x1F, 0xF0, 0xD1, 0x38, 0xC7, 0x61, 0xDF, 0xD6, 0xE6, 0x64, 0x9A, 0x85, 0x13, 0x3E, 0x1A, 0x6A, 0x0C, 0x68, 0x0E, 0x2B, 0xC4, 0xDF, 0x72, 0xF8, 0xC6, 0x55, 0xE4, 0x7B, 0x14, 0x36, 0x18, 0x3B, 0xA7, 0xD1, 0x20, 0x81, 0x22, 0xD1, 0xA9, 0x18, 0x84, 0x65, 0x13, 0x0B, 0xED, 0xA3, 0x00, 0xE5, 0xD9];
public static RSAParameters RSAParameters = new RSAParameters()
{
Exponent = [0x01, 0x00, 0x01],
Modulus = [0x89, 0xD6, 0x66, 0x00, 0x7D, 0xFC, 0x7D, 0xCE, 0x83, 0xA6, 0x62, 0xE3, 0x1A, 0x5E, 0x9A, 0x53, 0xC7, 0x8A, 0x27, 0xF3, 0x67, 0xC1, 0xF3, 0xD4, 0x37, 0xFE, 0x50, 0x6D, 0x38, 0x45, 0xDF, 0x7E, 0x73, 0x5C, 0xF4, 0x9D, 0x40, 0x4C, 0x8C, 0x63, 0x21, 0x97, 0xDF, 0x46, 0xFF, 0xB2, 0x0D, 0x0E, 0xDB, 0xB2, 0x72, 0xB4, 0xA8, 0x42, 0xCD, 0xEE, 0x48, 0x06, 0x74, 0x4F, 0xE9, 0x56, 0x6E, 0x9A, 0xB1, 0x60, 0x18, 0xBC, 0x86, 0x0B, 0xB6, 0x32, 0xA7, 0x51, 0x00, 0x85, 0x7B, 0xC8, 0x72, 0xCE, 0x53, 0x71, 0x3F, 0x64, 0xC2, 0x25, 0x58, 0xEF, 0xB0, 0xC9, 0x1D, 0xE3, 0xB3, 0x8E, 0xFC, 0x55, 0xCF, 0x8B, 0x02, 0xA5, 0xC8, 0x1E, 0xA7, 0x0E, 0x26, 0x59, 0xA8, 0x33, 0xA5, 0xF1, 0x11, 0xDB, 0xCB, 0xD3, 0xA7, 0x1F, 0xB1, 0xC6, 0x10, 0x39, 0xC8, 0x31, 0x1D, 0x60, 0xDB, 0x0D, 0xA4, 0x13, 0x4B, 0x2B, 0x0E, 0xF3, 0x6F, 0x69, 0xCB, 0xA8, 0x62, 0x03, 0x69, 0xE6, 0x95, 0x6B, 0x8D, 0x11, 0xF6, 0xAF, 0xD9, 0xC2, 0x27, 0x3A, 0x32, 0x12, 0x05, 0xC3, 0xB1, 0xE2, 0x81, 0x4B, 0x40, 0xF8, 0x8B, 0x8D, 0xBA, 0x1F, 0x55, 0x60, 0x2C, 0x09, 0xC6, 0xED, 0x73, 0x96, 0x32, 0xAF, 0x5F, 0xEE, 0x8F, 0xEB, 0x5B, 0x93, 0xCF, 0x73, 0x13, 0x15, 0x6B, 0x92, 0x7B, 0x27, 0x0A, 0x13, 0xF0, 0x03, 0x4D, 0x6F, 0x5E, 0x40, 0x7B, 0x9B, 0xD5, 0xCE, 0xFC, 0x04, 0x97, 0x7E, 0xAA, 0xA3, 0x53, 0x2A, 0xCF, 0xD2, 0xD5, 0xCF, 0x52, 0xB2, 0x40, 0x61, 0x28, 0xB1, 0xA6, 0xF6, 0x78, 0xFB, 0x69, 0x9A, 0x85, 0xD6, 0xB9, 0x13, 0x14, 0x6D, 0xC4, 0x25, 0x36, 0x17, 0xDB, 0x54, 0x0C, 0xD8, 0x77, 0x80, 0x9A, 0x00, 0x62, 0x83, 0xDD, 0xB0, 0x06, 0x64, 0xD0, 0x81, 0x5B, 0x0D, 0x23, 0x9E, 0x88, 0xBD],
DP = null
};
// Fields
private static StaticDataParser? _instance;
public static StaticDataParser Instance
private static GameData? _instance;
public static GameData Instance
{
get
{
@@ -46,31 +26,40 @@ namespace nksrv.StaticInfo
}
private ZipFile MainZip;
private MemoryStream ZipStream;
private JArray questDataRecords;
private JArray stageDataRecords;
private JArray rewardDataRecords;
private Dictionary<int, MainQuestCompletionRecord> questDataRecords;
private Dictionary<int, CampaignStageRecord> stageDataRecords;
private Dictionary<int, RewardTableRecord> rewardDataRecords;
private JArray userExpDataRecords;
private JArray chapterCampaignData;
private Dictionary<int, CampaignChapterRecord> chapterCampaignData;
private JArray characterCostumeTable;
private JArray characterTable;
private JArray tutorialTable;
private JArray itemEquipTable;
private Dictionary<int, CharacterRecord> characterTable;
private Dictionary<int, ClearedTutorialData> tutorialTable;
private Dictionary<int, ItemEquipRecord> itemEquipTable;
private Dictionary<string, JArray> FieldMapData = [];
private Dictionary<int, CharacterLevelData> LevelData = [];
private Dictionary<int, TacticAcademyLessonRecord> TacticAcademyLessons = [];
public Dictionary<int, int> SidestoryRewardTable = [];
public Dictionary<string, int> PositionReward = new Dictionary<string, int>();
public Dictionary<int, FieldItemRecord> FieldItems = [];
public byte[] Sha256Hash;
public int Size;
static async Task<StaticDataParser> BuildAsync()
static async Task<GameData> BuildAsync()
{
Logger.Info("Loading static data");
await Load();
Logger.Info("Parsing static data");
Logger.Info("Preparing");
var stopWatch = new Stopwatch();
stopWatch.Start();
await Instance.Parse();
stopWatch.Stop();
Console.WriteLine("Preparing took " + stopWatch.Elapsed);
return Instance;
}
public StaticDataParser(string filePath)
public GameData(string filePath)
{
if (!File.Exists(filePath)) throw new ArgumentException("Static data file must exist", nameof(filePath));
@@ -90,16 +79,23 @@ namespace nksrv.StaticInfo
Sha256Hash = SHA256.HashData(rawBytes);
Size = rawBytes.Length;
DecryptStaticDataAndLoadZip(filePath);
LoadGameData(filePath);
if (MainZip == null) throw new Exception("failed to read zip file");
}
#region Decryption
private void DecryptStaticDataAndLoadZip(string file)
#region Data loading
private static byte[] PresharedValue = [0xCB, 0xC2, 0x1C, 0x6F, 0xF3, 0xF5, 0x07, 0xF5, 0x05, 0xBA, 0xCA, 0xD4, 0x98, 0x28, 0x84, 0x1F, 0xF0, 0xD1, 0x38, 0xC7, 0x61, 0xDF, 0xD6, 0xE6, 0x64, 0x9A, 0x85, 0x13, 0x3E, 0x1A, 0x6A, 0x0C, 0x68, 0x0E, 0x2B, 0xC4, 0xDF, 0x72, 0xF8, 0xC6, 0x55, 0xE4, 0x7B, 0x14, 0x36, 0x18, 0x3B, 0xA7, 0xD1, 0x20, 0x81, 0x22, 0xD1, 0xA9, 0x18, 0x84, 0x65, 0x13, 0x0B, 0xED, 0xA3, 0x00, 0xE5, 0xD9];
private static RSAParameters LoadParameters = new RSAParameters()
{
Exponent = [0x01, 0x00, 0x01],
Modulus = [0x89, 0xD6, 0x66, 0x00, 0x7D, 0xFC, 0x7D, 0xCE, 0x83, 0xA6, 0x62, 0xE3, 0x1A, 0x5E, 0x9A, 0x53, 0xC7, 0x8A, 0x27, 0xF3, 0x67, 0xC1, 0xF3, 0xD4, 0x37, 0xFE, 0x50, 0x6D, 0x38, 0x45, 0xDF, 0x7E, 0x73, 0x5C, 0xF4, 0x9D, 0x40, 0x4C, 0x8C, 0x63, 0x21, 0x97, 0xDF, 0x46, 0xFF, 0xB2, 0x0D, 0x0E, 0xDB, 0xB2, 0x72, 0xB4, 0xA8, 0x42, 0xCD, 0xEE, 0x48, 0x06, 0x74, 0x4F, 0xE9, 0x56, 0x6E, 0x9A, 0xB1, 0x60, 0x18, 0xBC, 0x86, 0x0B, 0xB6, 0x32, 0xA7, 0x51, 0x00, 0x85, 0x7B, 0xC8, 0x72, 0xCE, 0x53, 0x71, 0x3F, 0x64, 0xC2, 0x25, 0x58, 0xEF, 0xB0, 0xC9, 0x1D, 0xE3, 0xB3, 0x8E, 0xFC, 0x55, 0xCF, 0x8B, 0x02, 0xA5, 0xC8, 0x1E, 0xA7, 0x0E, 0x26, 0x59, 0xA8, 0x33, 0xA5, 0xF1, 0x11, 0xDB, 0xCB, 0xD3, 0xA7, 0x1F, 0xB1, 0xC6, 0x10, 0x39, 0xC8, 0x31, 0x1D, 0x60, 0xDB, 0x0D, 0xA4, 0x13, 0x4B, 0x2B, 0x0E, 0xF3, 0x6F, 0x69, 0xCB, 0xA8, 0x62, 0x03, 0x69, 0xE6, 0x95, 0x6B, 0x8D, 0x11, 0xF6, 0xAF, 0xD9, 0xC2, 0x27, 0x3A, 0x32, 0x12, 0x05, 0xC3, 0xB1, 0xE2, 0x81, 0x4B, 0x40, 0xF8, 0x8B, 0x8D, 0xBA, 0x1F, 0x55, 0x60, 0x2C, 0x09, 0xC6, 0xED, 0x73, 0x96, 0x32, 0xAF, 0x5F, 0xEE, 0x8F, 0xEB, 0x5B, 0x93, 0xCF, 0x73, 0x13, 0x15, 0x6B, 0x92, 0x7B, 0x27, 0x0A, 0x13, 0xF0, 0x03, 0x4D, 0x6F, 0x5E, 0x40, 0x7B, 0x9B, 0xD5, 0xCE, 0xFC, 0x04, 0x97, 0x7E, 0xAA, 0xA3, 0x53, 0x2A, 0xCF, 0xD2, 0xD5, 0xCF, 0x52, 0xB2, 0x40, 0x61, 0x28, 0xB1, 0xA6, 0xF6, 0x78, 0xFB, 0x69, 0x9A, 0x85, 0xD6, 0xB9, 0x13, 0x14, 0x6D, 0xC4, 0x25, 0x36, 0x17, 0xDB, 0x54, 0x0C, 0xD8, 0x77, 0x80, 0x9A, 0x00, 0x62, 0x83, 0xDD, 0xB0, 0x06, 0x64, 0xD0, 0x81, 0x5B, 0x0D, 0x23, 0x9E, 0x88, 0xBD],
DP = null
};
private void LoadGameData(string file)
{
using var fileStream = File.Open(file, FileMode.Open, FileAccess.Read);
var keyDecryptor = new Rfc2898DeriveBytes(PresharedKey, GameConfig.Root.StaticData.GetSalt2Bytes(), 10000, HashAlgorithmName.SHA256);
var key2 = keyDecryptor.GetBytes(32);
var a = new Rfc2898DeriveBytes(PresharedValue, GameConfig.Root.StaticData.GetSalt2Bytes(), 10000, HashAlgorithmName.SHA256);
var key2 = a.GetBytes(32);
byte[] decryptionKey = key2[0..16];
byte[] iv = key2[16..32];
@@ -110,22 +106,18 @@ namespace nksrv.StaticInfo
aes.Key = decryptionKey;
aes.IV = iv;
var transform = aes.CreateDecryptor();
// Decryption layer 1
using CryptoStream stream = new CryptoStream(fileStream, transform, CryptoStreamMode.Read);
using MemoryStream ms = new MemoryStream();
stream.CopyTo(ms);
var bytes = ms.ToArray();
// Decryption of layer 2
var zip = new ZipFile(ms, false);
var signEntry = zip.GetEntry("sign");
if (signEntry == null) throw new Exception("Sign entry not found in decrypted static data pack");
if (signEntry == null) throw new Exception("error 1");
var dataEntry = zip.GetEntry("data");
if (dataEntry == null) throw new Exception("Data entry not found in decrypted static data pack");
if (dataEntry == null) throw new Exception("error 2");
var signStream = zip.GetInputStream(signEntry);
var dataStream = zip.GetInputStream(dataEntry);
@@ -137,28 +129,26 @@ namespace nksrv.StaticInfo
dataStream.CopyTo(dataMs);
dataMs.Position = 0;
var rsa = RSA.Create(RSAParameters);
var rsa = RSA.Create(LoadParameters);
if (!rsa.VerifyData(dataMs, signMs.ToArray(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1))
throw new Exception("failed to decrypt static data (round 2)");
throw new Exception("error 3");
dataMs.Position = 0;
// Decryption of layer 3
var keyDecryptor2 = new Rfc2898DeriveBytes(PresharedKey, GameConfig.Root.StaticData.GetSalt1Bytes(), 10000, HashAlgorithmName.SHA256);
var keyDecryptor2 = new Rfc2898DeriveBytes(PresharedValue, GameConfig.Root.StaticData.GetSalt1Bytes(), 10000, HashAlgorithmName.SHA256);
var key3 = keyDecryptor2.GetBytes(32);
byte[] decryptionKey2 = key3[0..16];
byte[] val2 = key3[0..16];
byte[] iv2 = key3[16..32];
ZipStream = new MemoryStream();
AesCtrTransform(decryptionKey2, iv2, dataMs, ZipStream);
DoTransformation(val2, iv2, dataMs, ZipStream);
ZipStream.Position = 0;
MainZip = new ZipFile(ZipStream, false);
}
public static void AesCtrTransform(byte[] key, byte[] salt, Stream inputStream, Stream outputStream)
public static void DoTransformation(byte[] key, byte[] salt, Stream inputStream, Stream outputStream)
{
SymmetricAlgorithm aes = Aes.Create();
aes.Mode = CipherMode.ECB;
@@ -216,8 +206,24 @@ namespace nksrv.StaticInfo
_instance = new(targetFile);
}
#endregion
private async Task<T> LoadZip<T>(string entry, ProgressBar bar)
{
var mainQuestData = MainZip.GetEntry(entry);
if (mainQuestData == null) throw new Exception(entry + " does not exist in static data");
private async Task<JArray> LoadZip(string entry)
using StreamReader mainQuestReader = new StreamReader(MainZip.GetInputStream(mainQuestData));
var mainQuestDataString = await mainQuestReader.ReadToEndAsync();
var questdata = JsonConvert.DeserializeObject<T>(mainQuestDataString);
if (questdata == null) throw new Exception("failed to parse " + entry);
currentFile++;
bar.Report((double)currentFile / totalFiles);
return questdata;
}
private async Task<JArray> LoadZip(string entry, ProgressBar bar)
{
var mainQuestData = MainZip.GetEntry(entry);
if (mainQuestData == null) throw new Exception(entry + " does not exist in static data");
@@ -230,94 +236,166 @@ namespace nksrv.StaticInfo
if (questdata == null) throw new Exception("failed to parse " + entry);
var records = (JArray?)questdata["records"];
if (records == null ) throw new Exception(entry + " is missing records element");
if (records == null) throw new Exception(entry + " is missing records element");
currentFile++;
bar.Report((double)currentFile / totalFiles);
return records;
}
int totalFiles = 14;
int currentFile = 0;
public async Task Parse()
{
questDataRecords = await LoadZip("MainQuestTable.json");
stageDataRecords = await LoadZip("CampaignStageTable.json");
rewardDataRecords = await LoadZip("RewardTable.json");
userExpDataRecords = await LoadZip("UserExpTable.json");
chapterCampaignData = await LoadZip("CampaignChapterTable.json");
characterCostumeTable = await LoadZip("CharacterCostumeTable.json");
characterTable = await LoadZip("CharacterTable.json");
tutorialTable = await LoadZip("ContentsTutorialTable.json");
itemEquipTable = await LoadZip("ItemEquipTable.json");
using var progress = new ProgressBar();
var questDataRecords = await LoadZip<MainQuestCompletionTable>("MainQuestTable.json", progress);
foreach (var obj in questDataRecords.records)
{
this.questDataRecords.Add(obj.id, obj);
}
var stageDataRecords = await LoadZip<CampaignStageTable>("CampaignStageTable.json", progress);
foreach (var obj in stageDataRecords.records)
{
this.stageDataRecords.Add(obj.id, obj);
}
var rewardDataRecords = await LoadZip<RewardTable>("RewardTable.json", progress);
foreach (var obj in rewardDataRecords.records)
{
this.rewardDataRecords.Add(obj.id, obj);
}
var chapterCampaignData = await LoadZip<CampaignChapterTable>("CampaignChapterTable.json", progress);
foreach (var obj in chapterCampaignData.records)
{
this.chapterCampaignData.Add(obj.chapter, obj);
}
userExpDataRecords = await LoadZip("UserExpTable.json", progress);
characterCostumeTable = await LoadZip("CharacterCostumeTable.json", progress);
var characterTable = await LoadZip<CharacterTable>("CharacterTable.json", progress);
foreach (var obj in characterTable.records)
{
this.characterTable.Add(obj.id, obj);
}
var tutorialTable = await LoadZip<TutorialTable>("ContentsTutorialTable.json", progress);
foreach (var obj in tutorialTable.records)
{
this.tutorialTable.Add(obj.id, obj);
}
var itemEquipTable = await LoadZip<ItemEquipTable>("ItemEquipTable.json", progress);
foreach (var obj in itemEquipTable.records)
{
this.itemEquipTable.Add(obj.id, obj);
}
var characterLevelTable = await LoadZip("CharacterLevelTable.json", progress);
foreach (JToken item in characterLevelTable)
{
var obj = item.ToObject<CharacterLevelData>();
if (obj != null)
LevelData.Add(obj.level, obj);
else
Logger.Warn("failed to read character level table entry");
}
var tacticLessonTable = await LoadZip("TacticAcademyFunctionTable.json", progress);
foreach (JToken item in tacticLessonTable)
{
var idRaw = item["id"];
var groupidRaw = item["group_id"];
var currencyIdRaw = item["currency_id"];
var currencyValueRaw = item["currency_value"];
if (idRaw == null) throw new InvalidDataException();
if (groupidRaw == null) throw new InvalidDataException();
if (currencyIdRaw == null) throw new InvalidDataException();
if (currencyValueRaw == null) throw new InvalidDataException();
var id = idRaw.ToObject<int>();
var currencyId = currencyIdRaw.ToObject<int>();
var currencyValue = currencyValueRaw.ToObject<int>();
var groupid = groupidRaw.ToObject<int>();
var fullId = int.Parse(groupid.ToString() + id.ToString());
TacticAcademyLessons.Add(id, new TacticAcademyLessonRecord() { CurrencyId = (CurrencyType)currencyId, CurrencyValue = currencyValue, GroupId = groupid, Id = id });
}
var sideStoryTable = await LoadZip("SideStoryStageTable.json", progress);
foreach (JToken item in sideStoryTable)
{
var idRaw = item["id"];
var rewardIdRaw = item["first_clear_reward"];
if (idRaw == null) throw new InvalidDataException();
if (rewardIdRaw != null)
{
var id2 = idRaw.ToObject<int>();
var reward = rewardIdRaw.ToObject<int>();
SidestoryRewardTable.Add(id2, reward);
}
}
foreach (ZipEntry item in MainZip)
{
if (item.Name.StartsWith("CampaignMap/"))
{
var x = await LoadZip(item.Name, progress);
var items = x[0]["ItemSpawner"];
foreach (var item2 in items)
{
var id = item2["positionId"].ToObject<string>();
var reward = item2["itemId"].ToObject<int>();
if (!PositionReward.ContainsKey(id))
PositionReward.Add(id, reward);
}
}
}
var fieldItems = await LoadZip<FieldItemTable>("FieldItemTable.json", progress);
foreach (var obj in fieldItems.records)
{
FieldItems.Add(obj.id, obj);
}
}
public MainQuestCompletionData? GetMainQuestForStageClearCondition(int stage)
public MainQuestCompletionRecord? GetMainQuestForStageClearCondition(int stage)
{
foreach (JObject item in questDataRecords)
foreach (var item in questDataRecords)
{
var id = item["condition_id"];
if (id == null) throw new Exception("expected condition_id field in quest data");
int value = id.ToObject<int>();
if (value == stage)
if (item.Value.condition_id == stage)
{
MainQuestCompletionData? data = JsonConvert.DeserializeObject<MainQuestCompletionData>(item.ToString());
if (data == null) throw new Exception("failed to deserialize main quest data item");
return data;
return item.Value;
}
}
return null;
}
public MainQuestCompletionData? GetMainQuestByTableId(int tid)
public MainQuestCompletionRecord? GetMainQuestByTableId(int tid)
{
foreach (JObject item in questDataRecords)
{
var id = item["id"];
if (id == null) throw new Exception("expected condition_id field in quest data");
int value = id.ToObject<int>();
if (value == tid)
{
MainQuestCompletionData? data = JsonConvert.DeserializeObject<MainQuestCompletionData>(item.ToString());
if (data == null) throw new Exception("failed to deserialize main quest data item");
return data;
}
}
return null;
return questDataRecords[tid];
}
public CampaignStageRecord? GetStageData(int stage)
{
foreach (JObject item in stageDataRecords)
{
var id = item["id"];
if (id == null) throw new Exception("expected id field in campaign data");
int value = id.ToObject<int>();
if (value == stage)
{
CampaignStageRecord? data = JsonConvert.DeserializeObject<CampaignStageRecord>(item.ToString());
if (data == null) throw new Exception("failed to deserialize stage data");
return data;
}
}
return null;
return stageDataRecords[stage];
}
public RewardTableRecord? GetRewardTableEntry(int rewardId)
{
foreach (JObject item in rewardDataRecords)
{
var id = item["id"];
if (id == null) throw new Exception("expected id field in reward data");
int value = id.ToObject<int>();
if (value == rewardId)
{
RewardTableRecord? data = JsonConvert.DeserializeObject<RewardTableRecord>(item.ToString());
if (data == null) throw new Exception("failed to deserialize reward data");
return data;
}
}
return null;
return rewardDataRecords[rewardId];
}
/// <summary>
/// Returns the level and its minimum value for XP value
@@ -355,26 +433,34 @@ namespace nksrv.StaticInfo
}
return (-1, -1);
}
public int GetUserMinXpForLevel(int targetLevel)
{
for (int i = 0; i < userExpDataRecords.Count; i++)
{
var item = userExpDataRecords[i];
var level = item["level"];
if (level == null) throw new Exception("expected level field in user exp table data");
int levelValue = level.ToObject<int>();
if (targetLevel == levelValue)
{
var exp = item["exp"];
if (exp == null) throw new Exception("expected exp field in user exp table data");
int expValue = exp.ToObject<int>();
return expValue;
}
}
return -1;
}
public int GetNormalChapterNumberFromFieldName(string field)
{
foreach (JObject item in chapterCampaignData)
foreach (var item in chapterCampaignData)
{
var id = item["field_id"];
if (id == null)
if (item.Value.field_id == field)
{
throw new Exception("expected id field in reward data");
}
string? value = id.ToObject<string>();
if (value == field)
{
var chapter = item["chapter"];
if (chapter == null)
{
throw new Exception("expected id field in reward data");
}
return chapter.ToObject<int>();
return item.Value.chapter;
}
}
@@ -383,14 +469,7 @@ namespace nksrv.StaticInfo
public IEnumerable<int> GetAllCharacterTids()
{
foreach (JObject item in characterTable)
{
var id = item["id"];
if (id == null) throw new Exception("expected id field in reward data");
int value = id.ToObject<int>();
yield return value;
}
return characterTable.Keys;
}
public IEnumerable<int> GetAllCostumes()
{
@@ -406,56 +485,20 @@ namespace nksrv.StaticInfo
internal ClearedTutorialData GetTutorialDataById(int TableId)
{
foreach (JObject item in tutorialTable)
{
var id = item["id"];
if (id == null)
{
throw new Exception("expected id field in reward data");
}
int idValue = id.ToObject<int>();
if (idValue == TableId)
{
ClearedTutorialData? data = JsonConvert.DeserializeObject<ClearedTutorialData>(item.ToString());
if (data == null) throw new Exception("failed to deserialize reward data");
return data;
}
}
throw new Exception("tutorial not found: " + TableId);
return tutorialTable[TableId];
}
public string? GetItemSubType(int itemType)
{
foreach (JObject item in itemEquipTable)
{
var id = item["id"];
if (id == null) throw new Exception("expected id field in reward data");
int? idValue = id.ToObject<int>();
if (idValue == itemType)
{
var subtype = item["item_sub_type"];
if (subtype == null)
{
throw new Exception("expected item_sub_type field in item equip data");
}
return subtype.ToObject<string>();
}
}
return null;
return itemEquipTable[itemType].item_sub_type;
}
internal IEnumerable<int> GetStageIdsForChapter(int chapterNumber, bool normal)
{
string mod = normal ? "Normal" : "Hard";
foreach (JObject item in stageDataRecords)
foreach (var item in stageDataRecords)
{
CampaignStageRecord? data = JsonConvert.DeserializeObject<CampaignStageRecord>(item.ToString());
if (data == null) throw new Exception("failed to deserialize stage data");
var data = item.Value;
int chVal = data.chapter_id - 1;
@@ -465,5 +508,15 @@ namespace nksrv.StaticInfo
}
}
}
public Dictionary<int, CharacterLevelData> GetCharacterLevelUpData()
{
return LevelData;
}
public TacticAcademyLessonRecord GetTacticAcademyLesson(int lessonId)
{
return TacticAcademyLessons[lessonId];
}
}
}

View File

@@ -0,0 +1,150 @@
namespace EpinelPS.StaticInfo
{
public class MainQuestCompletionRecord
{
public int id;
public int group_id;
public string category = "";
public int condition_id;
public int next_main_quest_id = 0;
public int reward_id = 0;
public int target_chapter_id;
}
public class MainQuestCompletionTable
{
public List<MainQuestCompletionRecord> records;
}
public class CampaignStageRecord
{
public int id;
public int chapter_id;
public string stage_category = "";
public int reward_id = 0;
/// <summary>
/// Can be Normal or Hard
/// </summary>
public string chapter_mod = "";
public string stage_type = "";
public string enter_scenario = "";
public string exit_scenario = "";
}
public class CampaignStageTable
{
public List<CampaignStageRecord> records;
}
public class RewardTableRecord
{
public int id;
public int user_exp;
public int character_exp;
public RewardEntry[]? rewards;
}
public class RewardTable
{
public List<RewardTableRecord> records;
}
public class RewardEntry
{
/// <summary>
/// example: 1000000
/// </summary>
public int reward_percent;
public string percent_display_type = "";
public string reward_type = "";
public int reward_id;
public int reward_value;
}
public class ClearedTutorialData
{
public int id;
public int VersionGroup = 0;
public int GroupId;
public int ClearedStageId;
public int NextId;
public bool SaveTutorial;
}
public class TutorialTable
{
public List<ClearedTutorialData> records;
}
public class CharacterLevelData
{
/// <summary>
/// level
/// </summary>
public int level;
/// <summary>
/// can be CharacterLevel or SynchroLevel
/// </summary>
public string type = "";
/// <summary>
/// amount of credits required
/// </summary>
public int gold = 0;
/// <summary>
/// amount of battle data required
/// </summary>
public int character_exp = 0;
/// <summary>
/// amount of core dust required
/// </summary>
public int character_exp2 = 0;
}
public class TacticAcademyLessonRecord
{
public CurrencyType CurrencyId;
public int CurrencyValue;
public int Id;
public int GroupId;
}
public class CampaignChapterRecord
{
public int id;
public int chapter;
public string field_id;
public string hard_field_id;
}
public class CampaignChapterTable
{
public List<CampaignChapterRecord> records;
}
public class CharacterRecord
{
public int id;
// TODO: There is more stuff here but it isn't needed yet
}
public class CharacterTable
{
public List<CharacterRecord> records;
}
public class ItemEquipRecord
{
public int id;
public string item_sub_type;
}
public class ItemEquipTable
{
public List<ItemEquipRecord> records;
}
public class FieldItemRecord
{
public int id;
public string item_type;
public int type_value;
public bool is_final_reward;
public string difficulty;
}
public class FieldItemTable
{
public List<FieldItemRecord> records;
}
}

View File

@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace nksrv.IntlServer
namespace EpinelPS.IntlServer
{
internal class AutoLoginEndpoint : IntlMsgHandler
{

View File

@@ -1,13 +1,4 @@
using EmbedIO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace nksrv.IntlServer
namespace EpinelPS.IntlServer
{
public class CodeStatusEndpoint : IntlMsgHandler
{
@@ -16,7 +7,7 @@ namespace nksrv.IntlServer
protected override async Task HandleAsync()
{
// pretend that any code is valid
await WriteJsonStringAsync("{\"expire_time\":759,\"msg\":\"Success\",\"ret\":0,\"seq\":\""+Seq+"\"}");
await WriteJsonStringAsync("{\"expire_time\":759,\"msg\":\"Success\",\"ret\":0,\"seq\":\"" + Seq + "\"}");
}
}
}

View File

@@ -1,11 +1,4 @@
using Google.Api;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace nksrv.IntlServer
namespace EpinelPS.IntlServer
{
public class GetNoticeContent : IntlMsgHandler
{

View File

@@ -1,14 +1,8 @@
using EmbedIO;
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using EpinelPS.Utils;
using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace nksrv.IntlServer
namespace EpinelPS.IntlServer
{
/// <summary>
/// redirect for /account endponts

View File

@@ -1,17 +1,11 @@
using EmbedIO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using static nksrv.IntlServer.IntlLogin2Endpoint;
using Newtonsoft.Json;
using static nksrv.IntlServer.IntlLogin1Endpoint;
using nksrv.Utils;
using EpinelPS.Database;
using EpinelPS.Utils;
using System.Net;
using static EpinelPS.IntlServer.IntlLogin2Endpoint;
namespace nksrv.IntlServer
namespace EpinelPS.IntlServer
{
public class IntlAccountRegisterEndpoint : IntlMsgHandler
{
@@ -43,7 +37,7 @@ namespace nksrv.IntlServer
}
}
var user = new User() { ID = uid, Password = ep.password, RegisterTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(), Username = ep.account, PlayerName = "Player_"+Rng.RandomString(8) };
var user = new User() { ID = uid, Password = ep.password, RegisterTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(), Username = ep.account, PlayerName = "Player_" + Rng.RandomString(8) };
JsonDb.Instance.Users.Add(user);

View File

@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace nksrv.IntlServer
namespace EpinelPS.IntlServer
{
internal class IntlGetAccountInfo : IntlMsgHandler
{

View File

@@ -1,5 +1,5 @@

namespace nksrv.IntlServer
namespace EpinelPS.IntlServer
{
internal class IntlGetProfileBindInfo : IntlMsgHandler
{

View File

@@ -0,0 +1,15 @@
namespace EpinelPS.IntlServer
{
public class IntlGetProfileInfo : IntlMsgHandler
{
public override bool RequiresAuth => true;
protected override async Task HandleAsync()
{
if (User == null)
throw new Exception("no user"); // should never happen
await WriteJsonStringAsync("{\"bind_list\":[{\"channel_info\":{\"birthday\":\"1970-01\",\"email\":\"" + User.Username + "\",\"is_receive_email\":1,\"lang_type\":\"en\",\"last_login_time\":1719075003,\"nick_name\":\"\",\"phone\":\"\",\"phone_area_code\":\"\",\"region\":\"724\",\"register_account\":\"" + User.Username + "\",\"register_account_type\":1,\"register_time\":" + User.RegisterTime + ",\"seq\":\"abc\",\"uid\":\"" + User.ID + "\",\"user_name\":\"" + User.PlayerName + "\",\"username_pass_verify\":0},\"channelid\":131,\"email\":\"" + User.Username + "\",\"picture_url\":\"\",\"user_name\":\"" + User.PlayerName + "\"}],\"birthday\":\"1970-01\",\"email\":\"" + User.Username + "\",\"gender\":0,\"msg\":\"success\",\"picture_url\":\"\",\"ret\":0,\"seq\":\"" + Seq + "\",\"user_name\":\"" + User.PlayerName + "\"}");
}
}
}

View File

@@ -1,14 +1,8 @@
using EmbedIO;
using nksrv.Utils;
using Swan.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Database;
using EpinelPS.Utils;
namespace nksrv.IntlServer
namespace EpinelPS.IntlServer
{
public static class IntlHandler
{
@@ -30,7 +24,11 @@ namespace nksrv.IntlServer
{"/gnconfig/acquire_config", new IntlReturnJsonHandler(AquireConfigResp) },
{"/auth/auto_login", new AutoLoginEndpoint() },
{"/reward/send", new IntlReturnJsonHandler(SetProtocolResp) }, // /v2/reward/send
{"/notice/get_notice_content", new GetNoticeContent() } // /v2/notice/get_notice_content
{"/notice/get_notice_content", new GetNoticeContent() }, // /v2/notice/get_notice_content
{"/fleet.repo.game.RepoSVC/GetVersion", new JuniperLauncherGetRepoVersion() }, // /api/v1/fleet.repo.game.RepoSVC/GetVersion
{"/fleet.repo.game.RepoMgr/GetGameLauncher", new JuniperLauncherGetGameLauncher() }, // /api/v1/fleet.repo.game.RepoMgr/
{"/fleet.repo.game.RepoSVC/GetRegion", new JuniperLauncherGetRegion() }, // /api/v1/fleet.repo.game.RepoMgr/ // GetGameLauncher
{"/fleet.auth.game.AuthSvr/Login", new JupiterAuthLogin() } // /api/v1/fleet.auth.game.AuthSvr/Login
};
public const string GetConfResp = "{\"conf_version\":\"102\",\"msg\":\"\",\"ret\":1,\"seq\":\"((SEGID))\"}";
public const string MinorcerResp = "{\"adult_age\":15,\"adult_age_map\":{},\"adult_check_status\":1,\"adult_check_status_expiration\":\"0\",\"adult_status_map\":{},\"certificate_type\":3,\"email\":\"\",\"eu_user_agree_status\":0,\"game_grade\":0,\"game_grade_map\":{},\"is_dma\":true,\"is_eea\":false,\"is_need_li_cert\":false,\"msg\":\"success\",\"need_parent_control\":0,\"need_realname_auth\":0,\"parent_certificate_status\":0,\"parent_certificate_status_expiration\":\"0\",\"parent_control_map\":{},\"qr_code_ret\":0,\"realname_auth_status\":0,\"region\":\"300\",\"ret\":0,\"seq\":\"((SEGID))\",\"ts\":\"1719156511\"}";
@@ -54,13 +52,15 @@ namespace nksrv.IntlServer
}
else
{
handler.Reset();
await handler.HandleAsync(context);
}
}
public static AccessToken CreateLauncherTokenForUser(User user)
{
AccessToken token = new() { ExpirationTime = DateTimeOffset.UtcNow.AddDays(1).ToUnixTimeSeconds() };
// TODO: implement access token expiration
AccessToken token = new() { ExpirationTime = DateTimeOffset.UtcNow.AddYears(1).ToUnixTimeSeconds() };
token.Token = Rng.RandomString(64);
token.UserID = user.ID;
JsonDb.Instance.LauncherAccessTokens.Add(token);

View File

@@ -1,15 +1,10 @@
using EmbedIO;
using Newtonsoft.Json;
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using EpinelPS.Database;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using static nksrv.IntlServer.IntlLogin2Endpoint;
using static EpinelPS.IntlServer.IntlLogin2Endpoint;
namespace nksrv.IntlServer
namespace EpinelPS.IntlServer
{
public class IntlLogin1Endpoint : IntlMsgHandler
{

View File

@@ -1,9 +1,9 @@
using EmbedIO;
using Newtonsoft.Json;
using nksrv.Utils;
using EpinelPS.Database;
using System.Net;
namespace nksrv.IntlServer
namespace EpinelPS.IntlServer
{
/// <summary>
/// This handles the login endpoint.

View File

@@ -1,14 +1,10 @@
using EmbedIO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using EpinelPS.Database;
using Swan.Logging;
using System.Text;
using System.Threading.Tasks;
namespace nksrv.IntlServer
namespace EpinelPS.IntlServer
{
public abstract class IntlMsgHandler
{
@@ -18,6 +14,15 @@ namespace nksrv.IntlServer
protected string Seq = "";
protected AccessToken? UsedToken;
public abstract bool RequiresAuth { get; }
public void Reset()
{
UsedToken = null;
Seq = "";
User = null;
Content = "";
ctx = null;
}
public async Task HandleAsync(IHttpContext ctx)
{
this.ctx = ctx;
@@ -27,7 +32,7 @@ namespace nksrv.IntlServer
{
var x = JsonConvert.DeserializeObject<AuthPkt>(Content);
string tokToCheck = "";
if(x != null && x.channel_info != null && !string.IsNullOrEmpty(x.channel_info.account_token))
if (x != null && x.channel_info != null && !string.IsNullOrEmpty(x.channel_info.account_token))
{
tokToCheck = x.channel_info.account_token;
}
@@ -60,12 +65,14 @@ namespace nksrv.IntlServer
if (User == null)
{
Logger.Warn("Unknown auth token");
await WriteJsonStringAsync("{\"msg\":\"expired verify_code!\",\"ret\":2022,\"seq\":\"" + Seq + "\"}\r\n");
return;
}
}
else
{
Logger.Warn("Failed to parse auth data");
await WriteJsonStringAsync("{\"msg\":\"expired verify_code!\",\"ret\":2022,\"seq\":\"" + Seq + "\"}\r\n");
return;
}
@@ -74,14 +81,22 @@ namespace nksrv.IntlServer
await HandleAsync();
}
protected abstract Task HandleAsync();
protected async Task WriteJsonStringAsync(string data)
protected async Task WriteJsonStringAsync(string data, bool juniper = false)
{
if (ctx != null)
{
var bt = Encoding.UTF8.GetBytes(data);
ctx.Response.ContentEncoding = null;
ctx.Response.ContentType = "application/json";
ctx.Response.ContentLength64 = bt.Length;
if (juniper)
{
ctx.Response.ContentEncoding = Encoding.UTF8;
ctx.Response.ContentType = "application/json";
}
else
{
ctx.Response.ContentEncoding = null;
ctx.Response.ContentType = "application/json";
ctx.Response.ContentLength64 = bt.Length;
}
await ctx.Response.OutputStream.WriteAsync(bt, ctx.CancellationToken);
await ctx.Response.OutputStream.FlushAsync();
}

View File

@@ -1,13 +1,4 @@
using EmbedIO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace nksrv.IntlServer
namespace EpinelPS.IntlServer
{
internal class IntlQueryAccountInfo : IntlMsgHandler
{

View File

@@ -1,12 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EmbedIO;
using Swan;
namespace nksrv.IntlServer
namespace EpinelPS.IntlServer
{
public class IntlReturnJsonHandler : IntlMsgHandler
{

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EpinelPS.IntlServer
{
internal class JuniperLauncherGetRegion : IntlMsgHandler
{
public override bool RequiresAuth => false;
protected override async Task HandleAsync()
{
await WriteJsonStringAsync(@"{
""result"": {
""error_code"": 0,
""error_message"": ""success""
},
""region_info"": [
{
""game_id"": ""16601"",
""region_id"": ""10001"",
""region_name_en_us"": ""Global"",
""region_name_i18n"": """",
""region_description_en_us"": ""Nikke Global Version"",
""region_description_i18n"": """",
""bind_branches"": """",
""meta_data"": """",
""sequence"": 0,
""status"": 2,
""branch_info"": [
{
""game_id"": ""16601"",
""branch_id"": ""1"",
""branch_name"": ""Official_release"",
""branch_type"": 0,
""description"": ""正式发布环境 release包""
}
]
}
]
}", true);
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EpinelPS.IntlServer
{
internal class JuniperLauncherGetRepoVersion : IntlMsgHandler
{
public override bool RequiresAuth => false;
protected override async Task HandleAsync()
{
await WriteJsonStringAsync(File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "gameversion.json")), true);
}
}
}

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EpinelPS.IntlServer
{
internal class JupiterAuthLogin : IntlMsgHandler
{
public override bool RequiresAuth => false;
protected override async Task HandleAsync()
{
await WriteJsonStringAsync(@"{
""result"": {
""error_code"": 0,
""error_message"": ""COMM_SUCC""
},
""channel"": 0,
""game_id"": ""0"",
""openid"": """",
""uid"": """",
""biz_ticket"": """",
""expire_interval"": 0,
""refresh_interval"": 0,
""login_key"": """",
""login_ticket"": """",
""third_uid"": """"
}", true);
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EpinelPS.IntlServer
{
internal class JuniperLauncherGetGameLauncher : IntlMsgHandler
{
public override bool RequiresAuth => false;
protected override async Task HandleAsync()
{
await WriteJsonStringAsync(@"{
""result"": {
""error_code"": 0,
""error_message"": ""COMM_SUCC""
},
""game_launcher_info"": [
{
""id"": 27,
""execute_file"": ""NIKKE\\Game\\NIKKE.exe"",
""param"": """",
""description"": ""Nikke main process"",
""os"": ""any"",
""branch_id"": 0,
""status"": 1,
""param_type"": 1
}
]
}", true);
}
}
}

View File

@@ -1,17 +1,10 @@
using EmbedIO;
using Newtonsoft.Json;
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using EpinelPS.Database;
using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using static nksrv.IntlServer.IntlLogin1Endpoint;
using static nksrv.IntlServer.IntlLogin2Endpoint;
using static EpinelPS.IntlServer.IntlLogin2Endpoint;
namespace nksrv.IntlServer
namespace EpinelPS.IntlServer
{
public class SendCodeEndpoint : IntlMsgHandler
{

View File

@@ -1,10 +1,11 @@
using ASodium;
using EmbedIO;
using Google.Protobuf;
using nksrv.Utils;
using EpinelPS.Database;
using EpinelPS.Utils;
using Swan.Logging;
namespace nksrv.LobbyServer
namespace EpinelPS.LobbyServer
{
public static class LobbyHandler
{
@@ -104,7 +105,7 @@ namespace nksrv.LobbyServer
{
NetUserData ret = new()
{
Lv = user.userPointData.UserLevel,
Level = user.userPointData.UserLevel,
Exp = user.userPointData.ExperiencePoint,
CommanderRoomJukebox = 5,
CostumeLv = 1,
@@ -114,7 +115,6 @@ namespace nksrv.LobbyServer
LobbyJukebox = 2,
InfraCoreExp = user.InfraCoreExp,
InfraCoreLv = user.InfraCoreLvl,
};
@@ -133,14 +133,14 @@ namespace nksrv.LobbyServer
{
var ret = new NetWholeUserData()
{
Lv = user.userPointData.UserLevel,
Frame = 1,
Level = user.userPointData.UserLevel,
Frame = user.ProfileFrame,
Icon = user.ProfileIconId,
IconPrism = user.ProfileIconIsPrism,
Nickname = user.Nickname,
Usn = (long)user.ID,
LastActionAt = DateTimeOffset.UtcNow.Ticks,
Server = 1001
};
return ret;

View File

@@ -1,13 +1,9 @@
using EmbedIO;
using Google.Protobuf;
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Database;
using EpinelPS.Utils;
namespace nksrv.LobbyServer
namespace EpinelPS.LobbyServer
{
public abstract class LobbyMsgHandler
{
@@ -116,5 +112,9 @@ namespace nksrv.LobbyServer
{
return JsonDb.GetUser(UserId) ?? throw new Exception("null user");
}
public User? GetUser(ulong id)
{
return JsonDb.GetUser(id);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Antibot
namespace EpinelPS.LobbyServer.Msgs.Antibot
{
[PacketPath("/antibot/battlereportdata")]
public class BattleReportData : LobbyMsgHandler

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Antibot
namespace EpinelPS.LobbyServer.Msgs.Antibot
{
[PacketPath("/antibot/recvdata")]
public class RecieveAntibotData : LobbyMsgHandler

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Archive
namespace EpinelPS.LobbyServer.Msgs.Archive
{
[PacketPath("/bookmark/scenario/exist")]
public class CheckBookmarkScenarioExists : LobbyMsgHandler

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Archive
namespace EpinelPS.LobbyServer.Msgs.Archive
{
[PacketPath("/archive/get")]
public class GetArchives : LobbyMsgHandler

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Arena
namespace EpinelPS.LobbyServer.Msgs.Arena
{
[PacketPath("/arena/getbaninfo")]
public class GetArenaBanInfo : LobbyMsgHandler

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Arena
namespace EpinelPS.LobbyServer.Msgs.Arena
{
[PacketPath("/arena/special/showreward")]
public class ShowSpecialArenaReward : LobbyMsgHandler

View File

@@ -1,13 +1,7 @@
using Google.Protobuf.WellKnownTypes;
using Google.Protobuf;
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Database;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Auth
namespace EpinelPS.LobbyServer.Msgs.Auth
{
[PacketPath("/auth/logout")]
public class AuthLogout : LobbyMsgHandler

View File

@@ -1,21 +1,17 @@
using Google.Protobuf.WellKnownTypes;
using EmbedIO;
using Google.Protobuf;
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EmbedIO;
using Google.Protobuf.WellKnownTypes;
using EpinelPS.Database;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Auth
namespace EpinelPS.LobbyServer.Msgs.Auth
{
[PacketPath("/auth/enterserver")]
public class GetUserOnlineStateLog : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<EnterServerRequest>();
var req = await ReadData<ReqEnterServer>();
// request has auth token
UsedAuthToken = req.AuthToken;
@@ -30,7 +26,7 @@ namespace nksrv.LobbyServer.Msgs.Auth
var user = GetUser();
var response = new EnterServerResponse();
var response = new ResEnterServer();
var rsp = LobbyHandler.GenGameClientTok(req.ClientPublicKey, req.AuthToken);
response.GameClientToken = rsp.ClientAuthToken;
response.FeatureDataInfo = new NetFeatureDataInfo() { UseFeatureData = true };

View File

@@ -0,0 +1,54 @@
using EmbedIO;
using Google.Protobuf.WellKnownTypes;
using EpinelPS.Database;
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Msgs.Auth
{
[PacketPath("/auth/intl")]
public class DoIntlAuth : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqAuthIntl>();
var response = new ResAuth();
UsedAuthToken = req.Token;
foreach (var item in JsonDb.Instance.LauncherAccessTokens)
{
if (item.Token == UsedAuthToken)
{
UserId = item.UserID;
}
}
if (UserId == 0)
{
response.AuthError = new NetAuthError() { ErrorCode = AuthErrorCode.AuthErrorCodeError };
}
else
{
var user = GetUser();
if (user.IsBanned && user.BanEnd < DateTime.UtcNow)
{
user.IsBanned = false;
user.BanId = 0;
user.BanStart = DateTime.MinValue;
user.BanEnd = DateTime.MinValue;
JsonDb.Save();
}
if (user.IsBanned)
{
response.BanInfo = new NetBanInfo() { BanId = user.BanId, Description = "The server admin is sad today because the hinge on his HP laptop broke which happened to be an HP Elitebook 8470p, and the RAM controller exploded and then fixed itself, please contact him", StartAt = Timestamp.FromDateTime(DateTime.SpecifyKind(user.BanStart, DateTimeKind.Utc)), EndAt = Timestamp.FromDateTime(DateTime.SpecifyKind(user.BanEnd, DateTimeKind.Utc)) };
}
else
{
response.AuthSuccess = new NetAuthSuccess() { AuthToken = req.Token, CentauriZoneId = "84", FirstAuth = false, PurchaseRestriction = new NetUserPurchaseRestriction() { PurchaseRestriction = PurchaseRestriction.PurchaseRestrictionChild, UpdatedAt = 638546758794611090 } };
}
}
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Badge
namespace EpinelPS.LobbyServer.Msgs.Badge
{
[PacketPath("/badge/delete")]
public class DeleteBadge : LobbyMsgHandler

View File

@@ -1,6 +1,6 @@
using nksrv.Utils;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Badge
namespace EpinelPS.LobbyServer.Msgs.Badge
{
[PacketPath("/badge/sync")]
public class SyncBadge : LobbyMsgHandler

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Campaign
namespace EpinelPS.LobbyServer.Msgs.Campaign
{
[PacketPath("/shutdownflags/campaignpackage/getall")]
public class CampaignPackageGetAll : LobbyMsgHandler

View File

@@ -1,14 +1,8 @@
using nksrv.LobbyServer.Msgs.Stage;
using nksrv.StaticInfo;
using nksrv.Utils;
using Swan.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.LobbyServer.Msgs.Stage;
using EpinelPS.StaticInfo;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Campaign
namespace EpinelPS.LobbyServer.Msgs.Campaign
{
[PacketPath("/campaign/getfield")]
public class GetCampaignField : LobbyMsgHandler
@@ -17,11 +11,11 @@ namespace nksrv.LobbyServer.Msgs.Campaign
{
var req = await ReadData<ReqGetCampaignFieldData>();
var user = GetUser();
Console.WriteLine("Map ID: " + req.MapId);
var response = new ResGetCampaignFieldData();
response.Field = GetStage.CreateFieldInfo(user, StaticDataParser.Instance.GetNormalChapterNumberFromFieldName(req.MapId), req.MapId.Contains("hard") ? "Hard" : "Normal");
response.Field = GetStage.CreateFieldInfo(user, GameData.Instance.GetNormalChapterNumberFromFieldName(req.MapId), req.MapId.Contains("hard") ? "Hard" : "Normal");
// todo save this data
response.Team = new NetUserTeamData() { LastContentsTeamNumber = 1, Type = 1 };

View File

@@ -0,0 +1,51 @@
using EpinelPS.Database;
using EpinelPS.LobbyServer.Msgs.Stage;
using EpinelPS.StaticInfo;
using EpinelPS.Utils;
using Swan.Logging;
namespace EpinelPS.LobbyServer.Msgs.Campaign
{
[PacketPath("/campaign/obtain/item")]
public class ObtainItem : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqObtainCampaignItem>();
var user = GetUser();
var response = new ResObtainCampaignItem();
var chapter = GameData.Instance.GetNormalChapterNumberFromFieldName(req.MapId);
var mod = req.MapId.Contains("hard") ? "Hard" : "Normal";
var key = chapter + "_" + mod;
var field = user.FieldInfoNew[key];
foreach (var item in field.CompletedObjects)
{
if (item.PositionId == req.FieldObject.PositionId)
{
Logger.Warn("attempted to collect campaign field object twice!");
return;
}
}
// Register and return reward
if (!GameData.Instance.PositionReward.ContainsKey(req.FieldObject.PositionId)) throw new Exception("bad position id");
var fieldReward = GameData.Instance.PositionReward[req.FieldObject.PositionId];
var positionReward = GameData.Instance.FieldItems[fieldReward];
var reward = GameData.Instance.GetRewardTableEntry(positionReward.type_value);
if (reward == null) throw new Exception("failed to get reward");
response.Reward = ClearStage.RegisterRewardsForUser(user, reward);
// Hide it from the field
field.CompletedObjects.Add(new NetFieldObject() { PositionId = req.FieldObject.PositionId, Type = req.FieldObject.Type});
JsonDb.Save();
await WriteDataAsync(response);
}
}
}

View File

@@ -1,6 +1,6 @@
using nksrv.Utils;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Campaign
namespace EpinelPS.LobbyServer.Msgs.Campaign
{
[PacketPath("/campaign/savefield")]
public class SaveField : LobbyMsgHandler
@@ -20,7 +20,7 @@ namespace nksrv.LobbyServer.Msgs.Campaign
}
else
{
user.MapJson[req.MapId] = req.Json;
user.MapJson[req.MapId] = req.Json;
}
await WriteDataAsync(response);

View File

@@ -1,12 +1,8 @@
using nksrv.StaticInfo;
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Database;
using EpinelPS.StaticInfo;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Campaign
namespace EpinelPS.LobbyServer.Msgs.Campaign
{
[PacketPath("/campaign/savefieldobject")]
public class SaveFieldObject : LobbyMsgHandler
@@ -20,14 +16,14 @@ namespace nksrv.LobbyServer.Msgs.Campaign
var response = new ResSaveCampaignFieldObject();
Console.WriteLine($"save {req.MapId} with {req.FieldObject.PositionID}");
Console.WriteLine($"save {req.MapId} with {req.FieldObject.PositionId}");
var chapter = StaticDataParser.Instance.GetNormalChapterNumberFromFieldName(req.MapId);
var chapter = GameData.Instance.GetNormalChapterNumberFromFieldName(req.MapId);
var mod = req.MapId.Contains("hard") ? "Hard" : "Normal";
var key = chapter + "_" + mod;
var field = user.FieldInfo[key];
var field = user.FieldInfoNew[key];
field.CompletedObjects.Add(new NetFieldObject() { PositionId = req.FieldObject.PositionID, Json = req.FieldObject.Json, Type = req.FieldObject.Type });
field.CompletedObjects.Add(new NetFieldObject() { PositionId = req.FieldObject.PositionId, Json = req.FieldObject.Json, Type = req.FieldObject.Type });
JsonDb.Save();
await WriteDataAsync(response);

View File

@@ -0,0 +1,54 @@
using EpinelPS.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EpinelPS.LobbyServer.Msgs.Character
{
[PacketPath("/character/SynchroDevice/Change")]
public class ChangeSynchroDevice : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqSynchroChange>();
var user = GetUser();
var response = new ResSynchroChange();
var highestLevelCharacters = user.Characters.OrderByDescending(x => x.Level).Take(5).ToList();
int slot = 1;
foreach (var item in highestLevelCharacters)
{
if (item.Level != 200)
{
throw new Exception("expected level to be 200");
}
response.Characters.Add(new NetUserCharacterData() { Default = new() { Csn = item.Csn, Skill1Lv = item.Skill1Lvl, Skill2Lv = item.Skill2Lvl, CostumeId = item.CostumeId, Level = item.Level, Grade = item.Grade, Tid = item.Tid, UltiSkillLv = item.UltimateLevel }, IsSynchro = user.GetSynchro(item.Csn) });
foreach (var s in user.SynchroSlots)
{
if (s.Slot == slot)
{
s.CharacterSerialNumber = item.Csn;
break;
}
}
slot++;
}
user.SynchroDeviceUpgraded = true;
foreach (var item in user.SynchroSlots)
{
response.Slots.Add(new NetSynchroSlot() { Slot = item.Slot, AvailableRegisterAt = item.AvailableAt, Csn = item.CharacterSerialNumber });
}
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Character
namespace EpinelPS.LobbyServer.Msgs.Character
{
[PacketPath("/character/attractive/get")]
public class GetCharacterAttractiveList : LobbyMsgHandler

View File

@@ -1,12 +1,7 @@
using nksrv.StaticInfo;
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.StaticInfo;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Character
namespace EpinelPS.LobbyServer.Msgs.Character
{
[PacketPath("/character/costume/get")]
public class GetCharacterCostume : LobbyMsgHandler
@@ -18,7 +13,7 @@ namespace nksrv.LobbyServer.Msgs.Character
var response = new ResGetCharacterCostumeData();
// return all
response.CostumeIds.AddRange(StaticDataParser.Instance.GetAllCostumes());
response.CostumeIds.AddRange(GameData.Instance.GetAllCostumes());
await WriteDataAsync(response);
}

View File

@@ -0,0 +1,30 @@
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Msgs.Character
{
[PacketPath("/character/get")]
public class GetCharacterData : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqGetCharacterData>();
var user = GetUser();
var response = new ResGetCharacterData();
// TODO: When Squad view opens in the game, or this request is sent, all character levels reset to 1 as well as costume IDs
//foreach (var item in user.Characters)
//{
// response.Character.Add(new NetUserCharacterData() { Default = new() { Csn = item.Csn, Skill1Lv = item.Skill1Lvl, Skill2Lv = item.Skill2Lvl, CostumeId = item.CostumeId, Level = user.GetCharacterLevel(item.Csn, item.Level), Grade = item.Grade, Tid = item.Tid, UltiSkillLv = item.UltimateLevel }, IsSynchro = user.GetSynchro(item.Csn) });
//}
var highestLevelCharacters = user.Characters.OrderByDescending(x => x.Level).Take(5).ToList();
foreach (var c in highestLevelCharacters)
{
response.SynchroStandardCharacters.Add(c.Csn);
}
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,54 @@
using EpinelPS.Database;
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Msgs.Character
{
[PacketPath("/character/synchrodevice/get")]
public class GetSynchrodevice : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqGetSynchroData>();
var user = GetUser();
if (user.SynchroSlots.Count == 0)
{
user.SynchroSlots = new() {
new SynchroSlot() { Slot = 1 },
new SynchroSlot() { Slot = 2},
new SynchroSlot() { Slot = 3 },
new SynchroSlot() { Slot = 4 },
new SynchroSlot() { Slot = 5 },
new SynchroSlot() { Slot = 6 },
new SynchroSlot() { Slot = 7 },
new SynchroSlot() { Slot = 8 },
new SynchroSlot() { Slot = 9 },
new SynchroSlot() { Slot = 10 },
};
}
var highestLevelCharacters = user.Characters.OrderByDescending(x => x.Level).Take(5).ToList();
var response = new ResGetSynchroData();
response.Synchro = new NetUserSynchroData();
foreach (var item in highestLevelCharacters)
{
response.Synchro.StandardCharacters.Add(new NetUserCharacterData() { Default = new() { Csn = item.Csn, Skill1Lv = item.Skill1Lvl, Skill2Lv = item.Skill2Lvl, CostumeId = item.CostumeId, Level = item.Level, Grade = item.Grade, Tid = item.Tid, UltiSkillLv = item.UltimateLevel }, IsSynchro = user.GetSynchro(item.Csn) });
}
foreach (var item in user.SynchroSlots)
{
response.Synchro.Slots.Add(new NetSynchroSlot() { Slot = item.Slot, AvailableRegisterAt = 1, Csn = item.CharacterSerialNumber });
}
response.Synchro.SynchroMaxLv = 1000;
response.Synchro.SynchroLv = user.GetSynchroLevel();
response.Synchro.IsChanged = user.SynchroDeviceUpgraded;
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,82 @@
using EpinelPS.Database;
using EpinelPS.StaticInfo;
using EpinelPS.Utils;
using Swan.Logging;
namespace EpinelPS.LobbyServer.Msgs.Character
{
[PacketPath("/character/levelup")]
public class LevelUp : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqCharacterLevelUp>();
var user = GetUser();
var response = new ResCharacterLevelUp();
var data = GameData.Instance.GetCharacterLevelUpData();
foreach (var item in user.Characters.ToArray())
{
if (item.Csn == req.Csn)
{
int requiredCredit = 0;
int requiredBattleData = 0;
int requiredCoreDust = 0;
for (int i = item.Level; i < req.Level; i++)
{
var levelUpData = data[i];
requiredCredit += levelUpData.gold;
requiredBattleData += levelUpData.character_exp;
requiredCoreDust += levelUpData.character_exp2;
}
if (user.CanSubtractCurrency(CurrencyType.Gold, requiredCredit) &&
user.CanSubtractCurrency(CurrencyType.CharacterExp, requiredBattleData) &&
user.CanSubtractCurrency(CurrencyType.CharacterExp2, requiredCoreDust))
{
user.SubtractCurrency(CurrencyType.Gold, requiredCredit);
user.SubtractCurrency(CurrencyType.CharacterExp, requiredBattleData);
user.SubtractCurrency(CurrencyType.CharacterExp2, requiredCoreDust);
item.Level = req.Level;
}
else
{
// TOOD: log this
Logger.Error("ERROR: Not enough currency for upgrade");
return;
}
response.Character = new()
{
CostumeId = item.CostumeId,
Csn = item.Csn,
Level = item.Level,
Skill1Lv = item.Skill1Lvl,
Skill2Lv = item.Skill2Lvl,
UltiSkillLv = item.UltimateLevel,
Grade = item.Grade,
Tid = item.Tid
};
var highestLevelCharacters = user.Characters.OrderByDescending(x => x.Level).Take(5).ToList();
response.SynchroLv = user.GetSynchroLevel();
foreach (var c in highestLevelCharacters)
{
response.SynchroStandardCharacters.Add(c.Csn);
}
foreach (var currency in user.Currency)
{
response.Currencies.Add(new NetUserCurrencyData() { Type = (int)currency.Key, Value = currency.Value });
}
break;
}
}
JsonDb.Save();
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,54 @@
using EpinelPS.Utils;
using Swan.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EpinelPS.LobbyServer.Msgs.Character
{
[PacketPath("/character/SynchroDevice/Regist")]
public class RegisterSynchroDevice : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqSynchroRegister>();
var user = GetUser();
var targetCharacter = user.GetCharacterBySerialNumber(req.Csn);
if (targetCharacter == null) throw new Exception("target character does not exist");
var response = new ResSynchroRegister();
foreach (var item in user.SynchroSlots)
{
if (item.Slot == req.Slot)
{
if (item.CharacterSerialNumber != 0)
{
Logger.Warn("must remove character from synchrodevice first");
}
else
{
item.CharacterSerialNumber = req.Csn;
response.IsSynchro = true;
response.Character = new NetUserCharacterDefaultData()
{
Csn = item.CharacterSerialNumber,
CostumeId = targetCharacter.CostumeId,
Grade = targetCharacter.Grade,
Level = user.GetSynchroLevel(),
Skill1Lv = targetCharacter.Skill1Lvl,
Skill2Lv = targetCharacter.Skill2Lvl,
Tid = targetCharacter.Tid,
UltiSkillLv = targetCharacter.UltimateLevel
};
response.Slot = new NetSynchroSlot() { AvailableRegisterAt = item.AvailableAt, Csn = item.CharacterSerialNumber, Slot = item.Slot };
}
}
}
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,87 @@
using EpinelPS.Database;
using EpinelPS.StaticInfo;
using EpinelPS.Utils;
using Swan.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EpinelPS.LobbyServer.Msgs.Character
{
[PacketPath("/character/growreset")]
public class ResetLevel : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqCharacterGrowReset>();
var user = GetUser();
var response = new ResCharacterGrowReset();
var data = GameData.Instance.GetCharacterLevelUpData();
foreach (var item in user.Characters.ToArray())
{
if (item.Csn == req.Csn)
{
if (item.Level == 1)
{
Logger.Warn("Character level is already 1 - cannot reset");
return;
}
if (item.Level == 200)
{
Logger.Warn("Character level is 200 - cannot reset");
return;
}
int requiredCredit = 0;
int requiredBattleData = 0;
int requiredCoreDust = 0;
for (int i = 1; i < item.Level; i++)
{
var levelUpData = data[i];
requiredCredit += levelUpData.gold;
requiredBattleData += levelUpData.character_exp;
requiredCoreDust += levelUpData.character_exp2;
}
user.AddCurrency(CurrencyType.Gold, requiredCredit);
user.AddCurrency(CurrencyType.CharacterExp, requiredBattleData);
user.AddCurrency(CurrencyType.CharacterExp2, requiredCoreDust);
item.Level = 1;
response.Character = new()
{
CostumeId = item.CostumeId,
Csn = item.Csn,
Level = item.Level,
Skill1Lv = item.Skill1Lvl,
Skill2Lv = item.Skill2Lvl,
UltiSkillLv = item.UltimateLevel,
Grade = item.Grade,
Tid = item.Tid
};
var highestLevelCharacters = user.Characters.OrderByDescending(x => x.Level).Take(5).ToList();
response.SynchroLv = highestLevelCharacters.Last().Level;
foreach (var c in highestLevelCharacters)
{
response.SynchroStandardCharacters.Add(c.Csn);
}
foreach (var currency in user.Currency)
{
response.Currencies.Add(new NetUserCurrencyData() { Type = (int)currency.Key, Value = currency.Value });
}
break;
}
}
JsonDb.Save();
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,7 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Database;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Character
namespace EpinelPS.LobbyServer.Msgs.Character
{
[PacketPath("/character/costume/set")]
public class SetCharacterCostume : LobbyMsgHandler
@@ -17,7 +13,7 @@ namespace nksrv.LobbyServer.Msgs.Character
foreach (var item in user.Characters)
{
if(item.Csn == req.Csn)
if (item.Csn == req.Csn)
{
item.CostumeId = req.CostumeId;
break;
@@ -27,7 +23,7 @@ namespace nksrv.LobbyServer.Msgs.Character
var response = new ResSetCharacterCostume();
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,60 @@
using EpinelPS.Database;
using EpinelPS.StaticInfo;
using EpinelPS.Utils;
using Swan.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EpinelPS.LobbyServer.Msgs.Character
{
[PacketPath("/character/SynchroDevice/LevelUp")]
public class SynchroLevelUp : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqSynchroLevelUp>();
var user = GetUser();
var response = new ResSynchroLevelUp();
var data = GameData.Instance.GetCharacterLevelUpData();
int requiredCredit = 0;
int requiredBattleData = 0;
int requiredCoreDust = 0;
var levelUpData = data[user.SynchroDeviceLevel + 1];
requiredCredit += levelUpData.gold;
requiredBattleData += levelUpData.character_exp;
requiredCoreDust += levelUpData.character_exp2;
if (user.CanSubtractCurrency(CurrencyType.Gold, requiredCredit) &&
user.CanSubtractCurrency(CurrencyType.CharacterExp, requiredBattleData) &&
user.CanSubtractCurrency(CurrencyType.CharacterExp2, requiredCoreDust))
{
user.SubtractCurrency(CurrencyType.Gold, requiredCredit);
user.SubtractCurrency(CurrencyType.CharacterExp, requiredBattleData);
user.SubtractCurrency(CurrencyType.CharacterExp2, requiredCoreDust);
user.SynchroDeviceLevel++;
}
else
{
// TOOD: log this
Logger.Error("ERROR: Not enough currency for upgrade");
return;
}
foreach (var currency in user.Currency)
{
response.Currencies.Add(new NetUserCurrencyData() { Type = (int)currency.Key, Value = currency.Value });
}
response.SynchroLv = user.SynchroDeviceLevel;
JsonDb.Save();
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,70 @@
using EpinelPS.Database;
using EpinelPS.Utils;
using Swan.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EpinelPS.LobbyServer.Msgs.Character
{
[PacketPath("/character/SynchroDevice/Unregist")]
public class UnregisterSynchroDevice : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqSynchroUnregist>();
var user = GetUser();
var response = new ResSynchroUnregist();
foreach (var item in user.SynchroSlots)
{
if (item.Slot == req.Slot)
{
if (item.CharacterSerialNumber == 0)
{
Logger.Warn("must add character from synchrodevice first");
}
else
{
var oldCSN = item.CharacterSerialNumber;
item.CharacterSerialNumber = 0;
var data = user.GetCharacterBySerialNumber(oldCSN);
if (data == null) throw new Exception("failed to lookup character");
response.Character = new NetUserCharacterDefaultData()
{
Csn = data.Csn,
CostumeId = data.CostumeId,
Grade = data.Grade,
Level = data.Level,
Skill1Lv = data.Skill1Lvl,
Skill2Lv = data.Skill2Lvl,
Tid = data.Tid,
UltiSkillLv = data.UltimateLevel
};
response.Slot = new NetSynchroSlot() { AvailableRegisterAt = item.AvailableAt, Csn = item.CharacterSerialNumber, Slot = item.Slot };
response.IsSynchro = false;
var highestLevelCharacters = user.Characters.OrderByDescending(x => x.Level).Take(5).ToList();
foreach (var item2 in highestLevelCharacters)
{
response.SynchroStandardCharacters.Add(item2.Csn);
}
response.SynchroLv = user.GetSynchroLevel();
}
}
}
JsonDb.Save();
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,7 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Database;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Event
namespace EpinelPS.LobbyServer.Msgs.Event
{
[PacketPath("/event/scenario/complete")]
public class CompleteEventScenario : LobbyMsgHandler
@@ -15,23 +11,23 @@ namespace nksrv.LobbyServer.Msgs.Event
var req = await ReadData<ReqSetEventScenarioComplete>();
var user = GetUser();
if (user.EventInfo.ContainsKey(req.EventID))
if (user.EventInfo.ContainsKey(req.EventId))
{
var evt = user.EventInfo[req.EventID];
var evt = user.EventInfo[req.EventId];
evt.CompletedScenarios.Add(req.ScenarioId);
}
else
{
var evt = new EventData();
evt.CompletedScenarios.Add(req.ScenarioId);
user.EventInfo.Add(req.EventID, evt);
user.EventInfo.Add(req.EventId, evt);
}
var response = new ResSetEventScenarioComplete();
// TODO reward
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Event
namespace EpinelPS.LobbyServer.Msgs.Event
{
[PacketPath("/eventfield/enter")]
public class EnterEventField : LobbyMsgHandler
@@ -18,7 +13,7 @@ namespace nksrv.LobbyServer.Msgs.Event
// TOOD
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Event
namespace EpinelPS.LobbyServer.Msgs.Event
{
[PacketPath("/event/mission/getclear")]
public class GetClearedMissions : LobbyMsgHandler
@@ -16,7 +11,7 @@ namespace nksrv.LobbyServer.Msgs.Event
var response = new ResGetEventMissionClear();
// TODO
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,7 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Database;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Event
namespace EpinelPS.LobbyServer.Msgs.Event
{
[PacketPath("/event/scenario/get")]
public class GetEventScenario : LobbyMsgHandler
@@ -16,17 +12,17 @@ namespace nksrv.LobbyServer.Msgs.Event
var user = GetUser();
var response = new ResGetEventScenarioData();
if (user.EventInfo.ContainsKey(req.EventID))
if (user.EventInfo.ContainsKey(req.EventId))
{
var evt = user.EventInfo[req.EventID];
var evt = user.EventInfo[req.EventId];
response.ScenarioIdList.AddRange(evt.CompletedScenarios);
}
else
{
user.EventInfo.Add(req.EventID, new EventData());
user.EventInfo.Add(req.EventId, new EventData());
}
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Event
namespace EpinelPS.LobbyServer.Msgs.Event
{
[PacketPath("/event/getjoinedevent")]
public class GetJoinedEvent : LobbyMsgHandler
@@ -69,7 +64,7 @@ namespace nksrv.LobbyServer.Msgs.Event
JoinAt = 0
});
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Event
namespace EpinelPS.LobbyServer.Msgs.Event
{
[PacketPath("/event/list")]
public class ListEvents : LobbyMsgHandler
@@ -19,10 +14,10 @@ namespace nksrv.LobbyServer.Msgs.Event
response.EventList.Add(new NetEventData()
{
Id = 81301,
EventDisableDate = 1000000000000000,
EventStartDate = 1,
EventEndDate = 1000000000000000,
EventVisibleDate = 0,
EventDisableDate = DateTime.Now.AddDays(20).Ticks,
EventStartDate = DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)).Ticks,
EventEndDate = DateTime.Now.AddDays(20).Ticks,
EventVisibleDate = DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)).Ticks,
EventSystemType = 34
});
@@ -32,10 +27,10 @@ namespace nksrv.LobbyServer.Msgs.Event
response.EventList.Add(new NetEventData()
{
Id = 20001,
EventDisableDate = 1000000000000000,
EventStartDate = 1,
EventEndDate = 1000000000000000,
EventVisibleDate = 0,
EventDisableDate = DateTime.Now.AddDays(20).Ticks,
EventStartDate = DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)).Ticks,
EventEndDate = DateTime.Now.AddDays(20).Ticks,
EventVisibleDate = DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)).Ticks,
EventSystemType = 1
});
@@ -102,7 +97,26 @@ namespace nksrv.LobbyServer.Msgs.Event
EventEndDate = DateTime.Now.AddDays(20).Ticks,
EventDisableDate = DateTime.Now.AddDays(20).Ticks,
});
await WriteDataAsync(response);
response.EventList.Add(new NetEventData()
{
Id = 40053,
EventSystemType = 5,
EventVisibleDate = DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)).Ticks,
EventStartDate = DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)).Ticks,
EventEndDate = DateTime.Now.AddDays(20).Ticks,
EventDisableDate = DateTime.Now.AddDays(20).Ticks,
});
//response.EventList.Add(new NetEventData()
//{
// Id = 40054,
// EventSystemType = 5,
// EventVisibleDate = DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)).Ticks,
// EventStartDate = DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)).Ticks,
// EventEndDate = DateTime.Now.AddDays(20).Ticks,
// EventDisableDate = DateTime.Now.AddDays(20).Ticks,
//});
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,29 @@
using EpinelPS.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EpinelPS.LobbyServer.Msgs.Event.StoryEvent
{
[PacketPath("/event/storydungeon/get")]
public class GetStoryDungeon : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqStoryDungeonEventData>();
var user = GetUser();
var response = new ResStoryDungeonEventData()
{
TeamData = new NetUserTeamData(),
RemainTicket = 10,
};
// TOOD
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.FavoriteItem
namespace EpinelPS.LobbyServer.Msgs.FavoriteItem
{
[PacketPath("/favoriteitem/library")]
public class GetFavoriteItemLibrary : LobbyMsgHandler

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.FavoriteItem
namespace EpinelPS.LobbyServer.Msgs.FavoriteItem
{
[PacketPath("/favoriteitem/list")]
public class ListFavoriteItem : LobbyMsgHandler

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.FavoriteItem
namespace EpinelPS.LobbyServer.Msgs.FavoriteItem
{
[PacketPath("/favoriteitem/quest/list")]
public class ListFavoriteItemQuests : LobbyMsgHandler

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Friend
namespace EpinelPS.LobbyServer.Msgs.Friend
{
[PacketPath("/friend/get")]
public class GetFriends : LobbyMsgHandler
@@ -16,7 +11,7 @@ namespace nksrv.LobbyServer.Msgs.Friend
var response = new ResGetFriendData();
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Gacha
namespace EpinelPS.LobbyServer.Msgs.Gacha
{
[PacketPath("/gacha/event/check")]
public class CheckGachaDailyEvent : LobbyMsgHandler
@@ -20,7 +15,7 @@ namespace nksrv.LobbyServer.Msgs.Gacha
response.FreeCount = 0;
response.EventData = new NetEventData() { Id = 1 };
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,46 @@
using EpinelPS.Database;
using EpinelPS.StaticInfo;
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Msgs.Gacha
{
[PacketPath("/gacha/execute")]
public class ExecGacha : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqExecuteGacha>();
var user = GetUser();
var response = new ResExecuteGacha();
// TODO: Pick random character that player does not have unless it supports limit break.
// TODO implement reward
response.Reward = new NetRewardData();
foreach (var c in GameData.Instance.GetAllCharacterTids())
{
if (!user.HasCharacter(c))
{
var id = user.GenerateUniqueCharacterId();
response.Gacha.Add(new NetGachaEntityData() { Corporation = 1, PieceCount = 1, CurrencyValue = 5, Sn = id, Tid = c, Type = 1 });
response.Characters.Add(new NetUserCharacterDefaultData() { CostumeId = 0, Csn = id, Grade = 0, Level = 1, Skill1Lv = 1, Skill2Lv = 1, Tid = c, UltiSkillLv = 1 });
user.Characters.Add(new Database.Character() { CostumeId = 0, Csn = id, Grade = 0, Level = 1, Skill1Lvl = 1, Skill2Lvl = 1, Tid = c, UltimateLevel = 1 });
}
else
{
// TODO add spare body
}
}
user.GachaTutorialPlayCount++;
JsonDb.Save();
await WriteDataAsync(response);
}
}
}

View File

@@ -1,7 +1,7 @@
using Google.Protobuf.WellKnownTypes;
using nksrv.Utils;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs
namespace EpinelPS.LobbyServer.Msgs
{
[PacketPath("/now")]
public class GetCurrentTime : LobbyMsgHandler
@@ -16,7 +16,7 @@ namespace nksrv.LobbyServer.Msgs
response.CheatShiftDuration = Duration.FromTimeSpan(TimeSpan.FromSeconds(0));
// todo: validate response with actual server
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,12 +1,6 @@
using Google.Protobuf.WellKnownTypes;
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs
namespace EpinelPS.LobbyServer.Msgs
{
[PacketPath("/Gacha/Get")]
public class GetGacha : LobbyMsgHandler
@@ -19,7 +13,7 @@ namespace nksrv.LobbyServer.Msgs
var response = new ResGetGachaData();
if (user.GachaTutorialPlayCount > 0)
response.Gacha.Add(new NetUserGachaData() { GachaType = 3, PlayCount = 1 });
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,23 @@
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Msgs.Intercept
{
[PacketPath("/intercept/get")]
public class GetInterceptData : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqGetInterceptData>();
var response = new ResGetInterceptData
{
NormalInterceptGroup = 1,
SpecialInterceptId = 1,
TicketCount = 5,
MaxTicketCount = 10
};
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,20 @@
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Msgs.Intercept
{
[PacketPath("/intercept/check")]
public class CheckClearInterceptToday : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqCheckClearInterceptToday>();
var response = new ResCheckClearInterceptToday
{
Clear = true
};
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,23 @@
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Msgs.Intercept
{
[PacketPath("/intercept/clear")]
public class ClearInterceptData : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqClearIntercept>();
var response = new ResClearIntercept
{
Intercept = 1,
InterceptId = 1,
TicketCount = 5,
MaxTicketCount = 10
};
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,17 @@
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Msgs.Intercept
{
[PacketPath("/intercept/enter")]
public class EnterInterceptData : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqEnterIntercept>();
var response = new ResEnterIntercept();
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,22 @@
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Msgs.Intercept
{
[PacketPath("/intercept/fastclear")]
public class FastClearInterceptData : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqFastClearIntercept>();
var response = new ResFastClearIntercept
{
TicketCount = 3,
MaxTicketCount = 10,
Damage = 2
};
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,7 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Database;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Inventory
namespace EpinelPS.LobbyServer.Msgs.Inventory
{
[PacketPath("/inventory/allclearequipment")]
public class ClearAllEquipment : LobbyMsgHandler
@@ -28,9 +24,10 @@ namespace nksrv.LobbyServer.Msgs.Inventory
response.Items.Add(NetUtils.ToNet(item));
}
}
JsonDb.Save();
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,12 +1,7 @@
using nksrv.LobbyServer.Msgs.User;
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Database;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Inventory
namespace EpinelPS.LobbyServer.Msgs.Inventory
{
[PacketPath("/inventory/clearequipment")]
public class ClearEquipment : LobbyMsgHandler
@@ -26,12 +21,13 @@ namespace nksrv.LobbyServer.Msgs.Inventory
item.Csn = 0;
response.Item = NetUtils.ToNet(item);
break;
}
}
JsonDb.Save();
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Inventory
namespace EpinelPS.LobbyServer.Msgs.Inventory
{
[PacketPath("/inventory/get")]
public class GetInventoryData : LobbyMsgHandler
@@ -18,12 +13,11 @@ namespace nksrv.LobbyServer.Msgs.Inventory
var response = new ResGetInventoryData();
foreach (var item in user.Items)
{
response.Items.Add(new NetUserItemData() { Count = item.Count, Tid = item.ItemType, Csn = item.Csn, Lv = item.Level, Exp = item.Exp, Corporation = item.Corp, Isn = item.Isn, Position = item.Position });
response.Items.Add(new NetUserItemData() { Count = item.Count, Tid = item.ItemType, Csn = item.Csn, Level = item.Level, Exp = item.Exp, Corporation = item.Corp, Isn = item.Isn, Position = item.Position });
}
// TODO: HarmonyCubes, RunAwakeningIsnList, UserRedeems
// TODO implement
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,7 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Database;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Inventory
namespace EpinelPS.LobbyServer.Msgs.Inventory
{
[PacketPath("/inventory/wearequipment")]
public class WearEquipment : LobbyMsgHandler
@@ -17,26 +13,32 @@ namespace nksrv.LobbyServer.Msgs.Inventory
var response = new ResWearEquipment();
var pos = NetUtils.GetItemPos(user, req.Isn);
// unequip old item
foreach (var item in user.Items.ToArray())
{
if (item.Csn == req.Csn && item.Position == pos)
{
item.Csn = 0;
}
}
foreach (var item in user.Items.ToArray())
{
if (item.Isn == req.Isn)
{
// update character id
item.Csn = req.Csn;
item.Position = NetUtils.GetItemPos(user, item.Isn);
}
}
foreach (var item in user.Items.ToArray())
{
if (item.Csn == req.Csn)
{
item.Position = pos;
response.Items.Add(NetUtils.ToNet(item));
break;
}
}
JsonDb.Save();
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,7 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Database;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Inventory
namespace EpinelPS.LobbyServer.Msgs.Inventory
{
[PacketPath("/inventory/wearequipmentlist")]
public class WearEquipmentList : LobbyMsgHandler
@@ -17,14 +13,27 @@ namespace nksrv.LobbyServer.Msgs.Inventory
var response = new ResWearEquipmentList();
foreach (var item in user.Items.ToArray())
// TODO optimize
foreach (var item2 in req.IsnList)
{
foreach (var item2 in req.IsnList)
var pos = NetUtils.GetItemPos(user, item2);
// unequip previous items
foreach (var item in user.Items.ToArray())
{
if (item.Position == pos && item.Csn == req.Csn)
{
item.Csn = 0;
item.Position = 0;
}
}
foreach (var item in user.Items.ToArray())
{
if (item2 == item.Isn)
{
item.Csn = req.Csn;
item.Position = NetUtils.GetItemPos(user, item.Isn);
item.Position = pos;
response.Items.Add(NetUtils.ToNet(item));
}
}

View File

@@ -0,0 +1,24 @@
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Msgs.Liberate
{
[PacketPath("/liberate/choosecharacter")]
public class ChooseCharacter : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqChooseLiberateCharacter>();
var user = GetUser();
var response = new ResChooseLiberateCharacter();
// TODO
response.Data = new NetLiberateData() { CharacterId = req.CharacterId };
response.Data.MissionData.Add(new NetLiberateMissionData() { MissionState = LiberateMissionState.LiberateMissionStateRunning, Id = 1 });
response.Data.MissionData.Add(new NetLiberateMissionData() { MissionState = LiberateMissionState.LiberateMissionStateRunning, Id = 2 });
response.Data.MissionData.Add(new NetLiberateMissionData() { MissionState = LiberateMissionState.LiberateMissionStateRunning, Id = 3 });
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Liberate
namespace EpinelPS.LobbyServer.Msgs.Liberate
{
[PacketPath("/liberate/get")]
public class GetLiberateData : LobbyMsgHandler

View File

@@ -0,0 +1,20 @@
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Msgs.Liberate
{
[PacketPath("/liberate/getprogresslist")]
public class GetProgressList : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqGetLiberateProgressList>();
var user = GetUser();
var response = new ResGetLiberateProgressList();
// TODO
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Lostsector
namespace EpinelPS.LobbyServer.Msgs.Lostsector
{
[PacketPath("/lostsector/get")]
public class GetLostSectorData : LobbyMsgHandler

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Messenger
namespace EpinelPS.LobbyServer.Msgs.Messenger
{
[PacketPath("/messenger/daily/pick")]
public class GetDailyMessage : LobbyMsgHandler
@@ -17,7 +12,7 @@ namespace nksrv.LobbyServer.Msgs.Messenger
// TODO: save these things
var response = new ResPickTodayDailyMessage();
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Messenger
namespace EpinelPS.LobbyServer.Msgs.Messenger
{
[PacketPath("/messenger/get")]
public class GetMessages : LobbyMsgHandler
@@ -17,7 +12,7 @@ namespace nksrv.LobbyServer.Msgs.Messenger
// TODO: save these things
var response = new ResGetMessages();
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Messenger
namespace EpinelPS.LobbyServer.Msgs.Messenger
{
[PacketPath("/messenger/picked/get")]
public class GetPickedMessage : LobbyMsgHandler
@@ -17,7 +12,7 @@ namespace nksrv.LobbyServer.Msgs.Messenger
// TODO: get proper response
var response = new ResGetPickedMessageList();
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,18 @@
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Msgs.Messenger
{
[PacketPath("/messenger/random/pick")]
public class GetRandomPick : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
var req = await ReadData<ReqPickTodayRandomMessage>();
// TODO: get proper response
var response = new ResPickTodayRandomMessage();
await WriteDataAsync(response);
}
}
}

View File

@@ -1,6 +1,6 @@
using nksrv.Utils;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Messenger
namespace EpinelPS.LobbyServer.Msgs.Messenger
{
[PacketPath("/messenger/proceed")]
public class ProceedMsg : LobbyMsgHandler
@@ -12,7 +12,7 @@ namespace nksrv.LobbyServer.Msgs.Messenger
// TODO: save these things
var response = new ResProceedMessage();
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Minigame.Dave
namespace EpinelPS.LobbyServer.Msgs.Minigame.Dave
{
[PacketPath("/event/minigame/dave/getalldavetrigger")]
public class GetAllDaveTrigger : LobbyMsgHandler
@@ -16,7 +11,7 @@ namespace nksrv.LobbyServer.Msgs.Minigame.Dave
var response = new ResGetAllMiniGameDaveTriggers();
// TODO
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Minigame
namespace EpinelPS.LobbyServer.Msgs.Minigame
{
[PacketPath("/minigame/nksv2/get")]
public class GetNksv2Minigame : LobbyMsgHandler
@@ -16,7 +11,7 @@ namespace nksrv.LobbyServer.Msgs.Minigame
var response = new ResGetMiniGameNKSV2Data();
// TODO
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Minigame.IslandAdventure
namespace EpinelPS.LobbyServer.Msgs.Minigame.IslandAdventure
{
[PacketPath("/event/minigame/islandadventure/get/fishing/stepupreward")]
public class GetFishingStepUpRewardStatus : LobbyMsgHandler
@@ -16,7 +11,7 @@ namespace nksrv.LobbyServer.Msgs.Minigame.IslandAdventure
var response = new ResGetIslandAdventureFishingStepUpRewardStatus();
// TODO
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Minigame.IslandAdventure
namespace EpinelPS.LobbyServer.Msgs.Minigame.IslandAdventure
{
[PacketPath("/event/minigame/islandadventure/get/photo/stepupreward")]
public class GetPhotoStepUpRewardStatus : LobbyMsgHandler
@@ -16,7 +11,7 @@ namespace nksrv.LobbyServer.Msgs.Minigame.IslandAdventure
var response = new ResGetIslandAdventurePhotoStepUpRewardStatus();
// TODO
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Minigame.IslandAdventure
namespace EpinelPS.LobbyServer.Msgs.Minigame.IslandAdventure
{
[PacketPath("/event/minigame/islandadventure/list/mission")]
public class ListMission : LobbyMsgHandler
@@ -16,7 +11,7 @@ namespace nksrv.LobbyServer.Msgs.Minigame.IslandAdventure
var response = new ResGetIslandAdventureMissionProgress();
// TODO
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,11 +1,6 @@
using nksrv.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Minigame.PlaySoda
namespace EpinelPS.LobbyServer.Msgs.Minigame.PlaySoda
{
[PacketPath("/event/minigame/playsoda/challenge/getinfo")]
public class GetChallengeInfo : LobbyMsgHandler
@@ -16,7 +11,7 @@ namespace nksrv.LobbyServer.Msgs.Minigame.PlaySoda
var response = new ResGetPlaySodaChallengeModeInfo();
// TODO
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

View File

@@ -1,6 +1,6 @@
using nksrv.Utils;
using EpinelPS.Utils;
namespace nksrv.LobbyServer.Msgs.Misc
namespace EpinelPS.LobbyServer.Msgs.Misc
{
[PacketPath("/enterlobbyping")]
public class EnterLobbyPing : LobbyMsgHandler
@@ -11,7 +11,7 @@ namespace nksrv.LobbyServer.Msgs.Misc
var response = new ResEnterLobbyPing();
await WriteDataAsync(response);
await WriteDataAsync(response);
}
}
}

Some files were not shown because too many files have changed in this diff Show More