Игровая логика PvP: сервер (C++) + клиент (Quintus.js)
Сервер: Game — authoritative-тик 20 Гц через uSockets timer, движение/коллизии танков и пуль, разрушаемые кирпичные стены, respawn, победа/поражение/ничья, технический проигрыш при дисконнекте. game.ready запускает матч, когда оба игрока в pvp-комнате готовы. Клиент: заведён Quintus.js (MIT, vendored) — Tank/Bullet/Wall спрайты, рендер по снапшотам game.state, keyboard-инпут (WASD/стрелки/space), экран результата матча. Найден и исправлен use-after-free: колбэк onFinished_ синхронно удалял Game изнутри его же метода — перенесено на uWS::Loop::defer.
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
import { Network, ServerMessage } from './network.js';
|
||||
import { ClientMessage } from './protocol.js';
|
||||
import { startBattle } from './battle.js';
|
||||
|
||||
const container = document.getElementById('lobby');
|
||||
|
||||
let net;
|
||||
let myPlayerId = null;
|
||||
let rooms = [];
|
||||
let currentRoom = null;
|
||||
let errorMessage = '';
|
||||
@@ -47,14 +50,19 @@ function renderLobbyView() {
|
||||
}
|
||||
|
||||
function renderRoomView() {
|
||||
const readyBtn =
|
||||
currentRoom.mode === 'pvp'
|
||||
? '<button data-action="ready">готов</button>'
|
||||
: '<p class="lobby-error">coop ещё не реализован (этап 5)</p>';
|
||||
container.innerHTML = `
|
||||
<div class="lobby-panel">
|
||||
${errorBanner()}
|
||||
<h3 class="prompt">room ${currentRoom.name}</h3>
|
||||
<p>режим: <span class="tag">${currentRoom.mode}</span></p>
|
||||
<ul class="room-players">
|
||||
${currentRoom.players.map((p) => `<li>${p.nickname}</li>`).join('')}
|
||||
${currentRoom.players.map((p) => `<li>${p.nickname}${p.ready ? ' — готов' : ''}</li>`).join('')}
|
||||
</ul>
|
||||
${readyBtn}
|
||||
<button data-action="leave">выйти в лобби</button>
|
||||
</div>`;
|
||||
}
|
||||
@@ -78,6 +86,9 @@ container?.addEventListener('click', (e) => {
|
||||
case 'join':
|
||||
net.joinRoom(btn.dataset.roomId);
|
||||
break;
|
||||
case 'ready':
|
||||
net.send(ClientMessage.GAME_READY);
|
||||
break;
|
||||
case 'leave':
|
||||
currentRoom = null;
|
||||
net.leaveRoom();
|
||||
@@ -99,8 +110,9 @@ export function initLobby() {
|
||||
net = new Network();
|
||||
|
||||
net.on('_open', () => net.hello(nickname));
|
||||
net.on(ServerMessage.HELLO_ACK, () => {
|
||||
net.on(ServerMessage.HELLO_ACK, (payload) => {
|
||||
connected = true;
|
||||
myPlayerId = payload.player_id;
|
||||
net.listRooms();
|
||||
});
|
||||
net.on('_close', () => {
|
||||
@@ -125,6 +137,10 @@ export function initLobby() {
|
||||
}
|
||||
});
|
||||
|
||||
net.on(ServerMessage.GAME_START, (payload) => {
|
||||
startBattle(net, payload, myPlayerId);
|
||||
});
|
||||
|
||||
net.on(ServerMessage.ERROR, (payload) => {
|
||||
console.error('[gameserver] error:', payload);
|
||||
errorMessage = payload.message;
|
||||
|
||||
Reference in New Issue
Block a user