# โรงประมูลและตลาดผู้เล่นในเกม

> รายการ การประมูล และเวลานับถอยหลังแบบสดในทุกไคลเอนต์ ขยายเวลาอัตโนมัติเมื่อประมูลในวินาทีสุดท้ายเพื่อกันการตัดหน้า มีซื้อทันทีและราคาขาย โดยเซิร์ฟเวอร์ของคุณตัดสินทุกการประมูล และเกมของคุณเก็บเหรียญกับไอเท็มไว้เอง

Source: https://hibira.io/th/use-cases/game-auction-house

โรงประมูล ตลาดซื้อขาย หรือตลาดของผู้เล่นช่วยให้เศรษฐกิจในเกมหมุนไปได้ แต่จะรู้สึกยุติธรรมก็ต่อเมื่อทุกคนเห็นสิ่งเดียวกันในเวลาเดียวกัน คือเห็นราคาใหม่ทันทีที่มีคนประมูล เห็นเวลานับถอยหลังเท่ากันทุกหน้าจอ และไม่มีใครแย่งไปได้ในวินาทีสุดท้าย แพ็กเกจโอเพนซอร์ส [@hibira-io/auction](https://www.npmjs.com/package/@hibira-io/auction) ให้สิ่งนี้บน Hibira โดยเซิร์ฟเวอร์ของคุณเองเป็นผู้ตัดสินทุกการประมูล

## สิ่งที่ผู้เล่นได้รับ

- รายการที่ลงขาย เปลี่ยนราคา และปิดลง จะแสดงในทุกไคลเอนต์ที่เปิดอยู่โดยไม่ต้องรีเฟรช
- เวลานับถอยหลังตรงกับนาฬิกาของเซิร์ฟเวอร์ และการประมูลในวินาทีสุดท้ายจะเพิ่มเวลาแทนที่จะถูกตัดหน้า
- แจ้งทันทีเมื่อมีคนประมูลสูงกว่า ซื้อทันทีได้ และดูราคาขายล่าสุดของแต่ละไอเท็มได้

![ตัวอย่างโรงประมูล: ผู้เล่นประมูลธนูนักล่า อีกหน้าต่างประมูลสูงกว่าในไม่กี่วินาที และผู้เล่นคนแรกเห็นราคาใหม่พร้อมการแจ้งเตือน](https://hibira.io/portal/articles/demo-game-auction-house-th.webp)

## สร้างด้วย

- **หนึ่งแชนแนลสาธารณะหรือส่วนตัวต่อหนึ่งโรงประมูล** (`auction-main`) มีอีเวนต์เดียวคือ `auction-update` ที่ส่งข้อมูลรายการทั้งหมดพร้อมหมายเลขเวอร์ชัน
- **HTTP API** (`trigger`) จากเซิร์ฟเวอร์ของคุณ แต่ละการเปลี่ยนแปลงถูกส่งครั้งเดียว ไม่ว่าจะมีผู้เล่นดูอยู่กี่คน
- **[@hibira-io/auction](https://github.com/hibira-io/auction-js)**: ฝั่งเซิร์ฟเวอร์ตรวจการประมูล ขยายเวลาการประมูลที่ใกล้ปิด และปิดการขาย ฝั่งไคลเอนต์เก็บเวอร์ชันล่าสุดของแต่ละรายการและตามทันหลังเชื่อมต่อใหม่

```text
sequence: การประมูลไปที่เซิร์ฟเวอร์ของคุณ แล้ว Hibira แจ้งผู้เล่นทุกคน
ผู้เล่น A | เซิร์ฟเวอร์ของคุณ | *Hibira | ผู้เล่น B
1 > 2: POST /auction/:id/bid | 120 ทอง
note: house.bid() ตรวจการประมูลและกันทองไว้
2 > 3: auction-update | ราคา 120, A นำ
3 > 1: auction-update
3 > 4: auction-update | "ประมูล 126"
```

## ขั้นที่ 1: สร้างโรงประมูลบนเซิร์ฟเวอร์

ไอเท็มและเหรียญยังอยู่ในระบบของเกมคุณ โรงประมูลจะขอให้คุณกันเหรียญของผู้ประมูลไว้ คืนให้เมื่อมีคนประมูลสูงกว่า และโอนไอเท็มกับเหรียญเมื่อการประมูลปิด

```js
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: เอนด์พอยต์สี่ตัว

ดึงผู้เล่นจากเซสชันของคุณเองเสมอ อย่าอ่านจากเนื้อหาคำขอ

```js
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: แสดงโรงประมูลในเกม

```js
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)` คืนราคาขายล่าสุดของไอเท็มแต่ละประเภทสำหรับกราฟราคา หลังเชื่อมต่อใหม่ โรงประมูลจะโหลดสแนปช็อตอีกครั้ง จึงไม่พลาดอะไร

## ลองใช้

[ตัวอย่างโรงประมูล](https://github.com/hibira-io/examples/tree/main/nextjs-auction) เป็นตลาดในเกมที่สมบูรณ์ด้วย Next.js มีพ่อค้า NPC กระเป๋า ซื้อทันที การแจ้งเตือนเมื่อถูกประมูลสูงกว่า และราคาขาย รองรับเจ็ดภาษา เปิดสองหน้าต่างแล้วลองประมูลในหน้าต่างหนึ่ง

## เคล็ดลับ

- ทุกการเปลี่ยนแปลงนับเป็นหนึ่งข้อความ ไม่ว่าจะมีผู้เล่นดูกี่คน แบ่งโรงประมูลขนาดใหญ่ตามหมวดหมู่หรือภูมิภาค (`name: 'weapons'`) เพื่อให้แต่ละไคลเอนต์โหลดเฉพาะส่วนที่แสดง
- ถ้ามีเกมเซิร์ฟเวอร์หลายเครื่อง ให้ส่ง `store` ที่อยู่บนฐานข้อมูลของคุณ การบันทึกเป็นแบบเปรียบเทียบก่อนเขียน สองเซิร์ฟเวอร์จึงรับการประมูลเดียวกันพร้อมกันไม่ได้
- มันโอนเหรียญและไอเท็มของเกมคุณเอง ไม่ใช่เงินจริง
- ไคลเอนต์ Unity, Godot หรือ Pusher SDK ใดก็ได้ สามารถติดตามแชนแนล `auction-main` เดียวกันและเรียกเอนด์พอยต์เดียวกันได้ สำหรับห้องและการจับคู่ ดู[ห้องเกมและล็อบบี้แบบผู้เล่นหลายคน](https://hibira.io/th/use-cases/game-lobbies)
