/
tytyshka
/
Typing_Race_Game
Обзор
Документация
Войти
/
tytyshka
/
Typing_Race_Game
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
Client
GameClient/typing_sfml.cpp
510 строк
16 KB
tytyshka
Файлы клиента
16 дек 2025, 21:52
16 дек 2025, 21:52
7041c0b
Код
Авторство
О чём код?
// Requires SFML (graphics + window) // Compile and link with sfml-graphics, sfml-window, sfml-system, plus net_client.* #include <SFML/Graphics.hpp> #include "net_client.h" #include "json.hpp" #include <thread> #include <mutex> #include <vector> #include <string> #include <iostream> using json = nlohmann::json; bool joinSent = false; // std::vector<std::pair<std::string, int>> results; enum class ClientState { LOGIN, ROOM_LIST, ROOM, GAME, RESULTS }; ClientState state = ClientState::ROOM_LIST; struct GameResult { std::string player; int score; }; std::vector<GameResult> results; struct RoomView { std::string id; std::string name; int players; int max_players; std::string state; }; std::vector<RoomView> rooms; int selectedRoom = 0; std::string currentRoomId; std::vector<std::string> roomPlayers; struct Letter { std::string id; char ch; uint64_t spawn_ts; int ttl; float x; // ← НОВОЕ float y; }; int main() { NetClient nc; std::string host = "127.0.0.1"; int port = 9000; if (!nc.connect_to(host, port)) return 1; std::string username; std::cout << "Enter username: "; std::getline(std::cin, username); json auth = { {"type","AUTH"}, {"payload", { {"username", username} }} }; nc.send_json(auth); // ClientState state = ClientState::LOGIN; sf::RenderWindow window(sf::VideoMode(600, 800), "Typing Race (SFML)"); sf::Font font; // Пытаемся загрузить стандартный системный шрифт bool fontLoaded = false; // Попробуем несколько стандартных шрифтов const char* fontPaths[] = { "C:/Windows/Fonts/arial.ttf", "C:/Windows/Fonts/tahoma.ttf", "C:/Windows/Fonts/verdana.ttf", "C:/Windows/Fonts/calibri.ttf", "arial.ttf", "tahoma.ttf", "verdana.ttf", "calibri.ttf" }; for (const char* path : fontPaths) { if (font.loadFromFile(path)) { std::cout << "Loaded font: " << path << std::endl; fontLoaded = true; break; } } // Если не удалось загрузить шрифт из файла, создаем простой текст с помощью примитивов if (!fontLoaded) { std::cout << "Could not load any font. Using simple graphics mode." << std::endl; } std::mutex mu; std::vector<Letter> letters; std::atomic<bool> running{ true }; std::thread recv([&]() { while (running) { json m = nc.receive_next(); std::cout << "[CLIENT] received: " << m.dump() << std::endl; // NEW if (m.is_null() || m.empty()) { running = false; break; } std::string type = m.value("type", ""); json payload = m.value("payload", json::object()); if (type == "AUTH_OK") { state = ClientState::ROOM_LIST; nc.send_json({ {"type","LIST_ROOMS"} }); } else if (type == "ROOM_LIST") { std::lock_guard<std::mutex> lk(mu); rooms.clear(); for (auto& r : payload["rooms"]) { rooms.push_back({ r["room_id"], r["name"], r["players"], r["max_players"], r["state"] }); } } else if (type == "ROOM_JOINED") { state = ClientState::ROOM; std::lock_guard<std::mutex> lk(mu); currentRoomId = payload["room_id"]; roomPlayers.clear(); for (auto& name : payload["players"]) { roomPlayers.push_back(name.get<std::string>()); } // если игра уже идёт — сразу в GAME if (payload.value("state", "") == "RUNNING") { state = ClientState::GAME; } } else if (type == "GAME_STARTED") { std::lock_guard<std::mutex> lk(mu); letters.clear(); state = ClientState::GAME; } else if (type == "SPAWN") { if (state != ClientState::GAME) { std::lock_guard<std::mutex> lk(mu); letters.clear(); state = ClientState::GAME; } Letter L; L.id = payload.value("id", ""); std::string s = payload.value("char", "?"); L.ch = s.empty() ? '?' : s[0]; L.ttl = payload.value("ttl", 2000); L.spawn_ts = payload.value("spawn_ts", (uint64_t)0); L.y = 0.f; static const int columns = 10; static const float startX = 50.f; static const float colWidth = 50.f; int col = rand() % columns; L.x = startX + col * colWidth; std::lock_guard<std::mutex> lk(mu); letters.push_back(L); } else if (state == ClientState::GAME && type == "DESPAWN") { std::string id = payload.value("id", ""); std::lock_guard<std::mutex> lk(mu); letters.erase(std::remove_if(letters.begin(), letters.end(), [&](const Letter& a) { return a.id == id; }), letters.end()); } /*else if (type == "ROOM_JOINED") { state = ClientState::ROOM; }*/ else if (state == ClientState::GAME && type == "HIT") { // could show feedback } else if (type == "GAME_FINISHED") { std::lock_guard<std::mutex> lk(mu); letters.clear(); results.clear(); for (auto& r : payload["scores"]) { results.push_back({ r["username"].get<std::string>(), r["score"].get<int>() }); } state = ClientState::RESULTS; } else { // other messages } } }); sf::Clock clock; while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); if (state == ClientState::ROOM_LIST && event.type == sf::Event::KeyPressed) { if (event.key.code == sf::Keyboard::Down && selectedRoom + 1 < rooms.size()) selectedRoom++; if (event.key.code == sf::Keyboard::Up && selectedRoom > 0) selectedRoom--; if (event.key.code == sf::Keyboard::Enter && !rooms.empty() && !joinSent) { joinSent = true; nc.send_json({ {"type","JOIN_ROOM"}, {"payload", { {"room_id", rooms[selectedRoom].id} }} }); } if (event.key.code == sf::Keyboard::C) { nc.send_json({ {"type","CREATE_ROOM"}, {"payload", { {"name","My Room"}, {"max_players",2} }} }); } } if (state == ClientState::ROOM && event.type == sf::Event::KeyPressed) { if (event.key.code == sf::Keyboard::S) { nc.send_json({ {"type","START_GAME"}, {"payload", {{"room_id", currentRoomId}}} }); } if (event.key.code == sf::Keyboard::Escape) { nc.send_json({ {"type","LEAVE_ROOM"}, {"payload", {{"room_id", currentRoomId}}} }); state = ClientState::ROOM_LIST; } } if (state == ClientState::GAME && event.type == sf::Event::TextEntered) { if (event.text.unicode < 128) { char c = static_cast<char>(event.text.unicode); std::string s(1, c); nc.send_json({ {"type","KEY_PRESS"}, {"payload", { {"char", s} }} }); } } if (state == ClientState::RESULTS && event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) { { std::lock_guard<std::mutex> lk(mu); letters.clear(); results.clear(); roomPlayers.clear(); currentRoomId.clear(); } joinSent = false; state = ClientState::ROOM_LIST; nc.send_json({ {"type","LIST_ROOMS"} }); } /*if (event.type == sf::Event::TextEntered) { if (event.text.unicode < 128) { char c = static_cast<char>(event.text.unicode); std::string s(1, c); json q = { {"type","KEY_PRESS"}, {"payload", { {"char", s} }} }; nc.send_json(q); } }*/ } float dt = clock.restart().asSeconds(); // animate letters { std::lock_guard<std::mutex> lk(mu); for (auto& L : letters) { L.y += 100.f * dt; // скорость падения } uint64_t now = (uint64_t)( std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::system_clock::now().time_since_epoch() ).count() ); letters.erase( std::remove_if(letters.begin(), letters.end(), [&](const Letter& a) { return now - a.spawn_ts > (uint64_t)a.ttl; }), letters.end() ); } window.clear(sf::Color::Black); // draw letters { std::lock_guard<std::mutex> lk(mu); for (size_t i = 0; i < letters.size(); ++i) { sf::Text t; if (fontLoaded) t.setFont(font); t.setString(std::string(1, letters[i].ch)); t.setCharacterSize(48); t.setFillColor(sf::Color::White); t.setPosition(letters[i].x, letters[i].y); window.draw(t); } } if (state == ClientState::LOGIN) { sf::Text t; if (fontLoaded) t.setFont(font); t.setString("Connecting..."); t.setCharacterSize(32); t.setFillColor(sf::Color::White); t.setPosition(150, 350); window.draw(t); } else if (state == ClientState::ROOM_LIST) { sf::Text title; if (fontLoaded) title.setFont(font); title.setString("Room List"); title.setCharacterSize(32); title.setPosition(200, 50); window.draw(title); for (size_t i = 0; i < rooms.size(); ++i) { sf::Text t; if (fontLoaded) t.setFont(font); std::string line = rooms[i].name + " (" + std::to_string(rooms[i].players) + "/" + std::to_string(rooms[i].max_players) + ") " + rooms[i].state; t.setString(line); t.setCharacterSize(22); t.setPosition(100, 120 + i * 30); if ((int)i == selectedRoom) t.setFillColor(sf::Color::Yellow); else t.setFillColor(sf::Color::White); window.draw(t); } sf::Text hint; if (fontLoaded) hint.setFont(font); // hint.setString("Up/Down — select | Enter — join | C — create room"); hint.setString("W/S - select | Enter - join | C - create room"); hint.setCharacterSize(18); hint.setPosition(80, 700); window.draw(hint); } else if (state == ClientState::ROOM) { sf::Text title; if (fontLoaded) title.setFont(font); title.setString("Room: " + currentRoomId); title.setCharacterSize(28); title.setPosition(100, 40); window.draw(title); sf::Text playersTitle; if (fontLoaded) playersTitle.setFont(font); playersTitle.setString("Players:"); playersTitle.setCharacterSize(22); playersTitle.setPosition(100, 100); window.draw(playersTitle); for (size_t i = 0; i < roomPlayers.size(); ++i) { sf::Text t; if (fontLoaded) t.setFont(font); t.setString("- " + roomPlayers[i]); t.setCharacterSize(20); t.setPosition(120, 140 + i * 25); window.draw(t); } sf::Text hint; if (fontLoaded) hint.setFont(font); hint.setString("S — start game | Esc — leave room"); hint.setCharacterSize(18); hint.setPosition(100, 700); window.draw(hint); } /*else if (state == ClientState::ROOM) { sf::Text t; if (fontLoaded) t.setFont(font); t.setString("In room\nPress ENTER to start"); t.setCharacterSize(28); t.setPosition(120, 300); window.draw(t); }*/ else if (state == ClientState::GAME) { sf::Text dbg; dbg.setFont(font); dbg.setString("GAME STATE"); dbg.setCharacterSize(60); dbg.setFillColor(sf::Color::Red); dbg.setPosition(100, 200); window.draw(dbg); } else if (state == ClientState::RESULTS) { sf::Text title; if (fontLoaded) title.setFont(font); title.setString("Results"); title.setCharacterSize(36); title.setPosition(200, 40); window.draw(title); for (size_t i = 0; i < results.size(); ++i) { sf::Text t; if (fontLoaded) t.setFont(font); t.setString( results[i].player + " : " + std::to_string(results[i].score) ); t.setCharacterSize(24); t.setPosition(160, 120 + i * 40); window.draw(t); } sf::Text hint; if (fontLoaded) hint.setFont(font); hint.setString("ESC - back to rooms"); hint.setCharacterSize(18); hint.setPosition(180, 700); window.draw(hint); } // Отображаем инструкцию sf::Text infoText; if (fontLoaded) { infoText.setFont(font); } infoText.setString("Type the falling letters! Close window to exit."); infoText.setCharacterSize(20); infoText.setFillColor(sf::Color::Green); infoText.setPosition(10, 10); window.draw(infoText); window.display(); } running = false; nc.disconnect(); if (recv.joinable()) recv.join(); return 0; }