From c187bed4c5902b38c527d802eab4aa5ac1f3c86c Mon Sep 17 00:00:00 2001
From: Amos <48657826+Mauler125@users.noreply.github.com>
Date: Sun, 6 Feb 2022 16:48:52 +0100
Subject: [PATCH] RCON implementation (see description)

* Fully rewritten protocol agnostic CNetAdr class
* Fully rebuilded legacy CNetAdr class
* Fully rebuilded dual-stack CSocketCreator class
* New project "netconsole" added (lightweight netconsole for RCON)

RCON is still work in progress
---
 detours.sln                      |  12 +-
 license/mathlib/CRC32.txt        |   2 +
 license/mathlib/SHA256.txt       |  36 ++
 r5dev/common/igameserverdata.h   |  48 +++
 r5dev/core/stdafx.cpp            |   7 +
 r5dev/core/stdafx.h              |  28 +-
 r5dev/dedicated.vcxproj          |  12 +
 r5dev/dedicated.vcxproj.filters  |  42 +++
 r5dev/engine/cl_rcon.cpp         |  13 +
 r5dev/engine/cl_rcon.h           |  10 +
 r5dev/engine/host_state.cpp      |  27 +-
 r5dev/engine/net.cpp             |  63 ++++
 r5dev/engine/net.h               |   3 +
 r5dev/engine/net_chan.cpp        |   2 +-
 r5dev/engine/net_chan.h          |  27 +-
 r5dev/engine/sv_rcon.cpp         | 230 ++++++++++++
 r5dev/engine/sv_rcon.h           |  32 ++
 r5dev/mathlib/sha256.cpp         | 132 +++++++
 r5dev/mathlib/sha256.h           |  52 +++
 r5dev/mathlib/swap.h             |  53 +++
 r5dev/netconsole.vcxproj         | 204 +++++++++++
 r5dev/netconsole.vcxproj.filters |  61 ++++
 r5dev/netconsole/netconsole.cpp  | 116 ++++++
 r5dev/netconsole/netconsole.h    |  19 +
 r5dev/r5dev.vcxproj              |  14 +
 r5dev/r5dev.vcxproj.filters      |  60 ++-
 r5dev/squirrel/sqinit.cpp        |  12 +-
 r5dev/tier0/basetypes.h          |  14 +
 r5dev/tier1/NetAdr.cpp           | 440 ++++++++++++++++++++++
 r5dev/tier1/NetAdr.h             |  69 ++++
 r5dev/tier1/NetAdr2.cpp          | 604 +++++++++++++++++++++++++++++++
 r5dev/tier1/NetAdr2.h            |  88 +++++
 r5dev/tier2/socketcreator.cpp    | 367 +++++++++++++++++++
 r5dev/tier2/socketcreator.h      |  78 ++++
 34 files changed, 2916 insertions(+), 61 deletions(-)
 create mode 100644 license/mathlib/CRC32.txt
 create mode 100644 license/mathlib/SHA256.txt
 create mode 100644 r5dev/common/igameserverdata.h
 create mode 100644 r5dev/engine/cl_rcon.cpp
 create mode 100644 r5dev/engine/cl_rcon.h
 create mode 100644 r5dev/engine/net.cpp
 create mode 100644 r5dev/engine/net.h
 create mode 100644 r5dev/engine/sv_rcon.cpp
 create mode 100644 r5dev/engine/sv_rcon.h
 create mode 100644 r5dev/mathlib/sha256.cpp
 create mode 100644 r5dev/mathlib/sha256.h
 create mode 100644 r5dev/mathlib/swap.h
 create mode 100644 r5dev/netconsole.vcxproj
 create mode 100644 r5dev/netconsole.vcxproj.filters
 create mode 100644 r5dev/netconsole/netconsole.cpp
 create mode 100644 r5dev/netconsole/netconsole.h
 create mode 100644 r5dev/tier1/NetAdr.cpp
 create mode 100644 r5dev/tier1/NetAdr.h
 create mode 100644 r5dev/tier1/NetAdr2.cpp
 create mode 100644 r5dev/tier1/NetAdr2.h
 create mode 100644 r5dev/tier2/socketcreator.cpp
 create mode 100644 r5dev/tier2/socketcreator.h

diff --git a/detours.sln b/detours.sln
index 4aed7e75..556de107 100644
--- a/detours.sln
+++ b/detours.sln
@@ -3,12 +3,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio Version 17
 VisualStudioVersion = 17.0.31808.319
 MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r5devdll", "r5dev\r5dev.vcxproj", "{28CC6B4F-7A95-4933-ADA9-65E38D48516D}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r5dev", "r5dev\r5dev.vcxproj", "{28CC6B4F-7A95-4933-ADA9-65E38D48516D}"
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdklauncher", "r5dev\sdklauncher.vcxproj", "{18F8C75E-3844-4AA6-AB93-980A08253519}"
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dedicated", "r5dev\dedicated.vcxproj", "{ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "netconsole", "r5dev\netconsole.vcxproj", "{9579B31F-CE24-4852-A941-CD1AD71E2248}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|x64 = Debug|x64
@@ -41,6 +43,14 @@ Global
 		{ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Release|x64.Build.0 = Release|x64
 		{ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Release|x86.ActiveCfg = Release|Win32
 		{ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Release|x86.Build.0 = Release|Win32
+		{9579B31F-CE24-4852-A941-CD1AD71E2248}.Debug|x64.ActiveCfg = Debug|x64
+		{9579B31F-CE24-4852-A941-CD1AD71E2248}.Debug|x64.Build.0 = Debug|x64
+		{9579B31F-CE24-4852-A941-CD1AD71E2248}.Debug|x86.ActiveCfg = Debug|Win32
+		{9579B31F-CE24-4852-A941-CD1AD71E2248}.Debug|x86.Build.0 = Debug|Win32
+		{9579B31F-CE24-4852-A941-CD1AD71E2248}.Release|x64.ActiveCfg = Release|x64
+		{9579B31F-CE24-4852-A941-CD1AD71E2248}.Release|x64.Build.0 = Release|x64
+		{9579B31F-CE24-4852-A941-CD1AD71E2248}.Release|x86.ActiveCfg = Release|Win32
+		{9579B31F-CE24-4852-A941-CD1AD71E2248}.Release|x86.Build.0 = Release|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/license/mathlib/CRC32.txt b/license/mathlib/CRC32.txt
new file mode 100644
index 00000000..db2dd059
--- /dev/null
+++ b/license/mathlib/CRC32.txt
@@ -0,0 +1,2 @@
+*  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+*  code or tables extracted from it, as desired without restriction.
diff --git a/license/mathlib/SHA256.txt b/license/mathlib/SHA256.txt
new file mode 100644
index 00000000..6bd905f6
--- /dev/null
+++ b/license/mathlib/SHA256.txt
@@ -0,0 +1,36 @@
+/*
+ * Updated to C++, zedwood.com 2012
+ * Based on Olivier Gay's version
+ * See Modified BSD License below: 
+ *
+ * FIPS 180-2 SHA-224/256/384/512 implementation
+ * Issue date:  04/30/2005
+ * http://www.ouah.org/ogay/sha2/
+ *
+ * Copyright (C) 2005, 2007 Olivier Gay <olivier.gay@a3.epfl.ch>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/r5dev/common/igameserverdata.h b/r5dev/common/igameserverdata.h
new file mode 100644
index 00000000..cdf8c4c2
--- /dev/null
+++ b/r5dev/common/igameserverdata.h
@@ -0,0 +1,48 @@
+//===========================================================================//
+//
+// Purpose: Enumerations for writing out the requests.
+//
+//===========================================================================//
+#pragma once
+
+enum class ServerDataRequestType_t : int
+{
+	SERVERDATA_REQUESTVALUE = 0,
+	SERVERDATA_SETVALUE,
+	SERVERDATA_EXECCOMMAND,
+	SERVERDATA_AUTH,
+	SERVERDATA_VPROF,
+	SERVERDATA_REMOVE_VPROF,
+	SERVERDATA_TAKE_SCREENSHOT,
+	SERVERDATA_SEND_CONSOLE_LOG,
+	SERVERDATA_SEND_REMOTEBUG,
+};
+
+enum class ServerDataResponseType_t : int
+{
+	SERVERDATA_RESPONSE_VALUE = 0,
+	SERVERDATA_UPDATE,
+	SERVERDATA_AUTH_RESPONSE,
+	SERVERDATA_VPROF_DATA,
+	SERVERDATA_VPROF_GROUPS,
+	SERVERDATA_SCREENSHOT_RESPONSE,
+	SERVERDATA_CONSOLE_LOG_RESPONSE,
+	SERVERDATA_RESPONSE_STRING,
+	SERVERDATA_RESPONSE_REMOTEBUG,
+};
+
+/* PACKET FORMAT **********************************
+
+REQUEST:
+  int requestID;
+  int ServerDataRequestType_t;
+  NullTerminatedString (variable or command)
+  NullTerminatedString (value)
+
+RESPONSE:
+  int requestID;
+  int ServerDataResponseType_t;
+  NullTerminatedString (variable)
+  NullTerminatedString (value)
+
+***************************************************/
diff --git a/r5dev/core/stdafx.cpp b/r5dev/core/stdafx.cpp
index 1f4c90b9..371042be 100644
--- a/r5dev/core/stdafx.cpp
+++ b/r5dev/core/stdafx.cpp
@@ -1 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// *.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
 #include "core/stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/r5dev/core/stdafx.h b/r5dev/core/stdafx.h
index 9e3cfef7..894b1a1f 100644
--- a/r5dev/core/stdafx.h
+++ b/r5dev/core/stdafx.h
@@ -21,26 +21,26 @@
 #include <cassert>
 #include <filesystem>
 
-#if !defined(DEDICATED)
+#if !defined(DEDICATED) && !defined(SDKLAUNCHER) && !defined (NETCONSOLE)
 #include <d3d11.h>
-#endif // !DEDICATED
+#endif // !DEDICATED && !SDKLAUNCHER && !NETCONSOLE
 
 #include "thirdparty/detours/include/detours.h"
 #include "thirdparty/detours/include/idetour.h"
 
-#if !defined(DEDICATED)
+#if !defined(DEDICATED) && !defined(SDKLAUNCHER) && !defined (NETCONSOLE)
 #include "thirdparty/imgui/include/imgui.h"
 #include "thirdparty/imgui/include/imgui_stdlib.h"
 #include "thirdparty/imgui/include/imgui_utility.h"
 #include "thirdparty/imgui/include/imgui_internal.h"
 #include "thirdparty/imgui/include/imgui_impl_dx11.h"
 #include "thirdparty/imgui/include/imgui_impl_win32.h"
-#endif // !DEDICATED
+#endif // !DEDICATED && !SDKLAUNCHER && !NETCONSOLE
 
-#if !defined(SDKLAUNCHER)
+#if !defined(SDKLAUNCHER) && !defined (NETCONSOLE)
 #include "thirdparty/lzham/include/lzham_types.h"
 #include "thirdparty/lzham/include/lzham.h"
-#endif // !SDKLAUNCHER
+#endif // !SDKLAUNCHER && !NETCONSOLE
 
 #include "thirdparty/spdlog/include/spdlog.h"
 #include "thirdparty/spdlog/include/async.h"
@@ -61,24 +61,23 @@
 #include "common/pseudodefs.h"
 #include "tier0/basetypes.h"
 
-#if !defined (SDKLAUNCHER)
+#if !defined(SDKLAUNCHER) && !defined (NETCONSOLE)
 namespace
 {
 #if !defined (DEDICATED)
 	MODULE g_mGameDll = MODULE("r5apex.exe");
-#else
+#else // No DX imports.
 	MODULE g_mGameDll = MODULE("r5apex_ds.exe");
 #endif // !DEDICATED
 	MODULE g_mRadVideoToolsDll   = MODULE("bink2w64.dll");
 	MODULE g_mRadAudioDecoderDll = MODULE("binkawin64.dll");
 	MODULE g_mRadAudioSystemDll  = MODULE("mileswin64.dll");
 }
-#endif // !SDKLAUNCHER
 
-#define MEMBER_AT_OFFSET(varType, varName, offset) \
-	varType& varName() \
-    { \
-		static int _##varName = offset; \
+#define MEMBER_AT_OFFSET(varType, varName, offset)             \
+	varType& varName()                                         \
+	{                                                          \
+		static int _##varName = offset;                        \
 		return *(varType*)((std::uintptr_t)this + _##varName); \
 	}
 
@@ -86,4 +85,5 @@ template <typename ReturnType, typename ...Args>
 ReturnType CallVFunc(int index, void* thisPtr, Args... args)
 {
 	return (*reinterpret_cast<ReturnType(__fastcall***)(void*, Args...)>(thisPtr))[index](thisPtr, args...);
-}
\ No newline at end of file
+}
+#endif // !SDKLAUNCHER && !NETCONSOLE
\ No newline at end of file
diff --git a/r5dev/dedicated.vcxproj b/r5dev/dedicated.vcxproj
index 65b655e1..b28f8f68 100644
--- a/r5dev/dedicated.vcxproj
+++ b/r5dev/dedicated.vcxproj
@@ -172,6 +172,7 @@
     <ClInclude Include="bsplib\bsplib.h" />
     <ClInclude Include="client\client.h" />
     <ClInclude Include="client\IVEngineClient.h" />
+    <ClInclude Include="common\igameserverdata.h" />
     <ClInclude Include="common\opcodes.h" />
     <ClInclude Include="common\protocol.h" />
     <ClInclude Include="common\psuedodefs.h" />
@@ -185,8 +186,10 @@
     <ClInclude Include="engine\baseclient.h" />
     <ClInclude Include="engine\host_cmd.h" />
     <ClInclude Include="engine\host_state.h" />
+    <ClInclude Include="engine\net.h" />
     <ClInclude Include="engine\net_chan.h" />
     <ClInclude Include="engine\sv_main.h" />
+    <ClInclude Include="engine\sv_rcon.h" />
     <ClInclude Include="engine\sys_dll.h" />
     <ClInclude Include="engine\sys_dll2.h" />
     <ClInclude Include="engine\sys_engine.h" />
@@ -198,6 +201,8 @@
     <ClInclude Include="mathlib\crc32.h" />
     <ClInclude Include="mathlib\IceKey.H" />
     <ClInclude Include="mathlib\parallel_for.h" />
+    <ClInclude Include="mathlib\sha256.h" />
+    <ClInclude Include="mathlib\swap.h" />
     <ClInclude Include="mathlib\vector.h" />
     <ClInclude Include="networksystem\r5net.h" />
     <ClInclude Include="networksystem\serverlisting.h" />
@@ -350,6 +355,8 @@
     <ClInclude Include="tier0\cvar.h" />
     <ClInclude Include="tier0\IConVar.h" />
     <ClInclude Include="tier0\interface.h" />
+    <ClInclude Include="tier1\NetAdr2.h" />
+    <ClInclude Include="tier2\socketcreator.h" />
     <ClInclude Include="vpc\basefilesystem.h" />
     <ClInclude Include="vpc\IAppSystem.h" />
     <ClInclude Include="vpc\interfaces.h" />
@@ -376,8 +383,10 @@
     <ClCompile Include="engine\baseclient.cpp" />
     <ClCompile Include="engine\host_cmd.cpp" />
     <ClCompile Include="engine\host_state.cpp" />
+    <ClCompile Include="engine\net.cpp" />
     <ClCompile Include="engine\net_chan.cpp" />
     <ClCompile Include="engine\sv_main.cpp" />
+    <ClCompile Include="engine\sv_rcon.cpp" />
     <ClCompile Include="engine\sys_dll.cpp" />
     <ClCompile Include="engine\sys_dll2.cpp" />
     <ClCompile Include="engine\sys_engine.cpp" />
@@ -387,6 +396,7 @@
     <ClCompile Include="mathlib\bits.cpp" />
     <ClCompile Include="mathlib\crc32.cpp" />
     <ClCompile Include="mathlib\IceKey.cpp" />
+    <ClCompile Include="mathlib\sha256.cpp" />
     <ClCompile Include="networksystem\r5net.cpp" />
     <ClCompile Include="public\bansystem.cpp" />
     <ClCompile Include="public\binstream.cpp" />
@@ -500,6 +510,8 @@
     <ClCompile Include="tier0\completion.cpp" />
     <ClCompile Include="tier0\cvar.cpp" />
     <ClCompile Include="tier0\IConVar.cpp" />
+    <ClCompile Include="tier1\NetAdr2.cpp" />
+    <ClCompile Include="tier2\socketcreator.cpp" />
     <ClCompile Include="vpc\basefilesystem.cpp" />
     <ClCompile Include="vpc\IAppSystem.cpp" />
     <ClCompile Include="vpc\interfaces.cpp" />
diff --git a/r5dev/dedicated.vcxproj.filters b/r5dev/dedicated.vcxproj.filters
index d29af520..c8249ec6 100644
--- a/r5dev/dedicated.vcxproj.filters
+++ b/r5dev/dedicated.vcxproj.filters
@@ -109,6 +109,12 @@
     <Filter Include="thirdparty\lzham\lzhamdecomp\include">
       <UniqueIdentifier>{463e0739-1e5f-47a0-94d1-6cf5b6bf3ea6}</UniqueIdentifier>
     </Filter>
+    <Filter Include="sdk\tier1">
+      <UniqueIdentifier>{da2c5c3d-eff4-404f-af3f-e30ec17dcc1a}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="sdk\tier2">
+      <UniqueIdentifier>{efae8c5b-e29e-497f-8bbb-af3b213f6c79}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="client\client.h">
@@ -678,6 +684,27 @@
     <ClInclude Include="engine\sv_main.h">
       <Filter>sdk\engine</Filter>
     </ClInclude>
+    <ClInclude Include="engine\sv_rcon.h">
+      <Filter>sdk\engine</Filter>
+    </ClInclude>
+    <ClInclude Include="tier2\socketcreator.h">
+      <Filter>sdk\tier2</Filter>
+    </ClInclude>
+    <ClInclude Include="mathlib\swap.h">
+      <Filter>sdk\mathlib</Filter>
+    </ClInclude>
+    <ClInclude Include="tier1\NetAdr2.h">
+      <Filter>sdk\tier1</Filter>
+    </ClInclude>
+    <ClInclude Include="mathlib\sha256.h">
+      <Filter>sdk\mathlib</Filter>
+    </ClInclude>
+    <ClInclude Include="common\igameserverdata.h">
+      <Filter>sdk\common</Filter>
+    </ClInclude>
+    <ClInclude Include="engine\net.h">
+      <Filter>sdk\engine</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="client\IVEngineClient.cpp">
@@ -899,6 +926,21 @@
     <ClCompile Include="engine\sv_main.cpp">
       <Filter>sdk\engine</Filter>
     </ClCompile>
+    <ClCompile Include="engine\sv_rcon.cpp">
+      <Filter>sdk\engine</Filter>
+    </ClCompile>
+    <ClCompile Include="tier2\socketcreator.cpp">
+      <Filter>sdk\tier2</Filter>
+    </ClCompile>
+    <ClCompile Include="tier1\NetAdr2.cpp">
+      <Filter>sdk\tier1</Filter>
+    </ClCompile>
+    <ClCompile Include="mathlib\sha256.cpp">
+      <Filter>sdk\mathlib</Filter>
+    </ClCompile>
+    <ClCompile Include="engine\net.cpp">
+      <Filter>sdk\engine</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <None Include="r5dev.def" />
diff --git a/r5dev/engine/cl_rcon.cpp b/r5dev/engine/cl_rcon.cpp
new file mode 100644
index 00000000..83077ef7
--- /dev/null
+++ b/r5dev/engine/cl_rcon.cpp
@@ -0,0 +1,13 @@
+//===========================================================================//
+//
+// Purpose: Implementation of the rcon client
+//
+//===========================================================================//
+
+#include "core/stdafx.h"
+#include "tier0/IConVar.h"
+#include "tier0/ConCommand.h"
+#include "engine/cl_rcon.h"
+#include "common/igameserverdata.h"
+
+// TODO..
\ No newline at end of file
diff --git a/r5dev/engine/cl_rcon.h b/r5dev/engine/cl_rcon.h
new file mode 100644
index 00000000..ff77590b
--- /dev/null
+++ b/r5dev/engine/cl_rcon.h
@@ -0,0 +1,10 @@
+#pragma once
+
+class CRConClient
+{
+	CRConClient(void){};
+	~CRConClient(void){};
+
+	void Authenticate(void){};
+	void ProcessMessage(void){};
+};
\ No newline at end of file
diff --git a/r5dev/engine/host_state.cpp b/r5dev/engine/host_state.cpp
index c9ae6ee9..5a4faee8 100644
--- a/r5dev/engine/host_state.cpp
+++ b/r5dev/engine/host_state.cpp
@@ -1,6 +1,14 @@
+//=============================================================================//
+//
+// Purpose: Runs the state machine for the host & server
+//
+//=============================================================================//
+
 #include "core/stdafx.h"
 #include "tier0/cvar.h"
 #include "tier0/commandline.h"
+#include "tier1/NetAdr2.h"
+#include "tier2/socketcreator.h"
 #include "engine/sys_utils.h"
 #include "engine/host_state.h"
 #include "engine/net_chan.h"
@@ -9,6 +17,7 @@
 #include "squirrel/sqinit.h"
 #include "public/include/bansystem.h"
 #include "engine/sys_engine.h"
+#include "engine/sv_rcon.h"
 
 //-----------------------------------------------------------------------------
 // Purpose: Send keep alive request to Pylon Master Server.
@@ -29,10 +38,6 @@ void KeepAliveToPylon()
 				g_pCVar->FindVar("hostport")->GetString(),
 				g_pCVar->FindVar("mp_gamemode")->GetString(),
 				false,
-
-				// BUG BUG: Checksum is null on dedi
-				// ADDITIONAL NOTES: seems to be related to scripts, this also happens when the listen server is started but the client from the same process never connects.
-				// Checksum only gets set on the server if the client from its own process connects to it.
 				std::to_string(*g_nServerRemoteChecksum),
 				std::string(),
 				g_szNetKey.c_str()
@@ -41,7 +46,6 @@ void KeepAliveToPylon()
 	}
 }
 
-
 //-----------------------------------------------------------------------------
 // Purpose: Check refuse list and kill netchan connection.
 //-----------------------------------------------------------------------------
@@ -130,25 +134,32 @@ void HCHostState_FrameUpdate(void* rcx, void* rdx, float time)
 	static bool bInitialized = false;
 	if (!bInitialized)
 	{
-		g_pConVar->ClearHostNames();
+		g_pRConServer = new CRConServer();
 
 		if (!g_pCmdLine->CheckParm("-devsdk"))
 		{
 			IVEngineClient_CommandExecute(NULL, "exec autoexec_server.cfg");
+			IVEngineClient_CommandExecute(NULL, "exec rcon_server.cfg");
 #ifndef DEDICATED
 			IVEngineClient_CommandExecute(NULL, "exec autoexec_client.cfg");
+			IVEngineClient_CommandExecute(NULL, "exec rcon_client.cfg");
 #endif // !DEDICATED
 			IVEngineClient_CommandExecute(NULL, "exec autoexec.cfg");
 		}
 		else // Development configs.
 		{
 			IVEngineClient_CommandExecute(NULL, "exec autoexec_server_dev.cfg");
+			IVEngineClient_CommandExecute(NULL, "exec rcon_server_dev.cfg");
 #ifndef DEDICATED
 			IVEngineClient_CommandExecute(NULL, "exec autoexec_client_dev.cfg");
+			IVEngineClient_CommandExecute(NULL, "exec rcon_client_dev.cfg");
 #endif // !DEDICATED
 			IVEngineClient_CommandExecute(NULL, "exec autoexec_dev.cfg");
 		}
 
+		g_pConVar->ClearHostNames();
+		g_pRConServer->Init();
+
 		*(bool*)m_bRestrictServerCommands = true; // Restrict commands.
 		ConCommandBase* disconnect = (ConCommandBase*)g_pCVar->FindCommand("disconnect");
 		disconnect->AddFlags(FCVAR_SERVER_CAN_EXECUTE); // Make sure server is not restricted to this.
@@ -181,6 +192,8 @@ void HCHostState_FrameUpdate(void* rcx, void* rdx, float time)
 		bInitialized = true;
 	}
 
+	g_pRConServer->RunFrame();
+
 	HostStates_t oldState{};
 	void* placeHolder = nullptr;
 	if (setjmpFn(*host_abortserver, placeHolder))
@@ -348,7 +361,7 @@ void HCHostState_FrameUpdate(void* rcx, void* rdx, float time)
 			}
 			}
 
-		} while ((oldState != HostStates_t::HS_RUN || g_pHostState->m_iNextState == HostStates_t::HS_LOAD_GAME && g_pCVar->FindVar("g_single_frame_shutdown_for_reload_cvar")->GetBool())
+		} while ((oldState != HostStates_t::HS_RUN || g_pHostState->m_iNextState == HostStates_t::HS_LOAD_GAME && g_pCVar->FindVar("single_frame_shutdown_for_reload")->GetBool())
 			&& oldState != HostStates_t::HS_SHUTDOWN
 			&& oldState != HostStates_t::HS_RESTART);
 
