2025-10-04 20:40:44 +02:00
|
|
|
export class WebSocketClient {
|
|
|
|
|
constructor(app) {
|
|
|
|
|
this.app = app;
|
|
|
|
|
this.ws = null;
|
|
|
|
|
this.reconnectAttempts = 0;
|
|
|
|
|
this.maxReconnectAttempts = 5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async connect(nickname) {
|
|
|
|
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
|
|
|
const wsUrl = `${protocol}//${window.location.host}/ws/${encodeURIComponent(nickname)}`;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
this.ws = new WebSocket(wsUrl);
|
|
|
|
|
this.ws.onopen = () => {
|
|
|
|
|
console.log('WebSocket connected');
|
|
|
|
|
this.reconnectAttempts = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.ws.onmessage = (event) => {
|
|
|
|
|
this.handleMessage(JSON.parse(event.data));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.ws.onclose = () => {
|
|
|
|
|
console.log('WebSocket disconnected');
|
|
|
|
|
this.attemptReconnect(nickname);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.ws.onerror = (error) => {
|
|
|
|
|
console.error('WebSocket error:', error);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Failed to connect:', error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attemptReconnect(nickname) {
|
|
|
|
|
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
|
|
|
this.reconnectAttempts++;
|
|
|
|
|
console.log(`Reconnect attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts}`);
|
|
|
|
|
setTimeout(() => this.connect(nickname), 2000);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleMessage(data) {
|
|
|
|
|
switch (data.type) {
|
|
|
|
|
case 'init':
|
|
|
|
|
this.app.onPlayerInit(data.player, data.game_state);
|
|
|
|
|
break;
|
|
|
|
|
|
2025-10-04 23:26:27 +02:00
|
|
|
case 'player_stats_update':
|
|
|
|
|
this.app.onPlayerStatsUpdate(data.player);
|
2025-10-04 20:40:44 +02:00
|
|
|
break;
|
|
|
|
|
case 'cursor_move':
|
|
|
|
|
this.app.onCursorMove(data.player_id, data.x, data.y);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'building_placed':
|
|
|
|
|
this.app.onBuildingPlaced(data.building);
|
|
|
|
|
break;
|
|
|
|
|
case 'building_removed':
|
|
|
|
|
this.app.onBuildingRemoved(data.x, data.y);
|
|
|
|
|
break;
|
|
|
|
|
case 'building_updated':
|
|
|
|
|
this.app.onBuildingUpdated(data.x, data.y, data.name);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'player_joined':
|
|
|
|
|
this.app.onPlayerJoined(data.player_id, data.nickname);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'player_left':
|
|
|
|
|
this.app.onPlayerLeft(data.player_id, data.nickname);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'chat':
|
|
|
|
|
this.app.onChatMessage(data.nickname, data.message, data.timestamp);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'error':
|
|
|
|
|
this.app.onError(data.message);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
send(data) {
|
|
|
|
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
|
|
|
this.ws.send(JSON.stringify(data));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sendCursorMove(x, y) {
|
|
|
|
|
this.send({
|
|
|
|
|
type: 'cursor_move',
|
|
|
|
|
x: x,
|
|
|
|
|
y: y
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
placeBuilding(buildingType, x, y) {
|
|
|
|
|
this.send({
|
|
|
|
|
type: 'place_building',
|
|
|
|
|
building_type: buildingType,
|
|
|
|
|
x: x,
|
|
|
|
|
y: y
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
removeBuilding(x, y) {
|
|
|
|
|
this.send({
|
|
|
|
|
type: 'remove_building',
|
|
|
|
|
x: x,
|
|
|
|
|
y: y
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
editBuilding(x, y, name) {
|
|
|
|
|
this.send({
|
|
|
|
|
type: 'edit_building',
|
|
|
|
|
x: x,
|
|
|
|
|
y: y,
|
|
|
|
|
name: name
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sendChat(message, timestamp) {
|
|
|
|
|
this.send({
|
|
|
|
|
type: 'chat',
|
|
|
|
|
message: message,
|
|
|
|
|
timestamp: timestamp
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|