Каркас проекта: ТЗ, nginx+gameserver(uWebSockets) в Docker, /ws echo
Frontend — чистый HTML/CSS/JS без сборки (резюме, проекты, игра-заглушки). Gameserver — C++ на uWebSockets (CMake FetchContent), протокол hello/lobby.list_rooms/error проверен end-to-end.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/* TODO: лобби и игровое поле — этапы 3-6 */
|
||||
@@ -0,0 +1 @@
|
||||
/* TODO: карточки проектов — этап 2 */
|
||||
@@ -0,0 +1 @@
|
||||
/* TODO: вёрстка резюме — этап 2 */
|
||||
@@ -0,0 +1,38 @@
|
||||
:root {
|
||||
--color-bg: #0a0e0c;
|
||||
--color-fg: #c7c7c7;
|
||||
--color-accent: #00ff41;
|
||||
--color-accent-dim: #0a8f2c;
|
||||
--color-silver: #9fa3a0;
|
||||
--font-mono: 'JetBrains Mono', 'Share Tech Mono', ui-monospace, monospace;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--color-bg);
|
||||
color: var(--color-fg);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
.prompt::before {
|
||||
content: 'cacto@matrix:~$ ';
|
||||
color: var(--color-accent-dim);
|
||||
}
|
||||
|
||||
#matrix-rain {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>./battlecity — cacto</title>
|
||||
<link rel="stylesheet" href="/css/theme.css" />
|
||||
<link rel="stylesheet" href="/css/game.css" />
|
||||
</head>
|
||||
<body>
|
||||
<main id="game-root">
|
||||
<!-- TODO: лобби (этап 3) и игровое поле на Quintus.js (этапы 4-5) -->
|
||||
<h1 class="prompt">./battlecity</h1>
|
||||
<div id="lobby"></div>
|
||||
<div id="battlecity-canvas-container"></div>
|
||||
</main>
|
||||
<script type="module" src="/js/game/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,23 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>cacto — Гаммель Дмитрий</title>
|
||||
<link rel="stylesheet" href="/css/theme.css" />
|
||||
<link rel="stylesheet" href="/css/resume.css" />
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="matrix-rain"></canvas>
|
||||
<main id="resume">
|
||||
<!-- TODO: вёрстка резюме, ASCII-арт кактуса — этап 2 -->
|
||||
<h1 class="prompt">whoami</h1>
|
||||
<p>Гаммель Дмитрий Викторович — PHP/Go backend, опыт КИПиА/АСУТП</p>
|
||||
<nav>
|
||||
<a href="/projects.html">ls projects/</a>
|
||||
<a href="/game.html">./battlecity</a>
|
||||
</nav>
|
||||
</main>
|
||||
<script type="module" src="/js/matrix-rain.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
// TODO: клиентские представления Tank/Bullet/Wall для Quintus — этапы 4-5
|
||||
@@ -0,0 +1,21 @@
|
||||
// TODO: полноценный UI лобби (список/создание/присоединение комнат) — этап 3.
|
||||
// Пока — проверка связки клиент <-> gameserver (hello -> lobby.list_rooms).
|
||||
import { Network, ServerMessage } from './network.js';
|
||||
|
||||
export function connectAndProbe() {
|
||||
const net = new Network();
|
||||
|
||||
net.on('_open', () => net.hello('cacto'));
|
||||
net.on(ServerMessage.HELLO_ACK, () => {
|
||||
console.log('[gameserver] hello.ack received');
|
||||
net.listRooms();
|
||||
});
|
||||
net.on(ServerMessage.LOBBY_ROOMS, (payload) => {
|
||||
console.log('[gameserver] rooms:', payload.rooms);
|
||||
});
|
||||
net.on(ServerMessage.ERROR, (payload) => {
|
||||
console.error('[gameserver] error:', payload);
|
||||
});
|
||||
|
||||
return net;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// TODO: инициализация Quintus и игрового цикла рендера — этапы 4-5
|
||||
import { connectAndProbe } from './lobby.js';
|
||||
|
||||
connectAndProbe();
|
||||
@@ -0,0 +1,38 @@
|
||||
import { ClientMessage, ServerMessage } from './protocol.js';
|
||||
|
||||
export class Network {
|
||||
constructor() {
|
||||
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
|
||||
this.socket = new WebSocket(`${proto}://${location.host}/ws`);
|
||||
this.handlers = new Map();
|
||||
|
||||
this.socket.addEventListener('open', () => this._emit('_open'));
|
||||
this.socket.addEventListener('close', () => this._emit('_close'));
|
||||
this.socket.addEventListener('message', (event) => {
|
||||
const msg = JSON.parse(event.data);
|
||||
this._emit(msg.type, msg.payload);
|
||||
});
|
||||
}
|
||||
|
||||
on(type, handler) {
|
||||
this.handlers.set(type, handler);
|
||||
}
|
||||
|
||||
_emit(type, payload) {
|
||||
this.handlers.get(type)?.(payload);
|
||||
}
|
||||
|
||||
send(type, payload = {}) {
|
||||
this.socket.send(JSON.stringify({ type, payload }));
|
||||
}
|
||||
|
||||
hello(nickname) {
|
||||
this.send(ClientMessage.HELLO, { nickname });
|
||||
}
|
||||
|
||||
listRooms() {
|
||||
this.send(ClientMessage.LOBBY_LIST_ROOMS);
|
||||
}
|
||||
}
|
||||
|
||||
export { ServerMessage };
|
||||
@@ -0,0 +1,21 @@
|
||||
// Типы сообщений из TZ.md, раздел 5.
|
||||
export const ClientMessage = {
|
||||
HELLO: 'hello',
|
||||
LOBBY_LIST_ROOMS: 'lobby.list_rooms',
|
||||
LOBBY_CREATE_ROOM: 'lobby.create_room',
|
||||
LOBBY_JOIN_ROOM: 'lobby.join_room',
|
||||
LOBBY_LEAVE_ROOM: 'lobby.leave_room',
|
||||
GAME_READY: 'game.ready',
|
||||
GAME_INPUT: 'game.input',
|
||||
};
|
||||
|
||||
export const ServerMessage = {
|
||||
HELLO_ACK: 'hello.ack',
|
||||
ERROR: 'error',
|
||||
LOBBY_ROOMS: 'lobby.rooms',
|
||||
LOBBY_ROOM_JOINED: 'lobby.room_joined',
|
||||
LOBBY_ROOM_UPDATED: 'lobby.room_updated',
|
||||
GAME_START: 'game.start',
|
||||
GAME_STATE: 'game.state',
|
||||
GAME_OVER: 'game.over',
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
// TODO: полноценный canvas-эффект цифрового дождя — этап 2
|
||||
const canvas = document.getElementById('matrix-rain');
|
||||
if (canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
const resize = () => {
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
};
|
||||
resize();
|
||||
window.addEventListener('resize', resize);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
// TODO: рендер карточек проектов из данных — этап 2
|
||||
@@ -0,0 +1,22 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ls projects/ — cacto</title>
|
||||
<link rel="stylesheet" href="/css/theme.css" />
|
||||
<link rel="stylesheet" href="/css/projects.css" />
|
||||
</head>
|
||||
<body>
|
||||
<main id="projects">
|
||||
<!-- TODO: карточки проектов — этап 2 -->
|
||||
<h1 class="prompt">ls projects/</h1>
|
||||
<ul id="project-list"></ul>
|
||||
<nav>
|
||||
<a href="/">cd ~</a>
|
||||
<a href="/game.html">./battlecity</a>
|
||||
</nav>
|
||||
</main>
|
||||
<script type="module" src="/js/projects.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user