diff --git a/r5dev/engine/net.cpp b/r5dev/engine/net.cpp
new file mode 100644
index 00000000..3f66e06e
--- /dev/null
+++ b/r5dev/engine/net.cpp
@@ -0,0 +1,63 @@
+//=============================================================================//
+//
+// Purpose: Net system utilities
+//
+//=============================================================================//
+
+#include "core/stdafx.h"
+#include "engine/net.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the WSA error code
+//-----------------------------------------------------------------------------
+const char* NET_ErrorString(int iCode)
+{
+	switch (iCode)
+	{
+		case WSAEINTR          : return "WSAEINTR";
+		case WSAEBADF          : return "WSAEBADF";
+		case WSAEACCES         : return "WSAEACCES";
+		case WSAEDISCON        : return "WSAEDISCON";
+		case WSAEFAULT         : return "WSAEFAULT";
+		case WSAEINVAL         : return "WSAEINVAL";
+		case WSAEMFILE         : return "WSAEMFILE";
+		case WSAEWOULDBLOCK    : return "WSAEWOULDBLOCK";
+		case WSAEINPROGRESS    : return "WSAEINPROGRESS";
+		case WSAEALREADY       : return "WSAEALREADY";
+		case WSAENOTSOCK       : return "WSAENOTSOCK";
+		case WSAEDESTADDRREQ   : return "WSAEDESTADDRREQ";
+		case WSAEMSGSIZE       : return "WSAEMSGSIZE";
+		case WSAEPROTOTYPE     : return "WSAEPROTOTYPE";
+		case WSAENOPROTOOPT    : return "WSAENOPROTOOPT";
+		case WSAEPROTONOSUPPORT: return "WSAEPROTONOSUPPORT";
+		case WSAESOCKTNOSUPPORT: return "WSAESOCKTNOSUPPORT";
+		case WSAEOPNOTSUPP     : return "WSAEOPNOTSUPP";
+		case WSAEPFNOSUPPORT   : return "WSAEPFNOSUPPORT";
+		case WSAEAFNOSUPPORT   : return "WSAEAFNOSUPPORT";
+		case WSAEADDRINUSE     : return "WSAEADDRINUSE";
+		case WSAEADDRNOTAVAIL  : return "WSAEADDRNOTAVAIL";
+		case WSAENETDOWN       : return "WSAENETDOWN";
+		case WSAENETUNREACH    : return "WSAENETUNREACH";
+		case WSAENETRESET      : return "WSAENETRESET";
+		case WSAECONNABORTED   : return "WSWSAECONNABORTEDAEINTR";
+		case WSAECONNRESET     : return "WSAECONNRESET";
+		case WSAENOBUFS        : return "WSAENOBUFS";
+		case WSAEISCONN        : return "WSAEISCONN";
+		case WSAENOTCONN       : return "WSAENOTCONN";
+		case WSAESHUTDOWN      : return "WSAESHUTDOWN";
+		case WSAETOOMANYREFS   : return "WSAETOOMANYREFS";
+		case WSAETIMEDOUT      : return "WSAETIMEDOUT";
+		case WSAECONNREFUSED   : return "WSAECONNREFUSED";
+		case WSAELOOP          : return "WSAELOOP";
+		case WSAENAMETOOLONG   : return "WSAENAMETOOLONG";
+		case WSAEHOSTDOWN      : return "WSAEHOSTDOWN";
+		case WSASYSNOTREADY    : return "WSASYSNOTREADY";
+		case WSAVERNOTSUPPORTED: return "WSAVERNOTSUPPORTED";
+		case WSANOTINITIALISED : return "WSANOTINITIALISED";
+		case WSAHOST_NOT_FOUND : return "WSAHOST_NOT_FOUND";
+		case WSATRY_AGAIN      : return "WSATRY_AGAIN";
+		case WSANO_RECOVERY    : return "WSANO_RECOVERY";
+		case WSANO_DATA        : return "WSANO_DATA";
+	default                    : return "UNKNOWN ERROR";
+	}
+}
diff --git a/r5dev/engine/net.h b/r5dev/engine/net.h
new file mode 100644
index 00000000..20627996
--- /dev/null
+++ b/r5dev/engine/net.h
@@ -0,0 +1,3 @@
+#pragma once
+
+const char* NET_ErrorString(int iCode);
diff --git a/r5dev/engine/net_chan.cpp b/r5dev/engine/net_chan.cpp
index f0c1dc95..40423fd6 100644
--- a/r5dev/engine/net_chan.cpp
+++ b/r5dev/engine/net_chan.cpp
@@ -124,7 +124,7 @@ void HNET_PrintFunc(const char* fmt, ...)
 	buf[sizeof(buf) -1] = 0;
 	va_end(args);
 
-	DevMsg(eDLL_T::CLIENT, "%s\n", buf);
+	DevMsg(eDLL_T::CLIENT, "%s", buf);
 }
 
 //-----------------------------------------------------------------------------
diff --git a/r5dev/engine/net_chan.h b/r5dev/engine/net_chan.h
index 8bbd526c..b668d620 100644
--- a/r5dev/engine/net_chan.h
+++ b/r5dev/engine/net_chan.h
@@ -1,32 +1,7 @@
 #pragma once
-#include "core/stdafx.h"
 #include "common/protocol.h"
 #include "client/client.h"
