aboutsummaryrefslogtreecommitdiff
path: root/NetConsole
diff options
context:
space:
mode:
Diffstat (limited to 'NetConsole')
-rw-r--r--NetConsole/NetConsole.vcxproj122
-rw-r--r--NetConsole/NetConsole.vcxproj.filters63
-rw-r--r--NetConsole/cl_rcon.proto21
-rw-r--r--NetConsole/netconsole.cpp420
-rw-r--r--NetConsole/netconsole.h46
-rw-r--r--NetConsole/pch.cpp5
-rw-r--r--NetConsole/pch.h33
-rw-r--r--NetConsole/sv_rcon.proto21
8 files changed, 731 insertions, 0 deletions
diff --git a/NetConsole/NetConsole.vcxproj b/NetConsole/NetConsole.vcxproj
new file mode 100644
index 00000000..264973b4
--- /dev/null
+++ b/NetConsole/NetConsole.vcxproj
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <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|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|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|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <TargetName>netcon</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <TargetName>netcon</TargetName>
+ </PropertyGroup>
+ <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/cl_rcon.proto b/NetConsole/cl_rcon.proto
new file mode 100644
index 00000000..c8ba8dbf
--- /dev/null
+++ b/NetConsole/cl_rcon.proto
@@ -0,0 +1,21 @@
+syntax = "proto3";
+package cl_rcon;
+option optimize_for = LITE_RUNTIME;
+
+enum request_t
+{
+ SERVERDATA_REQUEST_VALUE = 0;
+ SERVERDATA_REQUEST_SETVALUE = 1;
+ SERVERDATA_REQUEST_EXECCOMMAND = 2;
+ SERVERDATA_REQUEST_AUTH = 3;
+ SERVERDATA_REQUEST_SEND_CONSOLE_LOG = 4;
+ SERVERDATA_REQUEST_SEND_REMOTEBUG = 5;
+}
+
+message request
+{
+ optional int32 requestID = 1;
+ optional request_t requestType = 2;
+ optional string requestBuf = 3;
+ optional string requestVal = 4;
+}
diff --git a/NetConsole/netconsole.cpp b/NetConsole/netconsole.cpp
new file mode 100644
index 00000000..4df47647
--- /dev/null
+++ b/NetConsole/netconsole.cpp
@@ -0,0 +1,420 @@
+//=====================================================================================//
+//
+// 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: destructor
+//-----------------------------------------------------------------------------
+CNetCon::~CNetCon()
+{
+ delete m_pNetAdr2;
+ delete m_pSocket;
+}
+
+//-----------------------------------------------------------------------------
+// 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 svSecondArg = svInput.substr(nPos + 1);
+ std::string svFirstArg = svInput;
+ svFirstArg = svFirstArg.erase(svFirstArg.find(" "));
+
+ if (strcmp(svFirstArg.c_str(), "PASS") == 0) // Auth with RCON server.
+ {
+ std::string svSerialized = this->Serialize(svSecondArg, "", cl_rcon::request_t::SERVERDATA_REQUEST_AUTH);
+ this->Send(svSerialized);
+ }
+ else if (strcmp(svFirstArg.c_str(), "SET") == 0) // Set value query.
+ {
+ std::string svSerialized = this->Serialize(svFirstArg, svSecondArg, cl_rcon::request_t::SERVERDATA_REQUEST_SETVALUE);
+ this->Send(svSerialized);
+ }
+ else // Execute command query.
+ {
+ std::string svSerialized = this->Serialize(svInput.c_str(), "", cl_rcon::request_t::SERVERDATA_REQUEST_EXECCOMMAND);
+ this->Send(svSerialized);
+ }
+ }
+ else // Single arg command query.
+ {
+ std::string svSerialized = this->Serialize(svInput.c_str(), "", 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..dbd6eb2c
--- /dev/null
+++ b/NetConsole/netconsole.h
@@ -0,0 +1,46 @@
+//===========================================================================//
+//
+// 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:
+ ~CNetCon();
+
+ 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..5e917f10
--- /dev/null
+++ b/NetConsole/pch.h
@@ -0,0 +1,33 @@
+#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 <chrono>
+
+#include "spdlog/spdlog.h"
+#endif
diff --git a/NetConsole/sv_rcon.proto b/NetConsole/sv_rcon.proto
new file mode 100644
index 00000000..ce2e9e35
--- /dev/null
+++ b/NetConsole/sv_rcon.proto
@@ -0,0 +1,21 @@
+syntax = "proto3";
+package sv_rcon;
+option optimize_for = LITE_RUNTIME;
+
+enum response_t
+{
+ SERVERDATA_RESPONSE_VALUE = 0;
+ SERVERDATA_RESPONSE_UPDATE = 1;
+ SERVERDATA_RESPONSE_AUTH = 2;
+ SERVERDATA_RESPONSE_CONSOLE_LOG = 3;
+ SERVERDATA_RESPONSE_STRING = 4;
+ SERVERDATA_RESPONSE_REMOTEBUG = 5;
+}
+
+message response
+{
+ optional int32 responseID = 1;
+ optional response_t responseType = 2;
+ optional string responseBuf = 3;
+ optional string responseVal = 4;
+}