From 3430871fbfda14c63ad2760c64af208a57a78fb6 Mon Sep 17 00:00:00 2001 From: liu5580 <602790634@qq.com> Date: Thu, 27 Nov 2025 15:14:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0CMakeLists.txt=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=A4=8DmacOS26=E7=BC=96=E8=AF=91=E8=BF=90=E8=A1=8Cop?= =?UTF-8?q?enGL=E7=89=88=E6=9C=AC=E4=B8=8D=E6=94=AF=E6=8C=81=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 0 -> 14340 bytes .gitmodules | 3 + CMakeLists.txt | 46 +++ online_game_tool.cpp | 894 +++++++++++++++++++++---------------------- 4 files changed, 479 insertions(+), 464 deletions(-) create mode 100644 .DS_Store create mode 100644 .gitmodules create mode 100644 CMakeLists.txt diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7ea3eccee625fc44ec66f9c30fc4b6ae001402d5 GIT binary patch literal 14340 zcmeI3du$v>8NlZozxJBDcx@*p&fVc{&S~QI>e#VkoLB3|xrt*tuk+h+>^tAaUb4P* zZ_kg^4Fo7{X+>2K6bcfLf`CF>As_(+h0q9E3Zf9K3Mc}h3JQpp3IuBZL3}f_cV3^* zCKk<0+Ld-^=9`(_ozLI5-~PT^6GEVQDpo^CH6cX8ot3g15BCzHu+p}%ezq^!a!>-e zm*S3fnULgkI1$&4_}G%M*#s1YY<@28IV4WhMe`v#scUAky81<+Ik|cH1yX^uMj8r_ z8#Cc#B$@4mj?B@3qX9<)js_eJI2!n$8ep*(H))M1oui`xM+1%q(i&jT2L*RlHd^Hw zQSwIz9@r~9>u0aMt05Kiq3LB1Pqg9>}rI=1Y zOebJn87x8pes!Fu@Zkg+Md=(J4LBNDssRs*@`y>2NZ%1M5uGt7qq-4A>9n-*$|spH>X>~LUW?2g}#(ZG`Mb9<94t0_4N-79vd1MmgOl^J*w)Fq&BUF zB1zR8mJ3Z>dE2NFKb%mfamBr(hCVeB$DX3mh(0=ngViQo4{LX^(Zy`Yp~<9X#LXTx zVPeTnF`6)R{jgzbtfRj#sm>JkGNCQ5QZ>YIvccTQ=^Q}ZQ7#j@KMsBlU0*<&$WZ>JGsSBmi- zpD(agk_MT)C86R`h~2*6tvDglW#SUVDcm*NT`t+|I*G<1rn>9jA-UvOw;qYAx+U7Z z2Sx3@(?WZeu(VNf^^F=66SzoI$b6wzNs^yC&Mw>$F_VWT)p+`rLT!>XV2(!;>Y*7l z&D4xeN#cJ|l(Q+VF)e8%gf;@cBa+n5PSzBQ;d$hZ-m-9)&xb~##iun)MB$%~<)fxd z`JyCA>+Gh8bqJ&cfySVy2F{O+@EX^AppoU?*d~#3vYmuT2N@w#Ec_gP0T*E&UWUKHtMD4U zNh!^txwMp)Q-$70Z=rQ`A8n%tXglqoL-YhS=qx=g7C=)jd0ezpM~X?s4BxXJGW6+X zd{5cmcA&kZ^T74sd%rH^-nPE6YppJ8x@F+oUkzl-1h3fjht=x8V-JZ6ecn z(Jy^|ce&Dy9-_`J_~T+nmJ65{OU!P4o7@$OoQFOnnp@nJiky%BBbq^_N|7nLt!UOL zwQe~FosvHReDz9|TLwuqcDeVs@6*Srr+-|a4MLT4rXA}va*A~R>ZjAs8L`91j~ zd6m40X1@kxSPvCw>b0;J&Ac5t;UFA=qiE_wa2!S;0`CMJCcr?$KLvAeH@pkp4fn$X zX!ak3hu~xID0~7w37>~Apz%KrUq;ja2AqX&!g+WGF2E1rN9Y8 z8MIAq3n{kTH*g*hxN+up4WO1&T8NjMkr*VtTZz6?F;MlXPcG_%M(WIlE+St zUs{;Ucpb1s#cQd`(q?%iiol@}oy2VKC+AYH=GlLLzYP3eIybi?sBEvUUE%HIexbZ& z>Hh+SC8>Fx6jqBgF{ z#arp&&sYu@43mPMDh~%8jucE%_PBhl-~y3&;z}QJSv=kanO+RXJ8iYLg=~2hC82z&%k5wId}pg=N!kJ7m)EU!Y|>s z@Cy7H{sR9%y3eEew2+q3jnqpk=w`ZwZl$~F9@le{9cbkHknO|Jg=XG|sjN|mp`nj)GdD4vHI1hJ9-h*AKimfo!-wF* znAUm(n7MzJoBNaS6nquF2H)YSt?$Eg@MCx$ehNQ>7c9R2JG`D@*4g#VAbyDK?-*cRl__di5zbZ+1hyPQr z;;Sn2#OP}F{a@?y-~X@boX%iJ1C9o+FAYF$C>(0Wdrtj+pLzT2TRR2!5!~6B^SHAO z>wF3x_~&?7^3U/libsteam_api.dylib +) diff --git a/online_game_tool.cpp b/online_game_tool.cpp index 85f35ae..e956133 100644 --- a/online_game_tool.cpp +++ b/online_game_tool.cpp @@ -1,32 +1,32 @@ +#include "steam/steam_networking_manager.h" +#include "steam/steam_room_manager.h" +#include "steam/steam_utils.h" +#include "tcp_server.h" #include +#include +#include +#include +#include +#include #include #include #include #include -#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include "tcp_server.h" -#include "steam/steam_networking_manager.h" -#include "steam/steam_room_manager.h" -#include "steam/steam_utils.h" +#include #ifdef _WIN32 #include #define GLFW_EXPOSE_NATIVE_WIN32 #include #else +#include #include #include -#include #endif using boost::asio::ip::tcp; @@ -41,71 +41,64 @@ std::unique_ptr server; // Windows implementation using mutex and shared memory HANDLE g_hMutex = nullptr; HANDLE g_hMapFile = nullptr; -HWND* g_pSharedHwnd = nullptr; +HWND *g_pSharedHwnd = nullptr; -bool checkSingleInstance() -{ - g_hMutex = CreateMutexW(nullptr, FALSE, L"Global\\OnlineGameTool_SingleInstance_Mutex"); - if (GetLastError() == ERROR_ALREADY_EXISTS) - { - // Another instance exists, try to find and activate it - g_hMapFile = OpenFileMappingW(FILE_MAP_READ, FALSE, L"Global\\OnlineGameTool_HWND_Share"); - if (g_hMapFile != nullptr) - { - HWND* pHwnd = (HWND*)MapViewOfFile(g_hMapFile, FILE_MAP_READ, 0, 0, sizeof(HWND)); - if (pHwnd != nullptr && *pHwnd != nullptr && IsWindow(*pHwnd)) - { - // Restore and bring to front - if (IsIconic(*pHwnd)) - { - ShowWindow(*pHwnd, SW_RESTORE); - } - SetForegroundWindow(*pHwnd); - UnmapViewOfFile(pHwnd); - } - CloseHandle(g_hMapFile); +bool checkSingleInstance() { + g_hMutex = CreateMutexW(nullptr, FALSE, + L"Global\\OnlineGameTool_SingleInstance_Mutex"); + if (GetLastError() == ERROR_ALREADY_EXISTS) { + // Another instance exists, try to find and activate it + g_hMapFile = OpenFileMappingW(FILE_MAP_READ, FALSE, + L"Global\\OnlineGameTool_HWND_Share"); + if (g_hMapFile != nullptr) { + HWND *pHwnd = + (HWND *)MapViewOfFile(g_hMapFile, FILE_MAP_READ, 0, 0, sizeof(HWND)); + if (pHwnd != nullptr && *pHwnd != nullptr && IsWindow(*pHwnd)) { + // Restore and bring to front + if (IsIconic(*pHwnd)) { + ShowWindow(*pHwnd, SW_RESTORE); } - if (g_hMutex) - { - CloseHandle(g_hMutex); - } - return false; + SetForegroundWindow(*pHwnd); + UnmapViewOfFile(pHwnd); + } + CloseHandle(g_hMapFile); } + if (g_hMutex) { + CloseHandle(g_hMutex); + } + return false; + } - // Create shared memory for HWND - g_hMapFile = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, sizeof(HWND), L"Global\\OnlineGameTool_HWND_Share"); - if (g_hMapFile != nullptr) - { - g_pSharedHwnd = (HWND*)MapViewOfFile(g_hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(HWND)); - } - return true; + // Create shared memory for HWND + g_hMapFile = + CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, + sizeof(HWND), L"Global\\OnlineGameTool_HWND_Share"); + if (g_hMapFile != nullptr) { + g_pSharedHwnd = (HWND *)MapViewOfFile(g_hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, + sizeof(HWND)); + } + return true; } -void storeWindowHandle(GLFWwindow* window) -{ - if (g_pSharedHwnd != nullptr) - { - *g_pSharedHwnd = glfwGetWin32Window(window); - } +void storeWindowHandle(GLFWwindow *window) { + if (g_pSharedHwnd != nullptr) { + *g_pSharedHwnd = glfwGetWin32Window(window); + } } -void cleanupSingleInstance() -{ - if (g_pSharedHwnd != nullptr) - { - UnmapViewOfFile(g_pSharedHwnd); - g_pSharedHwnd = nullptr; - } - if (g_hMapFile != nullptr) - { - CloseHandle(g_hMapFile); - g_hMapFile = nullptr; - } - if (g_hMutex != nullptr) - { - CloseHandle(g_hMutex); - g_hMutex = nullptr; - } +void cleanupSingleInstance() { + if (g_pSharedHwnd != nullptr) { + UnmapViewOfFile(g_pSharedHwnd); + g_pSharedHwnd = nullptr; + } + if (g_hMapFile != nullptr) { + CloseHandle(g_hMapFile); + g_hMapFile = nullptr; + } + if (g_hMutex != nullptr) { + CloseHandle(g_hMutex); + g_hMutex = nullptr; + } } #else @@ -113,414 +106,387 @@ void cleanupSingleInstance() int g_lockfd = -1; std::string g_lockFilePath; -void signalHandler(int signum) -{ - // Signal received to bring window to front - std::cout << "Received signal to activate window" << std::endl; +void signalHandler(int signum) { + // Signal received to bring window to front + std::cout << "Received signal to activate window" << std::endl; } -bool checkSingleInstance() -{ - std::string tempDir; +bool checkSingleInstance() { + std::string tempDir; #ifdef __APPLE__ - const char* tmpdir = getenv("TMPDIR"); - tempDir = tmpdir ? tmpdir : "/tmp"; + const char *tmpdir = getenv("TMPDIR"); + tempDir = tmpdir ? tmpdir : "/tmp"; #else - tempDir = "/tmp"; + tempDir = "/tmp"; #endif - - g_lockFilePath = tempDir + "/OnlineGameTool.lock"; - - g_lockfd = open(g_lockFilePath.c_str(), O_CREAT | O_RDWR, 0666); - if (g_lockfd < 0) - { - std::cerr << "Failed to open lock file" << std::endl; - return false; + + g_lockFilePath = tempDir + "/OnlineGameTool.lock"; + + g_lockfd = open(g_lockFilePath.c_str(), O_CREAT | O_RDWR, 0666); + if (g_lockfd < 0) { + std::cerr << "Failed to open lock file" << std::endl; + return false; + } + + // Try to acquire exclusive lock + if (flock(g_lockfd, LOCK_EX | LOCK_NB) != 0) { + // Lock failed, another instance is running + // Read PID and send signal + char pidBuf[32]; + ssize_t bytesRead = read(g_lockfd, pidBuf, sizeof(pidBuf) - 1); + if (bytesRead > 0) { + pidBuf[bytesRead] = '\0'; + pid_t existingPid = atoi(pidBuf); + if (existingPid > 0) { + // Send SIGUSR1 to existing instance + kill(existingPid, SIGUSR1); + } } + close(g_lockfd); + g_lockfd = -1; + return false; + } - // Try to acquire exclusive lock - if (flock(g_lockfd, LOCK_EX | LOCK_NB) != 0) - { - // Lock failed, another instance is running - // Read PID and send signal - char pidBuf[32]; - ssize_t bytesRead = read(g_lockfd, pidBuf, sizeof(pidBuf) - 1); - if (bytesRead > 0) - { - pidBuf[bytesRead] = '\0'; - pid_t existingPid = atoi(pidBuf); - if (existingPid > 0) - { - // Send SIGUSR1 to existing instance - kill(existingPid, SIGUSR1); - } - } - close(g_lockfd); - g_lockfd = -1; - return false; - } + // Write our PID to the lock file + ftruncate(g_lockfd, 0); + pid_t myPid = getpid(); + std::string pidStr = std::to_string(myPid); + write(g_lockfd, pidStr.c_str(), pidStr.length()); - // Write our PID to the lock file - ftruncate(g_lockfd, 0); - pid_t myPid = getpid(); - std::string pidStr = std::to_string(myPid); - write(g_lockfd, pidStr.c_str(), pidStr.length()); + // Set up signal handler + signal(SIGUSR1, signalHandler); - // Set up signal handler - signal(SIGUSR1, signalHandler); - - return true; + return true; } -void storeWindowHandle(GLFWwindow* window) -{ - // GLFW doesn't provide a standard way to bring window to front on Unix - // but we can request attention - glfwRequestWindowAttention(window); +void storeWindowHandle(GLFWwindow *window) { + // GLFW doesn't provide a standard way to bring window to front on Unix + // but we can request attention + glfwRequestWindowAttention(window); } -void cleanupSingleInstance() -{ - if (g_lockfd >= 0) - { - flock(g_lockfd, LOCK_UN); - close(g_lockfd); - g_lockfd = -1; - unlink(g_lockFilePath.c_str()); - } +void cleanupSingleInstance() { + if (g_lockfd >= 0) { + flock(g_lockfd, LOCK_UN); + close(g_lockfd); + g_lockfd = -1; + unlink(g_lockFilePath.c_str()); + } } #endif -int main() -{ - // Check for single instance - if (!checkSingleInstance()) - { - std::cout << "另一个实例已在运行,正在激活该窗口..." << std::endl; - return 0; - } - - // Initialize Steam API first - if (!SteamAPI_Init()) - { - std::cerr << "Failed to initialize Steam API" << std::endl; - return 1; - } - - boost::asio::io_context io_context; - auto work_guard = boost::asio::make_work_guard(io_context); - std::thread io_thread([&io_context]() - { io_context.run(); }); - - // Initialize Steam Networking Manager - SteamNetworkingManager steamManager; - if (!steamManager.initialize()) - { - std::cerr << "Failed to initialize Steam Networking Manager" << std::endl; - SteamAPI_Shutdown(); - return 1; - } - - // Initialize Steam Room Manager - SteamRoomManager roomManager(&steamManager); - - // Initialize GLFW - if (!glfwInit()) - { - std::cerr << "Failed to initialize GLFW" << std::endl; - steamManager.shutdown(); - return -1; - } - - // Create window - GLFWwindow *window = glfwCreateWindow(1280, 720, "在线游戏工具 - 1.0.0", nullptr, nullptr); - if (!window) - { - std::cerr << "Failed to create GLFW window" << std::endl; - glfwTerminate(); - cleanupSingleInstance(); - SteamAPI_Shutdown(); - return -1; - } - glfwMakeContextCurrent(window); - glfwSwapInterval(1); // Enable vsync - - // Store window handle for single instance activation - storeWindowHandle(window); - - // Initialize ImGui - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO &io = ImGui::GetIO(); - (void)io; - // Load Chinese font - io.Fonts->AddFontFromFileTTF("font.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesChineseSimplifiedCommon()); - ImGui::StyleColorsDark(); - - // Initialize ImGui backends - ImGui_ImplGlfw_InitForOpenGL(window, true); - ImGui_ImplOpenGL3_Init("#version 130"); - - // Set message handler dependencies - steamManager.setMessageHandlerDependencies(io_context, server, localPort); - steamManager.startMessageHandler(); - - // Steam Networking variables - bool isHost = false; - bool isClient = false; - char joinBuffer[256] = ""; - char filterBuffer[256] = ""; - - // Lambda to get connection info for a member - auto getMemberConnectionInfo = [&](const CSteamID &memberID, const CSteamID &hostSteamID) -> std::pair - { - int ping = 0; - std::string relayInfo = "-"; - - if (steamManager.isHost()) - { - // Find connection for this member - std::lock_guard lockConn(connectionsMutex); - for (const auto &conn : steamManager.getConnections()) - { - SteamNetConnectionInfo_t info; - if (steamManager.getInterface()->GetConnectionInfo(conn, &info)) - { - if (info.m_identityRemote.GetSteamID() == memberID) - { - ping = steamManager.getConnectionPing(conn); - relayInfo = steamManager.getConnectionRelayInfo(conn); - break; - } - } - } - } - else - { - // Client only shows ping to host, not to other clients - if (memberID == hostSteamID) - { - ping = steamManager.getHostPing(); - if (steamManager.getConnection() != k_HSteamNetConnection_Invalid) - { - relayInfo = steamManager.getConnectionRelayInfo(steamManager.getConnection()); - } - } - } - - return {ping, relayInfo}; - }; - - // Lambda to render invite friends UI - auto renderInviteFriends = [&]() - { - ImGui::InputText("过滤朋友", filterBuffer, IM_ARRAYSIZE(filterBuffer)); - ImGui::Text("朋友:"); - for (const auto &friendPair : SteamUtils::getFriendsList()) - { - std::string nameStr = friendPair.second; - std::string filterStr(filterBuffer); - // Convert to lowercase for case-insensitive search - std::transform(nameStr.begin(), nameStr.end(), nameStr.begin(), ::tolower); - std::transform(filterStr.begin(), filterStr.end(), filterStr.begin(), ::tolower); - if (filterStr.empty() || nameStr.find(filterStr) != std::string::npos) - { - ImGui::PushID(friendPair.first.ConvertToUint64()); - if (ImGui::Button(("邀请 " + friendPair.second).c_str())) - { - // Send invite via Steam to lobby - if (SteamMatchmaking()) - { - SteamMatchmaking()->InviteUserToLobby(roomManager.getCurrentLobby(), friendPair.first); - std::cout << "Sent lobby invite to " << friendPair.second << std::endl; - } - else - { - std::cerr << "SteamMatchmaking() is null! Cannot send invite." << std::endl; - } - } - ImGui::PopID(); - } - } - }; - - // Frame rate limiting - const double targetFrameTimeForeground = 1.0 / 60.0; // 60 FPS when focused - const double targetFrameTimeBackground = 1.0; // 1 FPS when in background - double lastFrameTime = glfwGetTime(); - - // Main loop - while (!glfwWindowShouldClose(window)) - { - // Frame rate control based on window focus - bool isFocused = glfwGetWindowAttrib(window, GLFW_FOCUSED); - double targetFrameTime = isFocused ? targetFrameTimeForeground : targetFrameTimeBackground; - - double currentTime = glfwGetTime(); - double deltaTime = currentTime - lastFrameTime; - if (deltaTime < targetFrameTime) - { - std::this_thread::sleep_for(std::chrono::duration(targetFrameTime - deltaTime)); - } - lastFrameTime = glfwGetTime(); - - // Poll events - glfwPollEvents(); - - SteamAPI_RunCallbacks(); - - // Update Steam networking info - steamManager.update(); - - // Start ImGui frame - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); - - // Create a window for online game tool - ImGui::Begin("在线游戏工具"); - if (server) - { - ImGui::Text("TCP服务器监听端口8888"); - ImGui::Text("已连接客户端: %d", server->getClientCount()); - } - ImGui::Separator(); - - if (!steamManager.isHost() && !steamManager.isConnected()) - { - if (ImGui::Button("主持游戏房间")) - { - roomManager.startHosting(); - } - ImGui::InputText("房间ID", joinBuffer, IM_ARRAYSIZE(joinBuffer)); - if (ImGui::Button("加入游戏房间")) - { - uint64 hostID = std::stoull(joinBuffer); - if (steamManager.joinHost(hostID)) - { - // Start TCP Server - server = std::make_unique(8888, &steamManager); - if (!server->start()) - { - std::cerr << "Failed to start TCP server" << std::endl; - } - } - } - } - if (steamManager.isHost() || steamManager.isConnected()) - { - ImGui::Text(steamManager.isHost() ? "正在主持游戏房间。邀请朋友!" : "已连接到游戏房间。邀请朋友!"); - ImGui::Separator(); - if (ImGui::Button("断开连接")) - { - roomManager.leaveLobby(); - steamManager.disconnect(); - if (server) - { - server->stop(); - server.reset(); - } - } - if (steamManager.isHost()) - { - ImGui::InputInt("本地端口", &localPort); - } - ImGui::Separator(); - renderInviteFriends(); - } - - ImGui::End(); - - // Room status window - only show when hosting or connected - if ((steamManager.isHost() || steamManager.isConnected()) && roomManager.getCurrentLobby().IsValid()) - { - ImGui::Begin("房间状态"); - ImGui::Text("用户列表:"); - if (ImGui::BeginTable("UserTable", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) - { - ImGui::TableSetupColumn("名称"); - ImGui::TableSetupColumn("延迟 (ms)"); - ImGui::TableSetupColumn("连接类型"); - ImGui::TableHeadersRow(); - { - std::vector members = roomManager.getLobbyMembers(); - CSteamID mySteamID = SteamUser()->GetSteamID(); - CSteamID hostSteamID = steamManager.getHostSteamID(); - for (const auto &memberID : members) - { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - const char *name = SteamFriends()->GetFriendPersonaName(memberID); - ImGui::Text("%s", name); - ImGui::TableNextColumn(); - - if (memberID == mySteamID) - { - ImGui::Text("-"); - ImGui::TableNextColumn(); - ImGui::Text("-"); - } - else - { - auto [ping, relayInfo] = getMemberConnectionInfo(memberID, hostSteamID); - - if (relayInfo != "-") - { - ImGui::Text("%d", ping); - } - else - { - ImGui::Text("-"); - } - ImGui::TableNextColumn(); - ImGui::Text("%s", relayInfo.c_str()); - } - } - } - ImGui::EndTable(); - } - ImGui::End(); - } - - // Rendering - ImGui::Render(); - int display_w, display_h; - glfwGetFramebufferSize(window, &display_w, &display_h); - glViewport(0, 0, display_w, display_h); - glClearColor(0.45f, 0.55f, 0.60f, 1.00f); - glClear(GL_COLOR_BUFFER_BIT); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - - // Swap buffers - glfwSwapBuffers(window); - } - - // Stop message handler - steamManager.stopMessageHandler(); - - // Cleanup - if (server) - { - server->stop(); - } - - // Stop io_context and join thread - work_guard.reset(); - io_context.stop(); - if (io_thread.joinable()) - { - io_thread.join(); - } - - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplGlfw_Shutdown(); - ImGui::DestroyContext(); - glfwDestroyWindow(window); - glfwTerminate(); - steamManager.shutdown(); - - // Cleanup single instance resources - cleanupSingleInstance(); - +int main() { + // Check for single instance + if (!checkSingleInstance()) { + std::cout << "另一个实例已在运行,正在激活该窗口..." << std::endl; return 0; + } + + // Initialize Steam API first + if (!SteamAPI_Init()) { + std::cerr << "Failed to initialize Steam API" << std::endl; + return 1; + } + + boost::asio::io_context io_context; + auto work_guard = boost::asio::make_work_guard(io_context); + std::thread io_thread([&io_context]() { io_context.run(); }); + + // Initialize Steam Networking Manager + SteamNetworkingManager steamManager; + if (!steamManager.initialize()) { + std::cerr << "Failed to initialize Steam Networking Manager" << std::endl; + SteamAPI_Shutdown(); + return 1; + } + + // Initialize Steam Room Manager + SteamRoomManager roomManager(&steamManager); + + // Initialize GLFW + if (!glfwInit()) { + std::cerr << "Failed to initialize GLFW" << std::endl; + steamManager.shutdown(); + return -1; + } + +#ifdef __APPLE__ + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac +#endif + + // Create window + GLFWwindow *window = + glfwCreateWindow(1280, 720, "在线游戏工具 - 1.0.0", nullptr, nullptr); + if (!window) { + std::cerr << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + cleanupSingleInstance(); + SteamAPI_Shutdown(); + return -1; + } + glfwMakeContextCurrent(window); + glfwSwapInterval(1); // Enable vsync + + // Store window handle for single instance activation + storeWindowHandle(window); + + // Initialize ImGui + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO &io = ImGui::GetIO(); + (void)io; + // Load Chinese font + io.Fonts->AddFontFromFileTTF( + "font.ttf", 18.0f, nullptr, + io.Fonts->GetGlyphRangesChineseSimplifiedCommon()); + ImGui::StyleColorsDark(); + + // Initialize ImGui backends + ImGui_ImplGlfw_InitForOpenGL(window, true); + const char *glsl_version = "#version 130"; +#ifdef __APPLE__ + glsl_version = "#version 150"; +#endif + ImGui_ImplOpenGL3_Init(glsl_version); + + // Set message handler dependencies + steamManager.setMessageHandlerDependencies(io_context, server, localPort); + steamManager.startMessageHandler(); + + // Steam Networking variables + bool isHost = false; + bool isClient = false; + char joinBuffer[256] = ""; + char filterBuffer[256] = ""; + + // Lambda to get connection info for a member + auto getMemberConnectionInfo = + [&](const CSteamID &memberID, + const CSteamID &hostSteamID) -> std::pair { + int ping = 0; + std::string relayInfo = "-"; + + if (steamManager.isHost()) { + // Find connection for this member + std::lock_guard lockConn(connectionsMutex); + for (const auto &conn : steamManager.getConnections()) { + SteamNetConnectionInfo_t info; + if (steamManager.getInterface()->GetConnectionInfo(conn, &info)) { + if (info.m_identityRemote.GetSteamID() == memberID) { + ping = steamManager.getConnectionPing(conn); + relayInfo = steamManager.getConnectionRelayInfo(conn); + break; + } + } + } + } else { + // Client only shows ping to host, not to other clients + if (memberID == hostSteamID) { + ping = steamManager.getHostPing(); + if (steamManager.getConnection() != k_HSteamNetConnection_Invalid) { + relayInfo = + steamManager.getConnectionRelayInfo(steamManager.getConnection()); + } + } + } + + return {ping, relayInfo}; + }; + + // Lambda to render invite friends UI + auto renderInviteFriends = [&]() { + ImGui::InputText("过滤朋友", filterBuffer, IM_ARRAYSIZE(filterBuffer)); + ImGui::Text("朋友:"); + for (const auto &friendPair : SteamUtils::getFriendsList()) { + std::string nameStr = friendPair.second; + std::string filterStr(filterBuffer); + // Convert to lowercase for case-insensitive search + std::transform(nameStr.begin(), nameStr.end(), nameStr.begin(), + ::tolower); + std::transform(filterStr.begin(), filterStr.end(), filterStr.begin(), + ::tolower); + if (filterStr.empty() || nameStr.find(filterStr) != std::string::npos) { + ImGui::PushID(friendPair.first.ConvertToUint64()); + if (ImGui::Button(("邀请 " + friendPair.second).c_str())) { + // Send invite via Steam to lobby + if (SteamMatchmaking()) { + SteamMatchmaking()->InviteUserToLobby(roomManager.getCurrentLobby(), + friendPair.first); + std::cout << "Sent lobby invite to " << friendPair.second + << std::endl; + } else { + std::cerr << "SteamMatchmaking() is null! Cannot send invite." + << std::endl; + } + } + ImGui::PopID(); + } + } + }; + + // Frame rate limiting + const double targetFrameTimeForeground = 1.0 / 60.0; // 60 FPS when focused + const double targetFrameTimeBackground = 1.0; // 1 FPS when in background + double lastFrameTime = glfwGetTime(); + + // Main loop + while (!glfwWindowShouldClose(window)) { + // Frame rate control based on window focus + bool isFocused = glfwGetWindowAttrib(window, GLFW_FOCUSED); + double targetFrameTime = + isFocused ? targetFrameTimeForeground : targetFrameTimeBackground; + + double currentTime = glfwGetTime(); + double deltaTime = currentTime - lastFrameTime; + if (deltaTime < targetFrameTime) { + std::this_thread::sleep_for( + std::chrono::duration(targetFrameTime - deltaTime)); + } + lastFrameTime = glfwGetTime(); + + // Poll events + glfwPollEvents(); + + SteamAPI_RunCallbacks(); + + // Update Steam networking info + steamManager.update(); + + // Start ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + // Create a window for online game tool + ImGui::Begin("在线游戏工具"); + if (server) { + ImGui::Text("TCP服务器监听端口8888"); + ImGui::Text("已连接客户端: %d", server->getClientCount()); + } + ImGui::Separator(); + + if (!steamManager.isHost() && !steamManager.isConnected()) { + if (ImGui::Button("主持游戏房间")) { + roomManager.startHosting(); + } + ImGui::InputText("房间ID", joinBuffer, IM_ARRAYSIZE(joinBuffer)); + if (ImGui::Button("加入游戏房间")) { + uint64 hostID = std::stoull(joinBuffer); + if (steamManager.joinHost(hostID)) { + // Start TCP Server + server = std::make_unique(8888, &steamManager); + if (!server->start()) { + std::cerr << "Failed to start TCP server" << std::endl; + } + } + } + } + if (steamManager.isHost() || steamManager.isConnected()) { + ImGui::Text(steamManager.isHost() ? "正在主持游戏房间。邀请朋友!" + : "已连接到游戏房间。邀请朋友!"); + ImGui::Separator(); + if (ImGui::Button("断开连接")) { + roomManager.leaveLobby(); + steamManager.disconnect(); + if (server) { + server->stop(); + server.reset(); + } + } + if (steamManager.isHost()) { + ImGui::InputInt("本地端口", &localPort); + } + ImGui::Separator(); + renderInviteFriends(); + } + + ImGui::End(); + + // Room status window - only show when hosting or connected + if ((steamManager.isHost() || steamManager.isConnected()) && + roomManager.getCurrentLobby().IsValid()) { + ImGui::Begin("房间状态"); + ImGui::Text("用户列表:"); + if (ImGui::BeginTable("UserTable", 3, + ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + ImGui::TableSetupColumn("名称"); + ImGui::TableSetupColumn("延迟 (ms)"); + ImGui::TableSetupColumn("连接类型"); + ImGui::TableHeadersRow(); + { + std::vector members = roomManager.getLobbyMembers(); + CSteamID mySteamID = SteamUser()->GetSteamID(); + CSteamID hostSteamID = steamManager.getHostSteamID(); + for (const auto &memberID : members) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + const char *name = SteamFriends()->GetFriendPersonaName(memberID); + ImGui::Text("%s", name); + ImGui::TableNextColumn(); + + if (memberID == mySteamID) { + ImGui::Text("-"); + ImGui::TableNextColumn(); + ImGui::Text("-"); + } else { + auto [ping, relayInfo] = + getMemberConnectionInfo(memberID, hostSteamID); + + if (relayInfo != "-") { + ImGui::Text("%d", ping); + } else { + ImGui::Text("-"); + } + ImGui::TableNextColumn(); + ImGui::Text("%s", relayInfo.c_str()); + } + } + } + ImGui::EndTable(); + } + ImGui::End(); + } + + // Rendering + ImGui::Render(); + int display_w, display_h; + glfwGetFramebufferSize(window, &display_w, &display_h); + glViewport(0, 0, display_w, display_h); + glClearColor(0.45f, 0.55f, 0.60f, 1.00f); + glClear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + // Swap buffers + glfwSwapBuffers(window); + } + + // Stop message handler + steamManager.stopMessageHandler(); + + // Cleanup + if (server) { + server->stop(); + } + + // Stop io_context and join thread + work_guard.reset(); + io_context.stop(); + if (io_thread.joinable()) { + io_thread.join(); + } + + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); + glfwDestroyWindow(window); + glfwTerminate(); + steamManager.shutdown(); + + // Cleanup single instance resources + cleanupSingleInstance(); + + return 0; } \ No newline at end of file