diff --git a/include/GEngine/libdev/Systems.hpp b/include/GEngine/libdev/Systems.hpp index 9a51bbf6..547f7e8b 100644 --- a/include/GEngine/libdev/Systems.hpp +++ b/include/GEngine/libdev/Systems.hpp @@ -8,12 +8,16 @@ #pragma once #include "GEngine/libdev/systems/AutoKiller.hpp" -#include "GEngine/libdev/systems/CLI.hpp" #include "GEngine/libdev/systems/Collisions.hpp" #include "GEngine/libdev/systems/Logger.hpp" #include "GEngine/libdev/systems/MainLoop.hpp" #include "GEngine/libdev/systems/Motions.hpp" +/* TODO: Thomas: these includes should never exists, it should be the developer to get them individually */ +namespace gengine::system { +class CLI; +} + namespace geg::system { using AutoKiller = gengine::system::AutoKiller; diff --git a/include/GEngine/libdev/systems/CLI.hpp b/include/GEngine/libdev/systems/CLI.hpp index c2bf5a23..e9dc20a4 100644 --- a/include/GEngine/libdev/systems/CLI.hpp +++ b/include/GEngine/libdev/systems/CLI.hpp @@ -15,6 +15,9 @@ #include "GEngine/libdev/systems/events/CLI.hpp" #include "GEngine/libdev/systems/events/MainLoop.hpp" #include "GEngine/libdev/systems/events/Native.hpp" +#include "GEngine/net/events/socket_event.hpp" +#include "GEngine/net/net_socket_system.hpp" +#include "GEngine/net/net_wait.hpp" #include #include @@ -36,10 +39,20 @@ class CLI : public gengine::System { private: void getInputs(void); std::vector splitInput(const std::string &input); + void processOutput(void); + + Network::NetWait m_wait; + Network::Event::SocketEvent m_socketEventStop; + Network::SocketSTD m_socketSTD; std::thread m_inputThread; std::atomic_bool m_stopReading; + std::atomic_bool m_stopProgram; /* due to EOF */ std::vector m_userInputHistory; mutable std::mutex m_historyMutex; + +#ifdef NET_USE_HANDLE + DWORD m_dwMod; +#endif }; } // namespace gengine::system diff --git a/include/GEngine/net/net.hpp b/include/GEngine/net/net.hpp index e4a4a2b9..7bc26658 100644 --- a/include/GEngine/net/net.hpp +++ b/include/GEngine/net/net.hpp @@ -113,6 +113,10 @@ class NET { return mg_client.getRecord(); } + static NetWait &getWaitHandler(void) { + return mg_wait; + } + public: /* todo : temp*/ static SocketUDP &getSocketUdp(void) { diff --git a/include/GEngine/net/net_event.hpp b/include/GEngine/net/net_event.hpp index 57ad90be..74343415 100644 --- a/include/GEngine/net/net_event.hpp +++ b/include/GEngine/net/net_event.hpp @@ -53,7 +53,7 @@ struct Info : public InfoHeader { */ class Manager { public: - Manager() = default; + Manager(); ~Manager() = default; template diff --git a/include/GEngine/net/net_socket_system.hpp b/include/GEngine/net/net_socket_system.hpp new file mode 100644 index 00000000..525aeaec --- /dev/null +++ b/include/GEngine/net/net_socket_system.hpp @@ -0,0 +1,45 @@ +/* +** EPITECH PROJECT, 2024 +** GameEngine +** File description: +** net_system_socket +*/ + +#pragma once + +#include "net_socket.hpp" + +#include + +namespace Network { + +#ifdef _WIN32 +#define STD_IN STD_INPUT_HANDLE +#define STD_OUT STD_OUTPUT_HANDLE +#define STD_ERR STD_ERROR_HANDLE +#else +#define STD_IN STDIN_FILENO +#define STD_OUT STDOUT_FILENO +#define STD_ERR STDERR_FILENO +#endif + +class SocketSTD : public ASocket { +public: + SocketSTD() = default; + SocketSTD(int stdNumber); + SocketSTD(const SocketSTD &other) = delete; + SocketSTD &operator=(const SocketSTD &) = delete; + SocketSTD(SocketSTD &&other); + SocketSTD &operator=(SocketSTD &&other); + ~SocketSTD() = default; + + int read(std::string &buffer) const; + int write(const std::string &buffer) const; + + int socketClose(void) override final { + return socketCloseAdv(false); + } + + void setStd(int stdNumber); +}; +} // namespace Network diff --git a/include/GEngine/net/net_wait.hpp b/include/GEngine/net/net_wait.hpp index f59ede3a..f5e82bd9 100644 --- a/include/GEngine/net/net_wait.hpp +++ b/include/GEngine/net/net_wait.hpp @@ -53,7 +53,7 @@ class NetWaitSet { m_resIndex = res; } - bool applyCallback(void) const; + bool applyCallback(bool shouldReset = true) const; private: static constexpr size_t MAX_SOCKETS = MAXIMUM_WAIT_OBJECTS; @@ -90,24 +90,27 @@ class NetWait { NetWait(); virtual ~NetWait() = default; + /** + * @brief Waits for a specified amount of time or until an event occurs in the NetWaitSet. + * + * @param ms The maximum number of milliseconds to wait. + * @param set The NetWaitSet to monitor for events. + * @return true if an event occurred within the specified time, false if the timeout was reached. + */ bool wait(uint32_t ms, NetWaitSet &set); public: - static void addSocketPool(ASocket &socket); - static void removeSocketPool(const ASocket &socket); + void addSocketPool(ASocket &socket); + void removeSocketPool(const ASocket &socket); -#ifdef NET_USE_HANDLE - -#else public: - static SOCKET getHighestSocket(void) { + SOCKET getHighestSocket(void) { return m_highFd; } private: - static fd_set m_fdSet; - static SOCKET m_highFd; -#endif + fd_set m_fdSet; + SOCKET m_highFd = -1; }; } // namespace Network \ No newline at end of file diff --git a/source/GEngine/libdev/systems/CLI.cpp b/source/GEngine/libdev/systems/CLI.cpp index 602b3ed7..c84c6ea2 100644 --- a/source/GEngine/libdev/systems/CLI.cpp +++ b/source/GEngine/libdev/systems/CLI.cpp @@ -7,7 +7,12 @@ #include "GEngine/libdev/systems/CLI.hpp" +#include "GEngine/net/events/socket_event.hpp" +#include "GEngine/net/net_socket_system.hpp" +#include "GEngine/net/net_wait.hpp" + namespace gengine::system { + CLI::CLI() : m_stopReading(false) { } @@ -19,32 +24,94 @@ void CLI::init(void) { } void CLI::onStartEngine(gengine::system::event::StartEngine &e [[maybe_unused]]) { + m_socketSTD.setStd(STD_IN); +#ifndef NET_USE_HANDLE + m_wait.addSocketPool(m_socketSTD); + m_wait.addSocketPool(m_socketEventStop); +#endif m_inputThread = std::thread(&CLI::getInputs, this); - m_inputThread.detach(); } void CLI::onStopEngine(gengine::system::event::StopEngine &e [[maybe_unused]]) { m_stopReading = true; + + m_socketEventStop.signal(); + +#ifdef NET_USE_HANDLE + SetConsoleMode(m_socketSTD.getHandle(), m_dwMod); +#endif + + if (m_inputThread.joinable()) + m_inputThread.join(); } void CLI::onMainLoop(gengine::system::event::MainLoop &e [[maybe_unused]]) { std::lock_guard lock(m_historyMutex); + if (m_stopProgram) { + publishEvent(gengine::system::event::StopMainLoop()); + return; + } + for (const auto &entry : m_userInputHistory) publishEvent(gengine::system::event::CLINewInput(splitInput(entry))); m_userInputHistory.clear(); } -void CLI::getInputs(void) { +void CLI::processOutput(void) { std::string input; + auto &res = std::getline(std::cin, input); + if (res.eof()) { + m_stopProgram = true; + m_stopReading = true; + return; + } + + if (!input.empty()) { + std::lock_guard lock(m_historyMutex); + m_userInputHistory.push_back(input); + } + + /* ugly way of output a > once it's printed */ + std::this_thread::sleep_for(std::chrono::milliseconds(100)); +} + +void CLI::getInputs(void) { +#ifdef NET_USE_HANDLE + GetConsoleMode(m_socketSTD.getHandle(), &m_dwMod); + + DWORD fdwMode = m_dwMod & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT); + SetConsoleMode(m_socketSTD.getHandle(), fdwMode); + + /* flush to remove existing events */ + FlushConsoleInputBuffer(m_socketSTD.getHandle()); +#endif while (!m_stopReading) { + Network::NetWaitSet set; + + set.setAlert(m_socketEventStop, [this]() { + /* do nothing, just loopback since the thread must be killed */ + return true; + }); + + set.setAlert(m_socketSTD, [this]() { + processOutput(); + return true; + }); + std::cout << "> " << std::flush; - if (std::getline(std::cin, input)) { - std::lock_guard lock(m_historyMutex); - m_userInputHistory.push_back(input); - } else + bool hasActivity = m_wait.wait(1000000000, set); + if (!hasActivity) + continue; + +#ifdef NET_USE_HANDLE + set.applyCallback(false); +#else + if (set.isSignaled(m_socketEventStop)) /**/ break; - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if (set.isSignaled(m_socketSTD)) + processOutput(); +#endif } } diff --git a/source/GEngine/libdev/systems/driver/output/VoIPAudio.cpp b/source/GEngine/libdev/systems/driver/output/VoIPAudio.cpp index 8da771ea..a758ee75 100644 --- a/source/GEngine/libdev/systems/driver/output/VoIPAudio.cpp +++ b/source/GEngine/libdev/systems/driver/output/VoIPAudio.cpp @@ -221,7 +221,8 @@ void VoIPAudio::processSoundInput(void) { VoIPAudio::~VoIPAudio() { m_running = false; - m_soundThread.join(); + if (m_soundThread.joinable()) + m_soundThread.join(); auto err = Pa_StopStream(playbackStream); err = Pa_CloseStream(playbackStream); diff --git a/source/GEngine/net/cl_net_client.cpp b/source/GEngine/net/cl_net_client.cpp index 51f902ed..35cf0af1 100644 --- a/source/GEngine/net/cl_net_client.cpp +++ b/source/GEngine/net/cl_net_client.cpp @@ -211,17 +211,17 @@ void CLNetClient::getPingResponse(const UDPMessage &msg, const Address &addr) { msg.readData(data); std::unique_ptr
addrPtr; - if (addr.getType() == AT_IPV4) + uint16_t port = -1; + if (addr.getType() == AT_IPV4) { addrPtr = std::make_unique(static_cast(addr)); - else if (addr.getType() == AT_IPV6) + port = data.tcpv4Port; + } else if (addr.getType() == AT_IPV6) { addrPtr = std::make_unique(static_cast(addr)); + port = data.tcpv6Port; + } - Event::PingInfo pinginfo = {addrPtr->toString(), - addrPtr->getPort(), - data.currentPlayers, - data.maxPlayers, - data.os, - Time::Clock::milliseconds() - m_pingSendTime}; + Event::PingInfo pinginfo = {addrPtr->toString(), port, data.currentPlayers, + data.maxPlayers, data.os, Time::Clock::milliseconds() - m_pingSendTime}; NET::getEventManager().invokeCallbacks(Event::CT_OnPingResult, pinginfo); m_pingedServers.push_back({data, std::move(addrPtr)}); diff --git a/source/GEngine/net/events/net_socket_event.cpp b/source/GEngine/net/events/net_socket_event.cpp index dad6076f..529f7126 100644 --- a/source/GEngine/net/events/net_socket_event.cpp +++ b/source/GEngine/net/events/net_socket_event.cpp @@ -7,7 +7,6 @@ #include "GEngine/net/events/socket_event.hpp" #include "GEngine/net/net_exception.hpp" -#include "GEngine/net/net_wait.hpp" #include @@ -44,7 +43,7 @@ SocketEvent::SocketEvent() { if (m_sock == -1) throw NetException("Failed to create eventfd", EL_ERR_SOCKET); #endif - NetWait::addSocketPool(*this); + #else m_handle = CreateEvent(NULL, TRUE, FALSE, NULL); #endif diff --git a/source/GEngine/net/net.cpp b/source/GEngine/net/net.cpp index 10a84a29..b1ccaf9e 100644 --- a/source/GEngine/net/net.cpp +++ b/source/GEngine/net/net.cpp @@ -56,12 +56,13 @@ namespace Network { /* Global vars */ +NetWait NET::mg_wait; + SocketUDP NET::mg_socketUdp; SocketTCPMaster NET::mg_socketListenTcp; SocketUDP NET::mg_socketUdpV6; SocketTCPMaster NET::mg_socketListenTcpV6; -NetWait NET::mg_wait; Event::Manager NET::mg_eventManager; NetServer NET::mg_server(mg_socketUdp, mg_socketUdpV6); CLNetClient NET::mg_client(CVar::net_ipv6.getIntValue() ? mg_socketUdpV6 : mg_socketUdp, @@ -156,7 +157,9 @@ void NET::stop(void) { auto &eventManager = NET::getEventManager(); eventManager.getSocketEvent().signal(); - mg_networkThread.join(); + if (mg_networkThread.joinable()) + mg_networkThread.join(); + /* end of network thread */ NET::mg_server.stop(); diff --git a/source/GEngine/net/net_event.cpp b/source/GEngine/net/net_event.cpp index 61ab1080..f7fad300 100644 --- a/source/GEngine/net/net_event.cpp +++ b/source/GEngine/net/net_event.cpp @@ -11,6 +11,12 @@ #include "GEngine/net/net.hpp" namespace Network::Event { +Manager::Manager() { +#ifndef NET_USE_HANDLE + NET::getWaitHandler().addSocketPool(m_socketEvent); +#endif +} + void Manager::createSets(NetWaitSet &set) { set.setAlert(m_socketEvent, [this]() { return handleEvent(); }); } diff --git a/source/GEngine/net/net_socket.cpp b/source/GEngine/net/net_socket.cpp index 2627889e..a7e540d6 100644 --- a/source/GEngine/net/net_socket.cpp +++ b/source/GEngine/net/net_socket.cpp @@ -7,6 +7,7 @@ #include "GEngine/net/net_socket.hpp" #include "GEngine/cvar/net.hpp" +#include "GEngine/net/net.hpp" #include "GEngine/net/net_exception.hpp" #include "GEngine/net/net_socket_error.hpp" #include "GEngine/net/net_wait.hpp" @@ -89,7 +90,7 @@ int ASocket::socketCloseAdv(bool shouldShutdown) { if (m_handle != INVALID_HANDLE_VALUE) status = CloseHandle(m_handle) ? 0 : -1; #endif - NetWait::removeSocketPool(*this); + NET::getWaitHandler().removeSocketPool(*this); return status; } @@ -180,7 +181,7 @@ SocketTCPMaster::SocketTCPMaster(const IP &ip, uint16_t port) { if (listen(m_sock, MAX_LISTEN) < 0) throw SocketException("(TCP) Failed to listen on socket"); - NetWait::addSocketPool(*this); + NET::getWaitHandler().addSocketPool(*this); } SocketTCPMaster::SocketTCPMaster(uint16_t port, bool ipv6) { @@ -202,7 +203,7 @@ SocketTCPMaster::SocketTCPMaster(uint16_t port, bool ipv6) { if (listen(m_sock, MAX_LISTEN) < 0) throw SocketException("(TCP) Failed to listen on socket"); - NetWait::addSocketPool(*this); + NET::getWaitHandler().addSocketPool(*this); } SocketTCPMaster::~SocketTCPMaster() { @@ -234,7 +235,7 @@ SocketTCP::SocketTCP(const SocketTCPMaster &socketMaster, UnknownAddress &unkwAd unkwAddr.updateType(); m_port = socketMaster.getPort(); m_notReady = false; - NetWait::addSocketPool(*this); + NET::getWaitHandler().addSocketPool(*this); } SocketTCP::SocketTCP(const AddressV4 &addr, uint16_t tcpPort, bool block) { @@ -254,7 +255,7 @@ SocketTCP::SocketTCP(const AddressV4 &addr, uint16_t tcpPort, bool block) { if (connect(m_sock, (sockaddr *)&address, sizeof(address)) < 0) { int e = socketError; if (e == WSAEINPROGRESS || e == WSAEWOULDBLOCK) { - NetWait::addSocketPool(*this); + NET::getWaitHandler().addSocketPool(*this); return; } socketClose(); @@ -262,7 +263,7 @@ SocketTCP::SocketTCP(const AddressV4 &addr, uint16_t tcpPort, bool block) { } m_notReady = false; - NetWait::addSocketPool(*this); + NET::getWaitHandler().addSocketPool(*this); } SocketTCP::SocketTCP(const AddressV6 &addr, uint16_t tcpPort, bool block) { @@ -282,7 +283,7 @@ SocketTCP::SocketTCP(const AddressV6 &addr, uint16_t tcpPort, bool block) { if (connect(m_sock, (sockaddr *)&address, sizeof(address)) < 0) { int e = socketError; if (e == WSAEINPROGRESS || e == WSAEWOULDBLOCK) { - NetWait::addSocketPool(*this); + NET::getWaitHandler().addSocketPool(*this); return; } socketClose(); @@ -290,7 +291,7 @@ SocketTCP::SocketTCP(const AddressV6 &addr, uint16_t tcpPort, bool block) { } m_notReady = false; - NetWait::addSocketPool(*this); + NET::getWaitHandler().addSocketPool(*this); } SocketTCP::~SocketTCP() { @@ -449,7 +450,7 @@ SocketUDP::SocketUDP(const IP &ip, uint16_t port, bool block) { if (bind(m_sock, (sockaddr *)&address, ip.type == AT_IPV6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in)) < 0) throw SocketException("(UDP) Failed to bind socket"); - NetWait::addSocketPool(*this); + NET::getWaitHandler().addSocketPool(*this); } SocketUDP::SocketUDP(uint16_t port, bool ipv6, bool block) { @@ -461,7 +462,7 @@ SocketUDP::SocketUDP(uint16_t port, bool ipv6, bool block) { if (bind(m_sock, (sockaddr *)&address, ipv6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in)) < 0) throw SocketException("(UDP) can't bind udp (only port)"); - NetWait::addSocketPool(*this); + NET::getWaitHandler().addSocketPool(*this); } SocketUDP::SocketUDP(SocketUDP &&other) diff --git a/source/GEngine/net/net_socket_system.cpp b/source/GEngine/net/net_socket_system.cpp new file mode 100644 index 00000000..8a3d1d32 --- /dev/null +++ b/source/GEngine/net/net_socket_system.cpp @@ -0,0 +1,26 @@ +/* +** EPITECH PROJECT, 2024 +** GameEngine +** File description: +** net_socket_system +*/ + +#include "GEngine/net/net_socket_system.hpp" + +namespace Network { +SocketSTD::SocketSTD(int stdNumber) { +#ifdef NET_USE_HANDLE + m_handle = GetStdHandle(stdNumber); +#else + m_sock = stdNumber; +#endif +} + +void SocketSTD::setStd(int stdNumber) { +#ifdef NET_USE_HANDLE + m_handle = GetStdHandle(stdNumber); +#else + m_sock = stdNumber; +#endif +} +} // namespace Network \ No newline at end of file diff --git a/source/GEngine/net/net_wait.cpp b/source/GEngine/net/net_wait.cpp index e05f968f..c80bc9a3 100644 --- a/source/GEngine/net/net_wait.cpp +++ b/source/GEngine/net/net_wait.cpp @@ -66,10 +66,12 @@ void NetWaitSet::reset(void) { } #ifdef NET_USE_HANDLE -bool NetWaitSet::applyCallback(void) const { - BOOL res = WSAResetEvent(m_events[m_resIndex - WSA_WAIT_EVENT_0]); - if (!res) - wprintf(L"WSAResetEvent failed with error = %d\n", WSAGetLastError()); +bool NetWaitSet::applyCallback(bool shouldReset) const { + if (shouldReset) { + BOOL res = WSAResetEvent(m_events[m_resIndex - WSA_WAIT_EVENT_0]); + if (!res) + wprintf(L"WSAResetEvent failed with error = %d\n", WSAGetLastError()); + } auto callbackRes = m_callbacks[m_resIndex](); return callbackRes; } @@ -77,12 +79,6 @@ bool NetWaitSet::applyCallback(void) const { /************************************************************/ -#ifdef NET_USE_HANDLE -#else -SOCKET NetWait::m_highFd = -1; -fd_set NetWait::m_fdSet; -#endif - NetWait::NetWait() { #ifdef NET_USE_HANDLE #else