← 使用场景

游戏拍卖场与玩家市集

每个客户端实时看到上架、出价和倒计时,最后几秒出价自动延长防狙击,支持一口价与成交价走势;每次出价由你的服务器决定,游戏币和道具仍由你的游戏管理。

拍卖场、交易所或玩家市集能让游戏经济持续运转,但只有在每个人同时看到同样的东西时才会让人觉得公平:有人出价的瞬间就看到新价格、每个屏幕上的倒计时都一样,最后一秒也不会被人抢走。开源包 @hibira-io/auction 在 Hibira 上提供这些功能,而每一次出价都由你自己的服务器决定。

玩家会得到什么

  • 拍卖品的上架、变价和下架会出现在每个打开的客户端,不用刷新。
  • 倒计时和服务器时钟一致,最后几秒出价会延长时间,而不是被人狙击。
  • 被超过的当下就收到通知,可以一口价购买,也看得到每件物品最近的成交价。
拍卖场示例:玩家对猎人之弓出价,另一个窗口随即出价超过,第一位玩家立刻看到新价格和被超过的提示。
拍卖场示例:玩家对猎人之弓出价,另一个窗口随即出价超过,第一位玩家立刻看到新价格和被超过的提示。

使用的功能

  • 每个拍卖场一个公开或私有频道(auction-main),只有一种事件 auction-update,带着完整的拍卖品和版本号。
  • 从你的服务器调用 HTTP API(trigger),每次变化只发布一次,不管有多少玩家在看。
  • @hibira-io/auction:服务器端负责检查出价、延长最后时刻的拍卖并结标;客户端保留每件拍卖品的最新版本,断线重连后自动补齐。
玩家 A你的服务器Hibira玩家 BPOST /auction/:id/bid120 金币house.bid() 检查出价并冻结金币auction-update价格 120,A 领先auction-updateauction-update“出价 126”
出价发到你的服务器,由 Hibira 通知每位玩家

第 1 步:在服务器上创建拍卖场

道具和游戏币仍然由你的游戏管理。拍卖场会请你冻结出价者的币、在有人出更高价时退回,并在结标时转移道具和币:

import Pusher from 'pusher';
import { createAuctionHouse } from '@hibira-io/auction/server';

const hibira = new Pusher({ appId, key, secret, host: 'ws.hibira.io', useTLS: true });

const house = createAuctionHouse({
  pusher: hibira,
  name: 'main',
  extendWithin: 30000, // a bid in the last 30 s adds time
  reserve: async ({ userId, amount }) => {
    if (!(await wallet.hold(userId, amount))) throw new Error('not enough gold');
  },
  release: async ({ userId, amount }) => wallet.unhold(userId, amount),
  settle: async ({ listing }) => game.deliver(listing), // idempotent by listing.id
});
house.start(); // closes and settles ended auctions

第 2 步:四个接口

玩家身份要从你自己的登录状态获取,不要从请求内容读取:

app.get('/auction', async (req, res) => res.json(await house.snapshot()));

app.post('/auction/:id/bid', requireLogin, async (req, res) => {
  try {
    res.json(await house.bid({ listingId: req.params.id, bidderId: req.user.id, bidder: { name: req.user.name }, amount: req.body.amount }));
  } catch (err) {
    res.status(err.status ?? 500).json({ error: err.code });
  }
});
// The same for house.buyout() and house.list()

第 3 步:在游戏中显示拍卖场

import { AuctionHouse } from '@hibira-io/auction';

const house = AuctionHouse.open(pusher, 'main', {
  load: () => fetch('/auction').then((r) => r.json()),
});

house.on('change', ({ listings }) => render(listings));
setInterval(() => listings.forEach((l) => showTimeLeft(l, house.timeLeft(l))), 250);

bidButton.onclick = () => fetch(`/auction/${l.id}/bid`, { method: 'POST', body: JSON.stringify({ amount: l.minBid }) });

house.prices(type) 会返回某种物品最近的成交价,可以画成价格走势图。断线重连后拍卖场会重新加载快照,不会漏掉任何变化。

试试看

拍卖场示例 是一个完整的 Next.js 游戏市集:NPC 商人、背包、一口价、被超过通知和成交价,支持七种语言。在两个窗口打开,在其中一个出价看看。

小提示

  • 每次变化算一条消息,不管有多少玩家在看。大型拍卖场可以按分类或地区拆开(name: 'weapons'),让每个客户端只加载它要显示的部分。
  • 有多台游戏服务器时,给拍卖场一个存在你数据库上的 store;写入是比较后再保存,两台服务器不可能同时接受同一次出价。
  • 它转移的是游戏自己的币和道具,不处理真钱。
  • Unity、Godot 或任何 Pusher SDK 的客户端都能订阅同一个 auction-main 频道并调用同样的接口。房间和匹配请看多人游戏房间与大厅。