События
WebGUI имеет двунаправленную систему именованных событий между сервером и страницей — прямой аналог системы событий WebView в alt:V.
Сервер → страница
Java API
java
// Отправить событие на страницу конкретного игрока
WebviewApi.emitToPage(player, "walletUpdate", "{\"balance\":1500}");
// Строка — тоже валидный JSON
WebviewApi.emitToPage(player, "serverRestart", "\"5 минут\"");
// Без данных
WebviewApi.emitToPage(player, "sessionExpired", null);Второй аргумент — имя события. Третий — любое валидное JSON-значение: объект, массив, строка, число, boolean или null.
Получение на странице — React-хук
tsx
import { useWebGUIEvent } from '@webgui/react'
interface WalletUpdate { balance: number }
export function Wallet() {
const [balance, setBalance] = useState(0)
useWebGUIEvent<WalletUpdate>('walletUpdate', ({ balance }) => {
setBalance(balance)
})
return <p>Баланс: {balance}</p>
}Получение на странице — vanilla JS
js
// Через window.webgui.on (инжектится модом)
window.webgui.on('walletUpdate', (data) => {
console.log('баланс:', data.balance)
})
// Или напрямую через DOM-событие
window.addEventListener('webgui:walletUpdate', (e) => {
console.log('баланс:', e.detail.balance)
})window.webgui.on и window.addEventListener слушают одно и то же CustomEvent('webgui:<eventName>').
Отписка
js
function onWalletUpdate(data) { ... }
window.webgui.on('walletUpdate', onWalletUpdate)
// позже:
window.webgui.off('walletUpdate', onWalletUpdate)В React хук useWebGUIEvent очищает подписку автоматически при анмаунте компонента.
Страница → сервер
Отправка со страницы
Используйте window.webgui.postToGame с любым каналом, кроме встроенных (log, close):
js
window.webgui.postToGame({ channel: 'shop:buy', itemId: 'minecraft:diamond', qty: 1 })Или через React-хук:
tsx
const post = usePostToGame()
post({ channel: 'shop:buy', itemId: 'minecraft:diamond', qty: 1 })Полный JSON-пейлоад (включая поле channel) пересылается на сервер как C2S-пакет.
Получение на сервере — Java API
java
// Регистрируется один раз, например при инициализации мода
WebviewApi.onPageEvent("shop:buy", (player, rawJson) -> {
JsonObject obj = JsonParser.parseString(rawJson).getAsJsonObject();
String item = obj.get("itemId").getAsString();
int qty = obj.get("qty").getAsInt();
giveItem(player, item, qty);
});Получение на сервере — Fabric-событие
Для модов, предпочитающих Fabric event bus:
java
WebviewServerEvents.PAGE_EVENT.register((player, channel, payload) -> {
if (!channel.equals("shop:buy")) return;
// разобрать payload...
});Встроенные каналы (только клиент, до сервера не доходят)
| Канал | Действие |
|---|---|
log | Логирует сообщение в клиентский лог игры |
close | Закрывает активный GUI или HUD-оверлей |
Пример полного round-trip
Сервер — открываем магазин и пушим данные инвентаря:
java
WebviewApi.openGui(player, "https://your-site.example.com/shop");
WebviewApi.emitToPage(player, "inventoryLoad", buildInventoryJson(player));
WebviewApi.onPageEvent("shop:buy", (player, rawJson) -> {
processShopPurchase(player, rawJson);
// возвращаем обновлённый баланс на страницу
WebviewApi.emitToPage(player, "walletUpdate", "{\"balance\":" + getBalance(player) + "}");
});Страница — React-компонент:
tsx
import { useWebGUIEvent, usePostToGame } from '@webgui/react'
export function ShopScreen() {
const [items, setItems] = useState([])
const [balance, setBalance] = useState(0)
const post = usePostToGame()
useWebGUIEvent('inventoryLoad', setItems)
useWebGUIEvent<{ balance: number }>('walletUpdate', ({ balance }) => setBalance(balance))
function buy(itemId: string) {
post({ channel: 'shop:buy', itemId, qty: 1 })
}
return (
<>
<p>Баланс: {balance}</p>
{items.map(item => (
<button key={item.id} onClick={() => buy(item.id)}>{item.name}</button>
))}
</>
)
}