Send authoritative multiplayer inputs from browser
This commit is contained in:
parent
7cf6103544
commit
909abb1487
@ -8,12 +8,15 @@ const KEYCLOAK_CONFIG = {
|
||||
clientId: 'airship-web',
|
||||
};
|
||||
|
||||
const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
|
||||
|
||||
const keycloak = new Keycloak(KEYCLOAK_CONFIG);
|
||||
let socket = null;
|
||||
let selfId = null;
|
||||
let reconnectTimer = null;
|
||||
let authenticated = false;
|
||||
let playerCount = 0;
|
||||
let inputSequence = 0;
|
||||
|
||||
const statusButton = document.createElement('button');
|
||||
statusButton.id = 'multiplayer-status';
|
||||
@ -30,7 +33,7 @@ style.textContent = `
|
||||
left: max(10px, env(safe-area-inset-left));
|
||||
z-index: 410;
|
||||
min-height: 38px;
|
||||
max-width: 180px;
|
||||
max-width: 190px;
|
||||
padding: 7px 11px;
|
||||
border: 1px solid rgba(220, 168, 79, .72);
|
||||
border-radius: 999px;
|
||||
@ -95,6 +98,7 @@ const connect = async () => {
|
||||
socket = ws;
|
||||
|
||||
ws.addEventListener('open', () => {
|
||||
inputSequence = 0;
|
||||
setStatus('online', 'MULTIPLAYER • ONLINE');
|
||||
emitConnection('online');
|
||||
});
|
||||
@ -110,13 +114,24 @@ const connect = async () => {
|
||||
}
|
||||
|
||||
if (message.type === 'snapshot') {
|
||||
const players = Array.isArray(message.players)
|
||||
? message.players.filter((player) => player?.id && player.id !== selfId)
|
||||
const allPlayers = Array.isArray(message.players)
|
||||
? message.players.filter((player) => player?.id)
|
||||
: [];
|
||||
playerCount = players.length + 1;
|
||||
setStatus('online', `MULTIPLAYER • ${playerCount}`);
|
||||
const self = allPlayers.find((player) => player.id === selfId) || null;
|
||||
const players = allPlayers.filter((player) => player.id !== selfId);
|
||||
const projectiles = Array.isArray(message.projectiles)
|
||||
? message.projectiles.filter((shot) => shot?.id)
|
||||
: [];
|
||||
playerCount = allPlayers.length;
|
||||
const score = self ? ` • ${self.kills || 0}/${self.deaths || 0}` : '';
|
||||
setStatus('online', `MULTIPLAYER • ${playerCount}${score}`);
|
||||
window.dispatchEvent(new CustomEvent('airship:multiplayer-snapshot', {
|
||||
detail: { players, serverTime: message.serverTime || 0 },
|
||||
detail: {
|
||||
self,
|
||||
players,
|
||||
projectiles,
|
||||
serverTime: message.serverTime || 0,
|
||||
},
|
||||
}));
|
||||
}
|
||||
});
|
||||
@ -134,15 +149,24 @@ const connect = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
const sendState = (state) => {
|
||||
if (socket?.readyState !== WebSocket.OPEN || !state) return false;
|
||||
const sendInput = (input = {}) => {
|
||||
if (socket?.readyState !== WebSocket.OPEN) return 0;
|
||||
inputSequence += 1;
|
||||
socket.send(JSON.stringify({
|
||||
type: 'state',
|
||||
x: Number(state.x) || 0,
|
||||
y: Number(state.y) || 0,
|
||||
angle: Number(state.angle) || 0,
|
||||
hull: Math.max(0, Math.round(Number(state.hull) || 0)),
|
||||
type: 'input',
|
||||
seq: inputSequence,
|
||||
throttle: clamp(Number(input.throttle) || 0, -1, 1),
|
||||
steer: clamp(Number(input.steer) || 0, -1, 1),
|
||||
stop: Boolean(input.stop),
|
||||
docked: Boolean(input.docked),
|
||||
}));
|
||||
return inputSequence;
|
||||
};
|
||||
|
||||
const fire = (side) => {
|
||||
if (socket?.readyState !== WebSocket.OPEN) return false;
|
||||
const normalized = side === 'port' || side === -1 ? 'port' : 'starboard';
|
||||
socket.send(JSON.stringify({ type: 'fire', side: normalized }));
|
||||
return true;
|
||||
};
|
||||
|
||||
@ -158,10 +182,12 @@ statusButton.addEventListener('click', () => {
|
||||
});
|
||||
|
||||
window.airshipMultiplayer = {
|
||||
sendState,
|
||||
sendInput,
|
||||
fire,
|
||||
login,
|
||||
logout,
|
||||
connect,
|
||||
getSelfId: () => selfId,
|
||||
isAuthenticated: () => authenticated,
|
||||
isConnected: () => socket?.readyState === WebSocket.OPEN,
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user