mirror of
https://github.com/HolographicHat/Yae.git
synced 2025-12-07 23:22:48 +08:00
[skip ci] export player store data
This commit is contained in:
74
YaeAchievement/res/proto/StoreData.proto
Normal file
74
YaeAchievement/res/proto/StoreData.proto
Normal file
@@ -0,0 +1,74 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option csharp_namespace = "Proto";
|
||||
|
||||
enum StoreType {
|
||||
STORE_TYPE_NONE = 0;
|
||||
STORE_TYPE_PACK = 1;
|
||||
STORE_TYPE_DEPOT = 2;
|
||||
}
|
||||
|
||||
message MaterialDeleteInfo {
|
||||
message CountDownDelete {
|
||||
map<uint32, uint32> delete_time_num_map = 1;
|
||||
uint32 config_count_down_time = 2;
|
||||
}
|
||||
message DateTimeDelete {
|
||||
uint32 delete_time = 1;
|
||||
}
|
||||
message DelayWeekCountDownDelete {
|
||||
map<uint32, uint32> delete_time_num_map = 1;
|
||||
uint32 config_delay_week = 2;
|
||||
uint32 config_count_down_time = 3;
|
||||
}
|
||||
bool has_delete_config = 1;
|
||||
oneof delete_info {
|
||||
CountDownDelete count_down_delete = 2;
|
||||
DateTimeDelete date_delete = 3;
|
||||
DelayWeekCountDownDelete delay_week_count_down_delete = 4;
|
||||
}
|
||||
}
|
||||
|
||||
message Material {
|
||||
uint32 count = 1;
|
||||
MaterialDeleteInfo delete_info = 2;
|
||||
}
|
||||
|
||||
message Reliquary {
|
||||
uint32 level = 1;
|
||||
uint32 exp = 2;
|
||||
uint32 promote_level = 3;
|
||||
uint32 main_prop_id = 4;
|
||||
repeated uint32 append_prop_id_list = 5;
|
||||
bool is_marked = 6;
|
||||
}
|
||||
|
||||
message Weapon {
|
||||
uint32 level = 1;
|
||||
uint32 exp = 2;
|
||||
uint32 promote_level = 3;
|
||||
map<uint32, uint32> affix_map = 4;
|
||||
bool is_arkhe_ousia = 5;
|
||||
}
|
||||
|
||||
message Equip {
|
||||
oneof detail {
|
||||
Reliquary reliquary = 1;
|
||||
Weapon weapon = 2;
|
||||
}
|
||||
bool is_locked = 3;
|
||||
}
|
||||
|
||||
message Furniture {
|
||||
uint32 count = 1;
|
||||
}
|
||||
|
||||
message Item {
|
||||
uint32 item_id = 1;
|
||||
uint64 guid = 2;
|
||||
oneof detail {
|
||||
Material material = 5;
|
||||
Equip equip = 6;
|
||||
Furniture furniture = 7;
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,8 @@ public static class GlobalVars {
|
||||
public const string RinBucketHost = "https://rin.holohat.work";
|
||||
public const string SakuraBucketHost = "https://cn-cd-1259389942.file.myqcloud.com";
|
||||
|
||||
public static CacheFile AchievementDataCache { get; } = new ("achievement_data");
|
||||
|
||||
[field:MaybeNull]
|
||||
public static AchievementInfo AchievementInfo =>
|
||||
field ??= AchievementInfo.Parser.ParseFrom(Utils.GetBucketFile("schicksal/metadata").GetAwaiter().GetResult());
|
||||
@@ -36,4 +38,5 @@ public static class GlobalVars {
|
||||
Directory.CreateDirectory(DataPath);
|
||||
Directory.CreateDirectory(CachePath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Google.Protobuf;
|
||||
using YaeAchievement.res;
|
||||
@@ -27,6 +26,12 @@ public class AchievementAllDataNotify {
|
||||
|
||||
public List<AchievementItem> AchievementList { get; private init; } = [];
|
||||
|
||||
public static bool OnReceive(byte[] bytes) {
|
||||
GlobalVars.AchievementDataCache.Write(bytes);
|
||||
Export.Choose(ParseFrom(bytes));
|
||||
return true;
|
||||
}
|
||||
|
||||
public static AchievementAllDataNotify ParseFrom(byte[] bytes) {
|
||||
using var stream = new CodedInputStream(bytes);
|
||||
var data = new List<Dictionary<uint, uint>>();
|
||||
@@ -36,7 +41,7 @@ public class AchievementAllDataNotify {
|
||||
while ((tag = stream.ReadTag()) != 0) {
|
||||
if ((tag & 7) == 2) { // is LengthDelimited
|
||||
var dict = new Dictionary<uint, uint>();
|
||||
using var eStream = new CodedInputStream(ReadRawBytes(stream, stream.ReadLength()));
|
||||
using var eStream = stream.ReadLengthDelimitedAsStream();
|
||||
try {
|
||||
while ((tag = eStream.ReadTag()) != 0) {
|
||||
if ((tag & 7) != 0) { // not VarInt
|
||||
@@ -107,9 +112,6 @@ public class AchievementAllDataNotify {
|
||||
};
|
||||
}
|
||||
|
||||
[UnsafeAccessor(UnsafeAccessorKind.Method)]
|
||||
private static extern byte[] ReadRawBytes(CodedInputStream stream, int size);
|
||||
|
||||
}
|
||||
|
||||
[JsonSerializable(typeof(AchievementAllDataNotify))]
|
||||
|
||||
60
YaeAchievement/src/Parsers/PlayerStoreNotify.cs
Normal file
60
YaeAchievement/src/Parsers/PlayerStoreNotify.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System.Text.Json;
|
||||
using Google.Protobuf;
|
||||
using Proto;
|
||||
|
||||
namespace YaeAchievement.Parsers;
|
||||
|
||||
public class PlayerStoreNotify {
|
||||
|
||||
public uint WeightLimit { get; set; }
|
||||
|
||||
public StoreType StoreType { get; set; }
|
||||
|
||||
public List<Item> ItemList { get; set; } = [];
|
||||
|
||||
public static bool OnReceive(byte[] bytes) {
|
||||
#if DEBUG
|
||||
var ntf = ParseFrom(bytes);
|
||||
File.WriteAllText("store_data.json", JsonSerializer.Serialize(ntf, new JsonSerializerOptions {
|
||||
WriteIndented = true
|
||||
}));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
private static PlayerStoreNotify ParseFrom(byte[] bytes) {
|
||||
using var stream = new CodedInputStream(bytes);
|
||||
var ntf = new PlayerStoreNotify();
|
||||
try {
|
||||
uint tag;
|
||||
while ((tag = stream.ReadTag()) != 0) {
|
||||
var wireType = tag & 7;
|
||||
switch (wireType) {
|
||||
case 0: { // is VarInt
|
||||
var value = stream.ReadUInt32();
|
||||
if (value < 10) {
|
||||
ntf.StoreType = (StoreType) value;
|
||||
} else {
|
||||
ntf.WeightLimit = value;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case 2: { // is LengthDelimited
|
||||
using var eStream = stream.ReadLengthDelimitedAsStream();
|
||||
while (eStream.PeekTag() != 0) {
|
||||
ntf.ItemList.Add(Item.Parser.ParseFrom(eStream));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (InvalidProtocolBufferException) {
|
||||
// ReSharper disable once LocalizableElement
|
||||
Console.WriteLine("Parse failed");
|
||||
File.WriteAllBytes("store_raw_data.bin", bytes);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
return ntf;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,7 +24,7 @@ Export.ExportTo = ToUIntOrNull(args.GetOrNull(1)) ?? uint.MaxValue;
|
||||
|
||||
await CheckUpdate(ToBooleanOrFalse(args.GetOrNull(2)));
|
||||
|
||||
var historyCache = new CacheFile("ExportData");
|
||||
var historyCache = GlobalVars.AchievementDataCache;
|
||||
|
||||
AchievementAllDataNotify? data = null;
|
||||
try {
|
||||
@@ -39,11 +39,7 @@ if (historyCache.LastWriteTime.AddMinutes(60) > DateTime.UtcNow && data != null)
|
||||
}
|
||||
}
|
||||
|
||||
StartAndWaitResult(AppConfig.GamePath, str => {
|
||||
GlobalVars.UnexpectedExit = false;
|
||||
var bytes = Convert.FromBase64String(str);
|
||||
var list = AchievementAllDataNotify.ParseFrom(bytes);
|
||||
historyCache.Write(bytes);
|
||||
Export.Choose(list);
|
||||
return true;
|
||||
StartAndWaitResult(AppConfig.GamePath, new Dictionary<byte, Func<byte[], bool>> {
|
||||
{ 1, AchievementAllDataNotify.OnReceive },
|
||||
{ 2, PlayerStoreNotify.OnReceive }
|
||||
});
|
||||
|
||||
16
YaeAchievement/src/Utilities/Extensions/Stream.cs
Normal file
16
YaeAchievement/src/Utilities/Extensions/Stream.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// ReSharper disable CheckNamespace
|
||||
|
||||
namespace Google.Protobuf;
|
||||
|
||||
public static class CodedInputStreamExtensions {
|
||||
|
||||
[UnsafeAccessor(UnsafeAccessorKind.Method)]
|
||||
private static extern byte[] ReadRawBytes(CodedInputStream stream, int size);
|
||||
|
||||
public static CodedInputStream ReadLengthDelimitedAsStream(this CodedInputStream stream) {
|
||||
return new CodedInputStream(ReadRawBytes(stream, stream.ReadLength()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -114,7 +114,7 @@ public static class Utils {
|
||||
Console.WriteLine(@"[DEBUG] Use local native lib.");
|
||||
File.Copy(Path.Combine(GlobalVars.AppPath, "YaeAchievementLib.dll"), GlobalVars.LibFilePath, true);
|
||||
} else if (info.EnableLibDownload) {
|
||||
var data = await GetBucketFile("schicksal/lib.dll");
|
||||
var data = await GetBucketFile("schicksal/lic.dll");
|
||||
await File.WriteAllBytesAsync(GlobalVars.LibFilePath, data);
|
||||
}
|
||||
_updateInfo = info;
|
||||
@@ -208,7 +208,7 @@ public static class Utils {
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedMethodReturnValue.Global
|
||||
public static Thread StartAndWaitResult(string exePath, Func<string, bool> onReceive) {
|
||||
public static Thread StartAndWaitResult(string exePath, Dictionary<byte, Func<byte[], bool>> handlers) {
|
||||
AppDomain.CurrentDomain.ProcessExit += (_, _) => {
|
||||
try {
|
||||
File.Delete(GlobalVars.LibFilePath);
|
||||
@@ -228,7 +228,7 @@ public static class Utils {
|
||||
proc = Process.GetProcessById(Convert.ToInt32(pid));
|
||||
proc.EnableRaisingEvents = true;
|
||||
proc.Exited += (_, _) => {
|
||||
if (GlobalVars.UnexpectedExit)
|
||||
if (handlers.Count != 0)
|
||||
{
|
||||
proc = null;
|
||||
Console.WriteLine(App.GameProcessExit);
|
||||
@@ -252,15 +252,13 @@ public static class Utils {
|
||||
var ts = new ThreadStart(() => {
|
||||
var server = new NamedPipeServerStream(GlobalVars.PipeName);
|
||||
server.WaitForConnection();
|
||||
using var reader = new StreamReader(server);
|
||||
using var reader = new BinaryReader(server);
|
||||
while (!proc.HasExited) {
|
||||
var line = reader.ReadLine();
|
||||
if (line?.Length > 0) {
|
||||
if (onReceive(line)) {
|
||||
break;
|
||||
}
|
||||
server.Disconnect();
|
||||
server.WaitForConnection();
|
||||
var type = reader.ReadByte();
|
||||
var length = reader.ReadInt32(); // huh
|
||||
var data = reader.ReadBytes(length);
|
||||
if (handlers.Remove(type, out var handler)) {
|
||||
handler(data);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user