// websocket #include #include #include #include #include #include // json #include // windows child process #include #include #include // windows terminate process #include //hash #include // time #include #include // fixed int #include // user #include "user.h" #include // session #include "sessionManager.h" namespace beast = boost::beast; namespace http = beast::http; namespace websocket = beast::websocket; namespace net = boost::asio; using tcp = boost::asio::ip::tcp; void EndProc(DWORD procId) { PROCESSENTRY32 pe; memset(&pe, 0, sizeof(PROCESSENTRY32)); pe.dwSize = sizeof(PROCESSENTRY32); HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (::Process32First(hSnap, &pe)) { BOOL bContinue = TRUE; // kill child processes while (bContinue) { // only kill child processes if (pe.th32ParentProcessID == procId) { HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID); if (hChildProc) { ::TerminateProcess(hChildProc, 1); ::CloseHandle(hChildProc); } } bContinue = ::Process32Next(hSnap, &pe); } // kill the main process HANDLE hProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, procId); if (hProc) { ::TerminateProcess(hProc, 1); ::CloseHandle(hProc); } } } template uint32_t hash6(T arg) { boost::hash h; return h(arg) % 1000000; } LRESULT CALLBACK GLWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (msg == WM_DESTROY) PostQuitMessage(0); return DefWindowProc(hWnd, msg, wParam, lParam); } int main(int argc, char* argv[]) { // disable console input HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE); DWORD prev_mode; GetConsoleMode(hInput, &prev_mode); SetConsoleMode(hInput, ENABLE_EXTENDED_FLAGS | (prev_mode & ~ENABLE_QUICK_EDIT_MODE)); // ip and socket init std::string ip("192.168.1.115"); uint16_t sess_port = 13001; auto const address = net::ip::make_address(ip); auto const port = static_cast(std::atoi("8083")); net::io_context ioc{ 1 }; tcp::acceptor acceptor{ ioc, {address, port} }; // sessions sessionManager sessManager; sessManager.setlimit(10); // stats count uint64_t user_n = 0; while (true) { tcp::socket socket(ioc); acceptor.accept(socket); std::thread([sock = std::move(socket), &sessManager, &ip, &sess_port, &user_n]() mutable { std::string user_ip = sock.remote_endpoint().address().to_string(); uint64_t user_id = std::chrono::system_clock::now().time_since_epoch().count(); user_id = hash6(user_ip + std::to_string(user_id)); uint64_t thisUserNum = ++user_n; std::cout << "user(" << thisUserNum << ") connected (ip: " << user_ip << "), (id: " << user_id << ")" << std::endl; websocket::stream ws(std::move(const_cast(sock))); ws.accept(); // Set a decorator to change the Server of the /*ws.set_option(websocket::stream_base::decorator( [](websocket::response_type& res) { res.set(http::field::server, std::string(BOOST_BEAST_VERSION_STRING) + " websocket-server-sync"); }));*/ //session sessTmp; session* sessCur = nullptr; uint32_t sessId = 0; nlohmann::json jsonData; std::string message; std::string content; while (true) { try { // This buffer will hold the incoming message // buffer types https://www.boost.org/doc/libs/1_75_0/libs/beast/doc/html/beast/using_io/buffer_types.html // check for the best one // beast::multi_buffer buffer; beast::flat_buffer buffer; // Read a message ws.read(buffer); // cout buffer to string //std::cout << beast::buffers_to_string(buffer.data()) << std::endl; // validate errors try { jsonData = nlohmann::json::parse(beast::buffers_to_string(buffer.data())); message = jsonData.at("message"); } catch (nlohmann::json::parse_error& e) { std::cout << "Parse error:" << e.what() << std::endl; } catch (nlohmann::json::out_of_range& e) { std::cout << "Out of range:" << e.what() << std::endl; } catch (nlohmann::json::exception& e) { std::cout << "JSON exception: " << e.what() << std::endl; } catch (std::exception& e) { std::cout << "Unknown exception" << e.what() << std::endl; } // validate message if (message == "NEW_USER") { buffer.clear(); boost::beast::ostream(buffer) << "connected"; ws.write(buffer.data()); } else if (message == "NEW_SESS") { buffer.clear(); if (sessManager.isLimitReached()) { boost::beast::ostream(buffer) << std::string("{\"message\" : \"SESS_LIMIT\"}").c_str(); ws.write(buffer.data()); continue; } if (sessManager.existsOwnerIp(user_ip)) { boost::beast::ostream(buffer) << std::string("{\"message\" : \"PERSON_LIMIT\"}").c_str(); ws.write(buffer.data()); continue; } //sessManager boost::beast::ostream(buffer) << std::string("{\"message\" : \"CREATING_SESSION\", \"content\" : \"true\"}").c_str(); ws.write(buffer.data()); std::string path = "D:\\Builds\\Fortis_UnStable_57\\FORTIS_Taktika.exe"; std::wstring path_ws = std::wstring(path.begin(), path.end()); const wchar_t* path_cw = path_ws.c_str(); std::string cmd = "-PixelStreamingIP=localhost -PixelStreamingPort=12005 -RenderOffScreen -PixelStreamingEncoderMaxBitrate=15000000 -ResX 1280 -ResY 720 -PixelStreamingEncoderMinQP=25 -PixelStreamingEncoderMultipass=QUARTER -PixelStreamingWebRTCMaxFps=60"; std::wstring cmd_ws = std::wstring(cmd.begin(), cmd.end()); wchar_t* cmd_w = const_cast(cmd_ws.c_str()); // creating process STARTUPINFO startupInfo = { sizeof(startupInfo) }; PROCESS_INFORMATION processInfo; if (CreateProcess(path_cw, cmd_w, NULL, NULL, TRUE, 0, NULL, NULL, &startupInfo, &processInfo)) { // bind process close listen std::thread([processInfo]() { WaitForSingleObject(processInfo.hProcess, INFINITE); CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); }).detach(); sessId = hash6(user_id); // create session sessManager.add( session( GetProcessId(processInfo.hProcess), sessId, ip + std::to_string(sess_port), user_ip )); std::cout << "user(" << thisUserNum << ") created session (id: " << sessId << ")" << std::endl; if (sessManager.getById(sessId, &sessCur)) { sessCur->addUser(user(user_id)); } buffer.clear(); boost::beast::ostream(buffer) << (std::string("{\"message\" : \"NEW_SESS\", \"content\" : \"") + std::to_string(sessId) + "\"}"); ws.write(buffer.data()); } else { std::cout << "CreateProcess failed (" << GetLastError() << ")." << std::endl; } } else if (message == "EXIS_SESS") { // read session id try { jsonData = nlohmann::json::parse(beast::buffers_to_string(buffer.data())); content = jsonData.at("content"); } catch (nlohmann::json::parse_error& e) { std::cout << "Parse error:" << e.what() << std::endl; } catch (nlohmann::json::out_of_range& e) { std::cout << "Out of range:" << e.what() << std::endl; } catch (nlohmann::json::exception& e) { std::cout << "JSON exception: " << e.what() << std::endl; } catch (std::exception& e) { std::cout << "Unknown exception" << e.what() << std::endl; } // connect to session if (sessManager.getById(atoi(content.c_str()), &sessCur)) { sessCur->addUser(user(user_id)); std::cout << "user(" << thisUserNum << ") connected to session (id: " << sessCur->getId() << "), (users: " << sessCur->getUsersCount() << ")" << std::endl; buffer.clear(); boost::beast::ostream(buffer) << (std::string("{\"message\" : \"EXIS_SESS\", \"content\" : \"") + std::to_string(sessCur->getId()) + "\"}"); ws.write(buffer.data()); } else { std::cout << "no session (id: " << content << ")" << std::endl; } } // Echo the message back // ws.text(ws.got_text()); // boost::beast::ostream(buffer) << "something"; // ws.write(buffer.data()); } catch (beast::system_error const& se) { std::cout << "user(" << thisUserNum << ") disconnected" << std::endl; if (sessCur) { sessCur->removeUserById(user_id); if (!sessCur->getUsersCount()) { std::cout << "destroy session (id: " << sessCur->getId() << ")" << std::endl; EndProc(sessCur->getProcId()); sessManager.remove(sessId); } } break; /* if (se.code() != websocket::error::closed) { std::cerr << "Error: " << se.code().message() << std::endl; break; }*/ } } }).detach(); } return 0; }