mirror of
https://github.com/MikuLeaks/MikuSB.git
synced 2026-06-04 16:43:59 +00:00
Add character & inventory manager
This commit is contained in:
@@ -14,6 +14,7 @@ public class HttpServerConfig
|
|||||||
public string BindAddress { get; set; } = "0.0.0.0";
|
public string BindAddress { get; set; } = "0.0.0.0";
|
||||||
public string PublicAddress { get; set; } = "127.0.0.1";
|
public string PublicAddress { get; set; } = "127.0.0.1";
|
||||||
public int Port { get; set; } = 21500;
|
public int Port { get; set; } = 21500;
|
||||||
|
public bool EnableLog { get; set; } = true;
|
||||||
|
|
||||||
public string GetDisplayAddress()
|
public string GetDisplayAddress()
|
||||||
{
|
{
|
||||||
|
|||||||
31
Common/Data/Excel/CardExcel.cs
Normal file
31
Common/Data/Excel/CardExcel.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace MikuSB.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("card.json")]
|
||||||
|
public class CardExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint Genre { get; set; }
|
||||||
|
public uint Detail { get; set; }
|
||||||
|
public uint Particular { get; set; }
|
||||||
|
public uint Level { get; set; }
|
||||||
|
public uint Icon { get; set; }
|
||||||
|
public uint InitBreak { get; set; }
|
||||||
|
public int BreakMatID { get; set; }
|
||||||
|
public int LevelLimitID { get; set; }
|
||||||
|
public int GrowupID { get; set; }
|
||||||
|
public int AppearID { get; set; }
|
||||||
|
public List<uint> DefaultWeaponGPDL { get; set; } = [];
|
||||||
|
[JsonProperty("profile")] public List<List<int>> Profile { get; set; } = [];
|
||||||
|
public List<List<int>> Pieces { get; set; } = [];
|
||||||
|
public List<int> Attribute { get; set; } = [];
|
||||||
|
public override uint GetId()
|
||||||
|
{
|
||||||
|
return Icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.CardData.Add(Icon, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Common/Data/Excel/CardSkinExcel.cs
Normal file
24
Common/Data/Excel/CardSkinExcel.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace MikuSB.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("card_skin.json")]
|
||||||
|
public class CardSkinExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint Genre { get; set; }
|
||||||
|
public uint Detail { get; set; }
|
||||||
|
public uint Particular { get; set; }
|
||||||
|
public uint Level { get; set; }
|
||||||
|
public uint Icon { get; set; }
|
||||||
|
public int AppearID { get; set; }
|
||||||
|
[JsonProperty("profile")] public List<List<int>> Profile { get; set; } = [];
|
||||||
|
public override uint GetId()
|
||||||
|
{
|
||||||
|
return Icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.CardSkinData.Add(Icon, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
Common/Data/Excel/WeaponExcel.cs
Normal file
29
Common/Data/Excel/WeaponExcel.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
namespace MikuSB.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("weapon.json")]
|
||||||
|
public class WeaponExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint Genre { get; set; }
|
||||||
|
public uint Detail { get; set; }
|
||||||
|
public uint Particular { get; set; }
|
||||||
|
public uint Level { get; set; }
|
||||||
|
public int Class { get; set; }
|
||||||
|
public uint Icon { get; set; }
|
||||||
|
public int EvolutionMatID { get; set; }
|
||||||
|
public int BreakMatID { get; set; }
|
||||||
|
public int LevelLimitID { get; set; }
|
||||||
|
public int BreakLimitID { get; set; }
|
||||||
|
public int AppearID { get; set; }
|
||||||
|
public List<int> DefaultSkillID { get; set; } = [];
|
||||||
|
public List<int> WeaponPartsLimit { get; set; } = [];
|
||||||
|
public string I18n { get; set; } = "";
|
||||||
|
public override uint GetId()
|
||||||
|
{
|
||||||
|
return (uint)I18n.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.WeaponData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,19 @@
|
|||||||
namespace MikuSB.Data;
|
using MikuSB.Data.Excel;
|
||||||
|
|
||||||
|
namespace MikuSB.Data;
|
||||||
|
|
||||||
public static class GameData
|
public static class GameData
|
||||||
{
|
{
|
||||||
|
public static Dictionary<uint, CardExcel> CardData { get; private set; } = [];
|
||||||
|
public static Dictionary<uint, WeaponExcel> WeaponData { get; private set; } = [];
|
||||||
|
public static Dictionary<uint, CardSkinExcel> CardSkinData { get; private set; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GameResourceTemplateId
|
||||||
|
{
|
||||||
|
public static ulong FromGdpl(uint genre, uint detail, uint particular, uint level) =>
|
||||||
|
((ulong)genre << 48) | ((ulong)detail << 32) | ((ulong)particular << 16) | level;
|
||||||
|
|
||||||
|
public static ulong FromGdpl(IReadOnlyList<uint> gdpl) =>
|
||||||
|
gdpl.Count >= 4 ? FromGdpl(gdpl[0], gdpl[1], gdpl[2], gdpl[3]) : 0;
|
||||||
}
|
}
|
||||||
50
Common/Database/Character/CharacterData.cs
Normal file
50
Common/Database/Character/CharacterData.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using MikuSB.Proto;
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace MikuSB.Database.Character;
|
||||||
|
|
||||||
|
[SugarTable("character_data")]
|
||||||
|
public class CharacterData : BaseDatabaseDataHelper
|
||||||
|
{
|
||||||
|
[SugarColumn(IsJson = true)] public List<CharacterInfo> Characters { get; set; } = [];
|
||||||
|
public uint NextCharacterGuid { get; set; } = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CharacterInfo
|
||||||
|
{
|
||||||
|
public uint Guid { get; set; }
|
||||||
|
public ulong TemplateId { get; set; }
|
||||||
|
public uint Level { get; set; }
|
||||||
|
public int Exp { get; set; }
|
||||||
|
public uint Break { get; set; }
|
||||||
|
public int Evolue { get; set; }
|
||||||
|
public int Trust { get; set; }
|
||||||
|
public uint WeaponUniqueId { get; set; }
|
||||||
|
public uint SkinId { get; set; }
|
||||||
|
[SugarColumn(IsJson = true)] public List<uint> UnlockedSkin { get; set; } = [];
|
||||||
|
[SugarColumn(IsJson = true)] public List<uint> Spines { get; set; } = [];
|
||||||
|
[SugarColumn(IsJson = true)] public List<uint> Affixs { get; set; } = [];
|
||||||
|
public long Timestamp { get; set; }
|
||||||
|
public uint Count { get; set; } = 1;
|
||||||
|
|
||||||
|
public Item ToProto()
|
||||||
|
{
|
||||||
|
var proto = new Item
|
||||||
|
{
|
||||||
|
Id = Guid,
|
||||||
|
Template = TemplateId,
|
||||||
|
Count = Count,
|
||||||
|
Enhance = new Enhance
|
||||||
|
{
|
||||||
|
Level = Level,
|
||||||
|
Break = Break
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
proto.Slots[4] = WeaponUniqueId;
|
||||||
|
proto.Slots[5] = SkinId;
|
||||||
|
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
71
Common/Database/Inventory/InventoryData.cs
Normal file
71
Common/Database/Inventory/InventoryData.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using MikuSB.Proto;
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace MikuSB.Database.Inventory;
|
||||||
|
|
||||||
|
[SugarTable("inventory_data")]
|
||||||
|
public class InventoryData : BaseDatabaseDataHelper
|
||||||
|
{
|
||||||
|
public uint NextUniqueUid { get; set; } = 100000;
|
||||||
|
|
||||||
|
[SugarColumn(IsJson = true)]
|
||||||
|
public Dictionary<uint, BaseGameItemInfo> Items { get; set; } = []; // Key: UniqueId
|
||||||
|
|
||||||
|
[SugarColumn(IsJson = true)]
|
||||||
|
public Dictionary<uint, GameWeaponInfo> Weapons { get; set; } = []; // Key: UniqueId
|
||||||
|
|
||||||
|
[SugarColumn(IsJson = true)]
|
||||||
|
public Dictionary<uint, GameSkinInfo> Skins { get; set; } = []; // Key: UniqueId
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class BaseGameItemInfo
|
||||||
|
{
|
||||||
|
public uint UniqueId { get; set; }
|
||||||
|
public ulong TemplateId { get; set; }
|
||||||
|
public uint ItemCount { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class GrowableItemInfo : BaseGameItemInfo
|
||||||
|
{
|
||||||
|
public bool IsLocked { get; set; }
|
||||||
|
public uint Level { get; set; }
|
||||||
|
public uint Exp { get; set; }
|
||||||
|
public uint EquipAvatarId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GameWeaponInfo : GrowableItemInfo
|
||||||
|
{
|
||||||
|
public Item ToProto()
|
||||||
|
{
|
||||||
|
var proto = new Item
|
||||||
|
{
|
||||||
|
Id = UniqueId,
|
||||||
|
Template = TemplateId,
|
||||||
|
Count = ItemCount,
|
||||||
|
Enhance = new Enhance
|
||||||
|
{
|
||||||
|
Level = Level
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GameSkinInfo : BaseGameItemInfo
|
||||||
|
{
|
||||||
|
public uint Level { get; set; }
|
||||||
|
public Item ToProto()
|
||||||
|
{
|
||||||
|
var proto = new Item
|
||||||
|
{
|
||||||
|
Id = UniqueId,
|
||||||
|
Template = TemplateId,
|
||||||
|
Count = ItemCount,
|
||||||
|
Enhance = new Enhance
|
||||||
|
{
|
||||||
|
Level = Level
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using MikuSB.Util.Extensions;
|
using MikuSB.Util.Extensions;
|
||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
|
using MikuSB.Proto;
|
||||||
|
|
||||||
namespace MikuSB.Database.Player;
|
namespace MikuSB.Database.Player;
|
||||||
|
|
||||||
@@ -12,61 +13,18 @@ public class PlayerGameData : BaseDatabaseDataHelper
|
|||||||
public int Exp { get; set; } = 0;
|
public int Exp { get; set; } = 0;
|
||||||
public long RegisterTime { get; set; } = Extensions.GetUnixSec();
|
public long RegisterTime { get; set; } = Extensions.GetUnixSec();
|
||||||
public long LastActiveTime { get; set; }
|
public long LastActiveTime { get; set; }
|
||||||
[SugarColumn(IsJson = true)] public List<PlayerAttrs> Attrs { get; set; } = [];
|
public Sex Gender { get; set; } = Sex.Female;
|
||||||
|
[SugarColumn(IsJson = true)] public List<PlayerAttr> Attrs { get; set; } = [];
|
||||||
|
|
||||||
public static PlayerGameData? GetPlayerByUid(long uid)
|
public static PlayerGameData? GetPlayerByUid(long uid)
|
||||||
{
|
{
|
||||||
var result = DatabaseHelper.GetInstance<PlayerGameData>((int)uid);
|
var result = DatabaseHelper.GetInstance<PlayerGameData>((int)uid);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Proto.Player ToProto()
|
|
||||||
{
|
|
||||||
var proto = new Proto.Player
|
|
||||||
{
|
|
||||||
Pid = (ulong)Uid,
|
|
||||||
Account = Name,
|
|
||||||
Name = Name,
|
|
||||||
Level = Level,
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var x in Attrs)
|
|
||||||
{
|
|
||||||
uint gid = x.Gid;
|
|
||||||
uint sid = x.Sid;
|
|
||||||
uint val = x.Val;
|
|
||||||
|
|
||||||
if (gid == 0)
|
|
||||||
{
|
|
||||||
proto.Attrs[sid] = val;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
proto.Attrs[ToPackedAttrKey(gid, sid)] = val;
|
|
||||||
proto.Attrs[ToShiftedAttrKey(gid, sid)] = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
return proto;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static uint ToPackedAttrKey(uint gid, uint sid)
|
|
||||||
{
|
|
||||||
if (gid == 0)
|
|
||||||
return sid;
|
|
||||||
|
|
||||||
return (gid * 10000) + sid;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static uint ToShiftedAttrKey(uint gid, uint sid)
|
|
||||||
{
|
|
||||||
if (gid == 0)
|
|
||||||
return sid;
|
|
||||||
|
|
||||||
return (gid << 16) | sid;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PlayerAttrs
|
public class PlayerAttr
|
||||||
{
|
{
|
||||||
public uint Gid { get; set; }
|
public uint Gid { get; set; }
|
||||||
public uint Sid { get; set; }
|
public uint Sid { get; set; }
|
||||||
|
|||||||
28
Common/Enums/Item/ItemTypeEnum.cs
Normal file
28
Common/Enums/Item/ItemTypeEnum.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
namespace MikuSB.Enums.Item;
|
||||||
|
|
||||||
|
public enum ItemTypeEnum
|
||||||
|
{
|
||||||
|
TYPE_CARD = 1, // 角色卡
|
||||||
|
TYPE_WEAPON = 2, // 武器卡
|
||||||
|
TYPE_SUPPORT = 3, // 后勤卡
|
||||||
|
TYPE_USEABLE = 4, // 可使用道具
|
||||||
|
TYPE_SUPPLIES = 5, // 消耗类道具
|
||||||
|
TYPE_WEAPON_PART = 6, // 武器配件
|
||||||
|
TYPE_CARD_SKIN = 7, // 角色皮肤
|
||||||
|
TYPE_HOUSE = 8, // 宿舍家具
|
||||||
|
TYPE_PROFILE = 9, // 头像
|
||||||
|
TYPE_FRAME = 10, // 头像框
|
||||||
|
TYPE_BADGE = 11, // 勋章
|
||||||
|
TYPE_COVER = 12, // 封面
|
||||||
|
TYPE_NAMECARD = 13, // 名片
|
||||||
|
TYPE_EXPRESSION = 14, // 表情
|
||||||
|
TYPE_BUBBLE = 15, // 聊天气泡
|
||||||
|
TYPE_ANALYST = 16, // 墨镜分析员
|
||||||
|
TYPE_WEAPON_SKIN = 17, //武器皮肤
|
||||||
|
TYPE_MONSTER_CARD = 18, //怪物卡
|
||||||
|
TYPE_MANIFESTATION = 19, //角色皮肤互动场景道具
|
||||||
|
TYPE_CARD_SKIN_PART = 20, //角色皮肤部件
|
||||||
|
TYPE_MAIN_SCENE = 21, //主界面场景道具
|
||||||
|
TYPE_AR = 24, //AR道具
|
||||||
|
TYPE_CALL = 25, //电话陪伴道具
|
||||||
|
}
|
||||||
54
GameServer/Game/Character/CharacterManager.cs
Normal file
54
GameServer/Game/Character/CharacterManager.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using MikuSB.Data;
|
||||||
|
using MikuSB.Data.Excel;
|
||||||
|
using MikuSB.Database;
|
||||||
|
using MikuSB.Database.Character;
|
||||||
|
using MikuSB.Enums.Item;
|
||||||
|
using MikuSB.GameServer.Game.Player;
|
||||||
|
using MikuSB.Util.Extensions;
|
||||||
|
|
||||||
|
namespace MikuSB.GameServer.Game.Character;
|
||||||
|
|
||||||
|
public class CharacterManager(PlayerInstance player) : BasePlayerManager(player)
|
||||||
|
{
|
||||||
|
public CharacterData CharacterData { get; } = DatabaseHelper.GetInstanceOrCreateNew<CharacterData>(player.Uid);
|
||||||
|
public async ValueTask<CardExcel?> AddCharacter(ItemTypeEnum genre, uint detail, uint particular, uint level = 1)
|
||||||
|
{
|
||||||
|
var characterId = GameResourceTemplateId.FromGdpl((uint)genre,detail,particular,level);
|
||||||
|
if (CharacterData.Characters.Any(a => a.TemplateId == characterId)) return null;
|
||||||
|
var CharacterExcel = GameData.CardData.Values.FirstOrDefault(x => x.Genre == (int)genre && x.Detail == detail && x.Particular == particular);
|
||||||
|
if (CharacterExcel == null) return null;
|
||||||
|
|
||||||
|
var character = new CharacterInfo
|
||||||
|
{
|
||||||
|
Guid = CharacterData.NextCharacterGuid++,
|
||||||
|
TemplateId = characterId,
|
||||||
|
Level = level,
|
||||||
|
Break = CharacterExcel.InitBreak,
|
||||||
|
Timestamp = Extensions.GetUnixSec(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var weaponInfo = await Player.InventoryManager!.AddWeaponItem((ItemTypeEnum)CharacterExcel.DefaultWeaponGPDL[0], CharacterExcel.DefaultWeaponGPDL[1], CharacterExcel.DefaultWeaponGPDL[2], (uint)CharacterExcel.DefaultWeaponGPDL[3]);
|
||||||
|
if (weaponInfo != null) character.WeaponUniqueId = weaponInfo.UniqueId;
|
||||||
|
|
||||||
|
var skinInfo = await Player.InventoryManager!.AddSkinItem(ItemTypeEnum.TYPE_CARD_SKIN,detail,particular,level);
|
||||||
|
if (skinInfo != null)
|
||||||
|
{
|
||||||
|
character.SkinId = skinInfo.UniqueId;
|
||||||
|
character.UnlockedSkin.Add(skinInfo.UniqueId);
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterData.Characters.Add(character);
|
||||||
|
return CharacterExcel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharacterInfo? GetCharacter(ulong TemplateId)
|
||||||
|
{
|
||||||
|
return CharacterData.Characters.Find(Character => Character.TemplateId == TemplateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharacterInfo? GetCharacterGDPL(ItemTypeEnum genre, int detail, int particular)
|
||||||
|
{
|
||||||
|
var templateId = GameResourceTemplateId.FromGdpl((uint)genre,(uint)detail,(uint)particular,1);
|
||||||
|
return CharacterData.Characters.Find(Character => Character.TemplateId == templateId);
|
||||||
|
}
|
||||||
|
}
|
||||||
82
GameServer/Game/Inventory/InventoryManager.cs
Normal file
82
GameServer/Game/Inventory/InventoryManager.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using MikuSB.Data;
|
||||||
|
using MikuSB.Database;
|
||||||
|
using MikuSB.Database.Inventory;
|
||||||
|
using MikuSB.Enums.Item;
|
||||||
|
using MikuSB.GameServer.Game.Player;
|
||||||
|
|
||||||
|
namespace MikuSB.GameServer.Game.Inventory;
|
||||||
|
|
||||||
|
public class InventoryManager(PlayerInstance player) : BasePlayerManager(player)
|
||||||
|
{
|
||||||
|
public InventoryData InventoryData { get; } = DatabaseHelper.GetInstanceOrCreateNew<InventoryData>(player.Uid);
|
||||||
|
|
||||||
|
public async ValueTask<GameWeaponInfo?> AddWeaponItem(ItemTypeEnum genre, uint detail, uint particular, uint level = 1)
|
||||||
|
{
|
||||||
|
if (genre != ItemTypeEnum.TYPE_WEAPON) return null;
|
||||||
|
var weaponData = GameData.WeaponData.Values.FirstOrDefault(x => x.Genre == (int)genre && x.Detail == detail && x.Particular == particular);
|
||||||
|
if (weaponData == null) return null;
|
||||||
|
|
||||||
|
var templateId = GameResourceTemplateId.FromGdpl((uint)genre,detail,particular,level);
|
||||||
|
var weaponInfo = new GameWeaponInfo
|
||||||
|
{
|
||||||
|
TemplateId = templateId,
|
||||||
|
UniqueId = InventoryData.NextUniqueUid++,
|
||||||
|
Level = level,
|
||||||
|
ItemCount = 1
|
||||||
|
};
|
||||||
|
InventoryData.Weapons[weaponInfo.UniqueId] = weaponInfo;
|
||||||
|
|
||||||
|
return weaponInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameWeaponInfo? GetWeaponItem(uint uniqueId)
|
||||||
|
{
|
||||||
|
return InventoryData.Weapons.GetValueOrDefault(uniqueId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameWeaponInfo? GetWeaponItemByTemplateId(ulong templateId)
|
||||||
|
{
|
||||||
|
return InventoryData.Weapons.Values.FirstOrDefault(x => x.TemplateId == templateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameWeaponInfo? GetWeaponItemGDPL(ItemTypeEnum genre, int detail, int particular)
|
||||||
|
{
|
||||||
|
var templateId = GameResourceTemplateId.FromGdpl((uint)genre, (uint)detail, (uint)particular, 1);
|
||||||
|
return InventoryData.Weapons.Values.FirstOrDefault(x => x.TemplateId == templateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<GameSkinInfo?> AddSkinItem(ItemTypeEnum genre, uint detail, uint particular, uint level = 1)
|
||||||
|
{
|
||||||
|
if (genre != ItemTypeEnum.TYPE_CARD_SKIN) return null;
|
||||||
|
var skinData = GameData.CardSkinData.Values.FirstOrDefault(x => x.Genre == (int)genre && x.Detail == detail && x.Particular == particular);
|
||||||
|
if (skinData == null) return null;
|
||||||
|
|
||||||
|
var templateId = GameResourceTemplateId.FromGdpl((uint)genre,detail,particular,level);
|
||||||
|
var skinInfo = new GameSkinInfo
|
||||||
|
{
|
||||||
|
TemplateId = templateId,
|
||||||
|
UniqueId = InventoryData.NextUniqueUid++,
|
||||||
|
Level = level,
|
||||||
|
ItemCount = 1
|
||||||
|
};
|
||||||
|
InventoryData.Skins[skinInfo.UniqueId] = skinInfo;
|
||||||
|
|
||||||
|
return skinInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameSkinInfo? GetSkinItem(uint uniqueId)
|
||||||
|
{
|
||||||
|
return InventoryData.Skins.GetValueOrDefault(uniqueId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameSkinInfo? GetSkinItemByTemplateId(ulong templateId)
|
||||||
|
{
|
||||||
|
return InventoryData.Skins.Values.FirstOrDefault(x => x.TemplateId == templateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameSkinInfo? GetSkinItemGDPL(ItemTypeEnum genre, int detail, int particular)
|
||||||
|
{
|
||||||
|
var templateId = GameResourceTemplateId.FromGdpl((uint)genre, (uint)detail, (uint)particular, 1);
|
||||||
|
return InventoryData.Skins.Values.FirstOrDefault(x => x.TemplateId == templateId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
using MikuSB.Database;
|
using MikuSB.Data;
|
||||||
|
using MikuSB.Database;
|
||||||
using MikuSB.Database.Account;
|
using MikuSB.Database.Account;
|
||||||
using MikuSB.Database.Player;
|
using MikuSB.Database.Player;
|
||||||
|
using MikuSB.Enums.Item;
|
||||||
|
using MikuSB.GameServer.Game.Character;
|
||||||
|
using MikuSB.GameServer.Game.Inventory;
|
||||||
using MikuSB.GameServer.Server;
|
using MikuSB.GameServer.Server;
|
||||||
|
using MikuSB.Proto;
|
||||||
using MikuSB.TcpSharp;
|
using MikuSB.TcpSharp;
|
||||||
using MikuSB.Util.Extensions;
|
using MikuSB.Util.Extensions;
|
||||||
|
|
||||||
@@ -22,6 +27,8 @@ public class PlayerInstance(PlayerGameData data)
|
|||||||
#region Data & Manager
|
#region Data & Manager
|
||||||
|
|
||||||
public PlayerGameData Data { get; set; } = data;
|
public PlayerGameData Data { get; set; } = data;
|
||||||
|
public CharacterManager CharacterManager { get; set; } = null!;
|
||||||
|
public InventoryManager InventoryManager { get; set; } = null!;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -37,16 +44,47 @@ public class PlayerInstance(PlayerGameData data)
|
|||||||
var t = Task.Run(async () =>
|
var t = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await InitialPlayerManager();
|
await InitialPlayerManager();
|
||||||
|
foreach (var card in GameData.CardData.Values) await CharacterManager.AddCharacter((ItemTypeEnum)card.Genre, card.Detail, card.Particular, card.Level);
|
||||||
|
|
||||||
|
var bootstrapAttrs = BuildLobbyBootstrapAttrs();
|
||||||
|
var existingAttrs = Data.Attrs
|
||||||
|
.ToDictionary(x => (x.Gid, x.Sid));
|
||||||
|
var seenAttrs = new HashSet<(uint Gid, uint Sid)>();
|
||||||
|
|
||||||
|
foreach (var (gid, sid, value) in bootstrapAttrs)
|
||||||
|
{
|
||||||
|
if (!seenAttrs.Add((gid, sid)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (existingAttrs.TryGetValue((gid, sid), out var attr))
|
||||||
|
{
|
||||||
|
if (attr.Val < value)
|
||||||
|
attr.Val = value;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newAttr = new PlayerAttr
|
||||||
|
{
|
||||||
|
Gid = gid,
|
||||||
|
Sid = sid,
|
||||||
|
Val = value
|
||||||
|
};
|
||||||
|
|
||||||
|
Data.Attrs.Add(newAttr);
|
||||||
|
existingAttrs[(gid, sid)] = newAttr;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
t.Wait();
|
t.Wait();
|
||||||
|
|
||||||
Initialized = true;
|
Initialized = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
private async ValueTask InitialPlayerManager()
|
private async ValueTask InitialPlayerManager()
|
||||||
{
|
{
|
||||||
Uid = Data.Uid;
|
Uid = Data.Uid;
|
||||||
Data.LastActiveTime = Extensions.GetUnixSec();
|
Data.LastActiveTime = Extensions.GetUnixSec();
|
||||||
|
InventoryManager = new InventoryManager(this);
|
||||||
|
CharacterManager = new CharacterManager(this);
|
||||||
|
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@@ -94,5 +132,113 @@ public class PlayerInstance(PlayerGameData data)
|
|||||||
|
|
||||||
#region Serialization
|
#region Serialization
|
||||||
|
|
||||||
|
public Proto.Player ToPlayerProto()
|
||||||
|
{
|
||||||
|
var proto = new Proto.Player
|
||||||
|
{
|
||||||
|
Pid = (ulong)Data.Uid,
|
||||||
|
Account = Data.Name,
|
||||||
|
Provider = Data.Name,
|
||||||
|
Name = Data.Name,
|
||||||
|
Level = Data.Level,
|
||||||
|
Sex = Data.Gender,
|
||||||
|
Solutions =
|
||||||
|
{
|
||||||
|
new Lineup // TODO Lineup Manager
|
||||||
|
{
|
||||||
|
Index = 1,
|
||||||
|
Name = "Default",
|
||||||
|
Member1 = 1,
|
||||||
|
Member2 = 2,
|
||||||
|
Member3 = 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach(var weapon in InventoryManager.InventoryData.Weapons.Values) proto.Items.Add(weapon.ToProto());
|
||||||
|
foreach (var skin in InventoryManager.InventoryData.Skins.Values) proto.Items.Add(skin.ToProto());
|
||||||
|
foreach (var chara in CharacterManager.CharacterData.Characters) proto.Items.Add(chara.ToProto());
|
||||||
|
|
||||||
|
foreach (var x in Data.Attrs)
|
||||||
|
{
|
||||||
|
uint gid = x.Gid;
|
||||||
|
uint sid = x.Sid;
|
||||||
|
uint val = x.Val;
|
||||||
|
|
||||||
|
if (gid == 0)
|
||||||
|
{
|
||||||
|
proto.Attrs[sid] = val;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
proto.Attrs[ToPackedAttrKey(gid, sid)] = val;
|
||||||
|
proto.Attrs[ToShiftedAttrKey(gid, sid)] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint ToPackedAttrKey(uint gid, uint sid)
|
||||||
|
{
|
||||||
|
if (gid == 0)
|
||||||
|
return sid;
|
||||||
|
|
||||||
|
return (gid * 10000) + sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint ToShiftedAttrKey(uint gid, uint sid)
|
||||||
|
{
|
||||||
|
if (gid == 0)
|
||||||
|
return sid;
|
||||||
|
|
||||||
|
return (gid << 16) | sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<(uint Gid, uint Sid, uint Value)> BuildLobbyBootstrapAttrs()
|
||||||
|
{
|
||||||
|
// GuideLogic uses group 4. Value 999 is safely above every configured step count,
|
||||||
|
// so the client treats these guides as already completed.
|
||||||
|
yield return (4, 0, 5);
|
||||||
|
yield return (11, 1, 1);
|
||||||
|
yield return (57, 0, 1);
|
||||||
|
yield return (99, 3, 30);
|
||||||
|
yield return (110, 1, 1);
|
||||||
|
yield return (178, 1, 1_700_000_000);
|
||||||
|
yield return (187, 1, 2);
|
||||||
|
|
||||||
|
for (uint guideId = 1; guideId <= 150; guideId++)
|
||||||
|
yield return (4, guideId, 999);
|
||||||
|
|
||||||
|
for (uint guideId = 10_000; guideId <= 10_300; guideId++)
|
||||||
|
yield return (4, guideId, 999);
|
||||||
|
|
||||||
|
for (uint guideId = 11_000; guideId <= 11_300; guideId++)
|
||||||
|
yield return (4, guideId, 999);
|
||||||
|
|
||||||
|
for (uint guideId = 12_000; guideId <= 12_100; guideId++)
|
||||||
|
yield return (4, guideId, 999);
|
||||||
|
|
||||||
|
for (uint guideId = 22_000; guideId <= 22_100; guideId++)
|
||||||
|
yield return (4, guideId, 999);
|
||||||
|
|
||||||
|
// Additional guide ids referenced directly by the Lua scripts and observed client logs.
|
||||||
|
foreach (var guideId in new uint[] { 10_031, 10_041, 10_061, 10_081, 10_101, 10_224, 11_006, 11_202, 11_210, 22_002 })
|
||||||
|
yield return (4, guideId, 999);
|
||||||
|
|
||||||
|
// Launch.GPASSID = 22 stores pass counts. ChapterLevel.GID = 21 stores star flags.
|
||||||
|
// Completing the prologue/early chapter range prevents function conditions from
|
||||||
|
// treating the account as a fresh tutorial player.
|
||||||
|
for (uint levelId = 10_000; levelId <= 10_160; levelId++)
|
||||||
|
{
|
||||||
|
yield return (21, levelId, 7);
|
||||||
|
yield return (22, levelId, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var levelId in new uint[] { 10_121, 10_122, 10_123, 50_000, 50_151 })
|
||||||
|
{
|
||||||
|
yield return (21, levelId, 7);
|
||||||
|
yield return (22, levelId, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
17
GameServer/Server/Packet/Recv/Player/HandlerNtfReadItem.cs
Normal file
17
GameServer/Server/Packet/Recv/Player/HandlerNtfReadItem.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using Google.Protobuf;
|
||||||
|
using MikuSB.Database.Player;
|
||||||
|
using MikuSB.Proto;
|
||||||
|
using MikuSB.Util;
|
||||||
|
|
||||||
|
namespace MikuSB.GameServer.Server.Packet.Recv.Login;
|
||||||
|
|
||||||
|
[Opcode(CmdIds.NtfReadItem)]
|
||||||
|
public class HandlerNtfReadItem : Handler
|
||||||
|
{
|
||||||
|
public override async Task OnHandle(Connection connection, byte[] data, ushort seqNo)
|
||||||
|
{
|
||||||
|
var req = IDArray.Parser.ParseFrom(data);
|
||||||
|
var json = JsonFormatter.Default.Format(req);
|
||||||
|
Logger.GetByClassName().Debug($"{json}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ public class HandlerNtfSetAttr : Handler
|
|||||||
if (attr != null) attr.Val = req.Val;
|
if (attr != null) attr.Val = req.Val;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
player.Data.Attrs.Add(new PlayerAttrs
|
player.Data.Attrs.Add(new PlayerAttr
|
||||||
{
|
{
|
||||||
Gid = req.Gid,
|
Gid = req.Gid,
|
||||||
Sid = req.Sid,
|
Sid = req.Sid,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public class PacketRspLogin : BasePacket
|
|||||||
Timestamp = (uint)Extensions.GetUnixSec(),
|
Timestamp = (uint)Extensions.GetUnixSec(),
|
||||||
WorldChannel = 1,
|
WorldChannel = 1,
|
||||||
AreaId = 1,
|
AreaId = 1,
|
||||||
Data = player.Data.ToProto(),
|
Data = player.ToPlayerProto(),
|
||||||
NeedRename = false
|
NeedRename = false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public class RequestLoggingMiddleware(RequestDelegate next)
|
|||||||
|
|
||||||
if (path.StartsWith("/report") || path.Contains("/log/") || path == "/alive")
|
if (path.StartsWith("/report") || path.Contains("/log/") || path == "/alive")
|
||||||
return;
|
return;
|
||||||
|
if (!ConfigManager.Config.HttpServer.EnableLog) return;
|
||||||
if (statusCode == 200)
|
if (statusCode == 200)
|
||||||
{
|
{
|
||||||
logger.Info($"{method} {path} => {statusCode}");
|
logger.Info($"{method} {path} => {statusCode}");
|
||||||
|
|||||||
Reference in New Issue
Block a user