mirror of
https://github.com/EpinelPS/EpinelPS.git
synced 2025-12-12 15:04:36 +01:00
Initial commit
This commit is contained in:
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
135
.gitignore
vendored
Normal file
135
.gitignore
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
.vs/
|
||||
|
||||
# Build results
|
||||
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.log
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.Publish.xml
|
||||
*.pubxml
|
||||
*.azurePubxml
|
||||
|
||||
# NuGet Packages Directory
|
||||
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
|
||||
packages/
|
||||
## TODO: If the tool you use requires repositories.config, also uncomment the next line
|
||||
!packages/repositories.config
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
![Ss]tyle[Cc]op.targets
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.[Pp]ublish.xml
|
||||
|
||||
*.publishsettings
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
App_Data/*.mdf
|
||||
App_Data/*.ldf
|
||||
|
||||
# =========================
|
||||
# Windows detritus
|
||||
# =========================
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Mac desktop service store files
|
||||
.DS_Store
|
||||
|
||||
_NCrunch*
|
||||
10
DataFixupUtil/DataFixupUtil.csproj
Normal file
10
DataFixupUtil/DataFixupUtil.csproj
Normal file
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
57
DataFixupUtil/Program.cs
Normal file
57
DataFixupUtil/Program.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
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 != 0 && contents[0] == 31)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Mikhail Thompson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
37
README.md
Normal file
37
README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# nikke-server
|
||||
Private/local server for Nikke.
|
||||
|
||||
## Usage
|
||||
First, build and run nksrv project.
|
||||
|
||||
After that, add the following to your C:\Windows\System32\hosts or /etc/hosts file to use the local server:
|
||||
|
||||
```
|
||||
127.0.0.1 cloud.nikke-kr.com
|
||||
127.0.0.1 global-lobby.nikke-kr.com
|
||||
127.0.0.1 aws-na-dr.intlgame.com
|
||||
127.0.0.1 sg-vas.intlgame.com
|
||||
127.0.0.1 aws-na.intlgame.com
|
||||
127.0.0.1 common-web.intlgame.com
|
||||
127.0.0.1 li-sg.intlgame.com
|
||||
127.0.0.1 data-aws-na.intlgame.com
|
||||
255.255.221.21 sentry.io
|
||||
```
|
||||
|
||||
As Nikke encrypts packet data, you also need to replace C:\NIKKE\NIKKE\game\nikke_Data\Plugins\x86_64\sodium.dll. A patched version of this file can be obtained by contacting MishaProductions on Discord.
|
||||
|
||||
After doing the following steps, you can register an account in the launcher (enter anything into email verification code section), and play like normal.
|
||||
|
||||
## Progress
|
||||
Currently, stage data is not saved, only story completion is saved. There are also no rewards currently because those are given server side.
|
||||
|
||||
|
||||
## Contributing
|
||||
You can help by providing information about what rewards are given when a stage is completed, etc.
|
||||
|
||||
Server code structure:
|
||||
|
||||
nksrv/LobbyServer: Handles save data.
|
||||
nksrv/IntlServer: Provides Launcher APIs and authentication
|
||||
nksrv/Protos: Google protobuf definition files
|
||||
DataFixupUtil: Utility to parse packets from server/client
|
||||
51
nksrv.sln
Normal file
51
nksrv.sln
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
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
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5C24A07E-9B8D-4625-BF91-0E9CCBEF5CB0}.Release|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}.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
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {F779F2DC-B207-4091-83B8-0EA250461DCE}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
19
nksrv/IntlServer/AutoLoginEndpoint.cs
Normal file
19
nksrv/IntlServer/AutoLoginEndpoint.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.IntlServer
|
||||
{
|
||||
internal class AutoLoginEndpoint : IntlMsgHandler
|
||||
{
|
||||
public override bool RequiresAuth => true;
|
||||
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
WriteJsonString("{\"del_account_info\":\"{\\\"ret\\\":0,\\\"msg\\\":\\\"\\\",\\\"status\\\":0,\\\"created_at\\\":\\\"0\\\",\\\"target_destroy_at\\\":\\\"0\\\",\\\"destroyed_at\\\":\\\"0\\\",\\\"err_code\\\":0,\\\"seq\\\":\\\"" + Seq + "\\\"}\",\"del_account_status\":0,\"del_li_account_status\":0,\"extra_json\":{\"del_li_account_info\":\"{\\\"ret\\\":0,\\\"msg\\\":\\\"\\\",\\\"status\\\":0,\\\"created_at\\\":\\\"0\\\",\\\"target_destroy_at\\\":\\\"0\\\",\\\"destroyed_at\\\":\\\"0\\\",\\\"err_code\\\":0,\\\"seq\\\":\\\"1719078088-4128751114-032279-4229229759\\\"}\",\"get_status_msg\":\"success\",\"get_status_ret\":0,\"get_status_rsp\":{\"adult_age\":14,\"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\":\"724\",\"ret\":0,\"ts\":\""+ DateTimeOffset.UtcNow.ToUnixTimeSeconds()
|
||||
+ "\"},\"need_notify_msg\":\"success\",\"need_notify_ret\":0,\"need_notify_rsp\":{\"has_bind_li\":true,\"is_receive_email\":1,\"is_receive_email_in_night\":0,\"user_agreed_game_dma\":\"2\",\"user_agreed_game_pp\":\"1\",\"user_agreed_game_tos\":\"1\",\"user_agreed_li_dt\":\"\",\"user_agreed_li_pp\":\"1\",\"user_agreed_li_tos\":\"\"}},\"msg\":\"success\",\"ret\":0,\"seq\":\"" + Seq + "\"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
25
nksrv/IntlServer/CodeStatusEndpoint.cs
Normal file
25
nksrv/IntlServer/CodeStatusEndpoint.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
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
|
||||
{
|
||||
public class CodeStatusEndpoint : IntlMsgHandler
|
||||
{
|
||||
public override bool RequiresAuth => false;
|
||||
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
|
||||
// pretend that any code is valid
|
||||
|
||||
WriteJsonString("{\"expire_time\":759,\"msg\":\"Success\",\"ret\":0,\"seq\":\""+Seq+"\"}");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
19
nksrv/IntlServer/GetNoticeContent.cs
Normal file
19
nksrv/IntlServer/GetNoticeContent.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Google.Api;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.IntlServer
|
||||
{
|
||||
public class GetNoticeContent : IntlMsgHandler
|
||||
{
|
||||
public override bool RequiresAuth => false;
|
||||
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
WriteJsonString("{\r\n \"msg\": \"success\",\r\n \"notice_list\": [\r\n {\r\n \"app_id\": \"3001001\",\r\n \"app_notice_id\": \"post-6rpvwgrdx1b\",\r\n \"area_list\": \"[\\\"81\\\",\\\"82\\\",\\\"83\\\",\\\"84\\\",\\\"85\\\"]\",\r\n \"content_list\": [\r\n {\r\n \"app_content_id\": \"post-9ilpu79xxzp\",\r\n \"content\": \"This isn't working\",\r\n \"extra_data\": \"{}\",\r\n \"id\": 48706,\r\n \"lang_type\": \"en\",\r\n \"picture_list\": [\r\n {\r\n \"extra_data\": \"{\\\"id\\\":\\\"TitleImage\\\"}\",\r\n \"hash\": \"44a99a61152b5b80a0466ff9f0cee2bc\",\r\n \"redirect_url\": \"\",\r\n \"url\": \"pnt-console-cdn.playernetwork.intlgame.com/prod/29080/notice/022681b1121a40259a575fbe587651b4.jpg\"\r\n }\r\n ],\r\n \"title\": \"New Character\",\r\n \"update_time\": 1717637493\r\n }\r\n ],\r\n \"end_time\": 1719431999,\r\n \"extra_data\": \"{\\\"NoticeType\\\":\\\"Event\\\",\\\"Order\\\":\\\"11\\\",\\\"extra_reserved\\\":\\\"{\\\\\\\"Author\\\\\\\":\\\\\\\"\\\\\\\",\\\\\\\"Category\\\\\\\":\\\\\\\"\\\\\\\",\\\\\\\"CreateType\\\\\\\":\\\\\\\"4\\\\\\\",\\\\\\\"IsOpenService\\\\\\\":\\\\\\\"0\\\\\\\",\\\\\\\"IsToping\\\\\\\":false,\\\\\\\"Keyword\\\\\\\":\\\\\\\"\\\\\\\",\\\\\\\"Sort\\\\\\\":\\\\\\\"\\\\\\\",\\\\\\\"TopEnd\\\\\\\":\\\\\\\"2000-01-01 00:00:01\\\\\\\",\\\\\\\"TopStart\\\\\\\":\\\\\\\"2000-01-01 00:00:01\\\\\\\"}\\\"}\",\r\n \"id\": 7560,\r\n \"picture_list\": [],\r\n \"start_time\": 1717617599,\r\n \"status\": 1,\r\n \"update_time\": 1717637494\r\n }\r\n ],\r\n \"ret\": 0,\r\n \"seq\": \"" + Seq + "\"\r\n}");
|
||||
}
|
||||
}
|
||||
}
|
||||
57
nksrv/IntlServer/IntlAccountRedirect.cs
Normal file
57
nksrv/IntlServer/IntlAccountRedirect.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// redirect for /account endponts
|
||||
/// </summary>
|
||||
public class IntlAccountRedirect : IntlMsgHandler
|
||||
{
|
||||
public override bool RequiresAuth => false;
|
||||
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
Console.WriteLine("li-sg redirect in: " + Content);
|
||||
HttpClientHandler handler = new HttpClientHandler()
|
||||
{
|
||||
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
|
||||
ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true,
|
||||
AllowAutoRedirect = true // from gameassembly dll
|
||||
};
|
||||
|
||||
HttpClient client = new HttpClient(new LoggingHandler(handler));
|
||||
client.DefaultRequestHeaders
|
||||
.Accept
|
||||
.Add(new MediaTypeWithQualityHeaderValue("*/*"));//ACCEPT header
|
||||
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("identity"));
|
||||
client.DefaultRequestHeaders.Connection.Add("Keep-Alive");
|
||||
|
||||
|
||||
// client.DefaultRequestHeaders.Remove("User-agent");
|
||||
|
||||
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://49.51.129.135" + ctx.Request.RawUrl);
|
||||
request.Version = HttpVersion.Version11;
|
||||
request.Headers.TryAddWithoutValidation("Host", "li-sg.intlgame.com");
|
||||
|
||||
var systemContent = new StringContent(Content);
|
||||
systemContent.Headers.Remove("Content-Type");
|
||||
systemContent.Headers.Add("Content-Type", "application/json");
|
||||
systemContent.Headers.Add("Content-Length", ctx.Request.ContentLength64.ToString());
|
||||
|
||||
request.Content = systemContent;// CONTENT-TYPE header
|
||||
|
||||
|
||||
var result = await client.SendAsync(request);
|
||||
var s = await result.Content.ReadAsStringAsync();
|
||||
WriteJsonString(s);
|
||||
Console.WriteLine("li-sg redirect out: " + s);
|
||||
}
|
||||
}
|
||||
}
|
||||
77
nksrv/IntlServer/IntlAccountRegisterEndpoint.cs
Normal file
77
nksrv/IntlServer/IntlAccountRegisterEndpoint.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
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;
|
||||
|
||||
namespace nksrv.IntlServer
|
||||
{
|
||||
public class IntlAccountRegisterEndpoint : IntlMsgHandler
|
||||
{
|
||||
public override bool RequiresAuth => false;
|
||||
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
|
||||
RegisterEPReq? ep = JsonConvert.DeserializeObject<RegisterEPReq>(Content);
|
||||
if (ep != null)
|
||||
{
|
||||
string? seg = ctx.GetRequestQueryData().Get("seq");
|
||||
|
||||
// check if the account already exists
|
||||
foreach (var item in JsonDb.Instance.Users)
|
||||
{
|
||||
if (item.Username == ep.account)
|
||||
{
|
||||
WriteJsonString("{\"msg\":\"send code failed; invalid account\",\"ret\":2112,\"seq\":\"" + seg + "\"}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var uid = (ulong)new Random().Next(1, int.MaxValue);
|
||||
|
||||
// Check if we havent generated a UID that exists
|
||||
foreach (var item in JsonDb.Instance.Users)
|
||||
{
|
||||
if (item.ID == uid)
|
||||
{
|
||||
uid -= (ulong)new Random().Next(1, 1221);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
var tok = IntlHandler.CreateLauncherTokenForUser(user);
|
||||
WriteJsonString("{\"expire\":" + tok.ExpirationTime + ",\"is_login\":false,\"msg\":\"Success\",\"register_time\":" + user.RegisterTime + ",\"ret\":0,\"seq\":\"" + seg + "\",\"token\":\"" + tok.Token + "\",\"uid\":\"" + user.ID + "\"}");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new HttpException(HttpStatusCode.BadRequest);
|
||||
}
|
||||
}
|
||||
|
||||
public class RegisterEPReq
|
||||
{
|
||||
public DeviceInfo device_info { get; set; }
|
||||
public string verify_code { get; set; }
|
||||
public string account { get; set; }
|
||||
public int account_type { get; set; }
|
||||
public string phone_area_code { get; set; }
|
||||
public string password { get; set; }
|
||||
public string user_name { get; set; }
|
||||
public string birthday { get; set; }
|
||||
public string region { get; set; }
|
||||
public string user_lang_type { get; set; }
|
||||
public string extra_json { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
57
nksrv/IntlServer/IntlAwsNaRedirect.cs
Normal file
57
nksrv/IntlServer/IntlAwsNaRedirect.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
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 EmbedIO;
|
||||
|
||||
namespace nksrv.IntlServer
|
||||
{
|
||||
/// <summary>
|
||||
/// redirector for /v2/ endpoint
|
||||
/// </summary>
|
||||
internal class IntlAwsNaRedirect : IntlMsgHandler
|
||||
{
|
||||
public override bool RequiresAuth => false;
|
||||
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
Console.WriteLine("AWS NA redirect in: " + Content);
|
||||
HttpClientHandler handler = new HttpClientHandler()
|
||||
{
|
||||
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
|
||||
ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true,
|
||||
AllowAutoRedirect = true // from gameassembly dll
|
||||
};
|
||||
|
||||
HttpClient client = new HttpClient(new LoggingHandler(handler));
|
||||
client.DefaultRequestHeaders
|
||||
.Accept
|
||||
.Add(new MediaTypeWithQualityHeaderValue("*/*"));//ACCEPT header
|
||||
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("identity"));
|
||||
client.DefaultRequestHeaders.Connection.Add("Keep-Alive");
|
||||
|
||||
|
||||
// client.DefaultRequestHeaders.Remove("User-agent");
|
||||
|
||||
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://50.18.221.30" + ctx.Request.RawUrl);
|
||||
request.Version = HttpVersion.Version11;
|
||||
request.Headers.TryAddWithoutValidation("Host", "aws-na.intlgame.com");
|
||||
|
||||
var systemContent = new StringContent(Content);
|
||||
systemContent.Headers.Remove("Content-Type");
|
||||
systemContent.Headers.Add("Content-Type", "application/json;charset=UTF-8");
|
||||
systemContent.Headers.Add("Content-Length", ctx.Request.ContentLength64.ToString());
|
||||
|
||||
request.Content = systemContent;// CONTENT-TYPE header
|
||||
|
||||
|
||||
var result = await client.SendAsync(request);
|
||||
var s = await result.Content.ReadAsStringAsync();
|
||||
Console.WriteLine("Redirect out: " + s);
|
||||
WriteJsonString(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
nksrv/IntlServer/IntlGetAccountInfo.cs
Normal file
21
nksrv/IntlServer/IntlGetAccountInfo.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.IntlServer
|
||||
{
|
||||
internal class IntlGetAccountInfo : IntlMsgHandler
|
||||
{
|
||||
public override bool RequiresAuth => true;
|
||||
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
if (User == null || UsedToken == null)
|
||||
throw new Exception("no user"); // should never happen
|
||||
|
||||
WriteJsonString("{\"account_type\":1,\"birthday\":\"1970-01\",\"email\":\"" + User.Username + "\",\"expire\":" + UsedToken.ExpirationTime + ",\"is_receive_email\":1,\"is_receive_email_in_night\":0,\"is_receive_video\":-1,\"lang_type\":\"en\",\"msg\":\"Success\",\"nick_name\":\"\",\"phone\":\"\",\"phone_area_code\":\"\",\"privacy_policy\":\"1\",\"privacy_update_time\":1717783097,\"region\":\"724\",\"ret\":0,\"seq\":\"" + Seq + "\",\"terms_of_service\":\"\",\"terms_update_time\":0,\"uid\":\"" + User.ID + "\",\"user_agreed_dt\":\"\",\"user_agreed_pp\":\"1\",\"user_agreed_tos\":\"\",\"user_name\":\"" + User.PlayerName + "\",\"username_pass_verify\":0}");
|
||||
}
|
||||
}
|
||||
}
|
||||
18
nksrv/IntlServer/IntlGetProfileBindInfo.cs
Normal file
18
nksrv/IntlServer/IntlGetProfileBindInfo.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
namespace nksrv.IntlServer
|
||||
{
|
||||
internal class IntlGetProfileBindInfo : IntlMsgHandler
|
||||
{
|
||||
public override bool RequiresAuth => true;
|
||||
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
if (User == null)
|
||||
throw new Exception("no user"); // should never happen
|
||||
|
||||
|
||||
// TODO: last login time, but is it needed?
|
||||
WriteJsonString("{\"bind_list\":[{\"bind_ts\":1717783095,\"channel_info\":{\"birthday\":\"1970-01\",\"email\":\"" + User.Username + "\",\"is_receive_email\":1,\"lang_type\":\"en\",\"last_login_time\":171000000,\"nick_name\":\"\",\"phone\":\"\",\"phone_area_code\":\"\",\"region\":\"724\",\"register_account\":\"" + User.Username + "\",\"register_account_type\":1,\"register_time\":" + User.RegisterTime + ",\"seq\":\"" + Seq + "\",\"uid\":\"2752409592679849\",\"user_name\":\"" + User.PlayerName + "\",\"username_pass_verify\":0},\"channelid\":131,\"email\":\"" + User.Username + "\",\"history_scopes\":[],\"is_primary\":1,\"picture_url\":\"\",\"user_name\":\"" + User.PlayerName + "\"}],\"create_ts\":" + User.RegisterTime + ",\"last_login_ts\":171000000,\"msg\":\"success\",\"ret\":0,\"seq\":\"" + Seq + "\"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
21
nksrv/IntlServer/IntlGetProfileInfo.cs
Normal file
21
nksrv/IntlServer/IntlGetProfileInfo.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.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
|
||||
|
||||
WriteJsonString("{\"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 + "\"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
85
nksrv/IntlServer/IntlHandler.cs
Normal file
85
nksrv/IntlServer/IntlHandler.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
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;
|
||||
|
||||
namespace nksrv.IntlServer
|
||||
{
|
||||
public static class IntlHandler
|
||||
{
|
||||
public static Dictionary<string, IntlMsgHandler> Handlers = new Dictionary<string, IntlMsgHandler>()
|
||||
{
|
||||
{"/login", new IntlLogin1Endpoint() }, // /account/login
|
||||
{"/auth/login", new IntlLogin2Endpoint() }, // /v2/auth/login
|
||||
{"/sendcode", new SendCodeEndpoint() }, // /account/sendcode
|
||||
{"/codestatus", new CodeStatusEndpoint() }, //
|
||||
{"/register", new IntlAccountRegisterEndpoint() }, // /account/register
|
||||
{"/profile/query_account_info", new IntlQueryAccountInfo() }, // /account/register
|
||||
{"/conf/get_conf", new IntlReturnJsonHandler(GetConfResp) }, // /v2/conf/get_conf
|
||||
{"/minorcer/get_status", new IntlReturnJsonHandler(MinorcerResp) }, // /v2/minorcer/get_status
|
||||
{"/minorcer/set_all_status", new IntlMinocerSetStatus() }, // /v2/minorcer/set_all_status
|
||||
{"/profile/set_protocol", new IntlReturnJsonHandler(SetProtocolResp) },
|
||||
{"/profile/userinfo", new IntlGetProfileInfo() },
|
||||
{"/getuserinfo", new IntlGetAccountInfo() },
|
||||
{"/lbs/ipregion", new IntlReturnJsonHandler(IpRegionResp) },
|
||||
{"/profile/get_bind_info", new IntlGetProfileBindInfo() },
|
||||
{"/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
|
||||
};
|
||||
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\"}";
|
||||
public const string SetProtocolResp = "{\"msg\":\"success\",\"ret\":0,\"seq\":\"((SEGID))\"}";
|
||||
public const string IpRegionResp = "{\"alpha2\":\"GR\",\"extra_json\":{\"certificate_type_map\":{}},\"msg\":\"success\",\"region\":\"300\",\"ret\":0,\"seq\":\"((SEGID))\",\"timestamp\":324234322}";
|
||||
public const string AquireConfigResp = "{\"ret\":23111202,\"msg\":\"no matched config error( [match logic]no match )\",\"rule_id\":\"\",\"resource_list\":\"\",\"sdk_enable\":0,\"sdk_debug_enable\":0,\"report_log_enable\":0,\"log_level\":0,\"inner_seq\":\"((SEGID))\",\"ab_test\":{\"id\":\"\",\"group\":\"\"},\"seq\":\"((SEGID))\"}";
|
||||
public static async Task Handle(IHttpContext context)
|
||||
{
|
||||
|
||||
//if (context.Request.RawUrl.StartsWith("/v2/"))
|
||||
//{
|
||||
// await new IntlAwsNaRedirect().HandleAsync(context);
|
||||
//}
|
||||
//else if (context.Request.RawUrl.StartsWith("/account/"))
|
||||
//{
|
||||
// await new IntlAccountRedirect().HandleAsync(context);
|
||||
//}
|
||||
//return;
|
||||
IntlMsgHandler? handler = null;
|
||||
foreach (var item in Handlers)
|
||||
{
|
||||
if (context.RequestedPath == item.Key)
|
||||
{
|
||||
handler = item.Value;
|
||||
}
|
||||
}
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
context.Response.StatusCode = 404;
|
||||
// Logger.Error("Launcher Endpoint - Not Found: " + context.RequestedPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
await handler.HandleAsync(context);
|
||||
}
|
||||
}
|
||||
|
||||
public static AccessToken CreateLauncherTokenForUser(User user)
|
||||
{
|
||||
AccessToken token = new() { ExpirationTime = DateTimeOffset.UtcNow.AddDays(1).ToUnixTimeSeconds() };
|
||||
token.Token = Rng.RandomString(64);
|
||||
token.UserID = user.ID;
|
||||
JsonDb.Instance.LauncherAccessTokens.Add(token);
|
||||
JsonDb.Save();
|
||||
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
55
nksrv/IntlServer/IntlLogin1Endpoint.cs
Normal file
55
nksrv/IntlServer/IntlLogin1Endpoint.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using EmbedIO;
|
||||
using Newtonsoft.Json;
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static nksrv.IntlServer.IntlLogin2Endpoint;
|
||||
|
||||
namespace nksrv.IntlServer
|
||||
{
|
||||
public class IntlLogin1Endpoint : IntlMsgHandler
|
||||
{
|
||||
public override bool RequiresAuth => false;
|
||||
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
LoginEndpoint2Req? ep = JsonConvert.DeserializeObject<LoginEndpoint2Req>(Content);
|
||||
if (ep != null)
|
||||
{
|
||||
foreach (var item in JsonDb.Instance.Users)
|
||||
{
|
||||
if (item.Username == ep.account && item.Password == ep.password)
|
||||
{
|
||||
var tok = IntlHandler.CreateLauncherTokenForUser(item);
|
||||
WriteJsonString("{\"expire\":" + tok.ExpirationTime + ",\"is_login\":true,\"msg\":\"Success\",\"register_time\":" + item.RegisterTime + ",\"ret\":0,\"seq\":\"" + Seq + "\",\"token\":\"" + tok.Token + "\",\"uid\":\"" + item.ID + "\"}");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string? seg = ctx.GetRequestQueryData().Get("seq");
|
||||
|
||||
WriteJsonString("{\"msg\":\"the account does not exists!\",\"ret\":2001,\"seq\":\"" + seg + "\"}");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new HttpException(HttpStatusCode.BadRequest);
|
||||
}
|
||||
}
|
||||
|
||||
public class LoginEndpoint2Req
|
||||
{
|
||||
public DeviceInfo device_info { get; set; }
|
||||
public string extra_json { get; set; }
|
||||
public string account { get; set; }
|
||||
public int account_type { get; set; }
|
||||
public string password { get; set; }
|
||||
public string phone_area_code { get; set; }
|
||||
public int support_captcha { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
92
nksrv/IntlServer/IntlLogin2Endpoint.cs
Normal file
92
nksrv/IntlServer/IntlLogin2Endpoint.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using EmbedIO;
|
||||
using Newtonsoft.Json;
|
||||
using nksrv.Utils;
|
||||
using System.Net;
|
||||
|
||||
namespace nksrv.IntlServer
|
||||
{
|
||||
/// <summary>
|
||||
/// This handles the login endpoint.
|
||||
/// </summary>
|
||||
public class IntlLogin2Endpoint : IntlMsgHandler
|
||||
{
|
||||
public override bool RequiresAuth => false;
|
||||
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
LoginEndpoint1Req? ep = JsonConvert.DeserializeObject<LoginEndpoint1Req>(Content);
|
||||
if (ep != null)
|
||||
{
|
||||
foreach (var tok in JsonDb.Instance.LauncherAccessTokens)
|
||||
{
|
||||
if (tok.Token == ep.channel_info.account_token)
|
||||
{
|
||||
var user = JsonDb.Instance.Users.Find(x => x.ID == tok.UserID);
|
||||
if (user != null)
|
||||
{
|
||||
|
||||
// todo: they use another token here, but we will reuse the same one.
|
||||
|
||||
WriteJsonString("{\"birthday\":\"1970-01\",\"channel_info\":{\"account\":\"" + user.Username + "\",\"account_plat_type\":131,\"account_token\":\"" + ep.channel_info.account_token + "\",\"account_type\":1,\"account_uid\":\"" + user.ID + "\",\"expire_ts\":1721667004,\"is_login\":true,\"lang_type\":\"en\",\"phone_area_code\":\"\",\"token\":\"" + ep.channel_info.account_token + "\"},\"del_account_info\":\"{\\\"ret\\\":0,\\\"msg\\\":\\\"\\\",\\\"status\\\":0,\\\"created_at\\\":\\\"0\\\",\\\"target_destroy_at\\\":\\\"0\\\",\\\"destroyed_at\\\":\\\"0\\\",\\\"err_code\\\":0,\\\"seq\\\":\\\"1719075066-0339089836-025921-1161847390\\\"}\",\"del_account_status\":0,\"del_li_account_status\":0,\"email\":\"" + user.Username + "\",\"extra_json\":{\"del_li_account_info\":\"{\\\"ret\\\":0,\\\"msg\\\":\\\"\\\",\\\"status\\\":0,\\\"created_at\\\":\\\"0\\\",\\\"target_destroy_at\\\":\\\"0\\\",\\\"destroyed_at\\\":\\\"0\\\",\\\"err_code\\\":0,\\\"seq\\\":\\\"1719075065-4128751114-032271-2064970828\\\"}\",\"get_status_rsp\":{\"adult_age\":14,\"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\":\"724\",\"ret\":0,\"ts\":\"1719075065\"},\"need_notify_rsp\":{\"game_sacc_openid\":\"\",\"game_sacc_uid\":\"\",\"has_game_sacc_openid\":false,\"has_game_sacc_uid\":false,\"has_li_openid\":true,\"has_li_uid\":true,\"is_receive_email\":1,\"is_receive_email_in_night\":0,\"li_openid\":\"43599204002070510000\",\"li_uid\":\"2752409592679849\",\"need_notify\":false,\"user_agreed_game_dma\":\"2\",\"user_agreed_game_pp\":\"1\",\"user_agreed_game_tos\":\"1\",\"user_agreed_li_dt\":\"\",\"user_agreed_li_pp\":\"1\",\"user_agreed_li_tos\":\"\"}},\"first_login\":0,\"gender\":0,\"msg\":\"success\",\"need_name_auth\":false,\"openid\":\"43599204002070510000\",\"pf\":\"LevelInfinite_LevelInfinite-Windows-windows-Windows-LevelInfinite-09af79d65d6e4fdf2d2569f0d365739d-43599204002070510000\",\"pf_key\":\"abc\",\"picture_url\":\"\",\"reg_channel_dis\":\"Windows\",\"ret\":0,\"seq\":\"29080-2d28ea26-d71f-4822-9118-0156f1e2dba4-1719075060-99\",\"token\":\"" + tok.Token + "\",\"token_expire_time\":" + tok.ExpirationTime + ",\"uid\":\"" + user.ID + "\",\"user_name\":\"" + user.PlayerName + "\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
throw new HttpException(HttpStatusCode.MultipleChoices);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new HttpException(HttpStatusCode.BadRequest);
|
||||
}
|
||||
}
|
||||
|
||||
public class ChannelInfo
|
||||
{
|
||||
public string openid { get; set; }
|
||||
public string token { get; set; }
|
||||
public int account_type { get; set; }
|
||||
public string account { get; set; }
|
||||
public string phone_area_code { get; set; }
|
||||
public int account_plat_type { get; set; }
|
||||
public string lang_type { get; set; }
|
||||
public bool is_login { get; set; }
|
||||
public string account_uid { get; set; }
|
||||
public string account_token { get; set; }
|
||||
}
|
||||
|
||||
public class DeviceInfo
|
||||
{
|
||||
public string guest_id { get; set; }
|
||||
public string lang_type { get; set; }
|
||||
public string root_info { get; set; }
|
||||
public string app_version { get; set; }
|
||||
public string screen_dpi { get; set; }
|
||||
public int screen_height { get; set; }
|
||||
public int screen_width { get; set; }
|
||||
public string device_brand { get; set; }
|
||||
public string device_model { get; set; }
|
||||
public int network_type { get; set; }
|
||||
public int ram_total { get; set; }
|
||||
public int rom_total { get; set; }
|
||||
public string cpu_name { get; set; }
|
||||
public string client_region { get; set; }
|
||||
public string vm_type { get; set; }
|
||||
public string xwid { get; set; }
|
||||
public string new_xwid { get; set; }
|
||||
public string xwid_flag { get; set; }
|
||||
public string cpu_arch { get; set; }
|
||||
}
|
||||
|
||||
public class LoginEndpoint1Req
|
||||
{
|
||||
public ChannelInfo channel_info { get; set; }
|
||||
public DeviceInfo device_info { get; set; }
|
||||
public string channel_dis { get; set; }
|
||||
public string login_extra_info { get; set; }
|
||||
public string lang_type { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
20
nksrv/IntlServer/IntlMinocerSetStatus.cs
Normal file
20
nksrv/IntlServer/IntlMinocerSetStatus.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using EmbedIO;
|
||||
|
||||
namespace nksrv.IntlServer
|
||||
{
|
||||
public class IntlMinocerSetStatus : IntlMsgHandler
|
||||
{
|
||||
public override bool RequiresAuth => true;
|
||||
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var c = await ctx.GetRequestBodyAsStringAsync();
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
113
nksrv/IntlServer/IntlMsgHandler.cs
Normal file
113
nksrv/IntlServer/IntlMsgHandler.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using EmbedIO;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.IntlServer
|
||||
{
|
||||
public abstract class IntlMsgHandler
|
||||
{
|
||||
protected IHttpContext ctx;
|
||||
protected string Content = "";
|
||||
protected User? User;
|
||||
protected string? Seq;
|
||||
protected AccessToken? UsedToken;
|
||||
public abstract bool RequiresAuth { get; }
|
||||
public async Task HandleAsync(IHttpContext ctx)
|
||||
{
|
||||
this.ctx = ctx;
|
||||
Content = await ctx.GetRequestBodyAsStringAsync();
|
||||
Seq = ctx.GetRequestQueryData().Get("seq");
|
||||
if (RequiresAuth)
|
||||
{
|
||||
var x = JsonConvert.DeserializeObject<AuthPkt>(Content);
|
||||
string tokToCheck = "";
|
||||
if(x != null && x.channel_info != null && !string.IsNullOrEmpty(x.channel_info.account_token))
|
||||
{
|
||||
tokToCheck = x.channel_info.account_token;
|
||||
}
|
||||
else
|
||||
{
|
||||
var x2 = JsonConvert.DeserializeObject<AuthPkt2>(Content);
|
||||
if (x2 != null)
|
||||
{
|
||||
tokToCheck = x2.token;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(tokToCheck))
|
||||
throw new Exception("missing auth token");
|
||||
if (x != null)
|
||||
{
|
||||
foreach (var tok in JsonDb.Instance.LauncherAccessTokens)
|
||||
{
|
||||
if (tok.Token == tokToCheck)
|
||||
{
|
||||
var user = JsonDb.Instance.Users.Find(x => x.ID == tok.UserID);
|
||||
if (user != null)
|
||||
{
|
||||
UsedToken = tok;
|
||||
User = user;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (User == null)
|
||||
{
|
||||
WriteJsonString("{\"msg\":\"expired verify_code!\",\"ret\":2022,\"seq\":\"" + Seq + "\"}\r\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteJsonString("{\"msg\":\"expired verify_code!\",\"ret\":2022,\"seq\":\"" + Seq + "\"}\r\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await HandleAsync();
|
||||
}
|
||||
protected abstract Task HandleAsync();
|
||||
|
||||
protected void WriteJsonString(string data)
|
||||
{
|
||||
// check if we are sending valid json
|
||||
JObject.Parse(data);
|
||||
|
||||
var bt = Encoding.UTF8.GetBytes(data);
|
||||
ctx.Response.ContentEncoding = null;
|
||||
ctx.Response.ContentType = "application/json";
|
||||
ctx.Response.ContentLength64 = bt.Length;
|
||||
ctx.Response.OutputStream.Write(bt, 0, bt.Length);
|
||||
ctx.Response.OutputStream.Flush();
|
||||
}
|
||||
public class ChannelInfo
|
||||
{
|
||||
public string openid { get; set; }
|
||||
public string token { get; set; }
|
||||
public int account_type { get; set; }
|
||||
public string account { get; set; }
|
||||
public string phone_area_code { get; set; }
|
||||
public int account_plat_type { get; set; }
|
||||
public string lang_type { get; set; }
|
||||
public bool is_login { get; set; }
|
||||
public string account_uid { get; set; }
|
||||
public string account_token { get; set; }
|
||||
}
|
||||
public class AuthPkt
|
||||
{
|
||||
public ChannelInfo channel_info { get; set; }
|
||||
}
|
||||
public class AuthPkt2
|
||||
{
|
||||
public string token;
|
||||
public string openid;
|
||||
public string account_token;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
nksrv/IntlServer/IntlQueryAccountInfo.cs
Normal file
22
nksrv/IntlServer/IntlQueryAccountInfo.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
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
|
||||
{
|
||||
internal class IntlQueryAccountInfo : IntlMsgHandler
|
||||
{
|
||||
public override bool RequiresAuth => true;
|
||||
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
// doesnt seem to be important, send some data
|
||||
WriteJsonString("{\"game_sacc_openid\":\"\",\"game_sacc_uid\":\"\",\"has_game_sacc_openid\":false,\"has_game_sacc_uid\":false,\"has_li_openid\":false,\"has_li_uid\":true,\"is_receive_email\":-1,\"is_receive_email_in_night\":-1,\"li_openid\":\"\",\"li_uid\":\"" + User.ID + "\",\"msg\":\"success\",\"need_notify\":false,\"ret\":0,\"seq\":\"" + Seq + "\",\"user_agreed_game_dma\":\"\",\"user_agreed_game_pp\":\"\",\"user_agreed_game_tos\":\"\",\"user_agreed_li_dt\":\"\",\"user_agreed_li_pp\":\"\",\"user_agreed_li_tos\":\"\"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
29
nksrv/IntlServer/IntlReturnJsonHandler.cs
Normal file
29
nksrv/IntlServer/IntlReturnJsonHandler.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using EmbedIO;
|
||||
using Swan;
|
||||
|
||||
namespace nksrv.IntlServer
|
||||
{
|
||||
public class IntlReturnJsonHandler : IntlMsgHandler
|
||||
{
|
||||
private string JsonToReturn;
|
||||
public override bool RequiresAuth => false;
|
||||
|
||||
public IntlReturnJsonHandler(string jsonToReturn)
|
||||
{
|
||||
JsonToReturn = jsonToReturn;
|
||||
}
|
||||
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var str= await ctx.GetRequestBodyAsStringAsync();
|
||||
|
||||
string? seg = ctx.GetRequestQueryData().Get("seq");
|
||||
WriteJsonString(JsonToReturn.Replace("((SEGID))", seg));
|
||||
}
|
||||
}
|
||||
}
|
||||
59
nksrv/IntlServer/SendCodeEndpoint.cs
Normal file
59
nksrv/IntlServer/SendCodeEndpoint.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using EmbedIO;
|
||||
using Newtonsoft.Json;
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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;
|
||||
|
||||
namespace nksrv.IntlServer
|
||||
{
|
||||
public class SendCodeEndpoint : IntlMsgHandler
|
||||
{
|
||||
public override bool RequiresAuth => false;
|
||||
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
SendCodeRequest? ep = JsonConvert.DeserializeObject<SendCodeRequest>(Content);
|
||||
if (ep != null)
|
||||
{
|
||||
string? seg = ctx.GetRequestQueryData().Get("seq");
|
||||
|
||||
// check if the account already exists
|
||||
|
||||
foreach (var item in JsonDb.Instance.Users)
|
||||
{
|
||||
if (item.Username == ep.account)
|
||||
{
|
||||
WriteJsonString("{\"msg\":\"send code failed; invalid account\",\"ret\":2112,\"seq\":\"" + seg + "\"}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// pretend that we sent the code
|
||||
WriteJsonString("{\"expire_time\":898,\"msg\":\"Success\",\"ret\":0,\"seq\":\"" + seg + "\"}");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new HttpException(HttpStatusCode.BadRequest);
|
||||
}
|
||||
}
|
||||
|
||||
public class SendCodeRequest
|
||||
{
|
||||
public DeviceInfo device_info { get; set; }
|
||||
public string extra_json { get; set; }
|
||||
public string account { get; set; }
|
||||
public int account_type { get; set; }
|
||||
public string phone_area_code { get; set; }
|
||||
public int code_type { get; set; }
|
||||
public int support_captcha { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
130
nksrv/LobbyServer/LobbyHandler.cs
Normal file
130
nksrv/LobbyServer/LobbyHandler.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using ASodium;
|
||||
using EmbedIO;
|
||||
using Google.Protobuf;
|
||||
using nksrv.Utils;
|
||||
using Swan.Logging;
|
||||
|
||||
namespace nksrv.LobbyServer
|
||||
{
|
||||
public static class LobbyHandler
|
||||
{
|
||||
public static readonly Dictionary<string, LobbyMsgHandler> Handlers = new Dictionary<string, LobbyMsgHandler>();
|
||||
static LobbyHandler()
|
||||
{
|
||||
foreach (System.Type type in typeof(LobbyMsgHandler).Assembly.GetTypes())
|
||||
{
|
||||
if (type.GetCustomAttributes(typeof(PacketPathAttribute), true).Length > 0)
|
||||
{
|
||||
var attrib = (PacketPathAttribute?)Attribute.GetCustomAttribute(type, typeof(PacketPathAttribute));
|
||||
if (attrib == null)
|
||||
{
|
||||
Logger.Error("WARNING: Failed to get attribute for " + type.FullName);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
var instance = Activator.CreateInstance(type);
|
||||
if (instance is LobbyMsgHandler handler)
|
||||
{
|
||||
Handlers.Add(attrib.Url, handler);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error($"WARNING: Type {type.FullName} has PacketPathAttribute but does not implement LobbyMsgHandler");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public static async Task DispatchSingle(IHttpContext ctx)
|
||||
{
|
||||
//var x = new RedirectionHandler();
|
||||
//await x.HandleAsync(ctx);
|
||||
//return;
|
||||
LobbyMsgHandler? handler = null;
|
||||
foreach (var item in Handlers)
|
||||
{
|
||||
if (ctx.RequestedPath == item.Key)
|
||||
{
|
||||
handler = item.Value;
|
||||
}
|
||||
}
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
ctx.Response.StatusCode = 404;
|
||||
//Logger.Error("HTTPS: No handler for /v1/" + ctx.RequestedPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// todo move everClass1.csything to its own handler
|
||||
handler.Reset();
|
||||
await handler.HandleAsync(ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private key, Token
|
||||
/// </summary>
|
||||
/// <param name="publicKey"></param>
|
||||
/// <returns></returns>
|
||||
public static GameClientInfo GenGameClientTok(ByteString publicKey, string authToken)
|
||||
{
|
||||
var token = Rng.RandomString(381);
|
||||
|
||||
var info = new GameClientInfo() { ClientPublicKey = publicKey.ToArray() };
|
||||
|
||||
|
||||
var box = SodiumKeyExchange.CalculateServerSharedSecret(JsonDb.ServerPublicKey, JsonDb.ServerPrivateKey, publicKey.ToArray());
|
||||
|
||||
info.Keys = box;
|
||||
info.ClientAuthToken = token;
|
||||
|
||||
// look up user id
|
||||
foreach (var user in JsonDb.Instance.LauncherAccessTokens)
|
||||
{
|
||||
if (user.Token == authToken)
|
||||
{
|
||||
info.UserId = user.UserID;
|
||||
}
|
||||
}
|
||||
if (info.UserId == 0)
|
||||
throw new Exception("expected user account");
|
||||
|
||||
JsonDb.Instance.GameClientTokens.Add(token, info);
|
||||
JsonDb.Save();
|
||||
return info;
|
||||
}
|
||||
|
||||
public static GameClientInfo? GetInfo(string token)
|
||||
{
|
||||
return JsonDb.Instance.GameClientTokens[token];
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
// By calling this function, we force .NET to initialize handler dictanary to catch errors early on.
|
||||
}
|
||||
}
|
||||
|
||||
public class GameClientInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Client public key generated by game client
|
||||
/// </summary>
|
||||
public byte[] ClientPublicKey;
|
||||
/// <summary>
|
||||
/// Authentication token
|
||||
/// </summary>
|
||||
public string ClientAuthToken;
|
||||
/// <summary>
|
||||
/// Rx/Tx key pair
|
||||
/// </summary>
|
||||
public SodiumKeyExchangeSharedSecretBox Keys;
|
||||
/// <summary>
|
||||
/// User ID of the user
|
||||
/// </summary>
|
||||
public ulong UserId;
|
||||
|
||||
}
|
||||
}
|
||||
121
nksrv/LobbyServer/LobbyMsgHandler.cs
Normal file
121
nksrv/LobbyServer/LobbyMsgHandler.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using EmbedIO;
|
||||
using Google.Protobuf;
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer
|
||||
{
|
||||
public abstract class LobbyMsgHandler
|
||||
{
|
||||
protected IHttpContext? ctx;
|
||||
protected ulong UserId;
|
||||
protected string UsedAuthToken = "";
|
||||
public byte[] ReturnBytes = [];
|
||||
public byte[] Contents = [];
|
||||
|
||||
/// <summary>
|
||||
/// Call before calling HandleAsync
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
Contents = [];
|
||||
ReturnBytes = [];
|
||||
UsedAuthToken = "";
|
||||
UserId = 0;
|
||||
ctx = null;
|
||||
}
|
||||
|
||||
public async Task HandleAsync(IHttpContext ctx)
|
||||
{
|
||||
this.ctx = ctx;
|
||||
await HandleAsync();
|
||||
}
|
||||
public async Task HandleAsync(string authToken)
|
||||
{
|
||||
this.UsedAuthToken = authToken;
|
||||
foreach (var item in JsonDb.Instance.GameClientTokens)
|
||||
{
|
||||
if (item.Key == authToken)
|
||||
{
|
||||
UserId = item.Value.UserId;
|
||||
}
|
||||
}
|
||||
if (UserId == 0) throw new HttpException(403);
|
||||
await HandleAsync();
|
||||
}
|
||||
|
||||
protected abstract Task HandleAsync();
|
||||
|
||||
protected void WriteData<T>(T data) where T : IMessage, new()
|
||||
{
|
||||
if (ctx == null)
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
var x2 = new CodedOutputStream(ms);
|
||||
data.WriteTo(x2);
|
||||
x2.Flush();
|
||||
ReturnBytes = ms.ToArray();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.Response.ContentEncoding = null;
|
||||
ctx.Response.ContentType = "application/octet-stream+protobuf";
|
||||
ctx.Response.ContentLength64 = data.CalculateSize();
|
||||
bool encrypted = false;
|
||||
var responseBytes = encrypted ? new MemoryStream() : ctx.Response.OutputStream;
|
||||
var x = new CodedOutputStream(responseBytes);
|
||||
data.WriteTo(x);
|
||||
x.Flush();
|
||||
|
||||
if (encrypted)
|
||||
{
|
||||
ctx.Response.Headers.Set(System.Net.HttpRequestHeader.ContentEncoding, "gzip,enc");
|
||||
var enc = PacketDecryption.EncryptData(((MemoryStream)responseBytes).ToArray(), UsedAuthToken);
|
||||
ctx.Response.OutputStream.Write(enc, 0, enc.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
protected async Task<T> ReadData<T>() where T : IMessage, new()
|
||||
{
|
||||
if (ctx == null)
|
||||
{
|
||||
T msg2 = new T();
|
||||
msg2.MergeFrom(Contents);
|
||||
return msg2;
|
||||
}
|
||||
else
|
||||
{
|
||||
var bin = await PacketDecryption.DecryptOrReturnContentAsync(ctx);
|
||||
|
||||
// return grpc IMessage from byte array with type T
|
||||
T msg = default(T);
|
||||
try
|
||||
{
|
||||
msg = new T();
|
||||
msg.MergeFrom(bin.Contents);
|
||||
}
|
||||
catch
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
UserId = bin.UserId;
|
||||
UsedAuthToken = bin.UsedAuthToken;
|
||||
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
public User GetUser()
|
||||
{
|
||||
User? user = JsonDb.GetUser(UserId);
|
||||
if (user == null) throw new Exception("null user");
|
||||
return user;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
nksrv/LobbyServer/Msgs/Antibot/BattleReportData.cs
Normal file
24
nksrv/LobbyServer/Msgs/Antibot/BattleReportData.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Antibot
|
||||
{
|
||||
[PacketPath("/antibot/battlereportdata")]
|
||||
public class BattleReportData : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqBattleReportData>();
|
||||
|
||||
// I don't really care about reimplementing the server side anticheat, so return
|
||||
|
||||
var response = new ResBattleReportData();
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
nksrv/LobbyServer/Msgs/Antibot/RecieveAntibotData.cs
Normal file
24
nksrv/LobbyServer/Msgs/Antibot/RecieveAntibotData.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Antibot
|
||||
{
|
||||
[PacketPath("/antibot/recvdata")]
|
||||
public class RecieveAntibotData : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqAntibotRecvData>();
|
||||
|
||||
// I don't really care about reimplementing the server side anticheat, so return
|
||||
|
||||
var response = new ResAntibotRecvData();
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
nksrv/LobbyServer/Msgs/Auth/DoEnterServer.cs
Normal file
34
nksrv/LobbyServer/Msgs/Auth/DoEnterServer.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
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;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Auth
|
||||
{
|
||||
[PacketPath("/auth/enterserver")]
|
||||
public class GetUserOnlineStateLog : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<EnterServerRequest>();
|
||||
|
||||
var response = new EnterServerResponse();
|
||||
var rsp = LobbyHandler.GenGameClientTok(req.ClientPublicKey, req.AuthToken);
|
||||
response.GameClientToken = rsp.ClientAuthToken;
|
||||
response.FeatureDataInfo = new NetFeatureDataInfo() { UseFeatureData = true };
|
||||
response.Identifier = new NetLegacyUserIdentifier() { Server = 21769, Usn = 10984769 };
|
||||
response.ShouldRestartAfter = Duration.FromTimeSpan(TimeSpan.FromSeconds(86400));
|
||||
|
||||
// This was probably the public key for communication at some point during the game's development
|
||||
// But, the developers chose to hardcode server public key in the client, which prevents this
|
||||
// private server from "just working", so thats why hex patch is required.
|
||||
// The only point of encrypting packets is to make it harder for me to develop this and users to use this.
|
||||
response.EncryptionToken = ByteString.CopyFromUtf8(rsp.ClientAuthToken);
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
nksrv/LobbyServer/Msgs/Auth/DoIntlAuth.cs
Normal file
18
nksrv/LobbyServer/Msgs/Auth/DoIntlAuth.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using nksrv.Utils;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Auth
|
||||
{
|
||||
[PacketPath("/auth/intl")]
|
||||
public class DoIntlAuth : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<ReqAuthIntl>();
|
||||
|
||||
var response = new AuthIntlResponse();
|
||||
//response.BanInfo = new NetBanInfo() { BanId = 123, 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.UtcNow), EndAt = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(256)) };
|
||||
response.AuthSuccess = new NetAuthSuccess() { AuthToken = req.Token, CentauriZoneId = "84", FirstAuth = "", PurchaseRestriction = new NetUserPurchaseRestriction() { PurchaseRestriction = PurchaseRestriction.Unknown2, UpdatedAt = 638546758794611090 } };
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
nksrv/LobbyServer/Msgs/Badge/DeleteBadge.cs
Normal file
22
nksrv/LobbyServer/Msgs/Badge/DeleteBadge.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Badge
|
||||
{
|
||||
[PacketPath("/badge/delete")]
|
||||
public class DeleteBadge : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqDeleteBadge>();
|
||||
|
||||
var response = new ResDeleteBadge();
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
nksrv/LobbyServer/Msgs/Badge/SyncBadge.cs
Normal file
16
nksrv/LobbyServer/Msgs/Badge/SyncBadge.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using nksrv.Utils;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Badge
|
||||
{
|
||||
[PacketPath("/badge/sync")]
|
||||
public class SyncBadge : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqSyncBadge>();
|
||||
|
||||
var response = new ResSyncBadge();
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
nksrv/LobbyServer/Msgs/Campaign/GetCampaignField.cs
Normal file
21
nksrv/LobbyServer/Msgs/Campaign/GetCampaignField.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Campaign
|
||||
{
|
||||
[PacketPath("/campaign/getfield")]
|
||||
public class GetCampaignField : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqGetCampaignFieldData>();
|
||||
|
||||
var response = new ResGetCampaignFieldData();
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
nksrv/LobbyServer/Msgs/Character/GetCharacterData.cs
Normal file
24
nksrv/LobbyServer/Msgs/Character/GetCharacterData.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Character
|
||||
{
|
||||
[PacketPath("/character/get")]
|
||||
public class GetCharacterData : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqGetCharacterData>();
|
||||
|
||||
var response = new ResGetCharacterData();
|
||||
|
||||
// TODO implement
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
nksrv/LobbyServer/Msgs/GetCurrentTime.cs
Normal file
22
nksrv/LobbyServer/Msgs/GetCurrentTime.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using nksrv.Utils;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs
|
||||
{
|
||||
[PacketPath("/now")]
|
||||
public class GetCurrentTime : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqGetNow>();
|
||||
|
||||
var response = new ResGetNow();
|
||||
response.Tick = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
response.ResetHour = 1;
|
||||
response.CheatShiftDuration = Duration.FromTimeSpan(TimeSpan.FromSeconds(0));
|
||||
// todo: validate response with actual server
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
nksrv/LobbyServer/Msgs/GetGacha.cs
Normal file
23
nksrv/LobbyServer/Msgs/GetGacha.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs
|
||||
{
|
||||
[PacketPath("/Gacha/Get")]
|
||||
public class GetGacha : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqGetGachaData>();
|
||||
|
||||
var response = new ResGetGachaData();
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
nksrv/LobbyServer/Msgs/Messenger/GetDailyMessage.cs
Normal file
23
nksrv/LobbyServer/Msgs/Messenger/GetDailyMessage.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Messenger
|
||||
{
|
||||
[PacketPath("/messenger/daily/pick")]
|
||||
public class GetDailyMessage : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqPickTodayDailyMessage>();
|
||||
|
||||
// TODO: save these things
|
||||
var response = new ResPickTodayDailyMessage();
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
nksrv/LobbyServer/Msgs/Messenger/GetMessages.cs
Normal file
23
nksrv/LobbyServer/Msgs/Messenger/GetMessages.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Messenger
|
||||
{
|
||||
[PacketPath("/messenger/get")]
|
||||
public class GetMessages : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqGetMessages>();
|
||||
|
||||
// TODO: save these things
|
||||
var response = new ResGetMessages();
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
nksrv/LobbyServer/Msgs/Messenger/GetPickedMessage.cs
Normal file
23
nksrv/LobbyServer/Msgs/Messenger/GetPickedMessage.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Messenger
|
||||
{
|
||||
[PacketPath("/messenger/picked/get")]
|
||||
public class GetPickedMessage : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqGetPickedMessageList>();
|
||||
|
||||
// TODO: get proper response
|
||||
var response = new ResGetPickedMessageList();
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
nksrv/LobbyServer/Msgs/Messenger/GetRandomPick.cs
Normal file
23
nksrv/LobbyServer/Msgs/Messenger/GetRandomPick.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Messenger
|
||||
{
|
||||
[PacketPath("/messenger/random/pick")]
|
||||
public class GetRandomPick : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqForcePickTodayRandomMessage>();
|
||||
|
||||
// TODO: get proper response
|
||||
var response = new ResForcePickTodayRandomMessage();
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
nksrv/LobbyServer/Msgs/Misc/GetMaintenanceNotice.cs
Normal file
21
nksrv/LobbyServer/Msgs/Misc/GetMaintenanceNotice.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Google.Protobuf;
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Misc
|
||||
{
|
||||
[PacketPath("/maintenancenotice")]
|
||||
public class GetMaintenanceNotice : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var r = new MaintenanceNoticeResponse();
|
||||
|
||||
WriteData(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
nksrv/LobbyServer/Msgs/Misc/GetResourceHosts2.cs
Normal file
23
nksrv/LobbyServer/Msgs/Misc/GetResourceHosts2.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Misc
|
||||
{
|
||||
[PacketPath("/resourcehosts2")]
|
||||
public class CheckClientVersion : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<ResourceHostRequest>();
|
||||
|
||||
var r = new ResourceHostResponse();
|
||||
r.BaseUrl = "https://cloud.nikke-kr.com/prdenv/121-b0630db21d/{Platform}";
|
||||
|
||||
WriteData(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
nksrv/LobbyServer/Msgs/Misc/GetSentryParams.cs
Normal file
20
nksrv/LobbyServer/Msgs/Misc/GetSentryParams.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Misc
|
||||
{
|
||||
[PacketPath("/system/sentry/getparams")]
|
||||
public class GetSentryParams : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var r = new SentryDataResponse();
|
||||
// TODO check proper response from real server
|
||||
WriteData(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
nksrv/LobbyServer/Msgs/Misc/GetServerInfo.cs
Normal file
25
nksrv/LobbyServer/Msgs/Misc/GetServerInfo.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Misc
|
||||
{
|
||||
[PacketPath("/getserverinfo")]
|
||||
public class GetServerInfo : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var r = new ResGetServerInfo();
|
||||
|
||||
// todo: reimplement this as well
|
||||
r.MatchUrl = "https://global-match.nikke-kr.com";
|
||||
r.WorldId = 84;
|
||||
|
||||
WriteData(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
nksrv/LobbyServer/Msgs/Misc/GetStaticDataPack.cs
Normal file
26
nksrv/LobbyServer/Msgs/Misc/GetStaticDataPack.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Google.Protobuf;
|
||||
using nksrv.Utils;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Misc
|
||||
{
|
||||
[PacketPath("/staticdatapack")]
|
||||
public class GetStaticDataPack : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<StaticDataPackRequest>();
|
||||
|
||||
var r = new StaticDataPackResponse();
|
||||
r.Url = "https://cloud.nikke-kr.com/prdenv/121-c5e64b1a1b/staticdata/data/qa-240620-05b-p1/307748/StaticData.pack";
|
||||
r.Version = "data/qa-240620-05b-p1/307748";
|
||||
r.Size = 11575712;
|
||||
|
||||
// TODO: Read the file and compute these values
|
||||
r.Sha256Sum = ByteString.CopyFrom(Convert.FromBase64String("PBcDa3PoHR2MJQ+4Xc3/FUSgkqx2gY25MBJ0ih9FMsM="));
|
||||
r.Salt1 = ByteString.CopyFrom(Convert.FromBase64String("WqyrQ8MGtzwHN3AGPkqVKyjdfWZjBJXw9K7nGblv/SA="));
|
||||
r.Salt2 = ByteString.CopyFrom(Convert.FromBase64String("6Gf2jEvAX2mt5OWIxIU5uDdbjKtIc+VgTjKKSLuYnsI="));
|
||||
|
||||
WriteData(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
nksrv/LobbyServer/Msgs/Misc/GetUserOnlineStateLog.cs
Normal file
22
nksrv/LobbyServer/Msgs/Misc/GetUserOnlineStateLog.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Misc
|
||||
{
|
||||
[PacketPath("/useronlinestatelog")]
|
||||
public class GetUserOnlineStateLog : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<ReqUserOnlineStateLog>();
|
||||
|
||||
|
||||
var response = new ResUserOnlineStateLog();
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
nksrv/LobbyServer/Msgs/Mission/Rewards/GetJukeboxRewards.cs
Normal file
18
nksrv/LobbyServer/Msgs/Mission/Rewards/GetJukeboxRewards.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using nksrv.Utils;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Outpost
|
||||
{
|
||||
[PacketPath("/mission/getrewarded/jukebox")]
|
||||
public class GetJukeboxRewards : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqGetJukeboxRewardedData>();
|
||||
|
||||
// TODO: save these things
|
||||
var response = new ResGetJukeboxRewardedData();
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
nksrv/LobbyServer/Msgs/Outpost/GetRecycleRoomData.cs
Normal file
18
nksrv/LobbyServer/Msgs/Outpost/GetRecycleRoomData.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using nksrv.Utils;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Outpost
|
||||
{
|
||||
[PacketPath("/outpost/recycleroom/get")]
|
||||
public class GetRecycleRoomData : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqGetRecycleRoomData>();
|
||||
|
||||
// TODO: save these things
|
||||
var response = new ResGetRecycleRoomData();
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
86
nksrv/LobbyServer/Msgs/RedirectionHandler.cs
Normal file
86
nksrv/LobbyServer/Msgs/RedirectionHandler.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
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 EmbedIO;
|
||||
using nksrv.Utils;
|
||||
using Sodium;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs
|
||||
{
|
||||
public class RedirectionHandler : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
byte[] reqBody = ctx == null ? Contents : (await DecryptOrReturnContentAsync(ctx)).Contents;
|
||||
|
||||
HttpClientHandler handler = new HttpClientHandler()
|
||||
{
|
||||
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
|
||||
AllowAutoRedirect = true // from gameassembly dll
|
||||
};
|
||||
|
||||
HttpClient client = new HttpClient(new LoggingHandler(handler));
|
||||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream+protobuf"));
|
||||
client.BaseAddress = new Uri("https://global-lobby.nikke-kr.com");
|
||||
client.DefaultRequestVersion = HttpVersion.Version20;
|
||||
var systemContent = new ByteArrayContent(reqBody);
|
||||
systemContent.Headers.Remove("Content-Type");
|
||||
if (ctx.Request.ContentLength64 != 0)
|
||||
systemContent.Headers.Add("Content-Type", "application/octet-stream+protobuf");
|
||||
systemContent.Headers.Add("Content-Length", ctx.Request.ContentLength64.ToString());
|
||||
|
||||
// request.Content = systemContent;// CONTENT-TYPE header
|
||||
|
||||
|
||||
var result = await client.PostAsync(ctx.Request.RawUrl, systemContent);
|
||||
var bt = await result.Content.ReadAsByteArrayAsync();
|
||||
|
||||
ctx.Response.ContentEncoding = null;
|
||||
ctx.Response.ContentType = result.Content.Headers.GetValues("Content-Type").First();
|
||||
ctx.Response.ContentLength64 = bt.Length;
|
||||
ctx.Response.OutputStream.Write(bt, 0, bt.Length);
|
||||
ctx.Response.OutputStream.Flush();
|
||||
}
|
||||
|
||||
public static async Task<PacketDecryptResponse> DecryptOrReturnContentAsync(IHttpContext ctx, bool decompress = false)
|
||||
{
|
||||
byte[] bin = Array.Empty<byte>();
|
||||
|
||||
using MemoryStream buffer = new MemoryStream();
|
||||
|
||||
var stream = ctx.Request.InputStream;
|
||||
|
||||
var encoding = ctx.Request.Headers[HttpHeaderNames.ContentEncoding]?.Trim();
|
||||
|
||||
Stream decryptedStream;
|
||||
switch (encoding)
|
||||
{
|
||||
case CompressionMethodNames.Gzip:
|
||||
decryptedStream = new GZipStream(stream, CompressionMode.Decompress);
|
||||
break;
|
||||
case CompressionMethodNames.Deflate:
|
||||
decryptedStream = new DeflateStream(stream, CompressionMode.Decompress);
|
||||
break;
|
||||
case CompressionMethodNames.None:
|
||||
case null:
|
||||
decryptedStream = stream;
|
||||
break;
|
||||
case "gzip,enc":
|
||||
|
||||
decryptedStream = stream;
|
||||
break;
|
||||
default:
|
||||
throw HttpException.BadRequest($"Unsupported content encoding \"{encoding}\"");
|
||||
}
|
||||
|
||||
|
||||
await stream.CopyToAsync(buffer, 81920, ctx.CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
|
||||
return new PacketDecryptResponse() { Contents = buffer.ToArray() };
|
||||
}
|
||||
}
|
||||
}
|
||||
32
nksrv/LobbyServer/Msgs/Shop/GetProductList.cs
Normal file
32
nksrv/LobbyServer/Msgs/Shop/GetProductList.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Shop
|
||||
{
|
||||
[PacketPath("/inappshop/jupiter/getproductlist")]
|
||||
public class GetProductList : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var x = await ReadData<ReqGetJupiterProductList>(); //todo: causes crash
|
||||
|
||||
var response = new ResGetJupiterProductList();
|
||||
foreach (var item in x.ProductIdList)
|
||||
{
|
||||
response.ProductInfoList.Add(new NetJupiterProductInfo() { CurrencyCode = "US", CurrencySymbol = "$", MicroPrice = 212, Price = "22", ProductId = item });
|
||||
}
|
||||
WriteData(response);
|
||||
}
|
||||
catch
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
nksrv/LobbyServer/Msgs/Stage/ClearStage.cs
Normal file
25
nksrv/LobbyServer/Msgs/Stage/ClearStage.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Stage
|
||||
{
|
||||
[PacketPath("/stage/clearstage")]
|
||||
public class ClearStage : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<ReqClearStage>();
|
||||
|
||||
var response = new ResClearStage();
|
||||
|
||||
// TOOD: save to user info
|
||||
Console.WriteLine($"Stage " + req.StageId + " completed, result is "+req.BattleResult);
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
nksrv/LobbyServer/Msgs/Stage/EnterStage.cs
Normal file
22
nksrv/LobbyServer/Msgs/Stage/EnterStage.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Stage
|
||||
{
|
||||
[PacketPath("/stage/enterstage")]
|
||||
public class EnterStage : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqEnterStage>();
|
||||
|
||||
var response = new ResEnterStage();
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
nksrv/LobbyServer/Msgs/Stage/GetStage.cs
Normal file
22
nksrv/LobbyServer/Msgs/Stage/GetStage.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Stage
|
||||
{
|
||||
[PacketPath("/stage/get")]
|
||||
public class GetStage : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqGetStageData>();
|
||||
|
||||
var response = new ResGetStageData();
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
nksrv/LobbyServer/Msgs/System/CheckClientVersion.cs
Normal file
17
nksrv/LobbyServer/Msgs/System/CheckClientVersion.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using nksrv.Utils;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.System
|
||||
{
|
||||
[PacketPath("/system/checkversion")]
|
||||
public class CheckClientVersion : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<CheckVersionRequest>();
|
||||
var r = new CheckVersionResponse();
|
||||
r.Availability = 0; // None = 0, Available = 1, Mandatory = 2
|
||||
|
||||
WriteData(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
nksrv/LobbyServer/Msgs/Team/ListSupportCharacterCount.cs
Normal file
25
nksrv/LobbyServer/Msgs/Team/ListSupportCharacterCount.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Team
|
||||
{
|
||||
[PacketPath("/team/support-character/list-used-count")]
|
||||
public class ListSupportCharacterCount : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<ReqListSupportCharacterUsedCount>();
|
||||
|
||||
var response = new ResListSupportCharacterUsedCount();
|
||||
foreach (var item in req.TeamTypeList)
|
||||
{
|
||||
Console.WriteLine("support character used: " + item);
|
||||
}
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
nksrv/LobbyServer/Msgs/Trigger/GetMainQuestData.cs
Normal file
17
nksrv/LobbyServer/Msgs/Trigger/GetMainQuestData.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using nksrv.Utils;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Trigger
|
||||
{
|
||||
[PacketPath("/Trigger/GetMainQuestData")]
|
||||
public class GetMainQuestData : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<ReqGetMainQuestData>();
|
||||
|
||||
var response = new ResGetMainQuestData();
|
||||
response.MainQuestList.Add(new NetMainQuestData() { IsReceived = true, Tid = 1 });
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
nksrv/LobbyServer/Msgs/Trigger/TriggerSync.cs
Normal file
16
nksrv/LobbyServer/Msgs/Trigger/TriggerSync.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using nksrv.Utils;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Trigger
|
||||
{
|
||||
[PacketPath("/trigger/sync")]
|
||||
public class TriggerSync : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = ReadData<ReqSyncTrigger>();
|
||||
|
||||
var response = new ResSyncTrigger();
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
48
nksrv/LobbyServer/Msgs/User/EnterLobbyServer.cs
Normal file
48
nksrv/LobbyServer/Msgs/User/EnterLobbyServer.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.User
|
||||
{
|
||||
[PacketPath("/enterlobbyserver")]
|
||||
public class EnterLobbyServer : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<ReqEnterLobbyServer>();
|
||||
|
||||
|
||||
var response = new ResEnterLobbyServer();
|
||||
response.User = new NetUserData();
|
||||
response.User.Lv = 1;
|
||||
response.User.CommanderRoomJukebox = 5;
|
||||
response.User.CostumeLv = 1;
|
||||
response.User.Frame = 1;
|
||||
response.User.Icon = 39900;
|
||||
response.User.LobbyJukebox = 2;
|
||||
response.ResetHour = 20;
|
||||
response.Nickname = "USER11143417";
|
||||
response.OutpostBattleTime = new NetOutpostBattleTime() { MaxBattleTime = 864000000000 ,MaxOverBattleTime = 12096000000000 };
|
||||
response.RepresentationTeam = new NetWholeUserTeamData() { TeamNumber = 1, Type = 2};
|
||||
response.RepresentationTeam.Slots.Add(new NetWholeTeamSlot() { Slot = 1 });
|
||||
response.RepresentationTeam.Slots.Add(new NetWholeTeamSlot() { Slot = 2 });
|
||||
response.RepresentationTeam.Slots.Add(new NetWholeTeamSlot() { Slot = 3 });
|
||||
response.RepresentationTeam.Slots.Add(new NetWholeTeamSlot() { Slot = 4 });
|
||||
response.RepresentationTeam.Slots.Add(new NetWholeTeamSlot() { Slot = 5 });
|
||||
response.Currency.Add(new NetUserCurrencyData() { Type = 6100, Value = 2 });
|
||||
|
||||
//var tTeams = new NetUserTeamData();
|
||||
|
||||
//var tTeam = new NetTeamData() { TeamNumber = 1 };
|
||||
//tTeam.Slots.Add(new NetTeamSlot() { Slot = 1, ValueType = 1, Value = 2 });
|
||||
//tTeams.Teams.Add(tTeam);
|
||||
//response.TypeTeams.Add(tTeams);
|
||||
// response.Character.Add(new NetUserCharacterData() { Default = new NetUserCharacterDefaultData() { Tid = 1 } });
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
nksrv/LobbyServer/Msgs/User/GetContentsData.cs
Normal file
17
nksrv/LobbyServer/Msgs/User/GetContentsData.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using nksrv.Utils;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.User
|
||||
{
|
||||
[PacketPath("/user/getcontentsdata")]
|
||||
public class GetContentsData : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<ReqGetContentsOpenData>();
|
||||
|
||||
|
||||
var response = new ResGetContentsOpenData();
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
nksrv/LobbyServer/Msgs/User/GetUser.cs
Normal file
23
nksrv/LobbyServer/Msgs/User/GetUser.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.User
|
||||
{
|
||||
[PacketPath("/User/Get")]
|
||||
public class GetUser : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<GetUserDataRequest>();
|
||||
|
||||
|
||||
var response = new UserDataResponse();
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
40
nksrv/LobbyServer/Msgs/User/GetUserScenarioExist.cs
Normal file
40
nksrv/LobbyServer/Msgs/User/GetUserScenarioExist.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using nksrv.Utils;
|
||||
using Swan.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.User
|
||||
{
|
||||
[PacketPath("/user/scenario/exist")]
|
||||
public class GetUserScenarioExist : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<ReqExistScenario>();
|
||||
|
||||
// TODO: Check response from real server
|
||||
|
||||
var response = new ResExistScenario();
|
||||
|
||||
var user = GetUser();
|
||||
|
||||
foreach (var item in req.ScenarioGroupIds)
|
||||
{
|
||||
Logger.Info("check scenario " + item);
|
||||
foreach (var completed in user.CompletedScenarios)
|
||||
{
|
||||
// story thingy was completed
|
||||
if (completed == item)
|
||||
{
|
||||
Logger.Info(item + " is completed");
|
||||
response.ExistGroupIds.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
35
nksrv/LobbyServer/Msgs/User/SetScenarioCompleted.cs
Normal file
35
nksrv/LobbyServer/Msgs/User/SetScenarioCompleted.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using nksrv.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.User
|
||||
{
|
||||
[PacketPath("/User/SetScenarioComplete")]
|
||||
public class SetScenarioCompleted : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<ReqSetScenarioComplete>();
|
||||
|
||||
var response = new ResSetScenarioComplete();
|
||||
|
||||
// Mark the story "scenario" as completed.
|
||||
// TODO: Get rewards by making a database of them from actual server.
|
||||
response.Reward = new NetRewardData();
|
||||
|
||||
|
||||
var user = JsonDb.GetUser(UserId);
|
||||
if (user == null)
|
||||
{
|
||||
throw new Exception("null user in SetScenarioComplete command");
|
||||
}
|
||||
user.CompletedScenarios.Add(req.ScenarioId);
|
||||
JsonDb.Save();
|
||||
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
nksrv/LobbyServer/Msgs/Wallet/WalletRefreshCharge.cs
Normal file
17
nksrv/LobbyServer/Msgs/Wallet/WalletRefreshCharge.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using nksrv.Utils;
|
||||
|
||||
namespace nksrv.LobbyServer.Msgs.Wallet
|
||||
{
|
||||
[PacketPath("/wallet/refreshcharge")]
|
||||
public class WalletRefreshCharge : LobbyMsgHandler
|
||||
{
|
||||
protected override async Task HandleAsync()
|
||||
{
|
||||
var req = await ReadData<ReqRefreshChargeCurrencyData>();
|
||||
|
||||
|
||||
var response = new ResRefreshChargeCurrencyData();
|
||||
WriteData(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
404
nksrv/Program.cs
Normal file
404
nksrv/Program.cs
Normal file
@@ -0,0 +1,404 @@
|
||||
using EmbedIO;
|
||||
using EmbedIO.Actions;
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using nksrv.IntlServer;
|
||||
using nksrv.LobbyServer;
|
||||
using nksrv.Utils;
|
||||
using Swan.Logging;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Net.Http.Formatting;
|
||||
using System.Net.Security;
|
||||
using Swan.Parsers;
|
||||
using System.Net.Sockets;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Swan;
|
||||
using Google.Api;
|
||||
|
||||
namespace nksrv
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
Logger.UnregisterLogger<ConsoleLogger>();
|
||||
Logger.RegisterLogger(new GreatLogger());
|
||||
JsonDb.Save();
|
||||
LobbyHandler.Init();
|
||||
|
||||
// Start Webserver
|
||||
using (var server = CreateWebServer())
|
||||
{
|
||||
await server.RunAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private static WebServer CreateWebServer()
|
||||
{
|
||||
var cert = new X509Certificate2(new X509Certificate(@"C:\Users\Misha\nkcert\site.pfx"));
|
||||
|
||||
var server = new WebServer(o => o
|
||||
.WithUrlPrefixes("https://*:443", "http://*:80")
|
||||
.WithMode(HttpListenerMode.EmbedIO).WithAutoLoadCertificate().WithCertificate(cert))
|
||||
// First, we will configure our web server by adding Modules.
|
||||
.WithLocalSessionManager()
|
||||
.WithModule(new ActionModule("/route/", HttpVerbs.Any, HandleRouteData))
|
||||
.WithModule(new ActionModule("/v1/", HttpVerbs.Any, LobbyHandler.DispatchSingle))
|
||||
.WithModule(new ActionModule("/v2/", HttpVerbs.Any, IntlHandler.Handle))
|
||||
.WithModule(new ActionModule("/prdenv/", HttpVerbs.Any, HandleAsset))
|
||||
.WithModule(new ActionModule("/account/", HttpVerbs.Any, IntlHandler.Handle))
|
||||
.WithModule(new ActionModule("/data/", HttpVerbs.Any, HandleDataEndpoint))
|
||||
.WithModule(new ActionModule("/$batch", HttpVerbs.Any, HandleBatchRequests));
|
||||
|
||||
// Listen for state changes.
|
||||
server.StateChanged += (s, e) => $"WebServer New State - {e.NewState}".Info();
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
private static async Task HandleBatchRequests(IHttpContext ctx)
|
||||
{
|
||||
var theBytes = await PacketDecryption.DecryptOrReturnContentAsync(ctx, true);
|
||||
|
||||
// this actually uses gzip compression, unlike other requests.
|
||||
|
||||
using MemoryStream streamforparser = new MemoryStream(theBytes.Contents);
|
||||
var content = new StreamContent(streamforparser);
|
||||
content.Headers.Remove("Content-Type");
|
||||
content.Headers.TryAddWithoutValidation("Content-Type", ctx.Request.Headers["Content-Type"]);
|
||||
|
||||
// we have the form contents,
|
||||
var multipart = await content.ReadAsMultipartAsync();
|
||||
|
||||
HttpClient cl = new HttpClient();
|
||||
|
||||
// TODO: the server returns different boundary each time, looks like a GUID
|
||||
List<byte> response = [.. Encoding.UTF8.GetBytes("--f5d5cf4d-5627-422f-b3c6-532f1a0cbc0a\r\n")];
|
||||
|
||||
int i = 0;
|
||||
foreach (var item in multipart.Contents)
|
||||
{
|
||||
i++;
|
||||
response.AddRange(Encoding.UTF8.GetBytes("Content-Type: application/http\r\n"));
|
||||
response.AddRange(Encoding.UTF8.GetBytes($"Content-ID: {item.Headers.NonValidated["Content-ID"]}\r\n"));
|
||||
response.AddRange(Encoding.UTF8.GetBytes("\r\n"));
|
||||
|
||||
var bin = await item.ReadAsByteArrayAsync();
|
||||
var res = await SendReqLocalAndReadResponseAsync(bin);
|
||||
|
||||
List<byte> ResponseWithBytes =
|
||||
[
|
||||
.. Encoding.UTF8.GetBytes("HTTP/1.1 200 OK\r\n"),
|
||||
.. Encoding.UTF8.GetBytes($"Content-Type: application/octet-stream+protobuf\r\n"),
|
||||
.. Encoding.UTF8.GetBytes($"Content-Length: {res.Length}\r\n"),
|
||||
.. Encoding.UTF8.GetBytes($"\r\n"),
|
||||
.. res,
|
||||
];
|
||||
response.AddRange(ResponseWithBytes.ToArray());
|
||||
|
||||
// add boundary, also include http newline if there is binary content
|
||||
|
||||
if (i == multipart.Contents.Count)
|
||||
response.AddRange(Encoding.UTF8.GetBytes("\r\n--f5d5cf4d-5627-422f-b3c6-532f1a0cbc0a--\r\n"));
|
||||
else
|
||||
response.AddRange(Encoding.UTF8.GetBytes("\r\n--f5d5cf4d-5627-422f-b3c6-532f1a0cbc0a\r\n"));
|
||||
|
||||
}
|
||||
|
||||
var responseBytes = response.ToArray();
|
||||
File.WriteAllBytes("batch-response", responseBytes);
|
||||
ctx.Response.ContentType = "multipart/mixed; boundary=\"f5d5cf4d-5627-422f-b3c6-532f1a0cbc0a\"";
|
||||
ctx.Response.OutputStream.Write(responseBytes);
|
||||
}
|
||||
private static (string key, string value) GetHeader(string line)
|
||||
{
|
||||
var pieces = line.Split([':'], 2);
|
||||
|
||||
return (pieces[0].Trim(), pieces[1].Trim());
|
||||
}
|
||||
private static async Task<byte[]> SendReqLocalAndReadResponseAsync(byte[] bytes)
|
||||
{
|
||||
int line = 0;
|
||||
var bodyStartStr = Encoding.UTF8.GetString(bytes);
|
||||
|
||||
string method = "";
|
||||
string url = "";
|
||||
string httpVer = "";
|
||||
string authToken = "";
|
||||
List<NameValueHeaderValue> headers = new List<NameValueHeaderValue>();
|
||||
|
||||
int currentByte = 0;
|
||||
|
||||
foreach (var item in bodyStartStr.Split("\r\n"))
|
||||
{
|
||||
if (line == 0)
|
||||
{
|
||||
var parts = item.Split(" ");
|
||||
method = parts[0];
|
||||
url = parts[1];
|
||||
httpVer = parts[2];
|
||||
}
|
||||
else if (item == null || string.IsNullOrEmpty(item))
|
||||
{
|
||||
currentByte += 2;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
var h = GetHeader(item);
|
||||
headers.Add(new NameValueHeaderValue(h.key, h.value));
|
||||
|
||||
if (h.key == "Authorization")
|
||||
{
|
||||
authToken = h.value.Replace("Bearer ", "");
|
||||
}
|
||||
}
|
||||
currentByte += (2 + item.Length);
|
||||
line++;
|
||||
}
|
||||
byte[] body;
|
||||
if (currentByte == bytes.Length)
|
||||
{
|
||||
// empty body
|
||||
body = [];
|
||||
}
|
||||
else
|
||||
{
|
||||
// not empty body, TODO
|
||||
File.WriteAllBytes("notemptybody", bytes);
|
||||
body = bytes.Skip(currentByte).ToArray();
|
||||
}
|
||||
|
||||
if (!url.StartsWith("/v1/"))
|
||||
{
|
||||
throw new NotImplementedException("handler for " + url + " not implemented");
|
||||
}
|
||||
|
||||
url = url.Replace("/v1", "");
|
||||
|
||||
// find appropriate handler
|
||||
Console.WriteLine("BATCH: /v1" + url);
|
||||
|
||||
foreach (var item in LobbyHandler.Handlers)
|
||||
{
|
||||
if (item.Key == url)
|
||||
{
|
||||
item.Value.Reset();
|
||||
item.Value.Contents = body;
|
||||
await item.Value.HandleAsync(authToken);
|
||||
return item.Value.ReturnBytes;
|
||||
}
|
||||
}
|
||||
Console.WriteLine("HANDLER NOT FOUND: " + url);
|
||||
throw new Exception("handler not found: " + url);
|
||||
}
|
||||
|
||||
private static byte[] ReadStream(Stream stream)
|
||||
{
|
||||
byte[] data = new byte[1024];
|
||||
List<byte> allData = new List<byte>();
|
||||
do
|
||||
{
|
||||
int numBytesRead = stream.Read(data, 0, data.Length);
|
||||
|
||||
if (numBytesRead == data.Length)
|
||||
{
|
||||
allData.AddRange(data);
|
||||
}
|
||||
else if (numBytesRead > 0)
|
||||
{
|
||||
allData.AddRange(data.Take(numBytesRead));
|
||||
}
|
||||
else if (numBytesRead == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
return allData.ToArray();
|
||||
}
|
||||
|
||||
private static async Task HandleDataEndpoint(IHttpContext ctx)
|
||||
{
|
||||
// this endpoint does not appear to be needed, it is used for telemetry
|
||||
if (ctx.RequestedPath == "/v1/dsr/query")
|
||||
{
|
||||
WriteJsonString(ctx, "{\"ret\":0,\"msg\":\"\",\"status\":0,\"created_at\":\"0\",\"target_destroy_at\":\"0\",\"destroyed_at\":\"0\",\"err_code\":0,\"seq\":\"1\"}");
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.Response.StatusCode = 404;
|
||||
}
|
||||
}
|
||||
|
||||
private static HttpClient hs = new HttpClient();
|
||||
private static async Task HandleAsset(IHttpContext ctx)
|
||||
{
|
||||
string fs = AppDomain.CurrentDomain.BaseDirectory + "cache" + ctx.RequestedPath;
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(fs));
|
||||
if (!File.Exists(fs))
|
||||
{
|
||||
Logger.Info("Download " + fs);
|
||||
|
||||
// TODO: Ip might change
|
||||
var requestUri = new Uri("https://43.132.66.200/prdenv/" + ctx.RequestedPath);
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
|
||||
request.Headers.TryAddWithoutValidation("host", "cloud.nikke-kr.com");
|
||||
using var response = await hs.SendAsync(request);
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
using (var fss = new FileStream(fs, FileMode.CreateNew))
|
||||
{
|
||||
await response.Content.CopyToAsync(fss);
|
||||
|
||||
fss.Close();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error("FAILED TO DOWNLOAD FILE: " + ctx.RequestedPath);
|
||||
ctx.Response.StatusCode = 404;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
using (var fss = new FileStream(fs, FileMode.Open))
|
||||
{
|
||||
using (var responseStream = ctx.OpenResponseStream())
|
||||
{
|
||||
fss.CopyTo(responseStream);
|
||||
fss.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteData<T>(IHttpContext ctx, T data, bool encrypted = false) where T : IMessage, new()
|
||||
{
|
||||
ctx.Response.ContentEncoding = null;
|
||||
ctx.Response.ContentType = "application/octet-stream+protobuf";
|
||||
ctx.Response.ContentLength64 = data.CalculateSize();
|
||||
var x = new CodedOutputStream(ctx.Response.OutputStream);
|
||||
data.WriteTo(x);
|
||||
x.Flush();
|
||||
}
|
||||
private static void WriteJsonString(IHttpContext ctx, string data)
|
||||
{
|
||||
var bt = Encoding.UTF8.GetBytes(data);
|
||||
ctx.Response.ContentEncoding = null;
|
||||
ctx.Response.ContentType = "application/json";
|
||||
ctx.Response.ContentLength64 = bt.Length;
|
||||
ctx.Response.OutputStream.Write(bt, 0, bt.Length);
|
||||
ctx.Response.OutputStream.Flush();
|
||||
}
|
||||
|
||||
private static async Task HandleRouteData(IHttpContext ctx)
|
||||
{
|
||||
if (ctx.RequestedPath.Contains("/route_config.json"))
|
||||
{
|
||||
await ctx.SendStringAsync(@"{
|
||||
""Config"": [
|
||||
{
|
||||
""VersionRange"": {
|
||||
""From"": ""121.8.9"",
|
||||
""To"": ""121.10.2"",
|
||||
""PackageName"": ""com.proximabeta.nikke""
|
||||
},
|
||||
""Route"": [
|
||||
{
|
||||
""WorldId"": 81,
|
||||
""Name"": ""pub:live-jp"",
|
||||
""Url"": ""https://jp-lobby.nikke-kr.com/"",
|
||||
""Description"": ""JAPAN"",
|
||||
""Tags"": []
|
||||
},
|
||||
{
|
||||
""WorldId"": 82,
|
||||
""Name"": ""pub:live-na"",
|
||||
""Url"": ""https://us-lobby.nikke-kr.com/"",
|
||||
""Description"": ""NA"",
|
||||
""Tags"": []
|
||||
},
|
||||
{
|
||||
""WorldId"": 83,
|
||||
""Name"": ""pub:live-kr"",
|
||||
""Url"": ""https://kr-lobby.nikke-kr.com/"",
|
||||
""Description"": ""KOREA"",
|
||||
""Tags"": []
|
||||
},
|
||||
{
|
||||
""WorldId"": 84,
|
||||
""Name"": ""pub:live-global"",
|
||||
""Url"": ""https://global-lobby.nikke-kr.com/"",
|
||||
""Description"": ""GLOBAL"",
|
||||
""Tags"": []
|
||||
},
|
||||
{
|
||||
""WorldId"": 85,
|
||||
""Name"": ""pub:live-sea"",
|
||||
""Url"": ""https://sea-lobby.nikke-kr.com/"",
|
||||
""Description"": ""SEA"",
|
||||
""Tags"": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
""VersionRange"": {
|
||||
""From"": ""121.8.9"",
|
||||
""To"": ""121.10.2"",
|
||||
""PackageName"": ""com.gamamobi.nikke""
|
||||
},
|
||||
""Route"": [
|
||||
{
|
||||
""WorldId"": 91,
|
||||
""Name"": ""pub:live-hmt"",
|
||||
""Url"": ""https://hmt-lobby.nikke-kr.com/"",
|
||||
""Description"": ""HMT"",
|
||||
""Tags"": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}", "application/json", Encoding.Default);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("ROUTE - Unknown: " + ctx.RequestedPath);
|
||||
ctx.Response.StatusCode = 404;
|
||||
}
|
||||
}
|
||||
}
|
||||
public class LoggingHandler : DelegatingHandler
|
||||
{
|
||||
public LoggingHandler(HttpMessageHandler innerHandler)
|
||||
: base(innerHandler)
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
Console.WriteLine("Request:");
|
||||
Console.WriteLine(request.ToString());
|
||||
if (request.Content != null)
|
||||
{
|
||||
Console.WriteLine(await request.Content.ReadAsStringAsync());
|
||||
}
|
||||
Console.WriteLine();
|
||||
|
||||
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
|
||||
|
||||
Console.WriteLine("Response:");
|
||||
Console.WriteLine(response.ToString());
|
||||
if (response.Content != null)
|
||||
{
|
||||
Console.WriteLine(await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
Console.WriteLine();
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
948
nksrv/Protos/allmsgs.proto
Normal file
948
nksrv/Protos/allmsgs.proto
Normal file
@@ -0,0 +1,948 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option csharp_namespace = "nksrv";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/Duration.proto";
|
||||
|
||||
// Network structure definitions. Note that I have not included field names for anticheat strucures.
|
||||
|
||||
|
||||
message SentryDataResponse {
|
||||
double samplingRate = 1;
|
||||
double traceSamplingRate = 2;
|
||||
}
|
||||
|
||||
message CheckVersionRequest {
|
||||
string version = 1;
|
||||
}
|
||||
|
||||
message CheckVersionResponse {
|
||||
int32 availability = 1;
|
||||
}
|
||||
|
||||
message ResGetServerInfo {
|
||||
int32 worldId = 1;
|
||||
string matchUrl = 2;
|
||||
}
|
||||
|
||||
|
||||
message NetMaintenanceWindow {
|
||||
//Timestamp from = 1;
|
||||
//Timestamp to = 2;
|
||||
}
|
||||
|
||||
message MaintenanceNoticeResponse {
|
||||
NetMaintenanceWindow window = 1;
|
||||
bool isWhitelisted = 2;
|
||||
}
|
||||
|
||||
message ResourceHostRequest {
|
||||
string rversion = 1;
|
||||
}
|
||||
|
||||
message ResourceHostResponse {
|
||||
string version = 1;
|
||||
string baseUrl = 2;
|
||||
map<string, string> coreVersionMap = 3;
|
||||
map<string, string> dataPackVersionMap = 4;
|
||||
}
|
||||
|
||||
message StaticDataPackRequest {
|
||||
}
|
||||
|
||||
message StaticDataPackResponse {
|
||||
string url = 1;
|
||||
int64 size = 2;
|
||||
bytes sha256Sum = 3;
|
||||
bytes salt1 = 4;
|
||||
bytes salt2 = 5;
|
||||
string version = 6;
|
||||
}
|
||||
|
||||
enum BillingPlatform{
|
||||
Unknown = 0;
|
||||
Ios = 1;
|
||||
Android = 2;
|
||||
MidasBuy = 3;
|
||||
Windows = 4;
|
||||
}
|
||||
enum TLogPlatform {
|
||||
iOs = 0;
|
||||
Androiding = 1;
|
||||
Others = 2;
|
||||
Pc = 5;
|
||||
Cloud = 6;
|
||||
}
|
||||
|
||||
enum INTLEnvironment {
|
||||
None = 0;
|
||||
Hmt = 1;
|
||||
Global = 2;
|
||||
}
|
||||
|
||||
enum ResultOneofCase {
|
||||
Nones = 0;
|
||||
AuthSuccess = 1;
|
||||
BanInfo = 2;
|
||||
AuthError = 3;
|
||||
}
|
||||
enum AuthErrorCode {
|
||||
Oks = 0;
|
||||
Error = 1;
|
||||
ExpiredToken = 2;
|
||||
}
|
||||
|
||||
message NetBillingInfo
|
||||
{
|
||||
BillingPlatform platform = 1;
|
||||
}
|
||||
|
||||
message NetTLogDeviceInfo {
|
||||
TLogPlatform plat = 1;
|
||||
string systemSoftware = 2;
|
||||
string systemHardware = 3;
|
||||
string telecomOper = 4;
|
||||
string network = 5;
|
||||
int32 screenWidth = 6;
|
||||
int32 screenHeight = 7;
|
||||
int32 density = 8;
|
||||
string cpuHardware_ = 9;
|
||||
int32 memory = 10;
|
||||
string glRender = 11;
|
||||
string glVersion = 12;
|
||||
string deviceId = 13;
|
||||
string language = 14;
|
||||
string country = 15;
|
||||
string systemLanguage = 16;
|
||||
string clientVersion = 17;
|
||||
string xwid = 18;
|
||||
}
|
||||
|
||||
message NetDeviceInfo {
|
||||
string clientVersion = 1;
|
||||
string staticDataVersion = 2;
|
||||
INTLEnvironment intlEnvironment = 3;
|
||||
}
|
||||
|
||||
message ReqAuthIntl {
|
||||
NetDeviceInfo deviceInfo = 1;
|
||||
string openId = 2;
|
||||
string token = 3;
|
||||
int32 channelId = 4;
|
||||
string os = 5;
|
||||
NetBillingInfo billingInfo = 6;
|
||||
NetTLogDeviceInfo logDeviceInfo = 7;
|
||||
}
|
||||
enum PurchaseRestriction {
|
||||
Unknown2 = 0;
|
||||
Child = 1;
|
||||
Youth = 2;
|
||||
Adult = 3;
|
||||
}
|
||||
message NetUserPurchaseRestriction {
|
||||
PurchaseRestriction purchaseRestriction = 1;
|
||||
int64 updatedAt = 2;
|
||||
}
|
||||
|
||||
message NetAuthSuccess {
|
||||
string authToken = 1;
|
||||
string firstAuth = 2;
|
||||
string centauriZoneId = 4;
|
||||
NetUserPurchaseRestriction purchaseRestriction = 5;
|
||||
}
|
||||
|
||||
message NetBanInfo {
|
||||
string description = 1;
|
||||
google.protobuf.Timestamp endAt = 3;
|
||||
google.protobuf.Timestamp startAt = 4;
|
||||
int64 banId = 5;
|
||||
}
|
||||
|
||||
message NetAuthError {
|
||||
AuthErrorCode errorCode = 1;
|
||||
}
|
||||
|
||||
message AuthIntlResponse {
|
||||
oneof ResultOneof {
|
||||
NetAuthSuccess AuthSuccess = 1;
|
||||
NetBanInfo BanInfo = 2;
|
||||
NetAuthError AuthError = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message ReqLogout {}
|
||||
message ResLogout {}
|
||||
|
||||
message EnterServerRequest {
|
||||
string AuthToken = 1;
|
||||
bytes clientPublicKey = 2;
|
||||
}
|
||||
|
||||
message NetFeatureDataInfo {
|
||||
string featureName = 1;
|
||||
bytes featureData = 2;
|
||||
int32 dataLen = 3;
|
||||
int32 crc = 4;
|
||||
bool useFeatureData = 100;
|
||||
}
|
||||
|
||||
message NetLegacyUserIdentifier {
|
||||
int32 server = 1;
|
||||
int64 usn = 2;
|
||||
}
|
||||
|
||||
message EnterServerResponse {
|
||||
string gameClientToken = 1;
|
||||
bytes encryptionToken = 2;
|
||||
NetFeatureDataInfo featureDataInfo = 3;
|
||||
google.protobuf.Duration shouldRestartAfter = 4;
|
||||
NetLegacyUserIdentifier identifier = 100;
|
||||
}
|
||||
|
||||
message GetUserDataRequest {
|
||||
bool IsSimple = 1;
|
||||
}
|
||||
message UserDataResponse {
|
||||
|
||||
}
|
||||
|
||||
message ReqGetContentsOpenData {
|
||||
|
||||
}
|
||||
|
||||
message ResGetContentsOpenData {
|
||||
int32 guildLevel = 2;
|
||||
int64 maxGachaCount = 4;
|
||||
int64 maxGachaPremiumCount = 5;
|
||||
repeated int32 clearStageList = 6;
|
||||
int32 tutorialGachaPlayCount = 7;
|
||||
repeated int32 clearSimRoomChapterList = 8;
|
||||
}
|
||||
|
||||
message ReqEnterLobbyServer {
|
||||
|
||||
}
|
||||
|
||||
message NetTutorialData {
|
||||
int32 groupId = 1;
|
||||
int32 lastClearedTid = 2;
|
||||
int32 lastClearedVersion = 3;
|
||||
}
|
||||
|
||||
message NetUserData {
|
||||
int32 exp = 2;
|
||||
int32 lv = 3;
|
||||
int32 icon = 7;
|
||||
bool iconPrism = 8;
|
||||
int32 frame = 9;
|
||||
string desc = 10;
|
||||
int32 sendFriendshipCount = 11;
|
||||
int32 receiveFriendshipCount = 12;
|
||||
int32 arenaCount = 14;
|
||||
string lastDialogId = 15;
|
||||
int32 counselCount = 16;
|
||||
int32 commanderRoomJukebox = 17;
|
||||
int32 lobbyJukebox = 18;
|
||||
int32 specialArenaCount = 19;
|
||||
int32 costumeLv = 20;
|
||||
int32 infraCoreExp = 21;
|
||||
int32 infraCoreLv = 22;
|
||||
repeated NetTutorialData tutorials = 24;
|
||||
int64 guildLeaveAt = 25;
|
||||
int32 outpostFastBattleCount = 26;
|
||||
}
|
||||
|
||||
message NetUserCurrencyData {
|
||||
int32 type = 1;
|
||||
int64 value = 2;
|
||||
}
|
||||
|
||||
message NetUserCharacterDefaultData {
|
||||
int64 csn = 1;
|
||||
int32 tid = 2;
|
||||
int32 lv = 4;
|
||||
int32 grade = 5;
|
||||
int32 dispatchTid = 6;
|
||||
int32 costumeId = 7;
|
||||
int32 ultiSkillLv = 8;
|
||||
int32 skill1Lv = 9;
|
||||
int32 skill2Lv = 10;
|
||||
}
|
||||
|
||||
message NetUserCharacterData {
|
||||
NetUserCharacterDefaultData default = 1;
|
||||
bool isSynchro = 2;
|
||||
int64 artifact = 3;
|
||||
}
|
||||
|
||||
message NetTeamSlot {
|
||||
int32 slot = 1;
|
||||
int64 value = 2;
|
||||
int32 valueType = 3;
|
||||
}
|
||||
|
||||
message NetTeamData {
|
||||
int32 teamNumber = 1;
|
||||
repeated NetTeamSlot slots = 2;
|
||||
}
|
||||
|
||||
message NetUserTeamData {
|
||||
int32 type = 1;
|
||||
int32 lastContentsTeamNumber = 2;
|
||||
repeated NetTeamData teams = 3;
|
||||
}
|
||||
|
||||
message NetUserItemData {
|
||||
int64 isn = 1;
|
||||
int32 tid = 2;
|
||||
int32 count = 3;
|
||||
int64 csn = 4;
|
||||
int32 corporation = 5;
|
||||
int32 lv = 6;
|
||||
int32 exp = 7;
|
||||
int32 position = 8;
|
||||
}
|
||||
|
||||
message NetUserAttractiveData {
|
||||
int32 nameCode = 1;
|
||||
int32 lvl = 2;
|
||||
int32 exp = 3;
|
||||
bool isCounsel = 4;
|
||||
repeated int32 completeCounsels = 5;
|
||||
repeated int32 completeRewardLevels = 6;
|
||||
bool isFavorites = 7;
|
||||
int32 counseledCount = 8;
|
||||
}
|
||||
|
||||
message NetWholeTeamSlot {
|
||||
int32 slot = 1;
|
||||
int32 tid = 2;
|
||||
int32 lvl = 3;
|
||||
int64 csn = 4;
|
||||
int32 costumeId = 5;
|
||||
}
|
||||
|
||||
message NetWholeUserTeamData {
|
||||
int32 type = 1;
|
||||
int32 teamNumber = 2;
|
||||
int32 teamCombat = 3;
|
||||
repeated NetWholeTeamSlot slots = 4;
|
||||
}
|
||||
|
||||
message NetUserOutpostData {
|
||||
int32 slotId = 1;
|
||||
int32 buildingId = 2;
|
||||
bool isDone = 3;
|
||||
int64 startAt = 4;
|
||||
int64 completeAt = 5;
|
||||
}
|
||||
|
||||
message NetUserHarmonyCubeData {
|
||||
int64 isn = 1;
|
||||
int32 tid = 2;
|
||||
int32 lvl = 3;
|
||||
repeated int64 csnList = 4;
|
||||
}
|
||||
|
||||
message NetUserRecycleRoomData {
|
||||
int32 tid = 1;
|
||||
int32 lvl = 2;
|
||||
int32 exp = 3;
|
||||
}
|
||||
|
||||
enum OutpostBuffSourceType {
|
||||
TacticAcademy = 0;
|
||||
Subscription = 1;
|
||||
}
|
||||
|
||||
message NetTimeRewardBuff {
|
||||
int32 tid = 1;
|
||||
int32 functionType = 2;
|
||||
int64 valueType = 3;
|
||||
OutpostBuffSourceType sourceType = 4;
|
||||
}
|
||||
|
||||
message NetTimeReward {
|
||||
int32 useId = 1;
|
||||
int64 valuePerMinAfterBuff = 2;
|
||||
int64 valuePerMinBeforeBuff = 3;
|
||||
repeated NetTimeRewardBuff buffs = 4;
|
||||
}
|
||||
|
||||
message NetOutpostBattleLevel {
|
||||
bool isLevelUp = 1;
|
||||
int32 level = 2;
|
||||
int32 exp = 3;
|
||||
}
|
||||
|
||||
message NetUserDispatchData {
|
||||
int32 tid = 1;
|
||||
int32 isRun = 2;
|
||||
int64 startAt = 3;
|
||||
int64 endAt = 4;
|
||||
}
|
||||
|
||||
message NetEquipmentAwakeningOption {
|
||||
int32 option1Id = 1;
|
||||
bool option1Lock = 2;
|
||||
int32 option2Id = 3;
|
||||
bool option2Lock = 4;
|
||||
int32 option3Id = 5;
|
||||
bool option3Lock = 6;
|
||||
}
|
||||
|
||||
message NetEquipmentAwakening {
|
||||
int64 isn = 1;
|
||||
NetEquipmentAwakeningOption option = 2;
|
||||
}
|
||||
|
||||
message NetUserRedeemData {
|
||||
int64 redeemId = 1;
|
||||
int32 tid = 2;
|
||||
int32 count = 3;
|
||||
int64 deleteTime = 4;
|
||||
}
|
||||
|
||||
message NetOutpostBattleTime {
|
||||
int64 battleTime = 1;
|
||||
int64 maxBattleTime = 2;
|
||||
int64 overBattleTime = 3;
|
||||
int64 maxOverBattleTime = 4;
|
||||
}
|
||||
|
||||
message ResEnterLobbyServer {
|
||||
string nickname = 2;
|
||||
NetUserData user = 4;
|
||||
repeated NetUserCurrencyData currency = 5;
|
||||
repeated NetUserCharacterData character = 7;
|
||||
repeated NetUserTeamData typeTeams = 8;
|
||||
repeated NetUserItemData items = 9;
|
||||
repeated NetUserAttractiveData attractives = 10;
|
||||
int32 lastClearedNormalMainStageId = 14;
|
||||
int32 lastClearedHardMainStageId = 15;
|
||||
int32 lastClearedMod = 16;
|
||||
int64 gsn = 18;
|
||||
NetWholeUserTeamData representationTeam = 19;
|
||||
repeated NetUserOutpostData outposts = 23;
|
||||
int32 synchroLv = 24;
|
||||
repeated NetUserHarmonyCubeData harmonyCubes = 25;
|
||||
repeated NetUserRecycleRoomData recycle = 26;
|
||||
repeated NetTimeReward timeRewardBuffs = 27;
|
||||
repeated int32 clearLessons = 28;
|
||||
string userGlobalOptions = 29;
|
||||
NetOutpostBattleLevel outpostBattleLevel = 31;
|
||||
int64 battleTime = 32;
|
||||
int64 maxBattleTime = 33;
|
||||
repeated int64 synchroStandardCharacters = 34;
|
||||
int32 resetHour = 35;
|
||||
repeated NetUserDispatchData dispatchList = 36;
|
||||
repeated NetEquipmentAwakening awakening = 37;
|
||||
repeated NetUserRedeemData redeems = 38;
|
||||
NetOutpostBattleTime outpostBattleTime = 39;
|
||||
}
|
||||
|
||||
message ReqGetMainQuestData {
|
||||
|
||||
}
|
||||
|
||||
message NetMainQuestData {
|
||||
int32 tid = 1;
|
||||
bool isReceived = 2;
|
||||
}
|
||||
|
||||
message ResGetMainQuestData {
|
||||
repeated NetMainQuestData mainQuestList = 2;
|
||||
}
|
||||
|
||||
message ReqUserOnlineStateLog {
|
||||
|
||||
}
|
||||
|
||||
message ResUserOnlineStateLog {
|
||||
|
||||
}
|
||||
|
||||
// Outpost
|
||||
|
||||
message ReqGetRecycleRoomData {
|
||||
|
||||
}
|
||||
|
||||
message ResGetRecycleRoomData {
|
||||
repeated NetUserRecycleRoomData recycle = 2;
|
||||
}
|
||||
|
||||
message ReqGetJukeboxRewardedData {}
|
||||
|
||||
message ResGetJukeboxRewardedData {
|
||||
repeated int32 jukeboxMissionTidList = 2;
|
||||
}
|
||||
|
||||
message ReqGetNow {}
|
||||
|
||||
message ResGetNow {
|
||||
int64 tick = 1;
|
||||
int32 resetHour = 2;
|
||||
google.protobuf.Duration cheatShiftDuration = 3;
|
||||
}
|
||||
|
||||
enum BadgeContents {
|
||||
|
||||
BadgeNone = 0;
|
||||
Mailbox = 2000;
|
||||
MailboxMessage = 2001;
|
||||
NikkeNew = 4000;
|
||||
JukeboxTheme = 5006;
|
||||
UserFrame = 6000;
|
||||
UserIcon = 6001;
|
||||
FriendNew = 8001;
|
||||
FriendRequestNew = 8002;
|
||||
FriendShipPointNew = 8003;
|
||||
UnionRequestNew = 9000;
|
||||
UnionEmblemNew = 9001;
|
||||
UnionChangeGrade = 9004;
|
||||
UnionGradeUp = 9005;
|
||||
UnionWorldRaid = 9006;
|
||||
UnionRaid = 9007;
|
||||
BargainCostumeNew = 10000;
|
||||
LiveWallpaper = 11004;
|
||||
ContentsOpen = 12024;
|
||||
LostSector = 13001;
|
||||
HarmonyCubeNew = 13002;
|
||||
CollectionCostumeLevelupEnabled = 14000;
|
||||
CollectionCostumeNew = 14001;
|
||||
InfraCore = 16000;
|
||||
InfraCoreRewardExist = 16001;
|
||||
Ranking = 17000;
|
||||
LoginAttendanceReward = 19000;
|
||||
EventFieldNew = 19001;
|
||||
StoryEventHardContentsOpen = 19002;
|
||||
ArchiveRecordNew = 19003;
|
||||
ArchiveMessengerNew = 19004;
|
||||
ArchiveMessengerFinish = 19005;
|
||||
MiniGameBbqCutScene = 20101;
|
||||
MiniGameDessertRushUnlock = 20111;
|
||||
MiniGameTabTabMaidLevelUp = 20121;
|
||||
MiniGameTabTabMaidNewNormalDifficulty = 20122;
|
||||
MiniGameTabTabMaidNewHardDifficulty = 20123;
|
||||
SimRoomNewDifficulty = 22001;
|
||||
SimRoomRefreshed = 22002;
|
||||
SoloRaidTicket = 23001;
|
||||
InterceptTicket = 31000;
|
||||
Notice = 32000;
|
||||
SynchroDeviceNewSlot = 33001;
|
||||
SynchroDeviceUpgrade = 33002;
|
||||
DispatchBoardDone = 36001;
|
||||
DispatchBoardRefreshed = 36002;
|
||||
DispatchBoardRankUp = 36003;
|
||||
InAppShopNewPackage = 40001;
|
||||
InAppShopNewSubscriptionProduct = 40002;
|
||||
WishlistContainsMaxGradeCore = 50001;
|
||||
}
|
||||
|
||||
message NetBadge {
|
||||
int64 SeqFieldNumber = 1;
|
||||
bytes badgeGuid = 2;
|
||||
BadgeContents badgeContent = 3;
|
||||
string location = 4;
|
||||
}
|
||||
|
||||
message NetUniqueBadge {
|
||||
int64 SeqFieldNumber = 1;
|
||||
bytes badgeGuid = 2;
|
||||
BadgeContents badgeContent = 3;
|
||||
string location = 4;
|
||||
}
|
||||
|
||||
message ReqSyncBadge {
|
||||
int64 lastBadgeSeq = 1;
|
||||
repeated NetBadge newClientBadgeList = 2;
|
||||
int64 lastUniqueBadgeSeq = 3;
|
||||
repeated NetUniqueBadge newUniqueBadgeList = 4;
|
||||
}
|
||||
|
||||
message ResSyncBadge {
|
||||
repeated NetBadge badgeList = 2;
|
||||
repeated NetUniqueBadge uniqueBadgeList = 4;
|
||||
}
|
||||
|
||||
message ReqSyncTrigger {
|
||||
int64 seq = 2;
|
||||
bool isTrustedClientSeq = 3;
|
||||
}
|
||||
|
||||
message NetTrigger {
|
||||
int64 seq = 1;
|
||||
int64 createdAt = 2;
|
||||
int32 trigger = 3;
|
||||
int32 conditionId = 4;
|
||||
int64 userValue = 5;
|
||||
bool reset = 6;
|
||||
}
|
||||
|
||||
message ResSyncTrigger {
|
||||
bool hasRemainData = 1;
|
||||
repeated NetTrigger triggers = 2;
|
||||
bool restart = 3;
|
||||
}
|
||||
|
||||
message ReqGetMessages {
|
||||
int64 seg = 2;
|
||||
}
|
||||
|
||||
message NetMessage {
|
||||
int64 seg = 1;
|
||||
string conversationId = 2;
|
||||
string messageId = 3;
|
||||
int64 createdAt = 4;
|
||||
int32 state = 5;
|
||||
}
|
||||
message NetPickedMessage {
|
||||
google.protobuf.Timestamp createdAt = 1;
|
||||
string conversationId = 2;
|
||||
}
|
||||
|
||||
message ResGetMessages {
|
||||
repeated NetMessage messages = 2;
|
||||
}
|
||||
|
||||
message ReqGetPickedMessageList {
|
||||
|
||||
}
|
||||
|
||||
message ResGetPickedMessageList {
|
||||
repeated NetPickedMessage data = 1;
|
||||
}
|
||||
|
||||
message ReqPickTodayDailyMessage {
|
||||
repeated int32 conditionTriggerIds = 1;
|
||||
}
|
||||
message ResPickTodayDailyMessage {}
|
||||
|
||||
message ReqGetGachaData {}
|
||||
|
||||
message NetUserGachaData {
|
||||
int32 gachaType = 6;
|
||||
int32 playCount = 7;
|
||||
}
|
||||
|
||||
message NetGachaCustomData {
|
||||
int32 type = 1;
|
||||
int32 tid = 2;
|
||||
}
|
||||
|
||||
message NetGachaEvent {
|
||||
int32 gachaTypeId = 1;
|
||||
int32 freeCount = 2;
|
||||
}
|
||||
|
||||
message NetUserGachaDiscountData {
|
||||
int32 gachaTypeId = 1;
|
||||
int32 count = 2;
|
||||
}
|
||||
|
||||
message ResGetGachaData {
|
||||
repeated NetUserGachaData gacha = 2;
|
||||
repeated NetGachaCustomData multipleCustom = 3;
|
||||
repeated NetGachaEvent gachaEventData = 4;
|
||||
repeated NetUserGachaDiscountData gachaDiscountData = 5;
|
||||
}
|
||||
|
||||
message NetJupiterProductInfo {
|
||||
string productId = 1;
|
||||
string price = 2;
|
||||
string currencyCode =3;
|
||||
string currencySymbol = 4;
|
||||
int64 microPrice = 5;
|
||||
}
|
||||
|
||||
message ReqGetJupiterProductList {
|
||||
repeated string productIdList = 1;
|
||||
}
|
||||
|
||||
message ResGetJupiterProductList {
|
||||
repeated NetJupiterProductInfo productInfoList = 1;
|
||||
}
|
||||
|
||||
message ReqForcePickTodayRandomMessage {
|
||||
int64 usn = 1;
|
||||
repeated int32 conditionTriggerIds = 2;
|
||||
}
|
||||
|
||||
message ResForcePickTodayRandomMessage {
|
||||
|
||||
}
|
||||
|
||||
message NetCentauriPurchaseExtraData {
|
||||
string productId = 1;
|
||||
string billId = 2;
|
||||
string chargeAmount = 3;
|
||||
}
|
||||
|
||||
message ReqRefreshChargeCurrencyData {
|
||||
NetCentauriPurchaseExtraData txCentauriExtra = 1;
|
||||
}
|
||||
|
||||
message ResRefreshChargeCurrencyData {
|
||||
NetUserCurrencyData chargeCash = 1;
|
||||
NetUserCurrencyData freeCash = 2;
|
||||
}
|
||||
|
||||
message NetAntiCheatBattleTLogAdditionalInfo {
|
||||
string a = 1;
|
||||
int64 b = 2;
|
||||
}
|
||||
|
||||
message ReqDeleteBadge {
|
||||
repeated int64 badgeSeqList = 1;
|
||||
}
|
||||
message ResDeleteBadge{}
|
||||
|
||||
message ReqEnterStage {
|
||||
int32 stageId = 2;
|
||||
int32 teamNumber = 3;
|
||||
NetAntiCheatBattleTLogAdditionalInfo antiCheatAdditionalInfo = 4;
|
||||
}
|
||||
message ResEnterStage {}
|
||||
|
||||
message NetFieldStageData {
|
||||
string positionId = 1;
|
||||
int32 stageId = 2;
|
||||
}
|
||||
|
||||
message NetFieldObject {
|
||||
int64 actionAt = 1;
|
||||
int32 type = 2;
|
||||
string positionId = 3;
|
||||
string json = 4;
|
||||
}
|
||||
|
||||
message NetFieldObjectData {
|
||||
repeated NetFieldStageData stages = 1;
|
||||
repeated NetFieldObject objects = 2;
|
||||
}
|
||||
|
||||
message ReqGetStageData {
|
||||
int32 chapter = 2;
|
||||
int32 mod = 3;
|
||||
}
|
||||
|
||||
message ResGetStageData {
|
||||
NetFieldObjectData field = 2;
|
||||
bool hasChapterBossEntered = 3;
|
||||
string squadData = 4;
|
||||
}
|
||||
|
||||
message NetVector3 {
|
||||
float x = 1;
|
||||
float y = 2;
|
||||
float z = 3;
|
||||
}
|
||||
|
||||
message NetCampaignTeamPosition {
|
||||
int32 teamNumber = 1;
|
||||
int32 type = 2;
|
||||
NetVector3 position = 3;
|
||||
}
|
||||
|
||||
message ReqGetCampaignFieldData {
|
||||
string mapId = 2;
|
||||
}
|
||||
|
||||
message ResGetCampaignFieldData {
|
||||
NetFieldObjectData field = 2;
|
||||
NetUserTeamData team = 3;
|
||||
NetCampaignTeamPosition teamPositions = 4;
|
||||
string json = 5;
|
||||
}
|
||||
|
||||
message ReqExistScenario {
|
||||
repeated string scenarioGroupIds = 1;
|
||||
}
|
||||
|
||||
message ResExistScenario {
|
||||
repeated string existGroupIds = 1;
|
||||
}
|
||||
|
||||
message ReqBattleReportData {
|
||||
int32 waveId = 1;
|
||||
bytes reportData3 = 2;
|
||||
}
|
||||
|
||||
message ResBattleReportData {}
|
||||
|
||||
|
||||
message ReqAntibotRecvData {
|
||||
bytes data = 1;
|
||||
}
|
||||
|
||||
message ResAntibotRecvData {}
|
||||
|
||||
message ReqSetScenarioComplete {
|
||||
string scenarioId = 2;
|
||||
}
|
||||
|
||||
message NetIncreaseExpData {
|
||||
int64 csn = 1;
|
||||
int32 nameCode = 2;
|
||||
int32 beforeLv = 3;
|
||||
int32 beforeExp = 4;
|
||||
int32 currentLv = 5;
|
||||
int32 currentExp = 6;
|
||||
int32 gainExp = 7;
|
||||
int32 increaseExp = 8;
|
||||
}
|
||||
|
||||
message NetItemData{
|
||||
int64 isn = 1;
|
||||
int32 tid = 2;
|
||||
int32 count = 3;
|
||||
int32 corporation = 4;
|
||||
}
|
||||
|
||||
message NetCurrencyData{
|
||||
int32 type = 1;
|
||||
int64 value = 2;
|
||||
int64 finalValue = 3;
|
||||
}
|
||||
|
||||
message NetCharacterData {
|
||||
int64 csn = 1;
|
||||
int32 tid = 2;
|
||||
int32 pieceCount = 3;
|
||||
int64 currencyValue = 4;
|
||||
}
|
||||
|
||||
message NetProfileFrameData {
|
||||
int32 frameTid = 1;
|
||||
int32 triggerTid = 2;
|
||||
google.protobuf.Timestamp acquiredAt = 3;
|
||||
}
|
||||
|
||||
message NetPointData {
|
||||
int32 tid = 1;
|
||||
int64 value = 2;
|
||||
}
|
||||
|
||||
message NetPassPointData{
|
||||
int32 value = 1;
|
||||
int32 finalValue = 2;
|
||||
}
|
||||
|
||||
message NetAutoChargeData {
|
||||
int32 autoChargeId = 1;
|
||||
int32 finalValue = 2;
|
||||
int32 value = 3;
|
||||
}
|
||||
|
||||
message NetRedeemData {
|
||||
int64 redeemId = 1;
|
||||
int32 tid = 2;
|
||||
int32 count = 3;
|
||||
int64 deleteTime = 4;
|
||||
}
|
||||
|
||||
message NetUserFavoriteItemData {
|
||||
int32 favoriteItemId = 1;
|
||||
int32 tid = 2;
|
||||
int64 csn = 3;
|
||||
int32 lv = 4;
|
||||
int32 exp = 5;
|
||||
}
|
||||
|
||||
message NetFavoriteItemData {
|
||||
int32 favoriteItemId = 1;
|
||||
int32 tid = 2;
|
||||
int64 csn = 3;
|
||||
int32 lv = 4;
|
||||
int32 exp = 5;
|
||||
}
|
||||
|
||||
message NetRewardData {
|
||||
NetIncreaseExpData userExp = 1;
|
||||
repeated NetItemData item = 2;
|
||||
repeated NetCurrencyData currency = 3;
|
||||
repeated NetCharacterData character = 4;
|
||||
repeated NetProfileFrameData profileFrame = 6;
|
||||
repeated NetIncreaseExpData characterAttractive = 7;
|
||||
repeated int32 jukeboxBgm = 8;
|
||||
repeated int32 memorial = 9;
|
||||
repeated int32 livewallpaper = 10;
|
||||
repeated int32 characterCostume = 11;
|
||||
repeated NetPointData point = 12;
|
||||
NetIncreaseExpData infraCoreExp = 13;
|
||||
repeated NetUserCharacterDefaultData userCharacters = 14;
|
||||
repeated NetUserItemData userItems = 15;
|
||||
NetPassPointData passPoint = 16;
|
||||
repeated NetAutoChargeData autoChargeList = 17;
|
||||
repeated NetUserRedeemData userRedeems = 18;
|
||||
repeated NetRedeemData redeem = 19;
|
||||
repeated NetUserFavoriteItemData userFavoriteItems = 20;
|
||||
NetFavoriteItemData favoriteItems = 21;
|
||||
repeated int32 profileCardObjects = 22;
|
||||
}
|
||||
|
||||
|
||||
message ResSetScenarioComplete {
|
||||
NetRewardData reward = 2;
|
||||
}
|
||||
|
||||
message NetAntiCheatCharacter{
|
||||
|
||||
}
|
||||
|
||||
message NetAntiCheatMonster{}
|
||||
|
||||
message NetAntiCheatBattleData {
|
||||
int32 a = 1;
|
||||
int32 b = 3;
|
||||
int32 c = 4;
|
||||
int32 d = 5;
|
||||
int32 e = 6;
|
||||
int32 f = 7;
|
||||
int32 g = 8;
|
||||
repeated NetAntiCheatCharacter h = 9;
|
||||
repeated NetAntiCheatMonster i = 10;
|
||||
bool b1 = 11;
|
||||
int32 i2 = 12;
|
||||
}
|
||||
|
||||
message ReqClearStage {
|
||||
int32 stageId = 2;
|
||||
int32 teamNumber = 3;
|
||||
int32 battleResult = 4;
|
||||
|
||||
}
|
||||
|
||||
message ResClearStage {
|
||||
NetRewardData reward = 2;
|
||||
NetOutpostBattleLevel outpostBattle = 4;
|
||||
NetRewardData userLevelUpReward = 5;
|
||||
NetRewardData outpostBattleLevelReward = 6;
|
||||
NetRewardData stageClearReward = 7;
|
||||
NetRewardData scenarioReward = 8;
|
||||
}
|
||||
|
||||
message ReqGetCharacterData {
|
||||
|
||||
}
|
||||
|
||||
message ResGetCharacterData {
|
||||
repeated NetUserCharacterData character = 2;
|
||||
repeated int64 synchroStandardCharacters = 3;
|
||||
}
|
||||
|
||||
message NetSupportCharacterUsedCount {
|
||||
int32 teamType = 1;
|
||||
int32 supportCharacterId = 2;
|
||||
int32 usedSupportCharacterCount = 3;
|
||||
}
|
||||
|
||||
message ReqListSupportCharacterUsedCount {
|
||||
repeated int32 teamTypeList = 1;
|
||||
}
|
||||
|
||||
message ResListSupportCharacterUsedCount {
|
||||
repeated NetSupportCharacterUsedCount supportCharacterUsedCountList = 1;
|
||||
}
|
||||
6
nksrv/Protos/outpost.proto
Normal file
6
nksrv/Protos/outpost.proto
Normal file
@@ -0,0 +1,6 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option csharp_namespace = "nksrv";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/Duration.proto";
|
||||
61
nksrv/Utils/GreatLogger.cs
Normal file
61
nksrv/Utils/GreatLogger.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using Swan.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.Utils
|
||||
{
|
||||
public class GreatLogger : ILogger
|
||||
{
|
||||
public LogLevel LogLevel => LogLevel.Info;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Log(LogMessageReceivedEventArgs logEvent)
|
||||
{
|
||||
var fg = Console.ForegroundColor;
|
||||
Console.ForegroundColor = GetColorForMsg(logEvent);
|
||||
|
||||
var msg = logEvent.Message;
|
||||
if (msg.StartsWith("["))
|
||||
{
|
||||
msg = msg.Substring(msg.IndexOf("]") + 2);
|
||||
}
|
||||
Console.WriteLine(msg);
|
||||
|
||||
Console.ForegroundColor = fg;
|
||||
}
|
||||
|
||||
private ConsoleColor GetColorForMsg(LogMessageReceivedEventArgs logEvent)
|
||||
{
|
||||
if (logEvent.Message.Contains("404 Not Found"))
|
||||
return ConsoleColor.Red;
|
||||
else if (logEvent.Message.Contains("200 OK"))
|
||||
return ConsoleColor.DarkGreen;
|
||||
switch (logEvent.MessageType)
|
||||
{
|
||||
case LogLevel.None:
|
||||
return ConsoleColor.White;
|
||||
case LogLevel.Trace:
|
||||
return ConsoleColor.Gray;
|
||||
case LogLevel.Debug:
|
||||
return ConsoleColor.Gray;
|
||||
case LogLevel.Info:
|
||||
return ConsoleColor.Gray;
|
||||
case LogLevel.Warning:
|
||||
return ConsoleColor.Yellow;
|
||||
case LogLevel.Error:
|
||||
return ConsoleColor.Red;
|
||||
case LogLevel.Fatal:
|
||||
return ConsoleColor.Red;
|
||||
default:
|
||||
return ConsoleColor.White;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
81
nksrv/Utils/JsonDb.cs
Normal file
81
nksrv/Utils/JsonDb.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using ASodium;
|
||||
using Newtonsoft.Json;
|
||||
using nksrv.LobbyServer;
|
||||
using Swan.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.Utils
|
||||
{
|
||||
public class AccessToken
|
||||
{
|
||||
public string Token;
|
||||
public long ExpirationTime;
|
||||
public ulong UserID;
|
||||
}
|
||||
public class User
|
||||
{
|
||||
// User info
|
||||
public string Username = "";
|
||||
public string Password = "";
|
||||
public string PlayerName = "";
|
||||
public ulong ID;
|
||||
public long RegisterTime;
|
||||
|
||||
|
||||
// Game data
|
||||
public List<string> CompletedScenarios = new List<string>();
|
||||
}
|
||||
public class CoreInfo
|
||||
{
|
||||
public List<User> Users = new List<User>();
|
||||
|
||||
public List<AccessToken> LauncherAccessTokens = new List<AccessToken>();
|
||||
|
||||
|
||||
public Dictionary<string, GameClientInfo> GameClientTokens = new Dictionary<string, GameClientInfo>();
|
||||
}
|
||||
internal class JsonDb
|
||||
{
|
||||
public static CoreInfo Instance { get; internal set; }
|
||||
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"))
|
||||
{
|
||||
Logger.Warn("users: warning: configuration not found, writing default data");
|
||||
Instance = new CoreInfo();
|
||||
Save();
|
||||
}
|
||||
Logger.Info("Loaded db");
|
||||
|
||||
|
||||
var j = JsonConvert.DeserializeObject<CoreInfo>(File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + "/db.json"));
|
||||
if (j != null)
|
||||
{
|
||||
Instance = j;
|
||||
Save();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Failed to read configuration json file");
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
316
nksrv/Utils/PacketDecryption.cs
Normal file
316
nksrv/Utils/PacketDecryption.cs
Normal file
@@ -0,0 +1,316 @@
|
||||
using EmbedIO;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using nksrv.LobbyServer;
|
||||
using Sodium;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.Utils
|
||||
{
|
||||
public class PacketDecryption
|
||||
{
|
||||
public static async Task<PacketDecryptResponse> DecryptOrReturnContentAsync(IHttpContext ctx, bool decompress = false)
|
||||
{
|
||||
byte[] bin = Array.Empty<byte>();
|
||||
|
||||
using MemoryStream buffer = new MemoryStream();
|
||||
|
||||
var stream = ctx.Request.InputStream;
|
||||
|
||||
var encoding = ctx.Request.Headers[HttpHeaderNames.ContentEncoding]?.Trim();
|
||||
|
||||
Stream decryptedStream;
|
||||
switch (encoding)
|
||||
{
|
||||
case CompressionMethodNames.Gzip:
|
||||
decryptedStream = new GZipStream(stream, CompressionMode.Decompress);
|
||||
break;
|
||||
case CompressionMethodNames.Deflate:
|
||||
decryptedStream = new DeflateStream(stream, CompressionMode.Decompress);
|
||||
break;
|
||||
case CompressionMethodNames.None:
|
||||
case null:
|
||||
decryptedStream = stream;
|
||||
break;
|
||||
case "gzip,enc":
|
||||
var responseLen = CBorReadItem(stream); // length of header (not including encrypted data)
|
||||
stream.ReadByte(); // ignore padding
|
||||
stream.ReadByte(); // ignore padding
|
||||
|
||||
var decryptionToken = CBorReadString(stream);
|
||||
var nonce = CBorReadByteString(stream);
|
||||
|
||||
MemoryStream encryptedBytes = new MemoryStream();
|
||||
stream.CopyTo(encryptedBytes);
|
||||
|
||||
var bytes = encryptedBytes.ToArray();
|
||||
|
||||
var key = LobbyHandler.GetInfo(decryptionToken);
|
||||
if (key == null)
|
||||
{
|
||||
throw HttpException.BadRequest("Invalid decryption token");
|
||||
}
|
||||
|
||||
var additionalData = GenerateAdditionalData(decryptionToken, false);
|
||||
|
||||
var x = SecretAeadXChaCha20Poly1305.Decrypt(bytes, nonce, key.Keys.ReadSharedSecret, additionalData.ToArray());
|
||||
|
||||
var ms = new MemoryStream(x);
|
||||
// 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;
|
||||
//Console.WriteLine("seg #: " + seqNum + ",actual:" + bytes.Length + "cntlen:" + ctx.Request.ContentLength64);
|
||||
|
||||
var contents = x.Skip(startPos).ToArray();
|
||||
if (contents.Length != 0 && contents[0] == 31)
|
||||
{
|
||||
//File.WriteAllBytes("contentsgzip", contents);
|
||||
// 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();
|
||||
}
|
||||
|
||||
return new PacketDecryptResponse() { Contents = contents, UserId = key.UserId, UsedAuthToken = decryptionToken };
|
||||
default:
|
||||
throw HttpException.BadRequest($"Unsupported content encoding \"{encoding}\"");
|
||||
}
|
||||
|
||||
|
||||
await stream.CopyToAsync(buffer, 81920, ctx.CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
|
||||
return new PacketDecryptResponse() { Contents = buffer.ToArray() };
|
||||
}
|
||||
|
||||
public static byte[] EncryptData(byte[] message, string authToken)
|
||||
{
|
||||
var key = LobbyHandler.GetInfo(authToken);
|
||||
if (key == null)
|
||||
{
|
||||
throw HttpException.BadRequest("Invalid decryption token");
|
||||
}
|
||||
|
||||
MemoryStream m = new MemoryStream();
|
||||
|
||||
m.WriteByte(89); // cbor ushort
|
||||
|
||||
// 42bytes of data past header, 3 bytes for auth token bytestring, 2 bytes for nonce prefix, 24 bytes for nonce data
|
||||
var headerLen = 2 + 3 + authToken.Length + 2 + 24;
|
||||
byte[] headerLenBytes = BitConverter.GetBytes((ushort)headerLen);
|
||||
if (BitConverter.IsLittleEndian) headerLenBytes = headerLenBytes.Reverse().ToArray();
|
||||
|
||||
m.Write(headerLenBytes, 0, headerLenBytes.Length);
|
||||
|
||||
// write 2 bytes that i am not sure about
|
||||
m.WriteByte(131);
|
||||
m.WriteByte(1);
|
||||
|
||||
// write auth token len
|
||||
m.WriteByte(89); // cbor ushort
|
||||
var authLenBytes = BitConverter.GetBytes((ushort)authToken.Length);
|
||||
if (BitConverter.IsLittleEndian) authLenBytes = authLenBytes.Reverse().ToArray();
|
||||
m.Write(authLenBytes, 0, authLenBytes.Length);
|
||||
|
||||
// write actual auth token
|
||||
var authBytes = Encoding.UTF8.GetBytes(authToken);
|
||||
m.Write(authBytes, 0, authBytes.Length);
|
||||
|
||||
// write nonce
|
||||
m.WriteByte(88); // cbor byte
|
||||
m.WriteByte(24); // nonce length
|
||||
|
||||
// generate it
|
||||
byte[] nonce = new byte[24];
|
||||
new Random().NextBytes(nonce);
|
||||
|
||||
// write nonce bytes
|
||||
m.Write(nonce, 0, nonce.Length);
|
||||
|
||||
// get additional data
|
||||
var additionalData = GenerateAdditionalData(authToken, true);
|
||||
|
||||
// prep payload
|
||||
MemoryStream msm = new MemoryStream();
|
||||
msm.WriteByte(88);
|
||||
msm.WriteByte(0);
|
||||
|
||||
msm.Write(message);
|
||||
|
||||
var encryptedBytes = SecretAeadXChaCha20Poly1305.Encrypt(msm.ToArray(), nonce, key.Keys.TransferSharedSecret, additionalData.ToArray());
|
||||
|
||||
// write encrypted data
|
||||
m.Write(encryptedBytes);
|
||||
|
||||
byte[] data = m.ToArray();
|
||||
//File.WriteAllBytes("our-encryption", data);
|
||||
|
||||
// we are done
|
||||
return data;
|
||||
}
|
||||
|
||||
private static byte[] GenerateAdditionalData(string authToken, bool encrypting)
|
||||
{
|
||||
// Generate "additional data" which consists of auth token and its length using cbor.
|
||||
// Not sure what the first bytes are but they are always the same. 89 represents start of UShort
|
||||
MemoryStream additionalData = new();
|
||||
additionalData.WriteByte((byte)(encrypting ? 129 : 130));
|
||||
additionalData.WriteByte(1);
|
||||
|
||||
// write auth token len
|
||||
if (!encrypting)
|
||||
{
|
||||
var authLen = BitConverter.GetBytes((ushort)authToken.Length);
|
||||
if (BitConverter.IsLittleEndian)
|
||||
authLen = authLen.Reverse().ToArray();
|
||||
additionalData.WriteByte(89);
|
||||
additionalData.Write(authLen, 0, authLen.Length);
|
||||
|
||||
// write our authentication token
|
||||
var authBytes = Encoding.UTF8.GetBytes(authToken);
|
||||
additionalData.Write(authBytes, 0, authBytes.Length);
|
||||
}
|
||||
|
||||
return additionalData.ToArray();
|
||||
}
|
||||
|
||||
private static string CBorReadString(Stream s)
|
||||
{
|
||||
CBorItem item = CBorReadItem(s);
|
||||
if (item.MajorType != 2)
|
||||
{
|
||||
throw new Exception("invalid string");
|
||||
}
|
||||
string resp = "";
|
||||
|
||||
var len = item.FullValue;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
var b = s.ReadByte();
|
||||
if (b == -1) throw new EndOfStreamException();
|
||||
|
||||
resp += (char)b;
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
private static byte[] CBorReadByteString(Stream s)
|
||||
{
|
||||
CBorItem item = CBorReadItem(s);
|
||||
if (item.MajorType != 2)
|
||||
{
|
||||
throw new Exception("invalid string");
|
||||
}
|
||||
|
||||
var len = item.FullValue;
|
||||
byte[] resp = new byte[len];
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
var b = s.ReadByte();
|
||||
if (b == -1) throw new EndOfStreamException();
|
||||
|
||||
resp[i] = (byte)b;
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
private static CBorItem CBorReadItem(Stream s)
|
||||
{
|
||||
var b = s.ReadByte();
|
||||
var type = b & 0x1f;
|
||||
var res = new CBorItem();
|
||||
res.MajorType = (b >> 5) & 7;
|
||||
switch (type)
|
||||
{
|
||||
case 24:
|
||||
// byte
|
||||
res.ByteValue = new byte[] { (byte)s.ReadByte() };
|
||||
res.type = CBorItemType.Byte;
|
||||
|
||||
res.FullValue = (int)res.ByteValue[0];
|
||||
break;
|
||||
case 25:
|
||||
byte[] arr = new byte[2];
|
||||
ReadToBuff(s, arr);
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
Array.Reverse(arr);
|
||||
|
||||
res.ByteValue = arr;
|
||||
res.type = CBorItemType.UShort;
|
||||
res.UShortValue = BitConverter.ToUInt16(arr, 0);
|
||||
|
||||
res.FullValue = res.UShortValue;
|
||||
break;
|
||||
default:
|
||||
// throw new NotImplementedException();
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static void ReadToBuff(Stream s, byte[] buf)
|
||||
{
|
||||
int i = 0;
|
||||
while (i != buf.Length)
|
||||
{
|
||||
if (i > buf.Length)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
var pos = buf.Length - i;
|
||||
var read = s.Read(buf, i, buf.Length - i);
|
||||
if (read == 0)
|
||||
break;
|
||||
i += read;
|
||||
}
|
||||
|
||||
if (i < buf.Length)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
}
|
||||
}
|
||||
public class PacketDecryptResponse
|
||||
{
|
||||
public ulong UserId;
|
||||
public string UsedAuthToken;
|
||||
public byte[] Contents;
|
||||
}
|
||||
public class CBorItem
|
||||
{
|
||||
public CBorItemType type;
|
||||
public byte[] ByteValue;
|
||||
public ushort UShortValue;
|
||||
public int MajorType;
|
||||
|
||||
public int FullValue;
|
||||
}
|
||||
public enum CBorItemType
|
||||
{
|
||||
Byte,
|
||||
UShort,
|
||||
ByteString,
|
||||
}
|
||||
}
|
||||
21
nksrv/Utils/PacketPathAttribute.cs
Normal file
21
nksrv/Utils/PacketPathAttribute.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents that this class handles a message
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class PacketPathAttribute : Attribute
|
||||
{
|
||||
public string Url { get; set; }
|
||||
public PacketPathAttribute(string url)
|
||||
{
|
||||
Url = url;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
nksrv/Utils/Rng.cs
Normal file
20
nksrv/Utils/Rng.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nksrv.Utils
|
||||
{
|
||||
public class Rng
|
||||
{
|
||||
private static Random random = new Random();
|
||||
|
||||
public static string RandomString(int length)
|
||||
{
|
||||
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
return new string(Enumerable.Repeat(chars, length)
|
||||
.Select(s => s[random.Next(s.Length)]).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
26
nksrv/nksrv.csproj
Normal file
26
nksrv/nksrv.csproj
Normal file
@@ -0,0 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IncludeHttpRuleProtos>true</IncludeHttpRuleProtos>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ASodium" Version="0.6.1" />
|
||||
<PackageReference Include="EmbedIO" Version="3.5.2" />
|
||||
<PackageReference Include="Google.Api.CommonProtos" Version="2.15.0" />
|
||||
<PackageReference Include="Google.Protobuf.Tools" Version="3.27.1" />
|
||||
<PackageReference Include="Grpc.AspNetCore" Version="2.63.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="PeterO.Cbor" Version="5.0.0-alpha1" />
|
||||
<PackageReference Include="Sodium.Core" Version="1.4.0-preview.1" />
|
||||
<PackageReference Include="System.Net.Http.Formatting.Extension" Version="5.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Protobuf Include="Protos\*.*" GrpcServices="Server" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user