123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- #include <SDL_events.h>
- #include <SDL_mouse.h>
- #include <SDL_mutex.h>
- #include <SDL_video.h>
- #include <asio/socket_base.hpp>
- #include <cstdint>
- #include <iostream>
- #include <condition_variable>
- #include <asio.hpp>
- #include <mutex>
- #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<char, 4> headerBuffer;
- std::vector<char> 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<std::mutex> 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;
- }
|