diff --git a/ServerPixel.sln b/ServerPixel.sln new file mode 100644 index 0000000..b099999 --- /dev/null +++ b/ServerPixel.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32616.157 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ServerPixel", "ServerPixel\ServerPixel.vcxproj", "{46B65B94-51FC-4B93-9B89-262FD262C49F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {46B65B94-51FC-4B93-9B89-262FD262C49F}.Debug|x64.ActiveCfg = Debug|x64 + {46B65B94-51FC-4B93-9B89-262FD262C49F}.Debug|x64.Build.0 = Debug|x64 + {46B65B94-51FC-4B93-9B89-262FD262C49F}.Debug|x86.ActiveCfg = Debug|Win32 + {46B65B94-51FC-4B93-9B89-262FD262C49F}.Debug|x86.Build.0 = Debug|Win32 + {46B65B94-51FC-4B93-9B89-262FD262C49F}.Release|x64.ActiveCfg = Release|x64 + {46B65B94-51FC-4B93-9B89-262FD262C49F}.Release|x64.Build.0 = Release|x64 + {46B65B94-51FC-4B93-9B89-262FD262C49F}.Release|x86.ActiveCfg = Release|Win32 + {46B65B94-51FC-4B93-9B89-262FD262C49F}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {069D2F46-F6B3-46C0-927E-D79C234FE899} + EndGlobalSection +EndGlobal diff --git a/ServerPixel/ServerPixel.cpp b/ServerPixel/ServerPixel.cpp new file mode 100644 index 0000000..4a66b40 --- /dev/null +++ b/ServerPixel/ServerPixel.cpp @@ -0,0 +1,363 @@ +// 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; +} \ No newline at end of file diff --git a/ServerPixel/ServerPixel.vcxproj b/ServerPixel/ServerPixel.vcxproj new file mode 100644 index 0000000..8b3d01a --- /dev/null +++ b/ServerPixel/ServerPixel.vcxproj @@ -0,0 +1,144 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {46b65b94-51fc-4b93-9b89-262fd262c49f} + ServerPixel + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + C:\Libraries\json-develop\include;C:\Libraries\boost\include\boost_1_79_0;%(AdditionalIncludeDirectories) + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ServerPixel/ServerPixel.vcxproj.filters b/ServerPixel/ServerPixel.vcxproj.filters new file mode 100644 index 0000000..864cf04 --- /dev/null +++ b/ServerPixel/ServerPixel.vcxproj.filters @@ -0,0 +1,42 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/ServerPixel/session.cpp b/ServerPixel/session.cpp new file mode 100644 index 0000000..a977594 --- /dev/null +++ b/ServerPixel/session.cpp @@ -0,0 +1 @@ +#include "session.h" diff --git a/ServerPixel/session.h b/ServerPixel/session.h new file mode 100644 index 0000000..31d6145 --- /dev/null +++ b/ServerPixel/session.h @@ -0,0 +1,40 @@ +#include +#include +#include +#include "user.h" + +#pragma once +class session +{ +private: + DWORD procId = 0; + uint32_t sessId = 0; + std::string sessLink; + std::vector users; + std::string ownerIp; + +public: + session() {} + session(DWORD procId, uint32_t sessId, std::string sessLink, std::string ownerIp) + { + this->procId = procId; + this->sessId = sessId; + this->sessLink = sessLink; + this->ownerIp = ownerIp; + } + void addUser(user u) + { + users.push_back(u); + } + void removeUserById(uint32_t id) + { + for (int i = 0; i < users.size(); ++i) + if (users[i].getId() == id) + users.erase(users.begin() + i); + } + inline size_t getUsersCount() const { return users.size(); } + inline const uint32_t& getId() const { return sessId; } + inline const DWORD& getProcId() const { return procId; } + inline const std::string& getOwnerIp() const { return ownerIp; } +}; + diff --git a/ServerPixel/sessionManager.cpp b/ServerPixel/sessionManager.cpp new file mode 100644 index 0000000..c421ba6 --- /dev/null +++ b/ServerPixel/sessionManager.cpp @@ -0,0 +1 @@ +#include "sessionManager.h" diff --git a/ServerPixel/sessionManager.h b/ServerPixel/sessionManager.h new file mode 100644 index 0000000..d70bb79 --- /dev/null +++ b/ServerPixel/sessionManager.h @@ -0,0 +1,52 @@ +#include "session.h" +#include + +#pragma once +class sessionManager +{ +private: + std::vector sessions; + uint32_t limit = 0; +public: + bool add(session s) + { + if (isLimitReached()) + return false; + sessions.push_back(s); + return true; + } + + void remove(uint32_t id) + { + for (uint32_t i = 0; i < sessions.size(); ++i) + if (sessions[i].getId() == id) + sessions.erase(sessions.begin() + i); + } + + bool getById(uint32_t id, session** s) + { + for (int i = 0; i < sessions.size(); ++i) + if (sessions[i].getId() == id) + { + *s = &(*(sessions.begin() + i)); + return true; + } + return false; + } + + bool existsOwnerIp(std::string ip) + { + for (auto& s : sessions) + if (s.getOwnerIp() == ip) + return true; + return false; + } + + size_t getCount() { return sessions.size(); } + void setlimit(uint32_t limit) { this->limit = limit; } + bool isLimitReached() + { + return limit == sessions.size(); + } +}; + diff --git a/ServerPixel/user.cpp b/ServerPixel/user.cpp new file mode 100644 index 0000000..1f83c34 --- /dev/null +++ b/ServerPixel/user.cpp @@ -0,0 +1 @@ +#include "user.h" diff --git a/ServerPixel/user.h b/ServerPixel/user.h new file mode 100644 index 0000000..ef40458 --- /dev/null +++ b/ServerPixel/user.h @@ -0,0 +1,32 @@ +#include +#include +//#ifndef FRAMEWORK_H +//# define FRAMEWORK_H +//# include +//# include +//# include +//#endif + +#pragma once +class user +{ +public: + std::string name = ""; + char age = 0; + uint32_t id = 0; +public: + user() {}; + user(std::string name, char age) + { + this->name = name; + this->age = age; + } + user(uint32_t id) + { + this->id = id; + } + std::string getName() { return name; } + char getAge() { return age; } + uint32_t getId() { return id; } +}; +