diff --git a/.gitmodules b/.gitmodules index 1b0b4f2e..101b973e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,9 +5,6 @@ [submodule "deps/json11"] path = deps/json11 url = https://github.com/dropbox/json11.git -[submodule "deps/asio"] - path = deps/asio - url = https://github.com/chriskohlhoff/asio.git [submodule "deps/libtommath"] path = deps/libtommath url = https://github.com/libtom/libtommath.git @@ -24,3 +21,6 @@ [submodule "deps/pdcurses"] path = deps/pdcurses url = https://github.com/wmcbrine/PDCurses.git +[submodule "deps/mongoose"] + path = deps/mongoose + url = https://github.com/cesanta/mongoose diff --git a/deps/asio b/deps/asio deleted file mode 160000 index 722f7e2b..00000000 --- a/deps/asio +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 722f7e2be05a51c69644662ec514d6149b2b7ef8 diff --git a/deps/mongoose b/deps/mongoose new file mode 160000 index 00000000..dfde5785 --- /dev/null +++ b/deps/mongoose @@ -0,0 +1 @@ +Subproject commit dfde5785a62bc4b59f504a5545ce8476ad09f88d diff --git a/premake5.lua b/premake5.lua index 65475b3a..b502f489 100644 --- a/premake5.lua +++ b/premake5.lua @@ -148,13 +148,13 @@ workspace "iw4x" filter {} -- Dependency libraries - links { "zlib", "json11", "pdcurses", "libtomcrypt", "libtommath", "protobuf" } + links { "zlib", "json11", "pdcurses", "libtomcrypt", "libtommath", "protobuf", "mongoose" } includedirs { "./deps/zlib", "./deps/json11", "./deps/pdcurses", - "./deps/asio/asio/include", + "./deps/mongoose", "./deps/libtomcrypt/src/headers", "./deps/libtommath", "./deps/protobuf/src", @@ -261,6 +261,23 @@ workspace "iw4x" kind "StaticLib" + -- json11 + project "mongoose" + language "C" + + files + { + "./deps/mongoose/*.c", + "./deps/mongoose/*.h" + } + + -- not our code, ignore POSIX usage warnings for now + warnings "Off" + + -- always build as static lib, as json11 doesn't export anything + kind "StaticLib" + + -- pdcurses project "pdcurses" language "C" diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index 3154cd17..bade2a81 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -2,426 +2,74 @@ namespace Components { - Download::Container Download::DataContainer; + mg_mgr Download::Mgr; - Download::Container::DownloadCL* Download::FindClientDownload(int id) + void Download::EventHandler(mg_connection *nc, int ev, void *ev_data) { - for (auto &download : Download::DataContainer.ClientDownloads) + // Only handle http requests + if (ev != MG_EV_HTTP_REQUEST) return; + + http_message* message = reinterpret_cast(ev_data); + + if (std::string(message->uri.p, message->uri.len) == "/") { - if (download.id == id) - { - return &download; - } + mg_printf(nc, "%s", + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html\r\n" + "Connection: close\r\n" + "\r\n" + "Hi fella!"); } - - return nullptr; - } - - Download::Container::DownloadSV* Download::FindServerDownload(int id) - { - for (auto &download : Download::DataContainer.ServerDownloads) + else { - if (download.id == id) - { - return &download; - } + mg_printf(nc, "%s", + "HTTP/1.1 404 Not Found\r\n" + "Content-Type: text/html\r\n" + "Connection: close\r\n" + "\r\n" + "

404 - Not Found

