Files

75 lines
3.4 KiB
C#

using Ananta.SDK.Rpc;
using Ananta.Server.ClientData.Client4229938;
using Ananta.Server.Protocol.Client4229938;
using GameMethods = Ananta.Server.RpcTypes.Client4229938.Methods.Game;
namespace Ananta.Server.Handlers.Game;
/// <summary>
/// Mall shelf purchases for build 4229938 (MallManager.AskBuyCommodity -> AskMallBuyCommodity).
///
/// The client sends (commodityId, buyCount, isAutoExchange) and its reply reader consumes nothing, so
/// the answer is a bare OK and every observable effect travels as a push: the wallet totals, the
/// backpack delta, and - for the wallet currencies - the money-gain popup. Failure is reported with a
/// non-zero err, which is exactly what the client turns into "purchase failed", so a successful
/// purchase must always answer OK.
///
/// Prices are charged in the commodity's ConsumeItemId (a wallet currency or a backpack item). This
/// private server never blocks a purchase for lack of funds: a shortfall is logged and waived.
/// </summary>
internal sealed partial class GameRouter
{
[Handler(MethodId.AskMallBuyCommodity, HandlerPacketKind.Invoke)]
private async Task AskMallBuyCommodity(Connection conn, UxRpcMessage msg)
{
var state = GetWorldState(msg.Context);
var request = ParseEconomyRequest4229938(msg.Body);
var count = Math.Clamp(request.Count == 0 ? 1u : request.Count, 1u, 999u);
if (!EconomyConfigRepository.TryMallCommodity(request.Id, out var commodity))
{
conn.Log.Warn($"[MALL] unknown commodity {request.Id} (body={Convert.ToHexString(msg.Body)})");
await conn.ReturnEmptyOkAsync(msg);
return;
}
var changes = new EconomyChanges4229938();
ChargeCurrency4229938(
state, changes, commodity.ConsumeItemId, commodity.UnitPrice * count, conn.Log, $"mall {commodity.Id}");
var granted = 0u;
if (commodity.DropId != 0 && EconomyConfigRepository.TryDrop(commodity.DropId, out var drop) && !drop.IsEmpty)
{
GrantDrop4229938(changes, drop, count, $"mall {commodity.Id}", conn.Log);
granted = count;
}
else
{
// Recharge vouchers and similar rows ship an empty drop: the row's BindId (or its first
// bound commodity id) is the thing the player actually receives.
var bindId = commodity.BindId != 0
? commodity.BindId
: (commodity.CommodityBindIds.Count > 0 ? commodity.CommodityBindIds[0] : 0u);
if (bindId != 0)
{
AddGrant4229938(changes, bindId, count, isBind: true);
granted = count;
conn.Log.Info($"[MALL] commodity {commodity.Id} has empty drop {commodity.DropId}, granted BindId {bindId} x{count}");
}
else
{
conn.Log.Warn($"[MALL] commodity {commodity.Id} grants nothing (drop={commodity.DropId} bindId=0)");
}
}
var summary = await ApplyEconomy4229938Async(conn, state, changes, $"mall {commodity.Id}");
conn.Log.Info(
$"[MALL] buy commodity={commodity.Id} mall={commodity.MallId} count={count} granted={granted} " +
$"price={commodity.UnitPrice} currency={commodity.ConsumeItemId} " +
$"autoExchange={request.Flag} drop={commodity.DropId} -> {summary}");
await conn.ReturnEmptyOkAsync(msg);
}
}