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,74 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user