75 lines
3.4 KiB
C#
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);
|
|
}
|
|
}
|