← 使用情境

遊戲拍賣場與玩家市集

每個用戶端即時看到上架、出價和倒數,最後幾秒出價自動延長防狙擊,支援直接購買與成交價走勢;每筆出價由你的伺服器決定,遊戲幣和道具仍由你的遊戲管理。

拍賣場、交易所或玩家市集能讓遊戲經濟持續運轉,但只有在每個人同時看到同樣的東西時才會讓人覺得公平:有人出價的瞬間就看到新價格、每個畫面上的倒數都一樣,最後一秒也不會被人偷走。開源套件 @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:四個 API

玩家身分要從你自己的登入狀態取得,不要從請求內容讀取:

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 頻道並呼叫同樣的 API。房間和配對請看多人遊戲房間與大廳。