#include #include #include #include #include #include #include #include #include #include #include "ui.h" using asio::ip::udp; int32_t bytesToInt(const char *bytes) { int32_t result = uint8_t(bytes[0]) << 24 | uint8_t(bytes[1]) << 16 | uint8_t(bytes[2]) << 8 | uint8_t(bytes[3]) << 0; return result; } class Client { public: Client(struct Application *a, asio::io_context& ioContext, uint16_t udpPort) : app(a), socket(ioContext), udpSocket(ioContext, udp::endpoint(udp::v4(), udpPort)) { udpSocket.set_option(asio::socket_base::broadcast(true)); // socket.connect(serverEndpoint); } void start() { udpReceive(); // receiveHeader(); } void disconnect() { socket.close(); udpSocket.close(); std::cout << "Disconnected from the server" << std::endl; } private: void udpReceive() { udpSocket.async_receive_from( asio::buffer(udpData, 256), senderEndpoint, [this](std::error_code ec, std::size_t length) { if (!ec && length > 0) { std::string msg(udpData, length); std::cout << "Received message: " << msg << std::endl; std::cout << "Sender IP: " << senderEndpoint.address().to_string() << std::endl; if (msg == "StreamControlTag") { std::cout << "Connect to server" << std::endl; asio::ip::tcp::endpoint serverEndpoint(senderEndpoint.address(), 8031); socket.connect(serverEndpoint); receiveHeader(); } } } ); } void receiveHeader() { std::cout << "start Received: Header" << std::endl; asio::async_read(socket, asio::buffer(headerBuffer), [this](std::error_code ec, std::size_t length) { if (!ec) { std::size_t messageLength = parseHeader(); receiveMessage(messageLength); } }); } std::size_t parseHeader() { return bytesToInt(headerBuffer.data()); } void receiveMessage(std::size_t messageLength) { bodyBuffer.resize(messageLength); asio::async_read(socket, asio::buffer(bodyBuffer), [this](std::error_code ec, std::size_t length) { if (!ec) { std::cout << "Received: Message" << std::endl; parseMessage(); receiveHeader(); // Continue to receive the next message } }); } void parseMessage() { // Parse the received message according to the specified format std::size_t offset = 0; // Extracting the message components uint8_t messageType = (uint8_t)(bodyBuffer[offset]); offset += 1; if (messageType == 1) { int32_t gameRound = bytesToInt(&bodyBuffer[offset]); offset += 4; int32_t player1NameLength = bytesToInt(&bodyBuffer[offset]); offset += 4; std::string player1Name(&bodyBuffer[offset], player1NameLength); offset += player1NameLength; int32_t player2NameLength = bytesToInt(&bodyBuffer[offset]); offset += 4; std::string player2Name(&bodyBuffer[offset], player2NameLength); offset += player2NameLength; int32_t player1Score = bytesToInt(&bodyBuffer[offset]); offset += 4; int32_t player2Score = bytesToInt(&bodyBuffer[offset]); offset += 4; updateApplication(app, player1Name.c_str(), player2Name.c_str(), gameRound, player1Score, player2Score); } } private: struct Application *app; asio::ip::udp::socket udpSocket; udp::endpoint senderEndpoint; char udpData[256]; asio::ip::tcp::socket socket; std::array headerBuffer; std::vector bodyBuffer; }; int main() { struct Application application, *app = &application; if (initApplication(app)) { printf("[Error]: initialize application failure: %s\n", app->errorString); return -1; } printf("width: %d, height: %d\n", app->winWidth, app->winHeight); std::mutex cv_mutex; std::condition_variable cv; asio::io_context ioContext; bool quit = false; bool network = true; SDL_Event event; std::thread clientThread = std::thread([&]() { try { constexpr uint16_t udpPort = 8032; /// asio::ip::tcp::endpoint serverEndpoint(asio::ip::make_address("192.168.1.80"), 8031); while (!quit) { std::unique_lock lk(cv_mutex); std::cout << "start Client Loop" << std::endl; while (network) { // 使用 lambda 检查条件 if (quit) break; // Client client(app, ioContext, serverEndpoint, udpPort); Client client(app, ioContext, udpPort); client.start(); ioContext.run(); client.disconnect(); } cv.wait(lk, [&]{ return network; }); // 等待直到 network 为 true } } catch (const std::exception& e) { std::cerr << "Exception: " << e.what() << std::endl; } }); SDL_ShowCursor(SDL_DISABLE); while (!quit) { SDL_LockMutex(app->mutex); while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { quit = true; network = true; } else if (event.type == SDL_MOUSEBUTTONDOWN) { if (event.button.button == SDL_BUTTON_RIGHT) { quit = true; network = true; } } else if (event.type == SDL_WINDOWEVENT) { if (event.type == SDL_WINDOWEVENT_FOCUS_LOST) { quit = true; network = true; } } else if (event.type == SDL_KEYDOWN) { switch(event.key.keysym.scancode) { case SDL_SCANCODE_O: network = !network; if (network == false) { ioContext.stop(); updateApplicationMode(app, 1); updateApplication(app, "", "", 0, 0, 0); } else { ioContext.restart(); updateApplicationMode(app, 0); updateApplication(app, "", "", 0, 0, 0); cv.notify_all(); } break; default: if (!network) { switch(event.key.keysym.scancode) { case SDL_SCANCODE_C: updateApplication(app, "", "", 0, 0, 0); break; case SDL_SCANCODE_S: updateApplication(app, "", "", app->roundNum, app->p2Num, app->p1Num); app->p1Win = 0; app->p2Win = 0; break; case SDL_SCANCODE_KP_1: if (app->p1Num == 99) { app->p1Num = 0; } else { ++app->p1Num; } updateApplication(app, "", "", app->roundNum, app->p1Num, app->p2Num); app->p1Win = 1; app->p2Win = 0; break; case SDL_SCANCODE_KP_2: if (app->p1Num > 0) { --app->p1Num; } updateApplication(app, "", "", app->roundNum, app->p1Num, app->p2Num); break; case SDL_SCANCODE_KP_3: updateApplication(app, "", "", app->roundNum, 0, app->p2Num); app->p1Win = 0; app->p2Win = 0; break; case SDL_SCANCODE_KP_4: if (app->p2Num == 99) { app->p2Num = 0; } else { ++app->p2Num; } updateApplication(app, "", "", app->roundNum, app->p1Num, app->p2Num); app->p1Win = 0; app->p2Win = 1; break; case SDL_SCANCODE_KP_5: if (app->p2Num > 0) { --app->p2Num; } updateApplication(app, "", "", app->roundNum, app->p1Num, app->p2Num); break; case SDL_SCANCODE_KP_6: updateApplication(app, "", "", app->roundNum, app->p1Num, 0); app->p1Win = 0; app->p2Win = 0; break; case SDL_SCANCODE_KP_7: if (app->roundNum == 99) { app->roundNum = 0; } else { ++app->roundNum; } updateApplication(app, "", "", app->roundNum, app->p1Num, app->p2Num); break; case SDL_SCANCODE_KP_8: if (app->roundNum > 0) { --app->roundNum; } updateApplication(app, "", "", app->roundNum, app->p1Num, app->p2Num); break; case SDL_SCANCODE_KP_9: updateApplication(app, "", "", 0, app->p1Num, app->p2Num); break; default: break; } } } } } if (app->updateRequire) { drawApplication(app); app->updateRequire = 0; } SDL_UnlockMutex(app->mutex); SDL_Delay(166); } // 停止客户端并等待线程结束 ioContext.stop(); cv.notify_all(); if (clientThread.joinable()) clientThread.join(); termApplication(app); return 0; }