-
-typedef struct netpacket_s netpacket_t;
-typedef struct __declspec(align(8)) netpacket_s
-{
-	DWORD         family_maybe;
-	sockaddr_in   sin;
-	WORD          sin_port;
-	char          gap16;
-	char          byte17;
-	DWORD         source;
-	double        received;
-	std::uint8_t* data;
-	std::uint64_t label;
-	BYTE          byte38;
-	std::uint64_t qword40;
-	std::uint64_t qword48;
-	BYTE          gap50[8];
-	std::uint64_t qword58;
-	std::uint64_t qword60;
-	std::uint64_t qword68;
-	int           less_than_12;
-	DWORD         wiresize;
-	BYTE          gap78[8];
-	struct netpacket_s *pNext;
-} netpacket_t;
+#include "tier1/NetAdr2.h"
 
 namespace
 {
diff --git a/r5dev/engine/sv_rcon.cpp b/r5dev/engine/sv_rcon.cpp
new file mode 100644
index 00000000..9acc2f13
--- /dev/null
+++ b/r5dev/engine/sv_rcon.cpp
@@ -0,0 +1,230 @@
+//===========================================================================//
+//
+// Purpose: Implementation of the rcon server
+//
+//===========================================================================//
+
+#include "core/stdafx.h"
+#include "tier0/cvar.h"
+#include "tier0/IConVar.h"
+#include "tier0/ConCommand.h"
+#include "tier1/NetAdr2.h"
+#include "tier2/socketcreator.h"
+#include "engine/sys_utils.h"
+#include "engine/sv_rcon.h"
+#include "mathlib/sha256.h"
+#include "client/IVEngineClient.h"
+#include "common/igameserverdata.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Create's listen socket for RCON
+//-----------------------------------------------------------------------------
+void CRConServer::Init(void)
+{
+	if (std::strlen(rcon_password->GetString()) < 8)
+	{
+		DevMsg(eDLL_T::SERVER, "RCON disabled\n");
+
+		m_bInitialized = false;
+		return;
+	}
+
+	static ConVar* hostport = g_pCVar->FindVar("hostport");
+	m_pAdr2 = new CNetAdr2(rcon_address->GetString(), hostport->GetString());
+	m_pSocket->CreateListenSocket(*m_pAdr2, false);
+
+	m_bInitialized = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: run tasks for RCON
+//-----------------------------------------------------------------------------
+void CRConServer::Think(void)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: server RCON main loop (run this every frame)
+//-----------------------------------------------------------------------------
+void CRConServer::RunFrame(void)
+{
+	if (m_bInitialized)
+	{
+		m_pSocket->RunFrame();
+		ProcessMessage();
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: process incoming packet
+//-----------------------------------------------------------------------------
+void CRConServer::ProcessMessage(void)
+{
+	int nCount = m_pSocket->GetAcceptedSocketCount();
+
+	for (int i = nCount - 1; i >= 0; i--)
+	{
+		CConnectedNetConsoleData* pData = m_pSocket->GetAcceptedSocketData(i);
+
+		{//////////////////////////////////////////////
+			if (CheckForBan(i, pData))
+			{
+				send(pData->m_hSocket, s_pszBannedMessage, strlen(s_pszBannedMessage), MSG_NOSIGNAL);
+				CloseConnection(i);
+				continue;
+			}
+
+			char szRecvBuf{};
+			int nPendingLen = recv(pData->m_hSocket, &szRecvBuf, sizeof(szRecvBuf), MSG_PEEK);
+
+			if (nPendingLen == SOCKET_ERROR && m_pSocket->IsSocketBlocking())
+			{
+				continue;
+			}
+
+			if (nPendingLen <= 0) // EOF or error.
+			{
+				CloseConnection(i);
+				continue;
+			}
+		}//////////////////////////////////////////////
+
+		u_long nReadLen; // Find out how much we have to read.
+		ioctlsocket(pData->m_hSocket, FIONREAD, &nReadLen);
+
+		while (nReadLen > 0 && nReadLen < MAX_NETCONSOLE_INPUT_LEN -1)
+		{
+			char szRecvBuf[MAX_NETCONSOLE_INPUT_LEN]{};
+			int nRecvLen = ::recv(pData->m_hSocket, szRecvBuf, MIN(sizeof(szRecvBuf), nReadLen), 0);
+
+			if (nRecvLen == 0) // Socket was closed.
+			{
+				CloseConnection(i);
+				break;
+			}
+
+			if (nRecvLen < 0 && !m_pSocket->IsSocketBlocking())
+			{
+				break;
+			}
+
+			nReadLen -= nRecvLen;
+
+			// Write what we've got into the command buffer.
+			HandleInputChars(szRecvBuf, nRecvLen, pData);
+		}
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: authenticate new connections
+// TODO   : implement logic for key exchange instead so we never network our
+// password in plain text over the wire. create a cvar for this so user could
+// also opt out and use legacy authentication instead for legacy RCON clients
+//-----------------------------------------------------------------------------
+void CRConServer::Authenticate(CConnectedNetConsoleData* pData)
+{
+	if (pData->m_bAuthorized)
+	{
+		return;
+	}
+	else if (std::memcmp(pData->m_pszInputCommandBuffer, "PASS ", 5) == 0)
+	{
+		if (std::strcmp(pData->m_pszInputCommandBuffer + 5, rcon_password->GetString()) == 0)
+		{ // TODO: Hash password instead!
+			pData->m_bAuthorized = true;
+		}
+		else // Bad password.
+		{
+			DevMsg(eDLL_T::SERVER, "Bad password attempt from net console\n");
+			::send(pData->m_hSocket, s_pszWrongPwMessage, strlen(s_pszWrongPwMessage), MSG_NOSIGNAL);
+
+			pData->m_bAuthorized = false;
+			pData->m_nFailedAttempts++;
+		}
+	}
+	else
+	{
+		::send(pData->m_hSocket, s_pszNoAuthMessage, strlen(s_pszNoAuthMessage), MSG_NOSIGNAL);
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: handles input command buffer
+//-----------------------------------------------------------------------------
+void CRConServer::HandleInputChars(const char* pszIn, int nRecvLen, CConnectedNetConsoleData* pData)
+{
+	while (nRecvLen)
+	{
+		switch (*pszIn)
+		{
+		case '\r':
+		case '\n':
+		{
+			if (pData->m_nCharsInCommandBuffer)
+			{
+				pData->m_pszInputCommandBuffer[pData->m_nCharsInCommandBuffer] = 0;
+				Authenticate(pData);
+
+				if (pData->m_bAuthorized)
+				{
+					Execute(pData);
+				}
+			}
+			pData->m_nCharsInCommandBuffer = 0;
+			break;
+		}
+		default:
+		{
+			if (pData->m_nCharsInCommandBuffer < MAX_NETCONSOLE_INPUT_LEN - 1)
+			{
+				pData->m_pszInputCommandBuffer[pData->m_nCharsInCommandBuffer++] = *pszIn;
+			}
+			break;
+		}
+		}
+		pszIn++;
+		nRecvLen--;
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: 
+//-----------------------------------------------------------------------------
+void CRConServer::Execute(CConnectedNetConsoleData* pData)
+{
+	IVEngineClient_CommandExecute(NULL, pData->m_pszInputCommandBuffer);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: checks for amount of failed attempts and bans netconsole accordingly
+//-----------------------------------------------------------------------------
+bool CRConServer::CheckForBan(int nIdx, CConnectedNetConsoleData* pData)
+{
+	CNetAdr2 netAdr2 = m_pSocket->GetAcceptedSocketAddress(nIdx);
+
+	// Check if IP is in the ban vector.
+	if (std::find(m_vBannedAddress.begin(), m_vBannedAddress.end(), 
+		netAdr2.GetIP(true)) != m_vBannedAddress.end())
+	{
+		return true;
+	}
+
+	// Check if netconsole has reached maximum number of attempts and add to ban vector.
+	if (pData->m_nFailedAttempts >= sv_rcon_maxfailures->GetInt())
+	{
+		m_vBannedAddress.push_back(netAdr2.GetIP(true));
+		return true;
+	}
+	return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: 
+//-----------------------------------------------------------------------------
+void CRConServer::CloseConnection(int nIdx) // NETMGR
+{
+	m_pSocket->CloseAcceptedSocket(nIdx);
+}
+
+CRConServer* g_pRConServer = nullptr;
diff --git a/r5dev/engine/sv_rcon.h b/r5dev/engine/sv_rcon.h
new file mode 100644
index 00000000..144f2ab0
--- /dev/null
+++ b/r5dev/engine/sv_rcon.h
@@ -0,0 +1,32 @@
+#pragma once
+#include "tier1/NetAdr2.h"
+
+constexpr char s_pszNoAuthMessage[]  = "This server is password protected for console access. Must send 'PASS <password>' command.\n\r";
+constexpr char s_pszWrongPwMessage[] = "Password incorrect.\n\r";
+constexpr char s_pszBannedMessage[]  = "Go away.\n\r";
+
+class CRConServer
+{
+public:
+	void Init(void);
+	void Think(void);
+
+	void RunFrame(void);
+
+	void ProcessMessage(void);
+	void Authenticate(CConnectedNetConsoleData* pData);
+
+	void HandleInputChars(const char* pIn, int recvLen, CConnectedNetConsoleData* pData);
+	void Execute(CConnectedNetConsoleData* pData);
+
+	bool CheckForBan(int nIdx, CConnectedNetConsoleData* pData);
+	void CloseConnection(int nIdx);
+
+private:
+
+	bool                     m_bInitialized = false;
+	CNetAdr2*                m_pAdr2        = new CNetAdr2();
+	CSocketCreator*          m_pSocket      = new CSocketCreator();
+	std::vector<std::string> m_vBannedAddress;
+};
+extern CRConServer* g_pRConServer;
\ No newline at end of file
diff --git a/r5dev/mathlib/sha256.cpp b/r5dev/mathlib/sha256.cpp
new file mode 100644
index 00000000..9b4cd768
--- /dev/null
+++ b/r5dev/mathlib/sha256.cpp
@@ -0,0 +1,132 @@
+#include "core/stdafx.h"
+#include "mathlib/sha256.h"
+ 
+const unsigned int SHA256::sha256_k[64] = //UL = uint32
+            {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+             0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+             0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+             0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+             0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+             0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+             0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+             0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+             0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+             0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+             0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+             0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+             0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+             0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+             0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+             0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
+ 
+void SHA256::transform(const unsigned char *message, unsigned int block_nb)
+{
+    uint32 w[64]{};
+    uint32 wv[8]{};
+    uint32 t1, t2;
+    const unsigned char *sub_block;
+    int i;
+    int j;
+    for (i = 0; i < (int) block_nb; i++) {
+        sub_block = message + (i << 6);
+        for (j = 0; j < 16; j++) {
+            SHA2_PACK32(&sub_block[j << 2], &w[j]);
+        }
+        for (j = 16; j < 64; j++) {
+            w[j] =  SHA256_F4(w[j -  2]) + w[j -  7] + SHA256_F3(w[j - 15]) + w[j - 16];
+        }
+        for (j = 0; j < 8; j++) {
+            wv[j] = m_h[j];
+        }
+        for (j = 0; j < 64; j++) {
+            t1 = wv[7] + SHA256_F2(wv[4]) + SHA2_CH(wv[4], wv[5], wv[6])
+                + sha256_k[j] + w[j];
+            t2 = SHA256_F1(wv[0]) + SHA2_MAJ(wv[0], wv[1], wv[2]);
+            wv[7] = wv[6];
+            wv[6] = wv[5];
+            wv[5] = wv[4];
+            wv[4] = wv[3] + t1;
+            wv[3] = wv[2];
+            wv[2] = wv[1];
+            wv[1] = wv[0];
+            wv[0] = t1 + t2;
+        }
+        for (j = 0; j < 8; j++) {
+            m_h[j] += wv[j];
+        }
+    }
+}
+ 
+void SHA256::init()
+{
+    m_h[0] = 0x6a09e667;
+    m_h[1] = 0xbb67ae85;
+    m_h[2] = 0x3c6ef372;
+    m_h[3] = 0xa54ff53a;
+    m_h[4] = 0x510e527f;
+    m_h[5] = 0x9b05688c;
+    m_h[6] = 0x1f83d9ab;
+    m_h[7] = 0x5be0cd19;
+    m_len = 0;
+    m_tot_len = 0;
+}
+ 
+void SHA256::update(const unsigned char *message, unsigned int len)
+{
+    unsigned int block_nb;
+    unsigned int new_len, rem_len, tmp_len;
+    const unsigned char *shifted_message;
+    tmp_len = SHA224_256_BLOCK_SIZE - m_len;
+    rem_len = len < tmp_len ? len : tmp_len;
+    memcpy(&m_block[m_len], message, rem_len);
+    if (m_len + len < SHA224_256_BLOCK_SIZE) {
+        m_len += len;
+        return;
+    }
+    new_len = len - rem_len;
+    block_nb = new_len / SHA224_256_BLOCK_SIZE;
+    shifted_message = message + rem_len;
+    transform(m_block, 1);
+    transform(shifted_message, block_nb);
+    rem_len = new_len % SHA224_256_BLOCK_SIZE;
+    memcpy(m_block, &shifted_message[block_nb << 6], rem_len);
+    m_len = rem_len;
+    m_tot_len += (block_nb + 1) << 6;
+}
+ 
+void SHA256::final(unsigned char *digest)
+{
+    unsigned int block_nb;
+    unsigned int pm_len;
+    unsigned int len_b;
+    int i;
+    block_nb = (1 + ((SHA224_256_BLOCK_SIZE - 9)
+                     < (m_len % SHA224_256_BLOCK_SIZE)));
+    len_b = (m_tot_len + m_len) << 3;
+    pm_len = block_nb << 6;
+    memset(m_block + m_len, 0, pm_len - m_len);
+    m_block[m_len] = 0x80;
+    SHA2_UNPACK32(len_b, m_block + pm_len - 4);
+    transform(m_block, block_nb);
+    for (i = 0 ; i < 8; i++) {
+        SHA2_UNPACK32(m_h[i], &digest[i << 2]);
+    }
+}
+ 
+std::string sha256(std::string input)
+{
+    unsigned char digest[SHA256::DIGEST_SIZE];
+    memset(digest,0,SHA256::DIGEST_SIZE);
+ 
+    SHA256 ctx = SHA256();
+    ctx.init();
+    ctx.update( (unsigned char*)input.c_str(), input.length());
+    ctx.final(digest);
+ 
+    char buf[2*SHA256::DIGEST_SIZE+1]{};
+    buf[2*SHA256::DIGEST_SIZE] = 0;
+    for (int i = 0; i < SHA256::DIGEST_SIZE; i++) {
+        sprintf_s(buf + i * 2, sizeof(buf), "%02x", digest[i]);
+    }
+    return std::string(buf);
+}
diff --git a/r5dev/mathlib/sha256.h b/r5dev/mathlib/sha256.h
new file mode 100644
index 00000000..b6ef8bc1
--- /dev/null
+++ b/r5dev/mathlib/sha256.h
@@ -0,0 +1,52 @@
+#ifndef SHA256_H
+#define SHA256_H
+ 
+class SHA256
+{
+protected:
+    typedef unsigned char uint8;
+    typedef unsigned int uint32;
+    typedef unsigned long long uint64;
+ 
+    const static uint32 sha256_k[];
+    static const unsigned int SHA224_256_BLOCK_SIZE = (512/8);
+public:
+    void init();
+    void update(const unsigned char *message, unsigned int len);
+    void final(unsigned char *digest);
+    static const unsigned int DIGEST_SIZE = ( 256 / 8);
+ 
+protected:
+    void transform(const unsigned char *message, unsigned int block_nb);
+    unsigned int m_tot_len;
+    unsigned int m_len;
+    unsigned char m_block[2*SHA224_256_BLOCK_SIZE];
+    uint32 m_h[8];
+};
+ 
+std::string sha256(std::string input);
+ 
+#define SHA2_SHFR(x, n)    (x >> n)
+#define SHA2_ROTR(x, n)   ((x >> n) | (x << ((sizeof(x) << 3) - n)))
+#define SHA2_ROTL(x, n)   ((x << n) | (x >> ((sizeof(x) << 3) - n)))
+#define SHA2_CH(x, y, z)  ((x & y) ^ (~x & z))
+#define SHA2_MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+#define SHA256_F1(x) (SHA2_ROTR(x,  2) ^ SHA2_ROTR(x, 13) ^ SHA2_ROTR(x, 22))
+#define SHA256_F2(x) (SHA2_ROTR(x,  6) ^ SHA2_ROTR(x, 11) ^ SHA2_ROTR(x, 25))
+#define SHA256_F3(x) (SHA2_ROTR(x,  7) ^ SHA2_ROTR(x, 18) ^ SHA2_SHFR(x,  3))
+#define SHA256_F4(x) (SHA2_ROTR(x, 17) ^ SHA2_ROTR(x, 19) ^ SHA2_SHFR(x, 10))
+#define SHA2_UNPACK32(x, str)                 \
+{                                             \
+    *((str) + 3) = (uint8) ((x)      );       \
+    *((str) + 2) = (uint8) ((x) >>  8);       \
+    *((str) + 1) = (uint8) ((x) >> 16);       \
+    *((str) + 0) = (uint8) ((x) >> 24);       \
+}
+#define SHA2_PACK32(str, x)                   \
+{                                             \
+    *(x) =   ((uint32) *((str) + 3)      )    \
+           | ((uint32) *((str) + 2) <<  8)    \
+           | ((uint32) *((str) + 1) << 16)    \
+           | ((uint32) *((str) + 0) << 24);   \
+}
+#endif
diff --git a/r5dev/mathlib/swap.h b/r5dev/mathlib/swap.h
new file mode 100644
index 00000000..cf4c52d9
--- /dev/null
+++ b/r5dev/mathlib/swap.h
@@ -0,0 +1,53 @@
+//===========================================================================//
+//
+// Purpose: basic endian swap utils.
+//
+//===========================================================================//
+#pragma once
+
+template <typename T>
+inline T WordSwapC(T w)
+{
+	std::uint16_t swap;
+
+	static_assert(sizeof(T) == sizeof(std::uint16_t));
+
+	swap = ((*((std::uint16_t*)&w) & 0xff00) >> 8);
+	swap |= ((*((std::uint16_t*)&w) & 0x00ff) << 8);
+
+	return *((T*)&swap);
+}
+
+template <typename T>
+inline T DWordSwapC(T dw)
+{
+	std::uint32_t swap;
+
+	static_assert(sizeof(T) == sizeof(std::uint32_t));
+
+	swap = *((std::uint32_t*)&dw) >> 24;
+	swap |= ((*((std::uint32_t*)&dw) & 0x00FF0000) >> 8);
+	swap |= ((*((std::uint32_t*)&dw) & 0x0000FF00) << 8);
+	swap |= ((*((std::uint32_t*)&dw) & 0x000000FF) << 24);
+
+	return *((T*)&swap);
+}
+
+template <typename T>
+inline T QWordSwapC(T dw)
+{
+	static_assert(sizeof(dw) == sizeof(std::uint64_t));
+
+	std::uint64_t swap;
+
+	swap = *((std::uint64_t*)&dw) >> 56;
+	swap |= ((*((std::uint64_t*)&dw) & 0x00FF000000000000ull) >> 40);
+	swap |= ((*((std::uint64_t*)&dw) & 0x0000FF0000000000ull) >> 24);
+	swap |= ((*((std::uint64_t*)&dw) & 0x000000FF00000000ull) >> 8);
+	swap |= ((*((std::uint64_t*)&dw) & 0x00000000FF000000ull) << 8);
+	swap |= ((*((std::uint64_t*)&dw) & 0x0000000000FF0000ull) << 24);
+	swap |= ((*((std::uint64_t*)&dw) & 0x000000000000FF00ull) << 40);
+	swap |= ((*((std::uint64_t*)&dw) & 0x00000000000000FFull) << 56);
+
+	return *((T*)&swap);
+}
diff --git a/r5dev/netconsole.vcxproj b/r5dev/netconsole.vcxproj
new file mode 100644
index 00000000..526a7ba6
--- /dev/null
+++ b/r5dev/netconsole.vcxproj
@@ -0,0 +1,204 @@
+<?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>
+  <ItemGroup>
+    <ClCompile Include="core\stdafx.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="engine\net.cpp" />
+    <ClCompile Include="netconsole\netconsole.cpp" />
+    <ClCompile Include="public\utility.cpp" />
+    <ClCompile Include="tier1\NetAdr2.cpp" />
+    <ClCompile Include="tier2\socketcreator.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="core\stdafx.h" />
+    <ClInclude Include="engine\net.h" />
+    <ClInclude Include="netconsole\netconsole.h" />
+    <ClInclude Include="tier1\NetAdr2.h" />
+    <ClInclude Include="tier2\socketcreator.h" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>16.0</VCProjectVersion>
+    <Keyword>Win32Proj</Keyword>
+    <ProjectGuid>{9579b31f-ce24-4852-a941-cd1ad71e2248}</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>MultiByte</CharacterSet>
+    <UseOfMfc>Static</UseOfMfc>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v143</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+    <UseOfMfc>Static</UseOfMfc>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v143</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+    <UseOfMfc>Static</UseOfMfc>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v143</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+    <UseOfMfc>Static</UseOfMfc>
+  </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>
+    <OutDir>$(SolutionDir)bin\$(Configuration)\</OutDir>
+    <IntDir>$(SolutionDir)build\$(ProjectName)\$(Configuration)\</IntDir>
+    <TargetName>netcon86</TargetName>
+    <IncludePath>$(SolutionDir)r5dev\;$(IncludePath);</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>$(SolutionDir)bin\$(Configuration)\</OutDir>
+    <IntDir>$(SolutionDir)build\$(ProjectName)\$(Configuration)\</IntDir>
+    <TargetName>netcon86</TargetName>
+    <IncludePath>$(SolutionDir)r5dev\;$(IncludePath);</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>$(SolutionDir)bin\$(Configuration)\</OutDir>
+    <IntDir>$(SolutionDir)build\$(ProjectName)\$(Configuration)\</IntDir>
+    <TargetName>netcon64</TargetName>
+    <IncludePath>$(SolutionDir)r5dev\;$(IncludePath);</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>$(SolutionDir)bin\$(Configuration)\</OutDir>
+    <IntDir>$(SolutionDir)build\$(ProjectName)\$(Configuration)\</IntDir>
+    <TargetName>netcon64</TargetName>
+    <IncludePath>$(SolutionDir)r5dev\;$(IncludePath);</IncludePath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <LanguageStandard>stdcpp17</LanguageStandard>
+      <AdditionalOptions>/D NETCONSOLE %(AdditionalOptions)</AdditionalOptions>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>core\stdafx.h</PrecompiledHeaderFile>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>User32.lib;Bcrypt.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </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>
+      <LanguageStandard>stdcpp17</LanguageStandard>
+      <AdditionalOptions>/D NETCONSOLE %(AdditionalOptions)</AdditionalOptions>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>core\stdafx.h</PrecompiledHeaderFile>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>User32.lib;Bcrypt.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <LanguageStandard>stdcpp17</LanguageStandard>
+      <AdditionalOptions>/D NETCONSOLE %(AdditionalOptions)</AdditionalOptions>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>core\stdafx.h</PrecompiledHeaderFile>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>User32.lib;Bcrypt.lib;Ws2_32.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>
+      <LanguageStandard>stdcpp17</LanguageStandard>
+      <AdditionalOptions>/D NETCONSOLE %(AdditionalOptions)</AdditionalOptions>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>core\stdafx.h</PrecompiledHeaderFile>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>User32.lib;Bcrypt.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/r5dev/netconsole.vcxproj.filters b/r5dev/netconsole.vcxproj.filters
new file mode 100644
index 00000000..ab5864ec
--- /dev/null
+++ b/r5dev/netconsole.vcxproj.filters
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="sdk">
+      <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="sdk\engine">
+      <UniqueIdentifier>{7ec4619a-05f9-4949-937b-4b945edc3fe8}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="sdk\tier1">
+      <UniqueIdentifier>{82bd526b-7838-4923-8c87-b035f2d5d2c7}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="sdk\public">
+      <UniqueIdentifier>{033185e7-f6ca-4225-8ddb-089bf5bd5891}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="core">
+      <UniqueIdentifier>{a6970588-331b-4da5-bfcf-b6f6cf1d87ee}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="sdk\tier2">
+      <UniqueIdentifier>{99b7ba90-bc5d-4f07-a299-b68322c5ca63}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="tier1\NetAdr2.cpp">
+      <Filter>sdk\tier1</Filter>
+    </ClCompile>
+    <ClCompile Include="public\utility.cpp">
+      <Filter>sdk\public</Filter>
+    </ClCompile>
+    <ClCompile Include="core\stdafx.cpp">
+      <Filter>core</Filter>
+    </ClCompile>
+    <ClCompile Include="tier2\socketcreator.cpp">
+      <Filter>sdk\tier2</Filter>
+    </ClCompile>
+    <ClCompile Include="engine\net.cpp">
+      <Filter>sdk\engine</Filter>
+    </ClCompile>
+    <ClCompile Include="netconsole\netconsole.cpp">
+      <Filter>core</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="tier1\NetAdr2.h">
+      <Filter>sdk\tier1</Filter>
+    </ClInclude>
+    <ClInclude Include="core\stdafx.h">
+      <Filter>core</Filter>
+    </ClInclude>
+    <ClInclude Include="tier2\socketcreator.h">
+      <Filter>sdk\tier2</Filter>
+    </ClInclude>
+    <ClInclude Include="engine\net.h">
+      <Filter>sdk\engine</Filter>
+    </ClInclude>
+    <ClInclude Include="netconsole\netconsole.h">
+      <Filter>core</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/r5dev/netconsole/netconsole.cpp b/r5dev/netconsole/netconsole.cpp
new file mode 100644
index 00000000..4b470054
--- /dev/null
+++ b/r5dev/netconsole/netconsole.cpp
@@ -0,0 +1,116 @@
+//=====================================================================================//
+//
+// Purpose: Lightweight netconsole.
+//
+//=====================================================================================//
+
+#include "core/stdafx.h"
+#include "tier1/NetAdr2.h"
+#include "tier2/socketcreator.h"
+#include "netconsole/netconsole.h"
+
+//-----------------------------------------------------------------------------
+// purpose: send datagram
+//-----------------------------------------------------------------------------
+void CNetCon::Send(void)
+{
+	char buf[MAX_NETCONSOLE_INPUT_LEN]{};
+	std::string svUserInput;
+
+	do
+	{
+		printf(">");
+		std::getline(std::cin, svUserInput);
+		svUserInput.append("\n\r");
+
+		int nSendResult = ::send(pSocket->GetAcceptedSocketData(0)->m_hSocket, svUserInput.c_str(), svUserInput.size(), MSG_NOSIGNAL);
+
+		if (nSendResult != SOCKET_ERROR)
+		{
+			memcpy(buf, "", MAX_NETCONSOLE_INPUT_LEN);
+		}
+	} while (svUserInput.size() > 0);
+}
+
+//-----------------------------------------------------------------------------
+// purpose: receive datagram
+//-----------------------------------------------------------------------------
+void CNetCon::Recv(void)
+{
+	static char buf[MAX_NETCONSOLE_INPUT_LEN]{};
+
+	for (;;)
+	{
+		std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+		int nRecvLen = ::recv(pSocket->GetAcceptedSocketData(0)->m_hSocket, buf, sizeof(buf), MSG_NOSIGNAL);
+		if (nRecvLen > 0 && nRecvLen < MAX_NETCONSOLE_INPUT_LEN - 1)
+		{
+			buf[nRecvLen + 1] = '\0';
+			printf("%s\n", buf);
+		}
+	}
+}
+
+//-----------------------------------------------------------------------------
+// purpose: WSA and NETCON systems init
+//-----------------------------------------------------------------------------
+bool CNetCon::Init(void)
+{
+	WSAData wsaData{};
+	int nError = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
+	if (nError != 0)
+	{
+		assert(nError != 0 && "Failed to start Winsock via WSAStartup.");
+		return false;
+	}
+
+	if (pSocket->ConnectSocket(*pNetAdr2, true) == SOCKET_ERROR)
+	{
+		assert(nError != 0 && "'pSocket->ConnectSocket()' returned 'SOCKET_ERROR'");
+		return false;
+	}
+
+	std::thread tRecv(&CNetCon::Recv, this, this->pSocket);
+	tRecv.detach();
+
+	return true;
+}
+
+//-----------------------------------------------------------------------------
+// purpose: WSA and NETCON systems shutdown
+//-----------------------------------------------------------------------------
+bool CNetCon::Shutdown(void)
+{
+	pSocket->CloseAllAcceptedSockets();
+
+	int nError = ::WSACleanup();
+	if (nError != 0)
+	{
+		assert(nError != 0 && "Failed to stop winsock via WSACleanup.\n");
+		return false;
+	}
+	return true;
+}
+
+//-----------------------------------------------------------------------------
+// purpose: entrypoint
+//-----------------------------------------------------------------------------
+int main(void)
+{
+	CNetCon* pNetCon = new CNetCon();
+
+	if (!pNetCon->Init())
+	{
+		return EXIT_FAILURE;
+	}
+
+	pNetCon->Send();
+
+	if (!pNetCon->Shutdown())
+	{
+		return EXIT_FAILURE;
+	}
+
+	return ERROR_SUCCESS;
+}
\ No newline at end of file
diff --git a/r5dev/netconsole/netconsole.h b/r5dev/netconsole/netconsole.h
new file mode 100644
index 00000000..abcc7f24
--- /dev/null
+++ b/r5dev/netconsole/netconsole.h
@@ -0,0 +1,19 @@
+//===========================================================================//
+//
+// Purpose: 
+//
+//===========================================================================//
+#pragma once
+
+class CNetCon
+{
+public:
+	bool Init(void);
+	bool Shutdown(void);
+
+	void Send(void);
+	void Recv(void);
+
+	CNetAdr2* pNetAdr2 = new CNetAdr2("localhost", "37015");
+	CSocketCreator* pSocket = new CSocketCreator();
+};
\ No newline at end of file
diff --git a/r5dev/r5dev.vcxproj b/r5dev/r5dev.vcxproj
index 1c44ff14..0b973bf1 100644
--- a/r5dev/r5dev.vcxproj
+++ b/r5dev/r5dev.vcxproj
@@ -35,10 +35,13 @@
     <ClCompile Include="ebisusdk\EbisuSDK.cpp" />
     <ClCompile Include="engine\baseclient.cpp" />
     <ClCompile Include="engine\baseclientstate.cpp" />
+    <ClCompile Include="engine\cl_rcon.cpp" />
     <ClCompile Include="engine\host_cmd.cpp" />
     <ClCompile Include="engine\host_state.cpp" />
+    <ClCompile Include="engine\net.cpp" />
     <ClCompile Include="engine\net_chan.cpp" />
     <ClCompile Include="engine\sv_main.cpp" />
+    <ClCompile Include="engine\sv_rcon.cpp" />
     <ClCompile Include="engine\sys_dll.cpp" />
     <ClCompile Include="engine\sys_dll2.cpp" />
     <ClCompile Include="engine\sys_engine.cpp" />
@@ -51,6 +54,7 @@
     <ClCompile Include="mathlib\bits.cpp" />
     <ClCompile Include="mathlib\crc32.cpp" />
     <ClCompile Include="mathlib\IceKey.cpp" />
+    <ClCompile Include="mathlib\sha256.cpp" />
     <ClCompile Include="networksystem\r5net.cpp" />
     <ClCompile Include="public\bansystem.cpp" />
     <ClCompile Include="public\binstream.cpp" />
@@ -200,6 +204,8 @@
     <ClCompile Include="tier0\completion.cpp" />
     <ClCompile Include="tier0\cvar.cpp" />
     <ClCompile Include="tier0\IConVar.cpp" />
+    <ClCompile Include="tier1\NetAdr2.cpp" />
+    <ClCompile Include="tier2\socketcreator.cpp" />
     <ClCompile Include="vguimatsurface\MatSystemSurface.cpp" />
     <ClCompile Include="vgui\CEngineVGui.cpp" />
     <ClCompile Include="vgui\vgui_fpspanel.cpp" />
@@ -219,6 +225,7 @@
     <ClInclude Include="client\cdll_engine_int.h" />
     <ClInclude Include="client\client.h" />
     <ClInclude Include="client\IVEngineClient.h" />
+    <ClInclude Include="common\igameserverdata.h" />
     <ClInclude Include="common\opcodes.h" />
     <ClInclude Include="common\protocol.h" />
     <ClInclude Include="common\pseudodefs.h" />
@@ -231,11 +238,14 @@
     <ClInclude Include="ebisusdk\EbisuSDK.h" />
     <ClInclude Include="engine\baseclient.h" />
     <ClInclude Include="engine\baseclientstate.h" />
+    <ClInclude Include="engine\cl_rcon.h" />
     <ClInclude Include="engine\debugoverlay.h" />
     <ClInclude Include="engine\host_cmd.h" />
     <ClInclude Include="engine\host_state.h" />
+    <ClInclude Include="engine\net.h" />
     <ClInclude Include="engine\net_chan.h" />
     <ClInclude Include="engine\sv_main.h" />
+    <ClInclude Include="engine\sv_rcon.h" />
     <ClInclude Include="engine\sys_dll.h" />
     <ClInclude Include="engine\sys_dll2.h" />
     <ClInclude Include="engine\sys_engine.h" />
@@ -252,6 +262,8 @@
     <ClInclude Include="mathlib\crc32.h" />
     <ClInclude Include="mathlib\IceKey.H" />
     <ClInclude Include="mathlib\parallel_for.h" />
+    <ClInclude Include="mathlib\sha256.h" />
+    <ClInclude Include="mathlib\swap.h" />
     <ClInclude Include="mathlib\vector.h" />
     <ClInclude Include="milessdk\win64_rrthreads.h" />
     <ClInclude Include="networksystem\r5net.h" />
@@ -417,6 +429,8 @@
     <ClInclude Include="tier0\cvar.h" />
     <ClInclude Include="tier0\IConVar.h" />
     <ClInclude Include="tier0\interface.h" />
+    <ClInclude Include="tier1\NetAdr2.h" />
+    <ClInclude Include="tier2\socketcreator.h" />
     <ClInclude Include="vguimatsurface\MatSystemSurface.h" />
     <ClInclude Include="vgui\CEngineVGui.h" />
     <ClInclude Include="vgui\vgui.h" />
diff --git a/r5dev/r5dev.vcxproj.filters b/r5dev/r5dev.vcxproj.filters
index 6753858e..973cb22d 100644
--- a/r5dev/r5dev.vcxproj.filters
+++ b/r5dev/r5dev.vcxproj.filters
@@ -97,12 +97,6 @@
     <Filter Include="windows">
       <UniqueIdentifier>{942b8ea5-ce53-4e1e-ad7a-845991aaead6}</UniqueIdentifier>
     </Filter>
-    <Filter Include="sdk\public">
-      <UniqueIdentifier>{b0696621-8658-4918-b0f2-ba20acc26829}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="sdk\public\include">
-      <UniqueIdentifier>{cbe60970-f348-4a8b-8cee-d4cfebbe0d99}</UniqueIdentifier>
-    </Filter>
     <Filter Include="sdk\resource">
       <UniqueIdentifier>{14a61eec-93ec-4e7c-b0bf-2ce23c3b782c}</UniqueIdentifier>
     </Filter>
@@ -139,6 +133,18 @@
     <Filter Include="thirdparty\lzham\lzhamdecomp\include">
       <UniqueIdentifier>{5beb12b5-0422-4337-9be6-2e6c0a05a69b}</UniqueIdentifier>
     </Filter>
+    <Filter Include="sdk\tier1">
+      <UniqueIdentifier>{c5adc45b-d14c-4d52-9835-29948cab931a}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="sdk\public">
+      <UniqueIdentifier>{b0696621-8658-4918-b0f2-ba20acc26829}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="sdk\public\include">
+      <UniqueIdentifier>{cbe60970-f348-4a8b-8cee-d4cfebbe0d99}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="sdk\tier2">
+      <UniqueIdentifier>{9da19829-c065-4584-9cf2-af751fb0d060}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="client\client.cpp">
@@ -417,6 +423,24 @@
     <ClCompile Include="engine\sys_engine.cpp">
       <Filter>sdk\engine</Filter>
     </ClCompile>
+    <ClCompile Include="engine\sv_rcon.cpp">
+      <Filter>sdk\engine</Filter>
+    </ClCompile>
+    <ClCompile Include="engine\cl_rcon.cpp">
+      <Filter>sdk\engine</Filter>
+    </ClCompile>
+    <ClCompile Include="tier2\socketcreator.cpp">
+      <Filter>sdk\tier2</Filter>
+    </ClCompile>
+    <ClCompile Include="tier1\NetAdr2.cpp">
+      <Filter>sdk\tier1</Filter>
+    </ClCompile>
+    <ClCompile Include="mathlib\sha256.cpp">
+      <Filter>sdk\mathlib</Filter>
+    </ClCompile>
+    <ClCompile Include="engine\net.cpp">
+      <Filter>sdk\engine</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="client\cdll_engine_int.h">
@@ -1067,6 +1091,30 @@
     <ClInclude Include="engine\sys_engine.h">
       <Filter>sdk\engine</Filter>
     </ClInclude>
+    <ClInclude Include="engine\sv_rcon.h">
+      <Filter>sdk\engine</Filter>
+    </ClInclude>
+    <ClInclude Include="engine\cl_rcon.h">
+      <Filter>sdk\engine</Filter>
+    </ClInclude>
+    <ClInclude Include="mathlib\swap.h">
+      <Filter>sdk\mathlib</Filter>
+    </ClInclude>
+    <ClInclude Include="tier2\socketcreator.h">
+      <Filter>sdk\tier2</Filter>
+    </ClInclude>
+    <ClInclude Include="tier1\NetAdr2.h">
+      <Filter>sdk\tier1</Filter>
+    </ClInclude>
+    <ClInclude Include="mathlib\sha256.h">
+      <Filter>sdk\mathlib</Filter>
+    </ClInclude>
+    <ClInclude Include="common\igameserverdata.h">
+      <Filter>sdk\common</Filter>
+    </ClInclude>
+    <ClInclude Include="engine\net.h">
+      <Filter>sdk\engine</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="r5dev.def" />
diff --git a/r5dev/squirrel/sqinit.cpp b/r5dev/squirrel/sqinit.cpp
index 03ade399..f630c2b8 100644
--- a/r5dev/squirrel/sqinit.cpp
+++ b/r5dev/squirrel/sqinit.cpp
@@ -38,7 +38,7 @@ namespace VSquirrel
     }
     namespace UI
     {
-        //----------------------------------------------------------------------------
+        //-----------------------------------------------------------------------------
         // Purpose: get server's current name from serverlist index
         //-----------------------------------------------------------------------------
         SQRESULT GetServerName(void* sqvm)
@@ -51,7 +51,7 @@ namespace VSquirrel
             return SQ_OK;
         }
 
-        //----------------------------------------------------------------------------
+        //-----------------------------------------------------------------------------
         // Purpose: get server's current playlist via serverlist index
         //-----------------------------------------------------------------------------
         SQRESULT GetServerPlaylist(void* sqvm)
@@ -64,7 +64,7 @@ namespace VSquirrel
             return SQ_OK;
         }
 
-        //----------------------------------------------------------------------------
+        //-----------------------------------------------------------------------------
         // Purpose: get server's current map via serverlist index
         //-----------------------------------------------------------------------------
         SQRESULT GetServerMap(void* sqvm)
@@ -77,7 +77,7 @@ namespace VSquirrel
             return SQ_OK;
         }
 
-        //----------------------------------------------------------------------------
+        //-----------------------------------------------------------------------------
         // Purpose: get current server count from pylon
         //-----------------------------------------------------------------------------
         SQRESULT GetServerCount(void* sqvm)
@@ -89,7 +89,7 @@ namespace VSquirrel
             return SQ_OK;
         }
 
-        //----------------------------------------------------------------------------
+        //-----------------------------------------------------------------------------
         // Purpose: expose SDK version to the UI VM
         //-----------------------------------------------------------------------------
         SQRESULT GetSDKVersion(void* sqvm)
@@ -99,7 +99,7 @@ namespace VSquirrel
             return SQ_OK;
         }
 
-        //----------------------------------------------------------------------------
+        //-----------------------------------------------------------------------------
         // Purpose: get promo data for serverbrowser panels
         //-----------------------------------------------------------------------------
         SQRESULT GetPromoData(void* sqvm)
diff --git a/r5dev/tier0/basetypes.h b/r5dev/tier0/basetypes.h
index bc9effff..6f5facdc 100644
--- a/r5dev/tier0/basetypes.h
+++ b/r5dev/tier0/basetypes.h
@@ -17,3 +17,17 @@
 #define MAX_PLAYERS 128 // Max R5 players.
 
 #define SDK_VERSION "beta 1.6"
+
+// #define COMPILETIME_MAX and COMPILETIME_MIN for max/min in constant expressions
+#define COMPILETIME_MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) )
+#define COMPILETIME_MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) )
+#ifndef MIN
+#define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) )
+#endif
+
+#ifndef MAX
+#define MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) )
+#endif
+
+constexpr int MAX_NETCONSOLE_INPUT_LEN = 4096;
+constexpr int MSG_NOSIGNAL             = 0;
diff --git a/r5dev/tier1/NetAdr.cpp b/r5dev/tier1/NetAdr.cpp
new file mode 100644
index 00000000..9010b74b
--- /dev/null
+++ b/r5dev/tier1/NetAdr.cpp
@@ -0,0 +1,440 @@
+//===========================================================================//
+//
+// Purpose: implementation of the CNetAdr class.
+// --------------------------------------------------------------------------
+// 
+// NOTE: This implementation is considered deprecated. I rebuilded this
+// not knowing that the engine supports IPv6 as well. I have fully rewritten
+// this class in 'tier1/NetAdr2.cpp' in modern C++. Please use this instead.
+// This class is for reference material only may some bits in the engine line
+// up with this original 'CNetAdr' implementation.
+// 
+//===========================================================================//
+
+#include "core/stdafx.h"
+#include "tier1/netadr.h"
+#include "mathlib/swap.h"
+
+//////////////////////////////////////////////////////////////////////
+// Constructors
+//////////////////////////////////////////////////////////////////////
+netadr_s::netadr_s(void)
+{
+	SetIP(0);
+	SetPort(0);
+	SetType(netadrtype_t::NA_IP);
+}
+netadr_s::netadr_s(std::uint32_t unIP, std::uint16_t usPort)
+{
+	SetIP(unIP);
+	SetPort(usPort);
+	SetType(netadrtype_t::NA_IP);
+}
+netadr_s::netadr_s(const char* pch)
+{
+	SetFromString(pch);
+}
+
+//////////////////////////////////////////////////////////////////////
+// 
+//////////////////////////////////////////////////////////////////////
+void netadr_t::SetFromSocket(int hSocket)
+{
+	Clear();
+	type = netadrtype_t::NA_IP;
+
+	struct sockaddr address{};
+	socklen_t namelen = sizeof(address);
+	if (getsockname(hSocket, (struct sockaddr*)&address, &namelen) == 0)
+	{
+		SetFromSockadr(&address);
+	}
+}
+
+//////////////////////////////////////////////////////////////////////
+// Compares IP for equality
+//////////////////////////////////////////////////////////////////////
+bool netadr_t::CompareAdr(const netadr_t& a, bool onlyBase) const
+{
+	if (a.type != type)
+	{
+		return false;
+	}
+
+	if (type == netadrtype_t::NA_LOOPBACK)
+	{
+		return true;
+	}
+
+	if (type == netadrtype_t::NA_BROADCAST)
+	{
+		return true;
+	}
+
+	if (type == netadrtype_t::NA_IP)
+	{
+		if (!onlyBase && (port != a.port))
+		{
+			return false;
+		}
+
+		if (a.ip[0] == ip[0] && a.ip[1] == ip[1] && a.ip[2] == ip[2] && a.ip[3] == ip[3])
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Compares Class-B IP for equality
+//////////////////////////////////////////////////////////////////////
+bool netadr_t::CompareClassBAdr(const netadr_t& a) const
+{
+	if (a.type != type)
+	{
+		return false;
+	}
+
+	if (type == netadrtype_t::NA_LOOPBACK)
+	{
+		return true;
+	}
+
+	if (type == netadrtype_t::NA_IP)
+	{
+		if (a.ip[0] == ip[0] && a.ip[1] == ip[1])
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Compares Class-C IP for equality
+//////////////////////////////////////////////////////////////////////
+bool netadr_t::CompareClassCAdr(const netadr_t& a) const
+{
+	if (a.type != type)
+	{
+		return false;
+	}
+
+	if (type == netadrtype_t::NA_LOOPBACK)
+	{
+		return true;
+	}
+
+	if (type == netadrtype_t::NA_IP)
+	{
+		if (a.ip[0] == ip[0] && a.ip[1] == ip[1] && a.ip[2] == ip[2])
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Convert address to string
+//////////////////////////////////////////////////////////////////////
+const char* netadr_t::ToString(bool onlyBase) const
+{
+	// Select a static buffer.
+	static char s[4][64]{};
+	static int slot = 0;
+	int useSlot = (slot++) % 4;
+
+	// Render into it.
+	ToString(s[useSlot], sizeof(s[0]), onlyBase);
+
+	// Pray the caller uses it before it gets clobbered.
+	return s[useSlot];
+}
+
+//////////////////////////////////////////////////////////////////////
+// Convert address to string
+//////////////////////////////////////////////////////////////////////
+void netadr_t::ToString(char* pchBuffer, std::uint32_t unBufferSize, bool bOnlyBase) const
+{
+	if (type == netadrtype_t::NA_LOOPBACK)
+	{
+		memmove(pchBuffer, "loopback", unBufferSize);
+	}
+	else if (type == netadrtype_t::NA_BROADCAST)
+	{
+		memmove(pchBuffer, "broadcast", unBufferSize);
+	}
+	else if (type == netadrtype_t::NA_IP)
+	{
+		if (bOnlyBase)
+		{
+			snprintf(pchBuffer, unBufferSize, "%i.%i.%i.%i", ip[0], ip[1], ip[2], ip[3]);
+		}
+		else
+		{
+			snprintf(pchBuffer, unBufferSize, "%i.%i.%i.%i:%i", ip[0], ip[1], ip[2], ip[3], ntohs(port));
+		}
+	}
+	else
+	{
+		memmove(pchBuffer, "unknown", unBufferSize);
+	}
+}
+
+//////////////////////////////////////////////////////////////////////
+// Clears IP
+//////////////////////////////////////////////////////////////////////
+void netadr_t::Clear(void)
+{
+	ip[0] = ip[1] = ip[2] = ip[3] = 0;
+	port = 0;
+	type = netadrtype_t::NA_NULL;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Sets IP
+//////////////////////////////////////////////////////////////////////
+void netadr_t::SetIP(std::uint8_t b1, std::uint8_t b2, std::uint8_t b3, std::uint8_t b4)
+{
+	ip[0] = b1;
+	ip[1] = b2;
+	ip[2] = b3;
+	ip[3] = b4;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Sets IP
+//////////////////////////////////////////////////////////////////////
+void netadr_t::SetIP(std::uint32_t unIP)
+{
+	*((std::uint32_t*)ip) = DWordSwapC(unIP);
+}
+
+//////////////////////////////////////////////////////////////////////
+// Sets type
+//////////////////////////////////////////////////////////////////////
+void netadr_t::SetType(netadrtype_t newtype)
+{
+	type = newtype;
+}
+
+//////////////////////////////////////////////////////////////////////
+// 
+//////////////////////////////////////////////////////////////////////
+netadrtype_t netadr_t::GetType(void) const
+{
+	return type;
+}
+
+//////////////////////////////////////////////////////////////////////
+// 
+//////////////////////////////////////////////////////////////////////
+std::uint16_t netadr_t::GetPort(void) const
+{
+	return WordSwapC(port);
+}
+
+//////////////////////////////////////////////////////////////////////
+// 
+//////////////////////////////////////////////////////////////////////
+std::uint32_t netadr_t::GetIPNetworkByteOrder(void) const
+{
+	return *(std::uint32_t*)&ip;
+}
+
+//////////////////////////////////////////////////////////////////////
+// 
+//////////////////////////////////////////////////////////////////////
+std::uint32_t netadr_t::GetIPHostByteOrder(void) const
+{
+	return DWordSwapC(GetIPNetworkByteOrder());
+}
+
+//////////////////////////////////////////////////////////////////////
+// 
+//////////////////////////////////////////////////////////////////////
+void netadr_t::ToSockadr(struct sockaddr* s) const
+{
+	memset(s, 0, sizeof(struct sockaddr));
+
+	if (type == netadrtype_t::NA_BROADCAST)
+	{
+		((struct sockaddr_in*)s)->sin_family = AF_INET;
+		((struct sockaddr_in*)s)->sin_port = port;
+		((struct sockaddr_in*)s)->sin_addr.s_addr = INADDR_BROADCAST;
+	}
+	else if (type == netadrtype_t::NA_IP)
+	{
+		((struct sockaddr_in*)s)->sin_family = AF_INET;
+		((struct sockaddr_in*)s)->sin_addr.s_addr = *(int*)&ip;
+		((struct sockaddr_in*)s)->sin_port = port;
+	}
+	else if (type == netadrtype_t::NA_LOOPBACK)
+	{
+		((struct sockaddr_in*)s)->sin_family = AF_INET;
+		((struct sockaddr_in*)s)->sin_port = port;
+		((struct sockaddr_in*)s)->sin_addr.s_addr = INADDR_LOOPBACK;
+	}
+}
+
+//////////////////////////////////////////////////////////////////////
+// 
+//////////////////////////////////////////////////////////////////////
+bool netadr_t::SetFromSockadr(const struct sockaddr* s)
+{
+	if (s->sa_family == AF_INET)
+	{
+		type = netadrtype_t::NA_IP;
+		*(int*)&ip = ((struct sockaddr_in*)s)->sin_addr.s_addr;
+		port = ((struct sockaddr_in*)s)->sin_port;
+		return true;
+	}
+	else
+	{
+		Clear();
+		return false;
+	}
+}
+
+//////////////////////////////////////////////////////////////////////
+// 
+//////////////////////////////////////////////////////////////////////
+bool netadr_t::IsValid(void) const
+{
+	return ((port != 0) && (type != netadrtype_t::NA_NULL) &&
+		(ip[0] != 0 || ip[1] != 0 || ip[2] != 0 || ip[3] != 0));
+}
+
+//////////////////////////////////////////////////////////////////////
+// 
+//////////////////////////////////////////////////////////////////////
+bool netadr_t::IsBaseAdrValid(void) const
+{
+	return ((type != netadrtype_t::NA_NULL) &&
+		(ip[0] != 0 || ip[1] != 0 || ip[2] != 0 || ip[3] != 0));
+}
+
+//////////////////////////////////////////////////////////////////////
+// Returns true if we are localhost
+//////////////////////////////////////////////////////////////////////
+bool netadr_t::IsLocalhost(void) const
+{
+	return (ip[0] == 127) && (ip[1] == 0) && (ip[2] == 0) && (ip[3] == 1);
+}
+
+//////////////////////////////////////////////////////////////////////
+// Returns true if we use the loopback buffers
+//////////////////////////////////////////////////////////////////////
+bool netadr_t::IsLoopback(void) const
+{
+	return type == netadrtype_t::NA_LOOPBACK;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Check if address is reserved and not routable.
+//////////////////////////////////////////////////////////////////////
+bool netadr_t::IsReservedAdr(void) const
+{
+	if (type == netadrtype_t::NA_LOOPBACK)
+	{
+		return true;
+	}
+
+	if (type == netadrtype_t::NA_IP)
+	{
+		if ((ip[0] == 10)                                || // 10.x.x.x is reserved
+			(ip[0] == 127)                               || // 127.x.x.x 
+			(ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) || // 172.16.x.x  - 172.31.x.x
+			(ip[0] == 192 && ip[1] >= 168))                 // 192.168.x.x
+		{
+			return true;
+		}
+	}
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////
+// 
+//////////////////////////////////////////////////////////////////////
+void netadr_t::SetPort(std::uint16_t newport)
+{
+	port = WordSwapC(newport);
+}
+
+//////////////////////////////////////////////////////////////////////
+// 
+//////////////////////////////////////////////////////////////////////
+bool netadr_t::SetFromString(const char* szIpAdr, bool bUseDNS)
+{
+	Clear();
+
+	if (!szIpAdr)
+	{
+		Assert(szIpAdr, "Invalid call: 'szIpAdr' was nullptr.");
+		return false;
+	}
+
+	type = netadrtype_t::NA_IP;
+
+	char szAddress[128]{};
+	strcpy_s(szAddress, szIpAdr);
+
+	if (!_strnicmp(szAddress, "loopback", 8))
+	{
+		char szNewAddress[128]{};
+		type = netadrtype_t::NA_LOOPBACK;
+
+		strcpy_s(szNewAddress, "127.0.0.1");
+		strcat_s(szNewAddress, szAddress + 8); // copy anything after "loopback"
+
+		strcpy_s(szAddress, szNewAddress);
+	}
+
+	if (!_strnicmp(szAddress, "localhost", 9))
+	{
+		memcpy(szAddress, "127.0.0.1", 9); // Note use of memcpy allows us to keep the colon and rest of string since localhost and 127.0.0.1 are both 9 characters.
+	}
+
+	// IPv4 starts with a number and has a dot.
+	if (szAddress[0] >= '0' && szAddress[0] <= '9' && strchr(szAddress, '.'))
+	{
+		int i0 = -1, i1 = -1, i2 = -1, i3 = -1, n0 = 0; // Initialize port as zero
+		int nRes = sscanf_s(szAddress, "%d.%d.%d.%d:%d", &i0, &i1, &i2, &i3, &n0);
+		if (
+			nRes < 4
+			|| i0 < 0 || i0 > 255
+			|| i1 < 0 || i1 > 255
+			|| i2 < 0 || i2 > 255
+			|| i3 < 0 || i3 > 255
+			|| n0 < 0 || n0 > 65535
+			)
+		{
+			return false;
+		}
+
+		SetIP(i0, i1, i2, i3);
+		SetPort((std::uint16_t)n0);
+		return true;
+	}
+}
+
+//////////////////////////////////////////////////////////////////////
+// 
+//////////////////////////////////////////////////////////////////////
+bool netadr_t::operator<(const netadr_t& netadr) const
+{
+	if (*((std::uint32_t*)netadr.ip) < *((std::uint32_t*)ip))
+	{
+		return true;
+	}
+	else if (*((std::uint32_t*)netadr.ip) > *((std::uint32_t*)ip))
+	{
+		return false;
+	}
+	return (netadr.port < port);
+}
diff --git a/r5dev/tier1/NetAdr.h b/r5dev/tier1/NetAdr.h
new file mode 100644
index 00000000..fc7383e8
--- /dev/null
+++ b/r5dev/tier1/NetAdr.h
@@ -0,0 +1,69 @@
+#pragma once
+
+enum class netadrtype_t
+{
+	NA_NULL = 0,
+	NA_LOOPBACK,
+	NA_BROADCAST,
+	NA_IP,
+};
+
+typedef struct netadr_s netadr_t;
+typedef struct netadr_s
+{
+public:
+	netadr_s(void);
+	netadr_s(std::uint32_t unIP, std::uint16_t usPort);
+	netadr_s(const char* pch);
+	void	Clear(void);
+
+	void	SetType(netadrtype_t type);
+	void	SetPort(std::uint16_t port);
+	bool	SetFromSockadr(const struct sockaddr* s);
+	void	SetIP(std::uint8_t b1, std::uint8_t b2, std::uint8_t b3, std::uint8_t b4);
+	void	SetIP(std::uint32_t unIP);
+	void	SetIPAndPort(std::uint32_t unIP, std::uint16_t usPort) { SetIP(unIP); SetPort(usPort); }
+	bool	SetFromString(const char* pch, bool bUseDNS = false);
+	void	SetFromSocket(int hSocket);
+
+	bool	CompareAdr(const netadr_s& a, bool onlyBase = false) const;
+	bool	CompareClassBAdr(const netadr_s& a) const;
+	bool	CompareClassCAdr(const netadr_s& a) const;
+
+	netadrtype_t    GetType(void) const;
+	std::uint16_t   GetPort(void) const;
+
+	const char* ToString(bool onlyBase = false) const;
+
+	void	ToString(char* pchBuffer, std::uint32_t unBufferSize, bool onlyBase = false) const;
+	template< size_t maxLenInChars >
+	void	ToString_safe(char(&pDest)[maxLenInChars], bool onlyBase = false) const
+	{
+		ToString(&pDest[0], maxLenInChars, onlyBase);
+	}
+
+	//[xxxx::xxxx:xxxx:xxxx:xxxx]:00000
+
+	void ToSockadr(struct sockaddr* s) const;
+
+	// Returns 0xAABBCCDD for AA.BB.CC.DD on all platforms, which is the same format used by SetIP().
+	std::uint32_t	GetIPHostByteOrder(void) const;
+
+	// Returns a number that depends on the platform.  In most cases, this probably should not be used.
+	std::uint32_t	GetIPNetworkByteOrder(void) const;
+
+	bool	IsValid(void)        const;	// ip & port != 0
+	bool	IsBaseAdrValid(void) const;	// ip != 0
+	bool	IsLocalhost(void)    const;	// true, if this is the localhost IP 
+	bool	IsLoopback(void)     const;	// true if engine loopback buffers are used
+	bool	IsReservedAdr(void)  const;	// true, if this is a private LAN IP
+
+	bool operator==(const netadr_s& netadr) const { return (CompareAdr(netadr)); }
+	bool operator!=(const netadr_s& netadr) const { return !(CompareAdr(netadr)); }
+	bool operator<(const netadr_s& netadr) const;
+
+public:	// members are public to avoid to much changes
+	netadrtype_t    type;
+	std::uint8_t    ip[4];
+	std::uint16_t   port;
+} netadr_t;
diff --git a/r5dev/tier1/NetAdr2.cpp b/r5dev/tier1/NetAdr2.cpp
new file mode 100644
index 00000000..b385efbd
--- /dev/null
+++ b/r5dev/tier1/NetAdr2.cpp
@@ -0,0 +1,604 @@
+//===========================================================================//
+// 
+// Purpose: Protocol-agnostic implementation of the CNetAdr class.
+// 
+//===========================================================================//
+
+#include <core/stdafx.h>
+#include <tier1/NetAdr2.h>
+#ifndef NETCONSOLE
+#include <engine/sys_utils.h> // !! IMPLEMENT 'Warning(..)' !!
+#endif // !NETCONSOLE
+
+//-----------------------------------------------------------------------------
+// Purpose: constructor (use this when string contains <[IP]:PORT> or 'loopback'/'localhost').
+// input  : svInAdr - 
+//-----------------------------------------------------------------------------
+CNetAdr2::CNetAdr2(std::string svInAdr)
+{
+	SetType(netadrtype_t::NA_IP);
+	if (std::strcmp(svInAdr.c_str(), "loopback") == 0 || std::strcmp(svInAdr.c_str(), "::1") == 0)
+	{
+		SetType(netadrtype_t::NA_LOOPBACK);
+		svInAdr = "[127.0.0.1" + GetPort(svInAdr);
+	}
+	else if (std::strcmp(svInAdr.c_str(), "localhost"))
+	{
+		svInAdr = "[127.0.0.1" + GetPort(svInAdr);
+	}
+
+	// [IP]:PORT
+	m_svip = GetBase(svInAdr);
+	SetVersion();
+
+	if (GetVersion() == netadrversion_t::NA_V4)
+	{
+		reinterpret_cast<sockaddr_in*>(&m_sadr)->sin_port = htons(stoi(GetPort()));
+	}
+	else if (GetVersion() == netadrversion_t::NA_V6)
+	{
+		reinterpret_cast<sockaddr_in6*>(&m_sadr)->sin6_port = htons(stoi(GetPort()));
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: constructor (expects string format <IPv4/IPv6> <PORT>).
+// input  : svInAdr - 
+//			svInPort - 
+//-----------------------------------------------------------------------------
+CNetAdr2::CNetAdr2(std::string svInAdr, std::string svInPort)
+{
+	SetType(netadrtype_t::NA_IP);
+
+	if (std::strcmp(svInAdr.c_str(), "loopback") == 0 || std::strcmp(svInAdr.c_str(), "::1") == 0)
+	{
+		SetType(netadrtype_t::NA_LOOPBACK);
+	}
+	else if (std::strcmp(svInAdr.c_str(), "localhost") == 0)
+	{
+		svInAdr = "127.0.0.1";
+	}
+
+	if (std::strstr(svInAdr.c_str(), "["))
+	{
+		svInAdr = GetBase(svInAdr);
+	}
+
+	SetIPAndPort(svInAdr, svInPort);
+
+	if (m_version == netadrversion_t::NA_V4)
+	{
+		reinterpret_cast<sockaddr_in*>(&m_sadr)->sin_port = htons(stoi(GetPort()));
+	}
+	else if (m_version == netadrversion_t::NA_V6)
+	{
+		reinterpret_cast<sockaddr_in6*>(&m_sadr)->sin6_port = htons(stoi(GetPort()));
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: destructor.
+//-----------------------------------------------------------------------------
+CNetAdr2::~CNetAdr2(void)
+{
+	Clear();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the IP address.
+// input  : svInAdr - 
+//-----------------------------------------------------------------------------
+void CNetAdr2::SetIP(std::string svInAdr)
+{
+	m_svip = "[" + svInAdr + "]";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the port.
+// input  : svInPort - 
+//-----------------------------------------------------------------------------
+void CNetAdr2::SetPort(std::string svInPort)
+{
+	m_svip += ":" + svInPort;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the IP address and port.
+// input  : svInAdr - 
+//			svInPort - 
+//-----------------------------------------------------------------------------
+void CNetAdr2::SetIPAndPort(std::string svInAdr, std::string svInPort)
+{
+	m_svip = "[" + svInAdr + "]:" + svInPort;
+	SetVersion();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the type.
+// Input  : type - 
+//-----------------------------------------------------------------------------
+void CNetAdr2::SetType(netadrtype_t type)
+{
+	m_type = type;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the IP version (IPv4/IPv6/INVALID) based on input.
+//-----------------------------------------------------------------------------
+void CNetAdr2::SetVersion(void)
+{
+	if (inet_pton(reinterpret_cast<sockaddr_in*>(&m_sadr)->sin_family, 
+		GetBase().c_str(), &reinterpret_cast<sockaddr_in*>(m_sadr)->sin_addr) &&
+		!std::strstr(GetBase().c_str(), "::"))
+	{
+		m_version = netadrversion_t::NA_V4;
+		return;
+	}
+	else if (inet_pton(reinterpret_cast<sockaddr_in6*>(&m_sadr)->sin6_family, 
+		GetBase().c_str(), &reinterpret_cast<sockaddr_in6*>(m_sadr)->sin6_addr))
+	{
+		m_version = netadrversion_t::NA_V6;
+		return;
+	}
+	m_version = netadrversion_t::NA_INVALID;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets IP address and port from sockaddr struct.
+// Input  : hSocket - 
+//-----------------------------------------------------------------------------
+void CNetAdr2:: SetFromSocket(int hSocket)
+{
+	Clear();
+	m_type = netadrtype_t::NA_IP;
+
+	sockaddr_storage address{};
+	socklen_t namelen = sizeof(address);
+	if (getsockname(hSocket, (sockaddr*)&address, &namelen) == 0)
+	{
+		SetFromSockadr(&address);
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets fields based on 'sockaddr' input.
+// Input  : *s - 
+//-----------------------------------------------------------------------------
+bool CNetAdr2::SetFromSockadr(sockaddr_storage* s)
+{
+	if (reinterpret_cast<sockaddr_in*>(s)->sin_family == AF_INET)
+	{
+		char szAdrv4[INET_ADDRSTRLEN]{};
+		sockaddr_in* pAdrv4 = reinterpret_cast<sockaddr_in*>(s);
+
+		inet_ntop(pAdrv4->sin_family, &pAdrv4->sin_addr, szAdrv4, sizeof(sockaddr_in));
+		SetIPAndPort(szAdrv4, std::to_string(ntohs(pAdrv4->sin_port)));
+		return true;
+	}
+	else if (reinterpret_cast<sockaddr_in6*>(s)->sin6_family == AF_INET6)
+	{
+		char szAdrv6[INET6_ADDRSTRLEN]{};
+		sockaddr_in6* pAdrv6 = reinterpret_cast<sockaddr_in6*>(s);
+
+		inet_ntop(pAdrv6->sin6_family, &pAdrv6->sin6_addr, szAdrv6, sizeof(sockaddr_in6));
+		SetIPAndPort(szAdrv6, std::to_string(ntohs(pAdrv6->sin6_port)));
+		return true;
+	}
+	return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: removes brackets and port from IP address.
+//-----------------------------------------------------------------------------
+std::string CNetAdr2::GetBase(void) const
+{
+	std::string svIpAdr = m_svip;
+	static std::regex rx("\\].*");
+	svIpAdr.erase(0, 1);
+	svIpAdr = std::regex_replace(svIpAdr, rx, "");
+
+	return svIpAdr;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: removes brackets and port from IP address.
+// Input  : svInAdr - 
+//-----------------------------------------------------------------------------
+std::string CNetAdr2::GetBase(std::string svInAdr) const
+{
+	static std::regex rx("\\].*");
+	svInAdr.erase(0, 1);
+	svInAdr = std::regex_replace(svInAdr, rx, "");
+
+	return svInAdr;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the IP address.
+// Input  : bBaseOnly - 
+//-----------------------------------------------------------------------------
+std::string CNetAdr2::GetIP(bool bBaseOnly) const
+{
+	if (GetType() == netadrtype_t::NA_LOOPBACK)
+	{
+		return "loopback";
+	}
+	else if (GetType() == netadrtype_t::NA_BROADCAST)
+	{
+		return "broadcast";
+	}
+	else if (GetType() == netadrtype_t::NA_IP)
+	{
+		if (bBaseOnly)
+		{
+			return GetBase();
+		}
+		else
+		{
+			return m_svip;
+		}
+	}
+	else
+	{
+		return "unknown";
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: removes brackets and port from IP address.
+//-----------------------------------------------------------------------------
+std::string CNetAdr2::GetPort(void) const
+{
+	std::string svport = m_svip;
+	static std::regex rx(".*\\]:");
+	svport = std::regex_replace(svport, rx, "");
+
+	return svport;
+}
+std::string CNetAdr2::GetPort(std::string svInPort) const
+{
+	static std::regex rx(".*\\]:");
+	svInPort = std::regex_replace(svInPort, rx, "");
+
+	return svInPort;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the IP address and port.
+//-----------------------------------------------------------------------------
+std::string CNetAdr2::GetIPAndPort(void) const
+{
+	return m_svip;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the address type.
+//-----------------------------------------------------------------------------
+netadrtype_t CNetAdr2::GetType(void) const
+{
+	return m_type;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the IP version.
+//-----------------------------------------------------------------------------
+netadrversion_t CNetAdr2::GetVersion(void) const
+{
+	return m_version;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: splits IP address into parts by their delimiters.
+// Output : string vector containing IP parts.
+//-----------------------------------------------------------------------------
+std::vector<std::string> CNetAdr2::GetParts(void) const
+{
+	std::vector<std::string> results;
+
+	// Make sure we have a valid address.
+	if (m_version == netadrversion_t::NA_INVALID || m_type != netadrtype_t::NA_IP)
+	{
+		assert(m_version == netadrversion_t::NA_INVALID && "Invalid IP address for 'GetParts()'.");
+		return results;
+	}
+
+	std::string svIpAdr = m_svip, svDelim;
+	std::string::size_type prev_pos = 0, curr_pos = 0;
+
+	// 000.000.000.000 -> vparts.
+	if (m_version == netadrversion_t::NA_V4)
+	{
+		svDelim = ".";
+	}
+	// 0000:0000:0000:0000:0000:0000:0000:0000 -> vparts.
+	else if (m_version == netadrversion_t::NA_V6)
+	{
+		svDelim = ":";
+		StringReplace(svIpAdr, "::", ":");
+	}
+
+	while ((curr_pos = svIpAdr.find(svDelim, curr_pos)) != std::string::npos)
+	{
+		std::string substr(svIpAdr.substr(prev_pos, curr_pos - prev_pos));
+
+		results.push_back(substr);
+		prev_pos = ++curr_pos;
+	}
+	results.push_back(m_svip.substr(prev_pos, curr_pos - prev_pos));
+
+	return results;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the size of the network family struct.
+//-----------------------------------------------------------------------------
+int CNetAdr2::GetSize(void) const
+{
+	if (GetVersion() == netadrversion_t::NA_V4)
+	{
+		return sizeof(sockaddr_in);
+	}
+	else if (GetVersion() == netadrversion_t::NA_V6)
+	{
+		return sizeof(sockaddr_in6);
+	}
+	return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets fields from 'sockaddr'.
+// Input  : *pSadr - 
+//-----------------------------------------------------------------------------
+void CNetAdr2::ToSockadr(sockaddr_storage* pSadr) const
+{
+	if (GetVersion() == netadrversion_t::NA_V4)
+	{
+		if (GetType() == netadrtype_t::NA_BROADCAST)
+		{
+			reinterpret_cast<sockaddr_in*>(pSadr)->sin_family = AF_INET;
+			reinterpret_cast<sockaddr_in*>(pSadr)->sin_port = htons(stoi(GetPort()));
+			reinterpret_cast<sockaddr_in*>(pSadr)->sin_addr.s_addr = INADDR_BROADCAST;
+		}
+		else if (GetType() == netadrtype_t::NA_IP)
+		{
+			reinterpret_cast<sockaddr_in*>(pSadr)->sin_family = AF_INET;
+			reinterpret_cast<sockaddr_in*>(pSadr)->sin_port = htons(stoi(GetPort()));;
+			inet_pton(AF_INET, GetBase().c_str(), &reinterpret_cast<sockaddr_in*>(pSadr)->sin_addr.s_addr);
+		}
+		else if (GetType() == netadrtype_t::NA_LOOPBACK)
+		{
+			reinterpret_cast<sockaddr_in*>(pSadr)->sin_family = AF_INET;
+			reinterpret_cast<sockaddr_in*>(pSadr)->sin_port = htons(stoi(GetPort()));;
+			reinterpret_cast<sockaddr_in*>(pSadr)->sin_addr.s_addr = INADDR_LOOPBACK;
+		}
+	}
+	else if (GetVersion() == netadrversion_t::NA_V6)
+	{
+		if (GetType() == netadrtype_t::NA_IP)
+		{
+			reinterpret_cast<sockaddr_in6*>(pSadr)->sin6_family = AF_INET6;
+			reinterpret_cast<sockaddr_in6*>(pSadr)->sin6_port = htons(stoi(GetPort()));;
+			inet_pton(AF_INET6, GetBase().c_str(), &reinterpret_cast<sockaddr_in6*>(pSadr)->sin6_addr);
+		}
+		else if (GetType() == netadrtype_t::NA_LOOPBACK)
+		{
+			reinterpret_cast<sockaddr_in6*>(pSadr)->sin6_family = AF_INET6;
+			reinterpret_cast<sockaddr_in6*>(pSadr)->sin6_port = htons(stoi(GetPort()));;
+			reinterpret_cast<sockaddr_in6*>(pSadr)->sin6_addr = in6addr_loopback;
+		}
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets fields from 'addrinfo'.
+// Input  : *pHint - 
+//-----------------------------------------------------------------------------
+void CNetAdr2::ToAdrinfo(addrinfo* pHint) const
+{
+	int results{ };
+	addrinfo hint{ }; // <-- TODO: Pass these instead.
+	if (GetVersion() == netadrversion_t::NA_V4)
+	{
+		hint.ai_family = AF_INET;
+		hint.ai_socktype = SOCK_STREAM;
+		hint.ai_protocol = IPPROTO_TCP;
+		hint.ai_flags = AI_PASSIVE;
+		results = getaddrinfo(GetBase().c_str(), GetPort().c_str(), &hint, &pHint);
+		if (results != 0)
+		{
+			// TODO: Implement 'Warning(..)' instead!
+#ifndef NETCONSOLE
+			DevMsg(eDLL_T::ENGINE, "Address info translation failed (%s)\n", gai_strerror(results));
+#else
+			printf("Address info translation failed (%s)\n", gai_strerror(results));
+#endif // !NETCONSOLE
+		}
+	}
+	else if (GetVersion() == netadrversion_t::NA_V6)
+	{
+		hint.ai_family = AF_INET6;
+		hint.ai_socktype = SOCK_STREAM;
+		hint.ai_protocol = IPPROTO_TCP;
+		hint.ai_flags = AI_PASSIVE;
+		results = getaddrinfo(GetBase().c_str(), GetPort().c_str(), &hint, &pHint);
+		if (results != 0)
+		{
+			// TODO: Implement 'Warning(..)' instead!
+#ifndef NETCONSOLE
+			DevMsg(eDLL_T::ENGINE, "Address info translation failed (%s)\n", gai_strerror(results));
+#else
+			printf("Address info translation failed (%s)\n", gai_strerror(results));
+#endif // !NETCONSOLE
+		}
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if we are localhost.
+//-----------------------------------------------------------------------------
+bool CNetAdr2::IsLocalhost(void) const
+{
+	return (std::strcmp(GetBase().c_str(), "127.0.0.1") == 0);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if we use the loopback buffers.
+//-----------------------------------------------------------------------------
+bool CNetAdr2::IsLoopback(void) const
+{
+	return GetType() == netadrtype_t::NA_LOOPBACK;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: check if address is reserved and not routable.
+//-----------------------------------------------------------------------------
+bool CNetAdr2::IsReservedAdr(void) const
+{
+	if (GetType() == netadrtype_t::NA_LOOPBACK)
+	{
+		return true;
+	}
+
+	if (GetType() == netadrtype_t::NA_IP)
+	{
+		std::vector<std::string> ip_parts = GetParts();
+
+		int n0 = stoi(ip_parts[0]);
+		int n1 = stoi(ip_parts[1]);
+
+		if ((n0 == 10)                          || // 10.x.x.x is reserved
+			(n0 == 127)                         || // 127.x.x.x 
+			(n0 == 172 && n1 >= 16 && n1 <= 31) || // 172.16.x.x - 172.31.x.x
+			(n0 == 192 && n1 >= 168))              // 192.168.x.x
+		{
+			return true;
+		}
+	}
+	return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: compares IP for equality (IPv4/IPv6).
+// Input  : *netAdr2 - 
+//			bBaseOnly - 
+// Output : true if equal, false otherwise.
+//-----------------------------------------------------------------------------
+bool CNetAdr2::CompareAdr(const CNetAdr2& netAdr2, bool bBaseOnly) const
+{
+	if (netAdr2.GetType() != GetType())
+	{
+		return false;
+	}
+
+	if (GetType() == netadrtype_t::NA_LOOPBACK)
+	{
+		return true;
+	}
+
+	if (GetType() == netadrtype_t::NA_BROADCAST)
+	{
+		return true;
+	}
+
+	if (GetType() == netadrtype_t::NA_IP)
+	{
+		if (!bBaseOnly && 
+			(std::strcmp(netAdr2.GetPort().c_str(), GetPort().c_str()) != 0))
+		{
+			return false;
+		}
+
+		if (std::strcmp(netAdr2.GetBase().c_str(), GetBase().c_str()) == 0)
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: compares Class-B IP for equality.
+// Input  : *netAdr2 - 
+// Output : true if equal, false otherwise.
+//-----------------------------------------------------------------------------
+bool CNetAdr2::CompareClassBAdr(const CNetAdr2& netAdr2) const
+{
+	if (netAdr2.m_version != netadrversion_t::NA_V4 && m_version != netadrversion_t::NA_V4)
+	{
+		return false;
+	}
+
+	if (netAdr2.GetType() != GetType())
+	{
+		return false;
+	}
+
+	if (GetType() == netadrtype_t::NA_LOOPBACK)
+	{
+		return true;
+	}
+
+	if (GetType() == netadrtype_t::NA_IP)
+	{
+		std::vector<std::string> v0 = netAdr2.GetParts();
+		std::vector<std::string> v1 = GetParts();
+
+		if (std::strcmp(v0[0].c_str(), v1[0].c_str()) == 0 && 
+			std::strcmp(v0[1].c_str(), v1[1].c_str()) == 0)
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: compares Class-C IP for equality.
+// Input  : *netAdr2 - 
+// Output : true if equal, false otherwise.
+//-----------------------------------------------------------------------------
+bool CNetAdr2::CompareClassCAdr(const CNetAdr2& netAdr2) const
+{
+	if (netAdr2.GetVersion() != netadrversion_t::NA_V4 && GetVersion() != netadrversion_t::NA_V4)
+	{
+		return false;
+	}
+
+	if (netAdr2.GetType() != GetType())
+	{
+		return false;
+	}
+
+	if (GetType() == netadrtype_t::NA_LOOPBACK)
+	{
+		return true;
+	}
+
+	if (GetType() == netadrtype_t::NA_IP)
+	{
+		std::vector<std::string> v0 = netAdr2.GetParts();
+		std::vector<std::string> v1 = GetParts();
+
+		if (std::strcmp(v0[0].c_str(), v1[0].c_str()) == 0 && 
+			std::strcmp(v0[1].c_str(), v1[1].c_str()) == 0 && 
+			std::strcmp(v0[2].c_str(), v1[2].c_str()) == 0)
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: clears IP address.
+//-----------------------------------------------------------------------------
+void CNetAdr2::Clear(void)
+{
+	m_svip.clear();
+	m_type    = netadrtype_t::NA_NULL;
+	m_version = netadrversion_t::NA_INVALID;
+	m_sadr    = {};
+}
diff --git a/r5dev/tier1/NetAdr2.h b/r5dev/tier1/NetAdr2.h
new file mode 100644
index 00000000..3086def3
--- /dev/null
+++ b/r5dev/tier1/NetAdr2.h
@@ -0,0 +1,88 @@
+#pragma once
+
+typedef struct netpacket_s netpacket_t;
+typedef struct __declspec(align(8)) netpacket_s
+{
+	DWORD         family_maybe;
+	sockaddr_in   sin;
+	WORD          sin_port;
+	char          gap16;
+	char          byte17;
+	DWORD         source;
+	double        received;
+	std::uint8_t* data;
+	std::uint64_t label;
+	BYTE          byte38;
+	std::uint64_t qword40;
+	std::uint64_t qword48;
+	BYTE          gap50[8];
+	std::uint64_t qword58;
+	std::uint64_t qword60;
+	std::uint64_t qword68;
+	int           less_than_12;
+	DWORD         wiresize;
+	BYTE          gap78[8];
+	struct netpacket_s* pNext;
+} netpacket_t;
+
+enum class netadrtype_t
+{
+	NA_NULL = 0,
+	NA_LOOPBACK,
+	NA_BROADCAST,
+	NA_IP,
+};
+
+enum class netadrversion_t
+{
+	NA_INVALID = -1,
+	NA_V4 = 4,
+	NA_V6 = 6,
+};
+
+class CNetAdr2
+{
+public:
+	CNetAdr2(void) {};
+	CNetAdr2(std::string svInAdr);
+	CNetAdr2(std::string svInAdr, std::string svInPort);
+	~CNetAdr2(void);
+
+	void SetIP(std::string svInAdr);
+	void SetPort(std::string svInPort);
+	void SetIPAndPort(std::string svInAdr, std::string svInPort);
+	void SetType(netadrtype_t version);
+	void SetVersion(void);
+	void SetFromSocket(int hSocket);
+	bool SetFromSockadr(sockaddr_storage* s);
+
+	std::string GetIP(bool bBaseOnly) const;
+	std::string GetPort(void) const;
+	std::string GetPort(std::string svInPort) const;
+	std::string GetIPAndPort(void) const;
+	netadrtype_t GetType(void) const;
+	netadrversion_t GetVersion(void) const;
+	std::string GetBase(void) const;
+	std::string GetBase(std::string svInAdr) const;
+	std::vector<std::string> GetParts(void) const;
+	int GetSize(void) const;
+
+	void ToSockadr(sockaddr_storage* pSadr) const;
+	void ToAdrinfo(addrinfo* pHint) const;
+
+	bool IsLocalhost(void) const;
+	bool IsLoopback(void) const;
+	bool IsReservedAdr(void) const;
+
+	bool CompareAdr(const CNetAdr2& adr2, bool bBaseOnly) const;
+	bool CompareClassBAdr(const CNetAdr2& adr2) const;
+	bool CompareClassCAdr(const CNetAdr2& adr2) const;
+
+	void Clear(void);
+
+private:
+	std::string       m_svip;
+	netadrtype_t      m_type{};
+	netadrversion_t   m_version{};
+	sockaddr_storage* m_sadr{};
+};
diff --git a/r5dev/tier2/socketcreator.cpp b/r5dev/tier2/socketcreator.cpp
new file mode 100644
index 00000000..2c0e564e
--- /dev/null
+++ b/r5dev/tier2/socketcreator.cpp
@@ -0,0 +1,367 @@
+//===========================================================================//
+//
+// Purpose: Server/Client dual-stack socket utility class
+//
+//===========================================================================//
+
+#include <core/stdafx.h>
+#include <tier1/NetAdr2.h>
+#include <tier2/socketcreator.h>
+#ifndef NETCONSOLE
+#include <engine/sys_utils.h>
+#endif // !NETCONSOLE
+#include <engine/net.h>
+#include <netconsole/netconsole.h>
+
+// TODO [AMOS] IMPLEMENT 'Warning(...)' for every DevMsg spew here..
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CSocketCreator::CSocketCreator(void)
+{
+	m_hListenSocket = -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CSocketCreator::~CSocketCreator(void)
+{
+	DisconnectSocket();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: accept new connections and walk open sockets and handle any incoming data
+//-----------------------------------------------------------------------------
+void CSocketCreator::RunFrame(void)
+{
+	if (IsListening())
+	{
+		ProcessAccept(); // handle any new connection requests
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: handle a new connection
+//-----------------------------------------------------------------------------
+void CSocketCreator::ProcessAccept(void)
+{
+	sockaddr_storage inClient{};
+	int nLengthAddr = sizeof(inClient);
+	int newSocket = accept(m_hListenSocket, reinterpret_cast<sockaddr*>(&inClient), &nLengthAddr);
+	if (newSocket == -1)
+	{
+		if (!IsSocketBlocking())
+		{
+#ifndef NETCONSOLE
+			DevMsg(eDLL_T::ENGINE, "Socket ProcessAccept Error: %s\n", NET_ErrorString(WSAGetLastError()));
+#else
+			printf("Socket ProcessAccept Error: %s\n", NET_ErrorString(WSAGetLastError()));
+#endif // !NETCONSOLE
+		}
+		return;
+	}
+
+	if (!ConfigureListenSocket(newSocket))
+	{
+		closesocket(newSocket);
+		return;
+	}
+
+	CNetAdr2 netAdr2;
+	netAdr2.SetFromSockadr(&inClient);
+
+	OnSocketAccepted(newSocket, netAdr2);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Configures a listen socket for use
+//-----------------------------------------------------------------------------
+bool CSocketCreator::ConfigureListenSocket(int iSocket)
+{
+	// Disable NAGLE as RCON cmds are small in size.
+	int nodelay = 1;
+	int v6only  = 0;
+	u_long opt  = 1;
+
+	setsockopt(iSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelay, sizeof(nodelay));
+	setsockopt(iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&nodelay, sizeof(nodelay));
+	setsockopt(iSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&v6only, sizeof(v6only));
+
+	int results = ioctlsocket(iSocket, FIONBIO, (u_long*)&opt); // Non-blocking.
+	if (results == -1)
+	{
+#ifndef NETCONSOLE
+		DevMsg(eDLL_T::ENGINE, "Socket accept 'ioctl(FIONBIO)' failed (%i)\n", WSAGetLastError());
+#else
+		printf("Socket accept 'ioctl(FIONBIO)' failed (%i)\n", WSAGetLastError());
+#endif // !NETCONSOLE
+		return false;
+	}
+	return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Configures a accepted socket for use
+//-----------------------------------------------------------------------------
+bool CSocketCreator::ConfigureConnectSocket(SocketHandle_t hSocket)
+{
+	int opt = 1;
+	int ret = 0;
+
+	ret = ioctlsocket(hSocket, FIONBIO, reinterpret_cast<u_long*>(&opt)); // Non-blocking
+	if (ret == -1)
+	{
+#ifndef NETCONSOLE
+		DevMsg(eDLL_T::ENGINE, "Socket ioctl(FIONBIO) failed (%s)\n", NET_ErrorString(WSAGetLastError()));
+#else
+		printf("Socket ioctl(FIONBIO) failed (%s)\n", NET_ErrorString(WSAGetLastError()));
+#endif // !NETCONSOLE
+		closesocket(hSocket);
+		return false;
+	}
+
+	// Disable NAGLE as RCON cmds are small in size.
+	int nodelay = 1;
+	setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelay, sizeof(nodelay));
+
+	return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: bind to a TCP port and accept incoming connections
+// Input  : *netAdr2 - 
+//			bListenOnAllInterfaces - 
+// Output : true on success, failed otherwise
+//-----------------------------------------------------------------------------
+bool CSocketCreator::CreateListenSocket(const CNetAdr2& netAdr2, bool bListenOnAllInterfaces = false)
+{
+	CloseListenSocket();
+	m_ListenAddress = netAdr2;
+	m_hListenSocket = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
+
+	if (m_hListenSocket != INVALID_SOCKET)
+	{
+		if (!ConfigureListenSocket(m_hListenSocket))
+		{
+			CloseListenSocket();
+			return false;
+		}
+
+		sockaddr_storage sadr{};
+		m_ListenAddress.ToSockadr(&sadr);
+
+		int results = bind(m_hListenSocket, reinterpret_cast<sockaddr*>(&sadr), m_ListenAddress.GetSize());
+		if (results == -1)
+		{
+#ifndef NETCONSOLE
+			DevMsg(eDLL_T::ENGINE, "Socket bind failed (%s)\n", NET_ErrorString(WSAGetLastError()));
+#else
+			printf("Socket bind failed (%s)\n", NET_ErrorString(WSAGetLastError()));
+#endif // !NETCONSOLE
+			CloseListenSocket();
+			return false;
+		}
+
+		results = listen(m_hListenSocket, SOCKET_TCP_MAX_ACCEPTS);
+		if (results == -1)
+		{
+#ifndef NETCONSOLE
+			DevMsg(eDLL_T::ENGINE, "Socket listen failed (%s)\n", NET_ErrorString(WSAGetLastError()));
+#else
+			printf("Socket listen failed (%s)\n", NET_ErrorString(WSAGetLastError()));
+#endif // !NETCONSOLE
+			CloseListenSocket();
+			return false;
+		}
+	}
+	return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: close an open rcon connection
+//-----------------------------------------------------------------------------
+void CSocketCreator::CloseListenSocket(void)
+{
+	if (m_hListenSocket != -1)
+	{
+		closesocket(m_hListenSocket);
+		m_hListenSocket = -1;
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: connect to the remote server
+// Input  : *netAdr2 - 
+//			bSingleSocker - 
+// Output : accepted socket index, SOCKET_ERROR (-1) if failed
+//-----------------------------------------------------------------------------
+int CSocketCreator::ConnectSocket(const CNetAdr2& netAdr2, bool bSingleSocket)
+{
+	if (bSingleSocket)
+	{ // NOTE: Closing an accepted socket will re-index all the sockets with higher indices
+		CloseAllAcceptedSockets();
+	}
+
+	SocketHandle_t hSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (hSocket == SOCKET_ERROR)
+	{
+#ifndef NETCONSOLE
+		DevMsg(eDLL_T::ENGINE, "Unable to create socket (%s)\n", NET_ErrorString(WSAGetLastError()));
+#else
+		printf("Unable to create socket (%s)\n", NET_ErrorString(WSAGetLastError()));
+#endif // !NETCONSOLE
+		return SOCKET_ERROR;
+	}
+
+	if (!ConfigureConnectSocket(hSocket))
+	{
+		return SOCKET_ERROR;
+	}
+
+	struct sockaddr_storage s{};
+	netAdr2.ToSockadr(&s);
+
+	int results = connect(hSocket, reinterpret_cast<sockaddr*>(&s), sizeof(s));
+	if (results == SOCKET_ERROR)
+	{
+		if (!IsSocketBlocking())
+		{
+#ifndef NETCONSOLE
+			DevMsg(eDLL_T::ENGINE, "Socket connection failed (%s)\n", NET_ErrorString(WSAGetLastError()));
+#else
+			printf("Socket connection failed (%s)\n", NET_ErrorString(WSAGetLastError()));
+#endif // !NETCONSOLE
+			closesocket(hSocket);
+			return SOCKET_ERROR;
+		}
+
+		fd_set writefds{};
+		timeval tv{};
+
+		tv.tv_usec = 0;
+		tv.tv_sec  = 1;
+
+		FD_ZERO(&writefds);
+		FD_SET(static_cast<u_int>(hSocket), &writefds);
+
+		if (select(hSocket + 1, NULL, &writefds, NULL, &tv) < 1) // block for at most 1 second
+		{
+			closesocket(hSocket); // took too long to connect to, give up
+			return SOCKET_ERROR;
+		}
+	}
+
+	// TODO: CRConClient check if connected.
+
+	int nIndex = OnSocketAccepted(hSocket, netAdr2);
+	return nIndex;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: closes all open sockets (listen + accepted)
+//-----------------------------------------------------------------------------
+void CSocketCreator::DisconnectSocket(void)
+{
+	CloseListenSocket();
+	CloseAllAcceptedSockets();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: handles new TCP requests and puts them in accepted queue
+// Input  : hSocket - 
+//			*netAdr2 - 
+// Output : accepted socket index, -1 if failed
+//-----------------------------------------------------------------------------
+int CSocketCreator::OnSocketAccepted(SocketHandle_t hSocket, CNetAdr2 netAdr2)
+{
+	AcceptedSocket_t pNewEntry;
+
+	pNewEntry.m_hSocket = hSocket;
+	pNewEntry.m_Address = netAdr2;
+	pNewEntry.m_pData   = new CConnectedNetConsoleData(hSocket);
+
+	m_hAcceptedSockets.push_back(pNewEntry);
+
+	int nIndex = (int)m_hAcceptedSockets.size() - 1;
+	return nIndex;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: close an accepted socket
+// Input  : nIndex - 
+//-----------------------------------------------------------------------------
+void CSocketCreator::CloseAcceptedSocket(int nIndex)
+{
+	if (nIndex >= m_hAcceptedSockets.size())
+	{
+		return;
+	}
+
+	AcceptedSocket_t& connected = m_hAcceptedSockets[nIndex];
+	closesocket(connected.m_hSocket);
+	m_hAcceptedSockets.erase(m_hAcceptedSockets.begin() + nIndex);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: close all accepted sockets
+//-----------------------------------------------------------------------------
+void CSocketCreator::CloseAllAcceptedSockets(void)
+{
+	int nCount = m_hAcceptedSockets.size();
+	for (int i = 0; i < nCount; ++i)
+	{
+		AcceptedSocket_t& connected = m_hAcceptedSockets[i];
+		closesocket(connected.m_hSocket);
+	}
+	m_hAcceptedSockets.clear();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the listening socket is created and listening
+//-----------------------------------------------------------------------------
+bool CSocketCreator::IsListening(void) const
+{
+	return m_hListenSocket != -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the socket would block because of the last socket command
+//-----------------------------------------------------------------------------
+bool CSocketCreator::IsSocketBlocking(void) const
+{
+	return (WSAGetLastError() == WSAEWOULDBLOCK);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns accepted socket count
+//-----------------------------------------------------------------------------
+int CSocketCreator::GetAcceptedSocketCount(void) const
+{
+	return m_hAcceptedSockets.size();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns accepted socket handle
+//-----------------------------------------------------------------------------
+SocketHandle_t CSocketCreator::GetAcceptedSocketHandle(int nIndex) const
+{
+	return m_hAcceptedSockets[nIndex].m_hSocket;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns accepted socket address
+//-----------------------------------------------------------------------------
+const CNetAdr2& CSocketCreator::GetAcceptedSocketAddress(int nIndex) const
+{
+	return m_hAcceptedSockets[nIndex].m_Address;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns accepted socket data
+//-----------------------------------------------------------------------------
+CConnectedNetConsoleData* CSocketCreator::GetAcceptedSocketData(int nIndex) const
+{
+	return m_hAcceptedSockets[nIndex].m_pData;
+}
diff --git a/r5dev/tier2/socketcreator.h b/r5dev/tier2/socketcreator.h
new file mode 100644
index 00000000..58c5b41d
--- /dev/null
+++ b/r5dev/tier2/socketcreator.h
@@ -0,0 +1,78 @@
+#pragma once
+#include "tier1/netadr2.h"
+
+typedef int SocketHandle_t;
+
+class CConnectedNetConsoleData
+{
+public:
+	SocketHandle_t m_hSocket                               {};
+	int  m_nCharsInCommandBuffer                           {};
+	char m_pszInputCommandBuffer[MAX_NETCONSOLE_INPUT_LEN] {};
+	bool m_bAuthorized                                     {}; // If set, this netconsole is authenticated.
+	bool m_bInputOnly                                      {}; // If set, don't send spew to this netconsole.
+	int  m_nFailedAttempts                                 {}; // Num failed authentication attempts.
+
+	CConnectedNetConsoleData(SocketHandle_t hSocket = -1)
+	{
+		m_nCharsInCommandBuffer = 0;
+		m_bAuthorized           = false;
+		m_hSocket               = hSocket;
+		m_bInputOnly            = false;
+	}
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: container class to handle network streams
+//-----------------------------------------------------------------------------
+class CSocketCreator
+{
+public:
+	CSocketCreator(void);
+	~CSocketCreator(void);
+
+	void RunFrame(void);
+	void ProcessAccept(void);
+
+	bool ConfigureListenSocket(int iSocket);
+	bool ConfigureConnectSocket(SocketHandle_t hSocket);
+
+	bool CreateListenSocket(const CNetAdr2& netAdr2, bool bListenOnAllInterfaces);
+	void CloseListenSocket(void);
+
+	int ConnectSocket(const CNetAdr2& netAdr2, bool bSingleSocket);
+	void DisconnectSocket(void);
+
+	int OnSocketAccepted(SocketHandle_t hSocket, CNetAdr2 netAdr2);
+
+	void CloseAcceptedSocket(int nIndex);
+	void CloseAllAcceptedSockets(void);
+
+	bool IsListening(void) const;
+	bool IsSocketBlocking(void) const;
+
+	int GetAcceptedSocketCount(void) const;
+	SocketHandle_t GetAcceptedSocketHandle(int nIndex) const;
+	const CNetAdr2& GetAcceptedSocketAddress(int nIndex) const;
+	CConnectedNetConsoleData* GetAcceptedSocketData(int nIndex) const;
+
+public:
+	struct AcceptedSocket_t
+	{
+		SocketHandle_t            m_hSocket{};
+		CNetAdr2                  m_Address{};
+		CConnectedNetConsoleData* m_pData = nullptr;
+
+		bool operator==(const AcceptedSocket_t& rhs) const { return (m_Address.CompareAdr(rhs.m_Address, false) == 0); }
+	};
+
+	std::vector<AcceptedSocket_t> m_hAcceptedSockets{};
+	SocketHandle_t                m_hListenSocket   {}; // Used to accept connections.
+	CNetAdr2                      m_ListenAddress   {}; // Address used to listen on.
+
+private:
+	enum
+	{
+		SOCKET_TCP_MAX_ACCEPTS = 2
+	};
+};