"); } - return nullptr; - } - - void Download::RemoveClientDownload(int id) - { - for (auto i = Download::DataContainer.ClientDownloads.begin(); i != Download::DataContainer.ClientDownloads.end(); ++i) - { - if (i->id == id) - { - Download::DataContainer.ClientDownloads.erase(i); - return; - } - } - } - - void Download::RemoveServerDownload(int id) - { - for (auto i = Download::DataContainer.ServerDownloads.begin(); i != Download::DataContainer.ServerDownloads.end(); ++i) - { - if (i->id == id) - { - Download::DataContainer.ServerDownloads.erase(i); - return; - } - } - } - - bool Download::HasSentPacket(Download::Container::DownloadSV* download, int packet) - { - for (auto sentPacket : download->sentParts) - { - if (packet == sentPacket) - { - return true; - } - } - - return false; - } - - bool Download::HasReceivedPacket(Download::Container::DownloadCL* download, int packet) - { - if (!download->parts.empty()) - { - for (auto i = download->parts.begin(); i != download->parts.end(); ++i) - { - if (i->first == packet) - { - return true; - } - } - } - - return false; - } - - bool Download::HasReceivedAllPackets(Download::Container::DownloadCL* download) - { - for (int i = 0; i < download->maxParts; ++i) - { - if (!Download::HasReceivedPacket(download, i)) - { - return false; - } - } - - return true; - } - - int Download::ReadPacketId(std::string &data) - { - int id = *(int*)data.data(); - data = std::string(data.data() + sizeof(int), data.size() - sizeof(int)); - return id; - } - - // Client handlers - void Download::AckRequest(Network::Address target, std::string data) - { - if (data.size() < sizeof(Download::Container::AckRequest)) return; // Drop invalid packets, if they were important, we'll re-request them later - Download::Container::AckRequest* request = (Download::Container::AckRequest*)data.data(); - - if (data.size() < (sizeof(Download::Container::AckRequest) + request->length)) return; // Again, drop invalid packets - - auto download = Download::FindClientDownload(request->id); - - if (download && download->target == target && !download->acknowledged) - { - std::string challenge(data.data() + sizeof(Download::Container::AckRequest), request->length); - - download->acknowledged = true; - download->lastPing = Game::Com_Milliseconds(); - download->maxParts = request->maxPackets; - - std::string packet; - packet.append(reinterpret_cast(&download->id), sizeof(int)); - packet.append(challenge); - - Network::SendCommand(target, "dlAckResponse", packet); - } - } - - void Download::PacketResponse(Network::Address target, std::string data) - { - //Logger::Print("Packet incoming!\n"); - if (data.size() < sizeof(Download::Container::Packet)) return; // Drop invalid packets, if they were important, we'll re-request them later - Download::Container::Packet* packet = (Download::Container::Packet*)data.data(); - //Logger::Print("Reading data!\n"); - if (data.size() < (sizeof(Download::Container::Packet) + packet->length)) return; // Again, drop invalid packets - //Logger::Print("Finding corresponding download!\n"); - auto download = Download::FindClientDownload(packet->id); - - if (download && download->target == target) - { - //Logger::Print("Parsing packet!\n"); - download->lastPing = Game::Com_Milliseconds(); - std::string packetData(data.data() + sizeof(Download::Container::Packet), packet->length); - - if (packet->hash == Utils::Cryptography::JenkinsOneAtATime::Compute(packetData.data(), packetData.size())) - { - //Logger::Print("Packet added!\n"); - download->parts[packet->partId] = packetData; - - if (Download::HasReceivedAllPackets(download)) - { - download->successCallback(download->id, Download::AssembleBuffer(download)); - Download::RemoveClientDownload(download->id); - } - } - else - { - Logger::Print("Hash invalid!\n"); - } - } - } - - - // Server handlers - void Download::AckResponse(Network::Address target, std::string data) - { - int id = Download::ReadPacketId(data); - std::string challenge = Utils::ParseChallenge(data); // TODO: Maybe optimize this to ensure length matches - - auto download = Download::FindServerDownload(id); - - if (download && download->target == target) - { - if (download->challenge != challenge) - { - Logger::Print("Invalid download challenge!\n"); - Download::RemoveServerDownload(id); - } - else - { - download->lastPing = Game::Com_Milliseconds(); - download->acknowledged = true; - Logger::Print("Client acknowledged!\n"); - } - } - } - - void Download::MissingRequest(Network::Address target, std::string data) - { - int id = Download::ReadPacketId(data); - - auto download = Download::FindServerDownload(id); - - if (download && download->target == target) - { - while ((data.size() % 4) >= 4) - { - Download::MarkPacketAsDirty(download, *reinterpret_cast(const_cast(data.data()))); - data = data.substr(4); - } - } - } - - void Download::DownloadRequest(Network::Address target, std::string data) - { - int id = Download::ReadPacketId(data); - - Download::Container::DownloadSV download; - download.id = id; - download.target = target; - download.acknowledged = false; - download.startTime = Game::Com_Milliseconds(); - download.lastPing = Game::Com_Milliseconds(); - download.maxParts = 0; - - for (int i = 0; i < 1000000; ++i) - { - download.buffer.append("1234567890"); - } - - download.maxParts = download.buffer.size() / PACKET_SIZE; - if (download.buffer.size() % PACKET_SIZE) download.maxParts++; - - download.challenge = Utils::VA("%X", Utils::Cryptography::Rand::GenerateInt()); - - Download::Container::AckRequest request; - request.id = id; - request.maxPackets = download.maxParts; - request.length = download.challenge.size(); - - - std::string packet; - packet.append(reinterpret_cast(&request), sizeof(request)); - packet.append(download.challenge); - - Download::DataContainer.ServerDownloads.push_back(download); - - Network::SendCommand(target, "dlAckRequest", packet); - } - - std::string Download::AssembleBuffer(Download::Container::DownloadCL* download) - { - std::string buffer; - - for (int i = 0; i < download->maxParts; ++i) - { - if (!Download::HasReceivedPacket(download, i)) return ""; - buffer.append(download->parts[i]); - } - - return buffer; - } - - void Download::RequestMissingPackets(Download::Container::DownloadCL* download, std::vector packets) - { - if (!packets.empty()) - { - download->lastPing = Game::Com_Milliseconds(); - - std::string data; - data.append(reinterpret_cast(&download->id), sizeof(int)); - - for (auto &packet : packets) - { - data.append(reinterpret_cast(&packet), sizeof(int)); - } - - Network::SendCommand(download->target, "dlMissRequest", data); - } - } - - void Download::MarkPacketAsDirty(Download::Container::DownloadSV* download, int packet) - { - if (!download->sentParts.empty()) - { - for (auto i = download->sentParts.begin(); i != download->sentParts.end(); ++i) - { - if (*i == packet) - { - download->sentParts.erase(i); - i = download->sentParts.begin(); - } - } - } - } - - void Download::SendPacket(Download::Container::DownloadSV* download, int packet) - { - if (!download || packet >= download->maxParts) return; - download->lastPing = Game::Com_Milliseconds(); - download->sentParts.push_back(packet); - - Download::Container::Packet packetContainer; - packetContainer.id = download->id; - packetContainer.partId = packet; - - int size = ((packet + 1) == download->maxParts ? (download->buffer.size() % PACKET_SIZE) : PACKET_SIZE); - size = (size == 0 ? PACKET_SIZE : size); // If remaining data equals packet PACKET_SIZE, size would be 0, so adjust it. - std::string data(download->buffer.data() + (packet * PACKET_SIZE), size); - - packetContainer.length = data.size(); - packetContainer.hash = Utils::Cryptography::JenkinsOneAtATime::Compute(data.data(), data.size()); - - std::string response = "dlPacketResponse\n"; - response.append(reinterpret_cast(&packetContainer), sizeof(packetContainer)); - response.append(data); - - Network::SendCommand(download->target, "dlPacketResponse", response); - } - - void Download::Frame() - { - if (!Download::DataContainer.ClientDownloads.empty()) - { - for (auto i = Download::DataContainer.ClientDownloads.begin(); i != Download::DataContainer.ClientDownloads.end(); ++i) - { - if ((Game::Com_Milliseconds() - i->lastPing) > (DOWNLOAD_TIMEOUT * 2)) - { - i->failureCallback(i->id); - Download::DataContainer.ClientDownloads.erase(i); - return; - } - - // Request missing parts - if (i->acknowledged && (Game::Com_Milliseconds() - i->lastPing) > DOWNLOAD_TIMEOUT) - { - std::vector missingPackets; - for (int j = 0; j < i->maxParts; ++j) - { - if (!Download::HasReceivedPacket(&(*i), j)) - { - missingPackets.push_back(j); - } - } - - Download::RequestMissingPackets(&(*i), missingPackets); - } - } - } - - if (!Download::DataContainer.ServerDownloads.empty()) - { - for (auto i = Download::DataContainer.ServerDownloads.begin(); i != Download::DataContainer.ServerDownloads.end(); ++i) - { - if ((Game::Com_Milliseconds() - i->lastPing) > (DOWNLOAD_TIMEOUT * 3)) - { - Download::DataContainer.ServerDownloads.erase(i); - return; - } - - int packets = 0; - for (int j = 0; j < i->maxParts && packets <= FRAME_PACKET_LIMIT && i->acknowledged; ++j) - { - if (!Download::HasSentPacket(&(*i), j)) - { - //Logger::Print("Sending packet...\n"); - Download::SendPacket(&(*i), j); - packets++; - } - } - } - } - } - - int Download::Get(Network::Address target, std::string file, std::function successCallback, std::function failureCallback) - { - Download::Container::DownloadCL download; - download.id = Game::Com_Milliseconds(); - download.target = target; - download.acknowledged = false; - download.startTime = Game::Com_Milliseconds(); - download.lastPing = Game::Com_Milliseconds(); - download.maxParts = 0; - - download.failureCallback = failureCallback; - download.successCallback = successCallback; - - Download::DataContainer.ClientDownloads.push_back(download); - - std::string response = "dlRequest\n"; - response.append(reinterpret_cast(&download.id), sizeof(int)); - response.append(file); - - Network::SendCommand(target, "dlRequest", response); - - return download.id; + nc->flags |= MG_F_SEND_AND_CLOSE; } Download::Download() { -#ifdef ENABLE_EXPERIMENTAL_UDP_DOWNLOAD - // Frame handlers - QuickPatch::OnFrame(Download::Frame); - - // Register client handlers - Network::Handle("dlAckRequest", Download::AckRequest); - Network::Handle("dlPacketResponse", Download::PacketResponse); - - // Register server handlers - Network::Handle("dlAckResponse", Download::AckResponse); - Network::Handle("dlMissRequest", Download::MissingRequest); - Network::Handle("dlAckResponse", Download::AckResponse); - Network::Handle("dlRequest", Download::DownloadRequest); - - Command::Add("zob", [] (Command::Params params) + if (Dedicated::IsDedicated()) { - Logger::Print("Requesting!\n"); - Download::Get(Network::Address("192.168.0.23:28960"), "test", [] (int id, std::string data) + mg_mgr_init(&Download::Mgr, NULL); + + Network::OnStart([] () { - Logger::Print("Download succeeded %d!\n", Game::Com_Milliseconds() - (Download::FindClientDownload(id)->startTime)); - }, [] (int id) - { - Logger::Print("Download failed!\n"); + mg_connection* nc = mg_bind(&Download::Mgr, Utils::VA("%hu", (Dvar::Var("net_port").Get() & 0xFFFF)), Download::EventHandler); + mg_set_protocol_http_websocket(nc); }); - }); -#endif + + QuickPatch::OnFrame([] + { + mg_mgr_poll(&Download::Mgr, 0); + }); + } + else + { + Utils::Hook(0x5AC6E9, [] () + { + // TODO: Perform moddownload here + + Game::CL_DownloadsComplete(0); + }, HOOK_CALL).Install()->Quick(); + } } Download::~Download() { - Download::DataContainer.ServerDownloads.clear(); - Download::DataContainer.ClientDownloads.clear(); + if (Dedicated::IsDedicated()) + { + mg_mgr_free(&Download::Mgr); + } + else + { + + } } } diff --git a/src/Components/Modules/Download.hpp b/src/Components/Modules/Download.hpp index a7ec12a2..7e363f20 100644 --- a/src/Components/Modules/Download.hpp +++ b/src/Components/Modules/Download.hpp @@ -1,93 +1,15 @@ -#define FRAME_PACKET_LIMIT 20 -#define DOWNLOAD_TIMEOUT 2500 -#define PACKET_SIZE 1000 - namespace Components { class Download : public Component { public: - struct Container - { - struct DownloadCL - { - int id; - int startTime; - int lastPing; - bool acknowledged; - int maxParts; - Network::Address target; - std::map parts; - std::function failureCallback; - std::function successCallback; - }; - - struct DownloadSV - { - int id; - int startTime; - int lastPing; - bool acknowledged; - std::vector sentParts; - int maxParts; - Network::Address target; - std::string challenge; - std::string buffer; - }; - - struct Packet - { - int id; - int partId; - int length; - unsigned int hash; - }; - - struct AckRequest - { - int id; - int maxPackets; - int length; - }; - - std::vector ClientDownloads; - std::vector ServerDownloads; - }; - Download(); ~Download(); const char* GetName() { return "Download"; }; - static int Get(Network::Address target, std::string file, std::function successCallback, std::function failureCallback); - - static Container::DownloadCL* FindClientDownload(int id); - static Container::DownloadSV* FindServerDownload(int id); - private: - static void Frame(); - static Container DataContainer; + static mg_mgr Mgr; - // Client handlers - static void AckRequest(Network::Address target, std::string data); - static void PacketResponse(Network::Address target, std::string data); - - // Server handlers - static void AckResponse(Network::Address target, std::string data); - static void MissingRequest(Network::Address target, std::string data); - static void DownloadRequest(Network::Address target, std::string data); - - // Helper functions - static void RemoveClientDownload(int id); - static void RemoveServerDownload(int id); - - static bool HasSentPacket(Container::DownloadSV* download, int packet); - static bool HasReceivedPacket(Container::DownloadCL* download, int packet); - static bool HasReceivedAllPackets(Container::DownloadCL* download); - static std::string AssembleBuffer(Container::DownloadCL* download); - static void RequestMissingPackets(Container::DownloadCL* download, std::vector packets); - - static void MarkPacketAsDirty(Container::DownloadSV* download, int packet); - static void SendPacket(Container::DownloadSV* download, int packet); - static int ReadPacketId(std::string &data); + static void EventHandler(mg_connection *nc, int ev, void *ev_data); }; } diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index ab34d031..234bb81a 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -11,6 +11,7 @@ namespace Game CL_GetClientName_t CL_GetClientName = (CL_GetClientName_t)0x4563D0; CL_IsCgameInitialized_t CL_IsCgameInitialized = (CL_IsCgameInitialized_t)0x43EB20; CL_ConnectFromParty_t CL_ConnectFromParty = (CL_ConnectFromParty_t)0x433D30; + CL_DownloadsComplete_t CL_DownloadsComplete = (CL_DownloadsComplete_t)0x42CE90; Cmd_AddCommand_t Cmd_AddCommand = (Cmd_AddCommand_t)0x470090; Cmd_AddServerCommand_t Cmd_AddServerCommand = (Cmd_AddServerCommand_t)0x4DCE00; diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 3a9beef9..4cfa69b5 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -18,6 +18,9 @@ namespace Game typedef void(__cdecl * CL_ConnectFromParty_t)(int controllerIndex, _XSESSION_INFO *hostInfo, netadr_t addr, int numPublicSlots, int numPrivateSlots, const char *mapname, const char *gametype); extern CL_ConnectFromParty_t CL_ConnectFromParty; + typedef void(__cdecl * CL_DownloadsComplete_t)(int controller); + extern CL_DownloadsComplete_t CL_DownloadsComplete; + typedef void(__cdecl * Cmd_AddCommand_t)(const char* name, void(*callback), cmd_function_t* data, char); extern Cmd_AddCommand_t Cmd_AddCommand; diff --git a/src/STDInclude.hpp b/src/STDInclude.hpp index b1b4f90a..e75025cb 100644 --- a/src/STDInclude.hpp +++ b/src/STDInclude.hpp @@ -63,7 +63,7 @@ #include #include -//#include +#include #include #include #include