diff options
author | Amos <48657826+Mauler125@users.noreply.github.com> | 2022-03-04 01:45:31 +0100 |
---|---|---|
committer | Amos <48657826+Mauler125@users.noreply.github.com> | 2022-03-04 01:45:31 +0100 |
commit | fbb581beddb2f96ff5bdb2a89522777e90fed312 (patch) | |
tree | cd2c2cce0e31781b8624cbbe8cc1e4acfd3455e9 /NetConsole | |
parent | c07baa7dc137daa006685bfa4757aaf429f81e1b (diff) | |
download | NorthstarLauncher-fbb581beddb2f96ff5bdb2a89522777e90fed312.tar.gz NorthstarLauncher-fbb581beddb2f96ff5bdb2a89522777e90fed312.zip |
Add non-game netconsole client for RCON
Allows users to connect to their dedicated servers without having the game installed. Its also a base for example for anyone that wants to create their own RCON client.
Diffstat (limited to 'NetConsole')
-rw-r--r-- | NetConsole/NetConsole.vcxproj | 181 | ||||
-rw-r--r-- | NetConsole/NetConsole.vcxproj.filters | 63 | ||||
-rw-r--r-- | NetConsole/netconsole.cpp | 415 | ||||
-rw-r--r-- | NetConsole/netconsole.h | 44 | ||||
-rw-r--r-- | NetConsole/pch.cpp | 5 | ||||
-rw-r--r-- | NetConsole/pch.h | 32 |
6 files changed, 740 insertions, 0 deletions
diff --git a/NetConsole/NetConsole.vcxproj b/NetConsole/NetConsole.vcxproj new file mode 100644 index 00000000..a1f192cf --- /dev/null +++ b/NetConsole/NetConsole.vcxproj @@ -0,0 +1,181 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <VCProjectVersion>16.0</VCProjectVersion> + <Keyword>Win32Proj</Keyword> + <ProjectGuid>{abb29780-5fea-4108-8793-fa50fb9cdafa}</ProjectGuid> + <RootNamespace>NetConsole</RootNamespace> + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + <AdditionalIncludeDirectories>$(SolutionDir)NorthstarDedicatedTest\;$(SolutionDir)NorthstarDedicatedTest\Include\;$(SolutionDir)NorthstarDedicatedTest\Include\protobuf\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> + <LanguageStandard>stdcpp17</LanguageStandard> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>Ws2_32.lib;$(SolutionDir)$(Platform)\$(Configuration)\libprotobuf_x64.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + <AdditionalIncludeDirectories>$(SolutionDir)NorthstarDedicatedTest\;$(SolutionDir)NorthstarDedicatedTest\Include\;$(SolutionDir)NorthstarDedicatedTest\Include\protobuf\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> + <LanguageStandard>stdcpp17</LanguageStandard> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>Ws2_32.lib;$(SolutionDir)$(Platform)\$(Configuration)\libprotobuf_x64.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="..\NorthstarDedicatedTest\cl_rcon.pb.h" /> + <ClInclude Include="..\NorthstarDedicatedTest\net.h" /> + <ClInclude Include="..\NorthstarDedicatedTest\NetAdr2.h" /> + <ClInclude Include="..\NorthstarDedicatedTest\socketcreator.h" /> + <ClInclude Include="..\NorthstarDedicatedTest\sv_rcon.pb.h" /> + <ClInclude Include="netconsole.h" /> + <ClInclude Include="pch.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\NorthstarDedicatedTest\cl_rcon.pb.cc"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="..\NorthstarDedicatedTest\net.cpp" /> + <ClCompile Include="..\NorthstarDedicatedTest\NetAdr2.cpp" /> + <ClCompile Include="..\NorthstarDedicatedTest\socketcreator.cpp" /> + <ClCompile Include="..\NorthstarDedicatedTest\sv_rcon.pb.cc"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="netconsole.cpp" /> + <ClCompile Include="pch.cpp"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader> + </ClCompile> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/NetConsole/NetConsole.vcxproj.filters b/NetConsole/NetConsole.vcxproj.filters new file mode 100644 index 00000000..617ef8c7 --- /dev/null +++ b/NetConsole/NetConsole.vcxproj.filters @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="netconsole.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="pch.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\NorthstarDedicatedTest\cl_rcon.pb.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\NorthstarDedicatedTest\sv_rcon.pb.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\NorthstarDedicatedTest\socketcreator.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\NorthstarDedicatedTest\NetAdr2.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\NorthstarDedicatedTest\net.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="netconsole.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="pch.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\NorthstarDedicatedTest\cl_rcon.pb.cc"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\NorthstarDedicatedTest\sv_rcon.pb.cc"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\NorthstarDedicatedTest\socketcreator.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\NorthstarDedicatedTest\NetAdr2.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\NorthstarDedicatedTest\net.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/NetConsole/netconsole.cpp b/NetConsole/netconsole.cpp new file mode 100644 index 00000000..210de097 --- /dev/null +++ b/NetConsole/netconsole.cpp @@ -0,0 +1,415 @@ +//=====================================================================================// +// +// Purpose: Lightweight netconsole client. +// +//=====================================================================================// + + +#include "pch.h" +#include "NetAdr2.h" +#include "socketcreator.h" +#include "sv_rcon.pb.h" +#include "cl_rcon.pb.h" +#include "net.h" +#include "netconsole.h" + +//----------------------------------------------------------------------------- +// Purpose: WSA and NETCON systems init +// Output : true on success, false otherwise +//----------------------------------------------------------------------------- +bool CNetCon::Init(void) +{ + WSAData wsaData{}; + int nError = ::WSAStartup(MAKEWORD(2, 2), &wsaData); + + if (nError != 0) + { + spdlog::error("Failed to start Winsock via WSAStartup: ({})", NET_ErrorString(WSAGetLastError())); + return false; + } + + this->TermSetup(); + + std::thread tFrame(&CNetCon::RunFrame, this); + tFrame.detach(); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: WSA and NETCON systems shutdown +// Output : true on success, false otherwise +//----------------------------------------------------------------------------- +bool CNetCon::Shutdown(void) +{ + m_pSocket->CloseAllAcceptedSockets(); + m_abConnEstablished = false; + + int nError = ::WSACleanup(); + if (nError != 0) + { + spdlog::error("Failed to stop winsock via WSACleanup: ({})", NET_ErrorString(WSAGetLastError())); + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: terminal setup +//----------------------------------------------------------------------------- +void CNetCon::TermSetup(void) +{ + DWORD dwMode = NULL; + HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE); + HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE); + + CONSOLE_SCREEN_BUFFER_INFOEX sbInfoEx{}; + COLORREF storedBG = sbInfoEx.ColorTable[0]; + sbInfoEx.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); + + GetConsoleScreenBufferInfoEx(hOutput, &sbInfoEx); + sbInfoEx.ColorTable[0] = 0x0000; + SetConsoleScreenBufferInfoEx(hOutput, &sbInfoEx); +} + +//----------------------------------------------------------------------------- +// Purpose: gets input IP and port for initialization +//----------------------------------------------------------------------------- +void CNetCon::UserInput(void) +{ + std::string svInput; + + if (std::getline(std::cin, svInput)) + { + if (strcmp(svInput.c_str(), "nquit") == 0) + { + m_bQuitApplication = true; + return; + } + if (m_abConnEstablished) + { + if (strcmp(svInput.c_str(), "disconnect") == 0) + { + this->Disconnect(); + return; + } + size_t nPos = svInput.find(" "); + if (!svInput.empty() + && nPos > 0 + && nPos < svInput.size() + && nPos != svInput.size()) + { + std::string svReqVal = svInput.substr(nPos + 1); + std::string svReqBuf = svInput.erase(svInput.find(" ")); + + if (strcmp(svReqBuf.c_str(), "PASS") == 0) // Auth with RCON server. + { + std::string svSerialized = this->Serialize(svReqBuf, svReqVal, cl_rcon::request_t::SERVERDATA_REQUEST_AUTH); + this->Send(svSerialized); + } + else // This is a ConVar. + { + std::string svSerialized = this->Serialize(svReqBuf, svReqVal, cl_rcon::request_t::SERVERDATA_REQUEST_SETVALUE); + this->Send(svSerialized); + } + } + else // This is a ConCommand. + { + std::string svSerialized = this->Serialize(svInput, "", cl_rcon::request_t::SERVERDATA_REQUEST_EXECCOMMAND); + this->Send(svSerialized); + } + } + else // Setup connection from input. + { + size_t nPos = svInput.find(" "); + if (!svInput.empty() + && nPos > 0 + && nPos < svInput.size() + && nPos != svInput.size()) + { + std::string svInPort = svInput.substr(nPos + 1); + std::string svInAdr = svInput.erase(svInput.find(" ")); + + if (!this->Connect(svInAdr, svInPort)) + { + m_abPromptConnect = true; + return; + } + } + else // Initialize as [127.0.0.1]:37015. + { + if (!this->Connect("", "")) + { + m_abPromptConnect = true; + return; + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: client's main processing loop +//----------------------------------------------------------------------------- +void CNetCon::RunFrame(void) +{ + for (;;) + { + if (m_abConnEstablished) + { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + this->Recv(); + } + else if (m_abPromptConnect) + { + std::cout << "Enter <IP> <PORT>: "; + m_abPromptConnect = false; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: checks if application should be terminated +// Output : true for termination, false otherwise +//----------------------------------------------------------------------------- +bool CNetCon::ShouldQuit(void) const +{ + return this->m_bQuitApplication; +} + +//----------------------------------------------------------------------------- +// Purpose: connect to specified address and port +// Input : *svInAdr - +// *svInPort - +// Output : true if connection succeeds, false otherwise +//----------------------------------------------------------------------------- +bool CNetCon::Connect(const std::string& svInAdr, const std::string& svInPort) +{ + if (svInAdr.size() > 0 && svInPort.size() > 0) + { + // Default is [127.0.0.1]:37015 + m_pNetAdr2->SetIPAndPort(svInAdr, svInPort); + } + + if (m_pSocket->ConnectSocket(*m_pNetAdr2, true) == SOCKET_ERROR) + { + spdlog::warn("Failed to connect. Error: (SOCKET_ERROR). Verify IP and PORT."); + return false; + } + spdlog::info("Connected to: {}", m_pNetAdr2->GetIPAndPort()); + + m_abConnEstablished = true; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: disconnect from current session +//----------------------------------------------------------------------------- +void CNetCon::Disconnect(void) +{ + ::closesocket(m_pSocket->GetAcceptedSocketHandle(0)); + m_abPromptConnect = true; + m_abConnEstablished = false; +} + +//----------------------------------------------------------------------------- +// Purpose: send message +// Input : *svMessage - +//----------------------------------------------------------------------------- +void CNetCon::Send(const std::string& svMessage) const +{ + int nSendResult = ::send(m_pSocket->GetAcceptedSocketData(0)->m_hSocket, svMessage.c_str(), svMessage.size(), MSG_NOSIGNAL); + if (nSendResult == SOCKET_ERROR) + { + spdlog::warn("Failed to send message: (SOCKET_ERROR)"); + } +} + +//----------------------------------------------------------------------------- +// Purpose: receive message +//----------------------------------------------------------------------------- +void CNetCon::Recv(void) +{ + static char szRecvBuf[MAX_NETCONSOLE_INPUT_LEN]{}; + + {////////////////////////////////////////////// + int nPendingLen = ::recv(m_pSocket->GetAcceptedSocketData(0)->m_hSocket, szRecvBuf, sizeof(szRecvBuf), MSG_PEEK); + if (nPendingLen == SOCKET_ERROR && m_pSocket->IsSocketBlocking()) + { + return; + } + if (nPendingLen <= 0 && m_abConnEstablished) // EOF or error. + { + this->Disconnect(); + spdlog::info("Server closed connection"); + return; + } + }////////////////////////////////////////////// + + u_long nReadLen; // Find out how much we have to read. + ::ioctlsocket(m_pSocket->GetAcceptedSocketData(0)->m_hSocket, FIONREAD, &nReadLen); + + while (nReadLen > 0) + { + memset(szRecvBuf, '\0', sizeof(szRecvBuf)); + int nRecvLen = ::recv(m_pSocket->GetAcceptedSocketData(0)->m_hSocket, szRecvBuf, MIN(sizeof(szRecvBuf), nReadLen), MSG_NOSIGNAL); + + if (nRecvLen == 0 && m_abConnEstablished) // Socket was closed. + { + this->Disconnect(); + spdlog::info("Server closed connection"); + break; + } + if (nRecvLen < 0 && !m_pSocket->IsSocketBlocking()) + { + break; + } + + nReadLen -= nRecvLen; // Process what we've got. + this->ProcessBuffer(szRecvBuf, nRecvLen); + } +} + +//----------------------------------------------------------------------------- +// Purpose: handles input response buffer +// Input : *pszIn - +// nRecvLen - +//----------------------------------------------------------------------------- +void CNetCon::ProcessBuffer(const char* pszIn, int nRecvLen) const +{ + int nCharsInRespondBuffer = 0; + char szInputRespondBuffer[MAX_NETCONSOLE_INPUT_LEN]{}; + + while (nRecvLen) + { + switch (*pszIn) + { + case '\r': + { + if (nCharsInRespondBuffer) + { + sv_rcon::response sv_response = this->Deserialize(szInputRespondBuffer); + this->ProcessMessage(sv_response); + } + nCharsInRespondBuffer = 0; + break; + } + + default: + { + if (nCharsInRespondBuffer < MAX_NETCONSOLE_INPUT_LEN - 1) + { + szInputRespondBuffer[nCharsInRespondBuffer++] = *pszIn; + } + break; + } + } + pszIn++; + nRecvLen--; + } +} + +//----------------------------------------------------------------------------- +// Purpose: processes received message +// Input : *sv_response - +//----------------------------------------------------------------------------- +void CNetCon::ProcessMessage(const sv_rcon::response& sv_response) const +{ + switch (sv_response.responsetype()) + { + case sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH: + case sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG: + { + std::string svOut = sv_response.responsebuf(); + svOut.erase(std::remove(svOut.begin(), svOut.end(), '\n'), svOut.end()); + spdlog::info(svOut.c_str()); + break; + } + default: + { + break; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: serializes input +// Input : *svReqBuf - +// *svReqVal - +// request_t - +// Output : serialized results as string +//----------------------------------------------------------------------------- +std::string CNetCon::Serialize(const std::string& svReqBuf, const std::string& svReqVal, cl_rcon::request_t request_t) const +{ + cl_rcon::request cl_request; + + cl_request.set_requestid(-1); + cl_request.set_requesttype(request_t); + + switch (request_t) + { + case cl_rcon::request_t::SERVERDATA_REQUEST_SETVALUE: + case cl_rcon::request_t::SERVERDATA_REQUEST_AUTH: + { + cl_request.set_requestbuf(svReqBuf); + cl_request.set_requestval(svReqVal); + break; + } + case cl_rcon::request_t::SERVERDATA_REQUEST_EXECCOMMAND: + { + cl_request.set_requestbuf(svReqBuf); + break; + } + } + return cl_request.SerializeAsString().append("\r"); +} + +//----------------------------------------------------------------------------- +// Purpose: de-serializes input +// Input : *svBuf - +// Output : de-serialized object +//----------------------------------------------------------------------------- +sv_rcon::response CNetCon::Deserialize(const std::string& svBuf) const +{ + sv_rcon::response sv_response; + sv_response.ParseFromArray(svBuf.c_str(), static_cast<int>(svBuf.size())); + + return sv_response; +} + +//----------------------------------------------------------------------------- +// Purpose: entrypoint +// Input : argc - +// *argv - +//----------------------------------------------------------------------------- +int main(int argc, char* argv[]) +{ + CNetCon* pNetCon = new CNetCon(); + std::cout << "Northstar TCP net console [Version " << NETCON_VERSION << "]" << std::endl; + spdlog::default_logger()->set_pattern("[%H:%M:%S] [%l] %v"); + + if (!pNetCon->Init()) + { + return EXIT_FAILURE; + } + + if (argc >= 3) // Get IP and Port from command line. + { + if (!pNetCon->Connect(argv[1], argv[2])) + { + return EXIT_FAILURE; + } + } + + while (!pNetCon->ShouldQuit()) + { + pNetCon->UserInput(); + } + + if (!pNetCon->Shutdown()) + { + return EXIT_FAILURE; + } + + return ERROR_SUCCESS; +}
\ No newline at end of file diff --git a/NetConsole/netconsole.h b/NetConsole/netconsole.h new file mode 100644 index 00000000..a777a27e --- /dev/null +++ b/NetConsole/netconsole.h @@ -0,0 +1,44 @@ +//===========================================================================// +// +// Purpose: +// +//===========================================================================// +#pragma once +#include "cl_rcon.pb.h" +#include "sv_rcon.pb.h" + +constexpr const char* NETCON_VERSION = "2.0.0.1"; + +class CNetCon +{ +public: + bool Init(void); + bool Shutdown(void); + + void TermSetup(void); + void UserInput(void); + + void RunFrame(void); + bool ShouldQuit(void) const; + + bool Connect(const std::string& svInAdr, const std::string& svInPort); + void Disconnect(void); + + void Send(const std::string& svMessage) const; + void Recv(void); + + void ProcessBuffer(const char* pszIn, int nRecvLen) const; + void ProcessMessage(const sv_rcon::response& sv_response) const; + + std::string Serialize(const std::string& svReqBuf, const std::string& svReqVal, cl_rcon::request_t request_t) const; + sv_rcon::response Deserialize(const std::string& svBuf) const; + +private: + CNetAdr2* m_pNetAdr2 = new CNetAdr2("localhost", "37015"); + CSocketCreator* m_pSocket = new CSocketCreator(); + + bool m_bInitialized = false; + bool m_bQuitApplication = false; + std::atomic<bool> m_abPromptConnect{ true }; + std::atomic<bool> m_abConnEstablished{ false }; +};
\ No newline at end of file diff --git a/NetConsole/pch.cpp b/NetConsole/pch.cpp new file mode 100644 index 00000000..64b7eef6 --- /dev/null +++ b/NetConsole/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/NetConsole/pch.h b/NetConsole/pch.h new file mode 100644 index 00000000..bc438237 --- /dev/null +++ b/NetConsole/pch.h @@ -0,0 +1,32 @@ +#ifndef PCH_H +#define PCH_H + +#define WIN32_LEAN_AND_MEAN +#define _CRT_SECURE_NO_WARNINGS + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +// add headers that you want to pre-compile here + +#include <Windows.h> +#include <WinSock2.h> +#include <Ws2tcpip.h> +#include <Psapi.h> +#include <comdef.h> +#include <filesystem> +#include <iostream> +#include <fstream> +#include <sstream> +#include <cstring> +#include <regex> +#include <thread> +#include <set> + +#include "spdlog/spdlog.h" +#endif |