feat: Upgrade server with HyNB v2.4 modular architecture, minigames, admin GM web panel, companion summons, and car radio
This commit is contained in:
1 parent
74e2963a12
commit
3e5adf1dc5
1571 files changed
+336468
-6348
No files matched your search
@@ -0,0 +1,111 @@
|
||||
using Ananta.SDK.Rpc;
|
||||
using Ananta.Server.ClientData.Client4229938;
|
||||
using Ananta.Server.Handlers;
|
||||
using Ananta.Server.Protocol.Client4229938;
|
||||
using Auto = Ananta.Server.RpcTypes.Client4229938.Auto;
|
||||
using GameMethods = Ananta.Server.RpcTypes.Client4229938.Methods.Game;
|
||||
|
||||
namespace Ananta.Server.Handlers.Game;
|
||||
|
||||
/// <summary>
|
||||
/// Phone menu skin parts (手机壁纸 / 装饰 / 挂件).
|
||||
///
|
||||
/// The client's "apply" button only sends AskSetMobileSkinPart and then waits for the answer.
|
||||
/// It never writes the worn suit locally: GameToClientImpl.SyncSpiritMobileSkinPartInfo is the
|
||||
/// sole writer of SpiritInfo.MobileSkinInfo, and it is also what fires
|
||||
/// ON_SYNC_SPIRIT_SKIN_PART_INFO_CHANGE so MainPhonePanelStore.OnSpiritSkinPartChange re-reads the
|
||||
/// value. Because this method previously fell through to the typed-neutral fallback, the whole
|
||||
/// sequence ended with "applied successfully" and an unchanged wallpaper: the preview closed, the
|
||||
/// panel re-read the untouched login struct, and the wallpaper snapped back.
|
||||
///
|
||||
/// This handler stores the choice per character (and mirrors it to PlayerProgressStore) and replays
|
||||
/// it as SyncSpiritMobileSkinPartInfo so the wallpaper actually changes — and stays changed across
|
||||
/// re-logins, because the login payload now carries the stored suit.
|
||||
///
|
||||
/// The reply stays an empty 0-error body, byte-identical to what the fallback produced, so the
|
||||
/// client's existing "close preview / show result toast" branch is untouched.
|
||||
/// </summary>
|
||||
internal sealed partial class GameRouter
|
||||
{
|
||||
[Handler(MethodId.AskSetMobileSkinPart, HandlerPacketKind.Invoke)]
|
||||
private async Task AskSetMobileSkinPart4229938(Connection conn, UxRpcMessage msg)
|
||||
{
|
||||
if (!msg.TryGetArgs<GameMethods.AskSetMobileSkinPart>(out var args) || args is null)
|
||||
{
|
||||
conn.Log.Warn($"[PHONE-SKIN] AskSetMobileSkinPart undecodable bytes={msg.Body.Length}");
|
||||
await conn.ReturnEmptyOkAsync(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
await ApplyPhoneSkin4229938Async(
|
||||
conn, msg,
|
||||
args.wallpaper, args.decoration, args.pendant,
|
||||
"set");
|
||||
}
|
||||
|
||||
[Handler(MethodId.AskResetMobileSkinPart, HandlerPacketKind.Invoke)]
|
||||
private Task AskResetMobileSkinPart4229938(Connection conn, UxRpcMessage msg)
|
||||
=> ApplyPhoneSkin4229938Async(
|
||||
conn, msg,
|
||||
NpcCultivationCatalog4229938.DefaultWallpaperPart,
|
||||
NpcCultivationCatalog4229938.DefaultDecorationPart,
|
||||
NpcCultivationCatalog4229938.DefaultPendantPart,
|
||||
"reset");
|
||||
|
||||
/// <summary>
|
||||
/// Stores the worn suit for the character currently in play and echoes it back.
|
||||
///
|
||||
/// The spirit id must be a FightSpiritConfig template id, not a unit instance id: the client
|
||||
/// looks the character up through SpiritManager.spiritViewDatas, which SyncPlayerInfoSpirit /
|
||||
/// SyncPlayerAllSpirits key by SpiritInfo.TemplateId, and GetCurrentSpiritSkinInfo resolves the
|
||||
/// same way through gBattleSpiritMgr.currentSpiritTemplateId (= InfoSpirit.ActiveSpirit).
|
||||
/// </summary>
|
||||
private async Task ApplyPhoneSkin4229938Async(
|
||||
Connection conn,
|
||||
UxRpcMessage msg,
|
||||
uint wallpaper,
|
||||
uint decoration,
|
||||
uint pendant,
|
||||
string reason)
|
||||
{
|
||||
var state = GetWorldState(msg.Context);
|
||||
var spiritId = state.ActiveSpiritTemplateId;
|
||||
|
||||
lock (state.SyncRoot)
|
||||
{
|
||||
state.PhoneSkinBySpirit[spiritId] = new Auto.SpiritMobileSkinInfo
|
||||
{
|
||||
Wallpaper = wallpaper,
|
||||
Decoration = decoration,
|
||||
Pendant = pendant,
|
||||
};
|
||||
}
|
||||
|
||||
// Re-state the full part list so the packet is self-contained. It must NOT be empty: an
|
||||
// empty Lua list is truthy and would wipe the phone's skin list. When the catalog is
|
||||
// unavailable (no config folder) we pass null instead, which the client reads as
|
||||
// "leave my list alone".
|
||||
var parts = NpcCultivationCatalog4229938.SkinParts;
|
||||
List<uint>? available = parts.Count > 0 ? parts.Select(part => part.Id).ToList() : null;
|
||||
|
||||
await conn.NotifyAsync(MethodId.SyncSpiritMobileSkinPartInfo, new GameMethods.SyncSpiritMobileSkinPartInfo
|
||||
{
|
||||
spiritId = spiritId,
|
||||
mobileSkinInfo = new Auto.SpiritMobileSkinInfo
|
||||
{
|
||||
Wallpaper = wallpaper,
|
||||
Decoration = decoration,
|
||||
Pendant = pendant,
|
||||
},
|
||||
availableSkinParts = available,
|
||||
});
|
||||
|
||||
PlayerProgressStore.Save(state, conn.Log);
|
||||
|
||||
conn.Log.Info(
|
||||
$"[PHONE-SKIN] {reason} spirit={spiritId} wallpaper={wallpaper} " +
|
||||
$"decoration={decoration} pendant={pendant} parts={available?.Count ?? -1}");
|
||||
|
||||
await conn.ReturnEmptyOkAsync(msg);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user