Browse Source

Updated to newest kNet.

Lasse Öörni 13 years ago
parent
commit
317a502b7e
71 changed files with 2337 additions and 1063 deletions
  1. 13 13
      Engine/Network/Network.cpp
  2. 2 2
      Engine/Network/Network.h
  3. 28 26
      ThirdParty/kNet/CMakeLists.txt
  4. 24 24
      ThirdParty/kNet/README.txt
  5. 31 0
      ThirdParty/kNet/WHATSNEW.txt
  6. 1 0
      ThirdParty/kNet/include/kNet.h
  7. 46 0
      ThirdParty/kNet/include/kNet/64BitAllocDebugger.h
  8. 3 5
      ThirdParty/kNet/include/kNet/BasicSerializedDataTypes.h
  9. 23 6
      ThirdParty/kNet/include/kNet/DataDeserializer.h
  10. 133 18
      ThirdParty/kNet/include/kNet/DataSerializer.h
  11. 5 8
      ThirdParty/kNet/include/kNet/DebugMemoryLeakCheck.h
  12. 5 14
      ThirdParty/kNet/include/kNet/EndPoint.h
  13. 2 4
      ThirdParty/kNet/include/kNet/EventArray.h
  14. 8 10
      ThirdParty/kNet/include/kNet/FragmentedTransferManager.h
  15. 9 4
      ThirdParty/kNet/include/kNet/IMessageHandler.h
  16. 1 1
      ThirdParty/kNet/include/kNet/Lockable.h
  17. 22 17
      ThirdParty/kNet/include/kNet/MessageConnection.h
  18. 9 11
      ThirdParty/kNet/include/kNet/MessageListParser.h
  19. 3 7
      ThirdParty/kNet/include/kNet/NetException.h
  20. 12 14
      ThirdParty/kNet/include/kNet/Network.h
  21. 14 14
      ThirdParty/kNet/include/kNet/NetworkMessage.h
  22. 8 10
      ThirdParty/kNet/include/kNet/NetworkServer.h
  23. 103 0
      ThirdParty/kNet/include/kNet/NetworkSimulator.h
  24. 2 4
      ThirdParty/kNet/include/kNet/NetworkWorkerThread.h
  25. 9 11
      ThirdParty/kNet/include/kNet/RingBuffer.h
  26. 2 1
      ThirdParty/kNet/include/kNet/SequentialIntegerSet.h
  27. 3 6
      ThirdParty/kNet/include/kNet/SerializationStructCompiler.h
  28. 1 3
      ThirdParty/kNet/include/kNet/SerializedDataIterator.h
  29. 21 15
      ThirdParty/kNet/include/kNet/Socket.h
  30. 1 3
      ThirdParty/kNet/include/kNet/Sort.h
  31. 21 23
      ThirdParty/kNet/include/kNet/StatsEventHierarchy.h
  32. 3 3
      ThirdParty/kNet/include/kNet/StdCMallocHeap.h
  33. 1 3
      ThirdParty/kNet/include/kNet/TCPMessageConnection.h
  34. 2 4
      ThirdParty/kNet/include/kNet/Thread.h
  35. 15 0
      ThirdParty/kNet/include/kNet/Types.h
  36. 28 11
      ThirdParty/kNet/include/kNet/UDPMessageConnection.h
  37. 1 0
      ThirdParty/kNet/include/kNet/WaitFreeQueue.h
  38. 1 1
      ThirdParty/kNet/include/kNet/qt/GraphDialog.h
  39. 3 1
      ThirdParty/kNet/include/kNet/qt/MessageConnectionDialog.h
  40. 1 1
      ThirdParty/kNet/include/kNet/qt/NetworkDialog.h
  41. 52 0
      ThirdParty/kNet/include/kNet/qt/NetworkSimulationDialog.h
  42. 7 0
      ThirdParty/kNet/include/kNet/win32/WS2Include.h
  43. 1 0
      ThirdParty/kNet/include/kNetFwd.h
  44. 144 0
      ThirdParty/kNet/src/64BitAllocDebugger.cpp
  45. 163 10
      ThirdParty/kNet/src/DataDeserializer.cpp
  46. 249 16
      ThirdParty/kNet/src/DataSerializer.cpp
  47. 36 38
      ThirdParty/kNet/src/FragmentedTransferManager.cpp
  48. 88 88
      ThirdParty/kNet/src/MessageConnection.cpp
  49. 11 13
      ThirdParty/kNet/src/MessageListParser.cpp
  50. 80 81
      ThirdParty/kNet/src/Network.cpp
  51. 12 14
      ThirdParty/kNet/src/NetworkLogging.cpp
  52. 1 3
      ThirdParty/kNet/src/NetworkMessage.cpp
  53. 68 70
      ThirdParty/kNet/src/NetworkServer.cpp
  54. 121 0
      ThirdParty/kNet/src/NetworkSimulator.cpp
  55. 40 42
      ThirdParty/kNet/src/NetworkWorkerThread.cpp
  56. 109 110
      ThirdParty/kNet/src/SerializationStructCompiler.cpp
  57. 20 22
      ThirdParty/kNet/src/SerializedDataIterator.cpp
  58. 68 49
      ThirdParty/kNet/src/Socket.cpp
  59. 39 30
      ThirdParty/kNet/src/TCPMessageConnection.cpp
  60. 8 4
      ThirdParty/kNet/src/Thread.cpp
  61. 257 155
      ThirdParty/kNet/src/UDPMessageConnection.cpp
  62. 1 1
      ThirdParty/kNet/src/boost/BoostThread.cpp
  63. 3 2
      ThirdParty/kNet/src/qt/GraphDialog.cpp
  64. 14 1
      ThirdParty/kNet/src/qt/MessageConnectionDialog.cpp
  65. 4 2
      ThirdParty/kNet/src/qt/NetworkDialog.cpp
  66. 90 0
      ThirdParty/kNet/src/qt/NetworkSimulationDialog.cpp
  67. 1 1
      ThirdParty/kNet/src/unix/UnixClock.cpp
  68. 1 1
      ThirdParty/kNet/src/unix/UnixEvent.cpp
  69. 26 9
      ThirdParty/kNet/src/unix/UnixEventArray.cpp
  70. 2 2
      ThirdParty/kNet/src/unix/UnixThread.cpp
  71. 1 1
      ThirdParty/kNet/src/win32/W32Clock.cpp

+ 13 - 13
Engine/Network/Network.cpp

@@ -67,7 +67,7 @@ Network::~Network()
     network_ = 0;
 }
 
-void Network::HandleMessage(kNet::MessageConnection* source, kNet::message_id_t id, const char* data, size_t numBytes)
+void Network::HandleMessage(kNet::MessageConnection *source, kNet::packet_id_t packetId, kNet::message_id_t msgId, const char *data, size_t numBytes)
 {
     // Only process messages from known sources
     Connection* connection = GetConnection(source);
@@ -75,31 +75,31 @@ void Network::HandleMessage(kNet::MessageConnection* source, kNet::message_id_t
     {
         MemoryBuffer msg(data, numBytes);
         
-        switch (id)
+        switch (msgId)
         {
         case MSG_IDENTITY:
-            connection->ProcessIdentity(id, msg);
+            connection->ProcessIdentity(msgId, msg);
             return;
         
         case MSG_CONTROLS:
-            connection->ProcessControls(id, msg);
+            connection->ProcessControls(msgId, msg);
             return;
             
         case MSG_SCENELOADED:
-            connection->ProcessSceneLoaded(id, msg);
+            connection->ProcessSceneLoaded(msgId, msg);
             return;
             
         case MSG_REQUESTPACKAGE:
         case MSG_PACKAGEDATA:
-            connection->ProcessPackageDownload(id, msg);
+            connection->ProcessPackageDownload(msgId, msg);
             return;
             
         case MSG_LOADSCENE:
-            connection->ProcessLoadScene(id, msg);
+            connection->ProcessLoadScene(msgId, msg);
             return;
         
         case MSG_SCENECHECKSUMERROR:
-            connection->ProcessSceneChecksumError(id, msg);
+            connection->ProcessSceneChecksumError(msgId, msg);
             return;
             
         case MSG_CREATENODE:
@@ -110,12 +110,12 @@ void Network::HandleMessage(kNet::MessageConnection* source, kNet::message_id_t
         case MSG_COMPONENTDELTAUPDATE:
         case MSG_COMPONENTLATESTDATA:
         case MSG_REMOVECOMPONENT:
-            connection->ProcessSceneUpdate(id, msg);
+            connection->ProcessSceneUpdate(msgId, msg);
             return;
             
         case MSG_REMOTEEVENT:
         case MSG_REMOTENODEEVENT:
-            connection->ProcessRemoteEvent(id, msg);
+            connection->ProcessRemoteEvent(msgId, msg);
             return;
         }
         
@@ -124,7 +124,7 @@ void Network::HandleMessage(kNet::MessageConnection* source, kNet::message_id_t
         
         VariantMap eventData;
         eventData[P_CONNECTION] = (void*)connection;
-        eventData[P_MESSAGEID] = (int)id;
+        eventData[P_MESSAGEID] = (int)msgId;
         eventData[P_DATA].SetBuffer(msg.GetData(), msg.GetSize());
         connection->SendEvent(E_NETWORKMESSAGE, eventData);
     }
@@ -132,9 +132,9 @@ void Network::HandleMessage(kNet::MessageConnection* source, kNet::message_id_t
         LOGWARNING("Discarding message from unknown MessageConnection " + ToString((void*)source));
 }
 
-u32 Network::ComputeContentID(kNet::message_id_t id, const char* data, size_t numBytes)
+u32 Network::ComputeContentID(kNet::message_id_t msgId, const char* data, size_t numBytes)
 {
-    switch (id)
+    switch (msgId)
     {
     case MSG_CONTROLS:
         // Return fixed content ID for controls

+ 2 - 2
Engine/Network/Network.h

@@ -51,9 +51,9 @@ public:
     ~Network();
     
     /// Handle a kNet message from either a client or the server.
-    virtual void HandleMessage(kNet::MessageConnection* source, kNet::message_id_t id, const char* data, size_t numBytes);
+    virtual void HandleMessage(kNet::MessageConnection *source, kNet::packet_id_t packetId, kNet::message_id_t msgId, const char *data, size_t numBytes);
     /// Compute the content ID for a message.
-    virtual u32 ComputeContentID(kNet::message_id_t id, const char* data, size_t numBytes);
+    virtual u32 ComputeContentID(kNet::message_id_t msgId, const char *data, size_t numBytes);
     /// Handle a new client connection.
     virtual void NewConnectionEstablished(kNet::MessageConnection* connection);
     /// Handle a client disconnection.

+ 28 - 26
ThirdParty/kNet/CMakeLists.txt

@@ -37,9 +37,14 @@ endmacro()
 #set(USE_BOOST TRUE)
 #set(BOOST_ROOT "TODO_SpecifyYourBoostRootHereIfCMakeAutoSearchFails")
 
+# TinyXML is embedded to the repository, so you can safely keep this true.
+# However, it is not required for core kNet use, but only for the MessageCompiler tool, in which
+# case you can disable it here by setting USE_TINYXML to FALSE, and excluding MessageCompiler from the build.
 #set(USE_TINYXML TRUE)
-#set(TINYXML_ROOT "TODO_SpecifyYourTinyXMLRootHereIfUsingTinyXML")
+set(TINYXML_ROOT "src/tinyxml")
 
+# If you want to enable the use of visual debugging/profiling windows, uncomment the following line (must
+# have Qt installed and set up)
 #set(USE_QT TRUE)
 
 # To enable specific flags only in debug mode, use the following syntax.
@@ -49,10 +54,13 @@ endmacro()
 # Affects only Boost threads. When native Win32 threads are used, the id is stored on creation.
 AddCompilationDefine(KNET_ENABLE_WINXP_SUPPORT)
 
-# If set, extra code is inserted in debug mode to assert that certain thread race conditions don't occur.
-AddCompilationDefine(KNET_THREAD_CHECKING_ENABLED)
+if (NOT USE_BOOST) # USE_BOOST and KNET_THREAD_CHECKING_ENABLED are mutually exclusive, because boost::thread_id() does not work across .dll boundaries.
+   # If set, extra code is inserted in debug mode to assert that certain thread race conditions don't occur.
+   # (This can considerably slow down kNet performance)
+   #AddCompilationDefine(KNET_THREAD_CHECKING_ENABLED)
+endif()
 
-# Enable internal LOG messaging if this flag is enabled. Comment this out to squeeze the last bit of
+# Enable internal LOG messaging if this flag is enabled. Comment this out to squeeze the last bit of 
 # extra performance by avoiding all logging-related string operations.
 #AddCompilationDefine(KNET_LOGGING_SUPPORT_ENABLED)
 
@@ -76,17 +84,17 @@ if (USE_BOOST)
    endif()
 endif()
 
+file(GLOB kNetSourceFiles ./src/*.cpp)
+file(GLOB kNetHeaderFiles ./include/*.h ./include/kNet/*.h ./include/kNet/*.inl)
+
 if (USE_TINYXML)
    AddCompilationDefine(KNET_USE_TINYXML)
    
+   file(GLOB TinyXmlSourceFiles ${TINYXML_ROOT}/*.cpp)
+   set(kNetSourceFiles ${kNetSourceFiles} ${TinyXmlSourceFiles})
    include_directories(${TINYXML_ROOT})
-   link_directories(${TINYXML_ROOT})
-   link_directories(${TINYXML_ROOT}/Release)
 endif()
 
-file(GLOB kNetSourceFiles ./src/*.cpp)
-file(GLOB kNetHeaderFiles ./include/*.h ./include/kNet/*.h ./include/kNet/*.inl)
-
 if (USE_QT)
    AddCompilationDefine(KNET_USE_QT)
    
@@ -157,33 +165,28 @@ elseif (UNIX)
 
    set(kNetSourceFiles ${kNetSourceFiles} ${kNetUnixSourceFiles})
    set(kNetHeaderFiles ${kNetHeaderFiles} ${kNetUnixHeaderFiles})
+
+   add_definitions(-DUNIX)
 endif()
 
 #AddCompilationUnitNameDefines(kNetSourceFiles)
 #TODO: To clean up, move the lines of code below into a macro, like shown above. Disabled for now since passing a list to a CMake macro was problematic.
-# Urho3D: use only on Windows
-if (WIN32)
-   foreach(srcFile ${kNetSourceFiles})
-      get_filename_component(baseName ${srcFile} NAME)
-      set_source_files_properties(${srcFile} PROPERTIES COMPILE_FLAGS "-DDEBUG_CPP_NAME=\"\\\"${baseName}\"\\\"")
-   endforeach()
-endif()
+foreach(srcFile ${kNetSourceFiles})
+   get_filename_component(baseName ${srcFile} NAME)
+   set_source_files_properties(${srcFile} PROPERTIES COMPILE_FLAGS "-DDEBUG_CPP_NAME=\"\\\"${baseName}\"\\\"")
+endforeach()
 
 add_library(kNet STATIC ${kNetSourceFiles} ${kNetHeaderFiles})
 
-if (USE_TINYXML)
-   target_link_libraries(kNet debug tinyxmld.lib optimized tinyxml.lib)
-endif()
-
 # Add the main kNet include directory root folder to all projects.
 include_directories(./include)
 
-# Urho3D: add the Container library
-include_directories(../../Engine/Container)
-set(kNetLinkLibraries ${kNetLinkLibraries} Container)
-
 if (USE_BOOST)
-   find_package(Boost 1.38.0 COMPONENTS thread)
+   if (WIN32)
+      find_package(Boost 1.38.0 COMPONENTS thread)
+   else()
+      find_package(Boost)
+   endif()
    if (Boost_FOUND)
       include_directories(${Boost_INCLUDE_DIRS})
       link_directories(${Boost_LIBRARY_DIRS})
@@ -210,4 +213,3 @@ target_link_libraries(kNet ${kNetLinkLibraries})
 
 #add_subdirectory(tests)
 
-#set_target_properties(kNet Tests ConnectFlood FileTransfer FirewallTest HelloClient HelloServer LatencyTest MessageCompiler SilenceTest SimpleChat SpeedTest TrashTalk PROPERTIES 

+ 24 - 24
ThirdParty/kNet/README.txt

@@ -3,42 +3,42 @@
 kNet is a low-level networking protocol library designed for bit-efficient realtime streaming of custom application-specified messages on top of TCP or UDP. kNet is written in C++.
 
 
-
    Supported Platforms.
 
 kNet has been tested to build on the following platforms:
  - Windows 7 & Visual Studio 2010 Professional
  - Windows 7 & Visual Studio 2008 Standard
  - Ubuntu 9.04 & GCC 4.4.1
-
-
-
+ - Windows 7 & MinGW GCC 4.6.1 versioned 20111118 (beware though, MinGW is not actively supported)
+
+ 
+   Documentation.
+ 
+kNet uses doxygen to generate its documentation. See the web page http://clb.demon.fi/knet/ for an online hosted copy.
+ 
+ 
    Building kNet.
 
 kNet uses cmake (2.6 or newer) as its build system. On Linux it can use pthreads or boost v1.38.0 or newer for threading support. On Windows a CMake flag USE_BOOST can be used to specify whether to depend on boost or not. By default USE_BOOST is on.
 
-Windows:
- - Install cmake. 
- - Optional: Install and build Boost. Edit the root CMakeLists.txt to specify the source directory to boost path.
- - If you do not want to use Boost, edit the root CMakeLists.txt and comment out the USE_BOOST directive.
- - Optional: Download and install TinyXML. In TinyXML configuration, adjust the CRT runtimes to use the DLL versions. In the root CMakeLists.txt, uncomment #set(USE_TINYXML) and change TINYXML_ROOT to point to the TinyXML path.
- - If you do not want to use TinyXML, edit the root CMakeLists.txt and make sure the USE_TINYXML directive is commented out. This will disable the functionality of SerializedMessageList/MessageListParser though. 
- - Optional: kNet can use Qt to provider debug statistics and profiling windows. Install and build Qt (4.6.2 or newer recommended) and uncomment #set(USE_QT TRUE) in the root CMakeLists.txt.
- - Execute in project root folder the command 'cmake -G "Visual Studio 10"' (case sensitive!), or click the cmake_vs2010.bat.
- - If CMake fails to find your Qt installation, or if you want to explicitly specify the source location, set the QMAKESPEC and QTDIR environment variables or alter the cmake_vs2008.bat/cmake_vs2010.bat files.
- - Open and build the kNet.sln.
-
-Linux:
- - Install Boost libraries and cmake.
- - If you want to use TinyXML, manually specify the TinyXML source directory to root CMakeLists.txt. Otherwise, comment out the USE_TINYXML directive.
- - If you want to use Qt, make sure USE_QT is defined in the root CMakeLists.txt. Otherwise, comment out that directive.
- - run 'cmake .' in kNet root folder.
- - run 'make'.
+kNet can optionally use Qt to provide a debugging and statistics window for profiling and simulation of different network conditions.
+
+Steps:
+ 1. Install cmake. 
+ 2a. Optional: Install and build Boost. Edit the root CMakeLists.txt to specify the source directory to boost path.
+ 2b. If you do not want to use Boost, edit the root CMakeLists.txt and comment out the USE_BOOST directive. If Boost is not used, kNet will utilize native threading APIs on each platform (WIN32 CreateThread or POSIX threads). There are no functional differences with using either.
+ 3. Optional: If you do not want to use TinyXML, edit the root CMakeLists.txt and make sure the USE_TINYXML directive is commented out. This will disable the functionality of SerializedMessageList/MessageListParser though. 
+ 4. Optional: kNet can use Qt to provider debug statistics and profiling windows. Install and build Qt (4.6.2 or newer recommended) and uncomment #set(USE_QT TRUE) in the root CMakeLists.txt.
+ 5a. Windows VS2008: Execute in project root folder the command 'cmake -G "Visual Studio 9"' (case sensitive!), or click on the cmake_vs2008.bat.
+ 5b. Windows VS2010: Execute in project root folder the command 'cmake -G "Visual Studio 10"' (case sensitive!), or click on the cmake_vs2010.bat.
+ 5c. Linux and Mac: Run 'cmake .' in kNet root folder.
+ 6. If CMake fails to find your Qt installation, or if you want to explicitly specify the source location, set the QMAKESPEC and QTDIR environment variables or alter the cmake_vs2008.bat/cmake_vs2010.bat files.
+ 7a. Windows: Open and build the kNet.sln.
+ 7b. Linux and Mac: Run 'make'.
 
 The project output files are placed in the directory kNet/lib.
 
 
-
    Contributors.
 
 The following people have contributed to the project:
@@ -53,9 +53,9 @@ The following people have contributed to the project:
    Kari Vatjus-Anttila
    Lasse Öörni
 
-
-
+   
    Links.
 
+The kNet repository is hosted at github: https://github.com/juj/kNet. Please report bugs using the github issue tracker.
 There exists a Wireshark dissector plugin for kNet: http://chiru.cie.fi/chiru-sharedfolder/knet-tundra-v.0.0.8.tar.gz .
 A SCTP -enabled branch of kNet is being developed at https://bitbucket.org/karivatj/knet-sctp/ .

+ 31 - 0
ThirdParty/kNet/WHATSNEW.txt

@@ -0,0 +1,31 @@
+Version 2.7, 2012-03-23
+-----------
+- Added new InOrderTest sample.
+- Added support for duplicating datagrams in NetworkSimulator.
+- Fixed a bug in duplicated message receive handling.
+- Added support for corrupting datagrams in NetworkSimulator.
+- Added support for 64-bit memory pointer to 32-bit splicing issue detecting for Windows.
+- Removed KNET_THREAD_CHECKING_ENABLED from being enabled if USE_BOOST is enabled. This is to avoid a bug with boost::thread_id() not working across dynamic library boundaries. (KNET_THREAD_CHECKING_ENABLED and USE_BOOST are now mutually exclusive)
+
+Version 2.6, 2012-01-25
+-----------
+- Implement a NetworkSimulator middle layer for adding delays and packet loss to outbound messaging (UDP only).
+- Enhance DataSerializer with new data types:
+   - lossy quantized floats
+   - minifloats
+   - arithmetic encoding
+   - linear algebra types: quaternion, vector, spherical direction vectors with or without magnitude
+- Incorporate TinyXML inside kNet repository, to avoid requiring extra configuration.
+- Pass UDP datagram packet ID to client code for manual client-side latest-data-guarantee checks.
+- Enable build on MinGW GCC 4.6.1 version 20111118.
+- Bug fixes.
+
+Version 2.5, 2012-01-02
+-----------
+- Fix build on Mac.
+- Performance improvements.
+- Added pthreads support for linux and Mac. Thanks to Lasse Öörni for the implementation.
+- Improved networking statistics window display.
+- Bug fixes.
+
+For history on older kNet versions, see the repository history at github.

+ 1 - 0
ThirdParty/kNet/include/kNet.h

@@ -52,6 +52,7 @@
 #include "kNet/Types.h"
 #include "kNet/VLEPacker.h"
 #include "kNet/WaitFreeQueue.h"
+#include "kNet/64BitAllocDebugger.h"
 
 #ifdef KNET_USE_QT
 #include "kNet/qt/NetworkDialog.h"

+ 46 - 0
ThirdParty/kNet/include/kNet/64BitAllocDebugger.h

@@ -0,0 +1,46 @@
+/* Copyright The kNet Project.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License. */
+#pragma once
+
+/** @file 64BitAllocDebugger.h
+	@brief Virtually reserves all memory on Windows in the < 4GB memory area so that all
+		pointers used by the application are outside the 32-bit range.
+	
+	Idea and code taken from http://randomascii.wordpress.com/2012/02/14/64-bit-made-easy/ */
+
+#ifdef _WIN64
+
+#include <vector>
+
+class BottomMemoryAllocator
+{
+public:
+	BottomMemoryAllocator();
+
+	~BottomMemoryAllocator();
+
+	std::vector<void *> virtualAllocated;
+	std::vector<void *> heapAllocated;
+	std::vector<void *> mallocAllocated;
+
+	void ReserveBottomMemory();
+	void FreeBottomMemory();
+};
+
+#else
+
+/// On other platforms than 64-bit Windows, this debugging feature is not enabled.
+class BottomMemoryAllocator {};
+
+#endif

+ 3 - 5
ThirdParty/kNet/include/kNet/BasicSerializedDataTypes.h

@@ -16,10 +16,8 @@
 /** @file BasicSerializedDataTypes.h
 	@brief Describes the basic POD data types that are used by the DataSerializer and DataDeserializer objects. */
 
-// Modified by Lasse Öörni for Urho3D
-
-#include "List.h"
-#include "Str.h"
+#include <list>
+#include <string>
 
 #include "Types.h"
 
@@ -87,6 +85,6 @@ template<> struct SerializedDataTypeTraits<float> { static const BasicSerialized
 template<> struct SerializedDataTypeTraits<double> { static const BasicSerializedDataType type = SerialDouble; static const int bitSize = 64; };
 template<> struct SerializedDataTypeTraits<char*> { static const BasicSerializedDataType type = SerialString; static const int bitSize = 0; };
 template<> struct SerializedDataTypeTraits<const char*> { static const BasicSerializedDataType type = SerialString; static const int bitSize = 0; };
-template<> struct SerializedDataTypeTraits<String> { static const BasicSerializedDataType type = SerialString; static const int bitSize = 0; };
+template<> struct SerializedDataTypeTraits<std::string> { static const BasicSerializedDataType type = SerialString; static const int bitSize = 0; };
 
 } // ~kNet

+ 23 - 6
ThirdParty/kNet/include/kNet/DataDeserializer.h

@@ -16,8 +16,6 @@
 /** @file DataDeserializer.h
 	@brief The class \ref kNet::DataDeserializer DataDeserializer. */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include "kNetBuildConfig.h"
 #include "kNet/Types.h"
 
@@ -82,12 +80,31 @@ public:
 	/// The returned string will only contain ascii values in the range [32, 253], 0x0D, 0x0A, 0x09. Other values will 
 	/// be replaced with a space bar character (0x20). Because of this string validation method, do not use this function
 	/// to extract binary data of any kind (base64-encoded is fine).
-	String ReadString();
+	std::string ReadString();
 
 	/// Reads the given amount of bits and packs them into a u32, which is returned.
 	/// @param numBits the number of bits to read, [1, 32].
 	u32 ReadBits(int numBits);
 
+	float ReadUnsignedFixedPoint(int numIntegerBits, int numDecimalBits);
+
+	float ReadSignedFixedPoint(int numIntegerBits, int numDecimalBits);
+
+	float ReadQuantizedFloat(float minRange, float maxRange, int numBits);
+
+	float ReadMiniFloat(bool signBit, int exponentBits, int mantissaBits, int exponentBias);
+
+	void ReadNormalizedVector2D(int numBits, float &x, float &y);
+
+	void ReadVector2D(int magnitudeIntegerBits, int magnitudeDecimalBits, int directionBits, float &x, float &y);
+	void ReadNormalizedVector3D(int numBitsYaw, int numBitsPitch, float &x, float &y, float &z);
+	void ReadVector3D(int numBitsYaw, int numBitsPitch, int magnitudeIntegerBits, int magnitudeDecimalBits, float &x, float &y, float &z);
+
+	void ReadArithmeticEncoded(int numBits, int &val1, int max1, int &val2, int max2);
+	void ReadArithmeticEncoded(int numBits, int &val1, int max1, int &val2, int max2, int &val3, int max3);
+	void ReadArithmeticEncoded(int numBits, int &val1, int max1, int &val2, int max2, int &val3, int max3, int &val4, int max4);
+	void ReadArithmeticEncoded(int numBits, int &val1, int max1, int &val2, int max2, int &val3, int max3, int &val4, int max4, int &val5, int max5);
+
 	u32 GetDynamicElemCount();
 
 	/// @return The number of bytes left in the stream to read.
@@ -109,10 +126,10 @@ public:
 	const char *CurrentData() const { return data + BytePos(); }
 
 	/// Advances the read pointer with the given amount of bits. Can only be used in nontemplate read mode.
-	void SkipBits(size_t numBits);
+	void SkipBits(int numBits);
 
 	/// Advances the read pointer with the given amount of bytes. Can only be used in nontemplate read mode.
-	void SkipBytes(size_t numBytes) { SkipBits(numBytes * 8); }
+	void SkipBytes(int numBytes) { SkipBits(numBytes * 8); }
 
 private:
 	/// The data pointer to read from.
@@ -148,7 +165,7 @@ T DataDeserializer::Read()
 	return value;
 }
 
-template<> String DataDeserializer::Read<String>();
+template<> std::string DataDeserializer::Read<std::string>();
 
 template<> bool DataDeserializer::Read<bit>();
 

+ 133 - 18
ThirdParty/kNet/include/kNet/DataSerializer.h

@@ -16,11 +16,9 @@
 /** @file DataSerializer.h
 	@brief The class \ref kNet::DataSerializer DataSerializer. Stores POD data to bit streams. */
 
-// Modified by Lasse Öörni for Urho3D
-
+#include <vector>
 #include <cassert>
-
-#include "Str.h"
+#include <string>
 
 #include "kNetBuildConfig.h"
 #include "kNet/SharedPtr.h"
@@ -36,7 +34,7 @@ namespace kNet
 
 struct SerializedMessage : public RefCountable
 {
-	PODVector<char> data;
+	std::vector<char> data;
 };
 
 /// DataSerializer is a helper class that can be used to serialize data types to a stream of raw bits 
@@ -61,15 +59,15 @@ public:
 
 	/// Instantiates a new DataSerializer that writes to the given vector. 
 	/// @param maxBytes The maximum number of bytes that the message can take up space.
-	explicit DataSerializer(PODVector<char> &data, size_t maxBytes);
+	explicit DataSerializer(std::vector<char> &data, size_t maxBytes);
 
 	/// Instantiates a new DataSerializer that writes to the given vector, using a message template. 
 	/// @param maxBytes The maximum number of bytes that the message can take up space.
-	DataSerializer(PODVector<char> &data, size_t maxBytes, const SerializedMessageDesc *msgTemplate);
+	DataSerializer(std::vector<char> &data, size_t maxBytes, const SerializedMessageDesc *msgTemplate);
 
 	/// Appends a single element of the passed type. If you are using a serialization template to
 	/// aid in serialization, the type T may be any of the types bit, u8, s8, u16, s16, u32, s32, u64, s64, float, double, 
-	/// const char * or String.
+	/// const char * or std::string.
 	/// If you are not using a serialization template, you may pass in any type that is a POD type and can be reinterpret_casted
 	/// to a u8 buffer and memcpy'd to a byte buffer.
 	template<typename T>
@@ -80,15 +78,15 @@ public:
 
 	/// Appends the given number of bits to the stream.
 	/// @param value The variable where the bits are taken from. The bits are read from the LSB first, towards the MSB end of the value.
-	/// @param amount The number of bits to read, in the range [1, 32].
-	void AppendBits(u32 value, int amount);
+	/// @param numBits The number of bits to write, in the range [1, 32].
+	void AppendBits(u32 value, int numBits);
 
 	/// Adds a given string as length-prepended (not zero-padded). In the message template, use a
 	/// parameter of type 's8' with dynamicCount field set to e.g. 8.
 	void AddString(const char *str);
 
 	/// See \ref void kNet::DataSerializer::AddString(const char *str); "".
-	void AddString(const String &str) { AddString(str.CString()); }
+	void AddString(const std::string &str) { AddString(str.c_str()); }
 
 	/// Appends the given amount of elements from the passed array.
 	template<typename T>
@@ -98,6 +96,111 @@ public:
 	/// this function. A serialization template may not be used when calling this function.
 	void AddAlignedByteArray(const void *data, u32 numBytes);
 
+	/// Writes the given non-negative float quantized to the given fixed-point precision.
+	/// @param value The floating-point value to send. This float must have a value in the range [0, 2^numIntegerBits[.
+	/// @param numIntegerBits The number of bits to use to represent the integer part.
+	/// @param numDecimalBits The number of bits to use to represent the fractional part.
+	/// @note Before writing the value, it is clamped to range specified above to ensure that the written value does not
+	///	 result in complete garbage due to over/underflow.
+	/// @note The total number of bits written is numIntegerBits + numDecimalBits, which must in total be <= 32.
+	/// @return The bit pattern that was written to the buffer.
+	u32 AddUnsignedFixedPoint(int numIntegerBits, int numDecimalBits, float value);
+
+	/// Writes the given float quantized to the given fixed-point precision.
+	/// @param value The floating-point value to send. This float must have a value in the range [-2^(numIntegerBits-1), 2^(numIntegerBits-1)[.
+	/// @param numIntegerBits The number of bits to use to represent the integer part.
+	/// @param numDecimalBits The number of bits to use to represent the fractional part.
+	/// @note Before writing the value, it is clamped to range specified above to ensure that the written value does not
+	///	 result in complete garbage due to over/underflow.
+	/// @note The total number of bits written is numIntegerBits + numDecimalBits, which must in total be <= 32.
+	/// @return The bit pattern that was written to the buffer.
+	u32 AddSignedFixedPoint(int numIntegerBits, int numDecimalBits, float value);
+
+	/// Writes the given float quantized to the number of bits, that are distributed evenly over the range [minRange, maxRange].
+	/// @param value The floating-point value to send. This float must have a value in the range [minRange, maxRange].
+	/// @param numBits The number of bits to use for representing the value. The total number of different values written is then 2^numBits, 
+	///	 which are evenly distributed across the range [minRange, maxRange]. The value numBits must satisfy 1 <= numBits <= 30.
+	/// @param minRange The lower limit for the value that is being written.
+	/// @param maxRange The upper limit for the value that is being written.
+	/// @return The bit pattern that was written to the buffer.
+	/// @note This function performs quantization, which results in lossy serialization/deserialization.
+	u32 AddQuantizedFloat(float minRange, float maxRange, int numBits, float value);
+
+	/// Writes the given float with a reduced amount of bit precision.
+	/// @param signBit If true, a signed float is written (one bit is reserved for sign-magnitude representation).
+	///                If false, an unsigned float is written. Negative numbers clamp to zero (-inf -> zero as well).
+	/// @param exponentBits The number of bits to use to store the exponent value, in the range [1, 8].
+	/// @param mantissaBits The number of bits to use to store the mantissa value, in the range [1, 23].
+	/// @param exponentBias For IEEE-754 floats, the signed exponent is converted to unsigned number by adding an offset bias.
+	///                This field specifies the bias to use. Usually it is ok to reserve the equal number of exponent values
+	///                for negative and positive exponents, meaning that exponentBias == (1 << (exponentBits - 1)) - 1 is an ok default.
+	/// @param value The floating point number to encode.
+	/// @note This function performs quantization, which results in lossy serialization/deserialization.
+	/// @note An example for 8-bit minifloats: signBit==true, exponentBits==3, mantissaBits==4, exponentBias==3.
+	/// @note IEEE-754 16-bit 'half16': signBit==true, exponentBits==5, mantissaBits==10, exponentBias==15.
+	///       See http://en.wikipedia.org/wiki/Half_precision_floating-point_format
+	/// @note IEEE-754 32-bit floats: signBit==true, exponentBits==8, mantissaBits==23, exponentBias==127.
+	void AddMiniFloat(bool signBit, int exponentBits, int mantissaBits, int exponentBias, float value);
+
+	/// Writes the given normalized 2D vector compressed to a single 1D polar angle value. Then the angle is quantized to the specified 
+	/// precision.
+	/// @param x The x coordinate of the 2D vector.
+	/// @param y The y coordinate of the 2D vector.
+	/// @param numBits The number of bits to quantize the representation down to. This value must satisfy 1 <= numBits <= 30.
+	/// @note The vector (x,y) does not need to be normalized for this function to work properly (don't bother enforcing normality in
+	///	advance prior to calling this). When deserializing, (x,y) is reconstructed as a normalized direction vector.
+	/// @note Do not call this function with (x,y) == (0,0).
+	/// @note This function performs quantization, which results in lossy serialization/deserialization.
+	void AddNormalizedVector2D(float x, float y, int numBits);
+
+	/// Writes the given 2D vector in polar form and quantized to the given precision.
+	/// The length of the 2D vector is stored as fixed-point in magnitudeIntegerBits.magnitudeDecimalBits format.
+	/// The direction of the 2D vector is stores with directionBits.
+	/// @param x The x coordinate of the 2D vector.
+	/// @param y The y coordinate of the 2D vector.
+	/// @param magnitudeIntegerBits The number of bits to use for the integral part of the vector's length. This means
+	///	 that the maximum length of the vector to be written by this function is < 2^magnitudeIntegerBits.
+	/// @param magnitudeDecimalBits The number of bits to use for the fractional part of the vector's length.
+	/// @param directionBits The number of bits of precision to use for storing the direction of the 2D vector.
+	/// @return The number of bits written to the stream.
+	/// @important This function does not write a fixed amount of bits to the stream, but omits the direction if the length is zero. 
+	///	 Therefore only use DataDeserializer::ReadVector2D to extract the vector from the buffer.
+	int AddVector2D(float x, float y, int magnitudeIntegerBits, int magnitudeDecimalBits, int directionBits);
+
+	/// Writes the given normalized 3D vector converted to spherical form (azimuth/yaw, inclination/pitch) and quantized to the specified range.
+	/// The given vector (x,y,z) must be normalized in advance.
+	/// @param numBitsYaw The number of bits to use for storing the azimuth/yaw part of the vector.
+	/// @param numBitsPitch The number of bits to use for storing the inclination/pitch part of the vector.
+	/// @note After converting the euclidean (x,y,z) to spherical (yaw, pitch) format, the yaw value is expressed in the range [-pi, pi] and pitch
+	///	 is expressed in the range [-pi/2, pi/2]. Therefore, to maintain consistent precision, the condition numBitsYaw == numBitsPitch + 1 
+	///	 should hold. E.g. If you specify 8 bits for numBitsPitch, then you should specify 9 bits for numBitsYaw to have yaw & pitch use the same
+	///	 amount of precision.
+	/// @note This function uses the convention that the +Y axis points towards up, i.e. +Y is the "Zenith direction", and the X-Z plane is the horizontal
+	///	 "map" plane.
+	void AddNormalizedVector3D(float x, float y, float z, int numBitsYaw, int numBitsPitch);
+
+	/// Writes the given 3D vector converted to spherical form (azimuth/yaw, inclination/pitch, length) and quantized to the specified range.
+	/// @param numBitsYaw The number of bits to use for storing the azimuth/yaw part of the vector.
+	/// @param numBitsPitch The number of bits to use for storing the inclination/pitch part of the vector.
+	/// @param magnitudeIntegerBits The number of bits to use for the integral part of the vector's length. This means
+	///	 that the maximum length of the vector to be written by this function is < 2^magnitudeIntegerBits.
+	/// @param magnitudeDecimalBits The number of bits to use for the fractional part of the vector's length.
+	/// @return The number of bits written to the stream.
+	/// @important This function does not write a fixed amount of bits to the stream, but omits the direction if the length is zero. 
+	///	 Therefore only use DataDeserializer::ReadVector3D to extract the vector from the buffer.
+	/// @note After converting the euclidean (x,y,z) to spherical (yaw, pitch) format, the yaw value is expressed in the range [-pi, pi] and pitch
+	///	 is expressed in the range [-pi/2, pi/2]. Therefore, to maintain consistent precision, the condition numBitsYaw == numBitsPitch + 1 
+	///	 should hold. E.g. If you specify 8 bits for numBitsPitch, then you should specify 9 bits for numBitsYaw to have yaw & pitch use the same
+	///	 amount of precision.
+	/// @note This function uses the convention that the +Y axis points towards up, i.e. +Y is the "Zenith direction", and the X-Z plane is the horizontal
+	///	 "map" plane.
+	int AddVector3D(float x, float y, float z, int numBitsYaw, int numBitsPitch, int magnitudeIntegerBits, int magnitudeDecimalBits);
+
+	void AddArithmeticEncoded(int numBits, int val1, int max1, int val2, int max2);
+	void AddArithmeticEncoded(int numBits, int val1, int max1, int val2, int max2, int val3, int max3);
+	void AddArithmeticEncoded(int numBits, int val1, int max1, int val2, int max2, int val3, int max3, int val4, int max4);
+	void AddArithmeticEncoded(int numBits, int val1, int max1, int val2, int max2, int val3, int max3, int val4, int max4, int val5, int max5);
+
 	/// Sets the number of instances in a varying element.
 	void SetVaryingElemSize(u32 count);
 
@@ -115,7 +218,7 @@ public:
 	/// @return The number of bits filled so far total.
 	size_t BitsFilled() const { return elemOfs * 8 + bitOfs; }
 
-	/// @return The total capacity of the buffer we are filling into.
+	/// @return The total capacity of the buffer we are filling into, in bytes.
 	size_t Capacity() const { return maxBytes; }
 
 	/// Returns the current byte offset the DataSerializer is writing to.
@@ -124,6 +227,18 @@ public:
 	/// Returns the current bit offset in the current byte this DataSerializer is writing to, [0, 7].
 	size_t BitOffset() const { return bitOfs; }
 
+    /// Returns the total number of bits that can still be serialized into this DataSerializer object before overflowing (which throws an exception).
+    size_t BitsLeft() const { return Capacity()*8 - BitsFilled(); }
+
+    /// Returns the total number of full bytes that can still be serialized into this DataSerializer object before overflowing (which throws an exception).
+    /// @return floor(BitsLeft()/8).
+    size_t BytesLeft() const { return BitsLeft() / 8; }
+
+	/// Returns the bit serialized at the given bit index of this buffer.
+	bool DebugReadBit(int bitIndex) const;
+
+	/// Returns a string of 0's and 1's corresponding to the given bit indices.
+	std::string DebugReadBits(int startIndex, int endIndex) const;
 private:
 	void AppendByte(u8 byte);
 	void AppendUnalignedByte(u8 byte);
@@ -168,7 +283,7 @@ void DataSerializer::Add(const T &value)
 
 template<> void DataSerializer::Add<char*>(char * const & value);
 template<> void DataSerializer::Add<const char*>(const char * const & value);
-template<> void DataSerializer::Add<String>(const String &value);
+template<> void DataSerializer::Add<std::string>(const std::string &value);
 
 template<> void DataSerializer::Add<bit>(const bit &value);
 
@@ -252,15 +367,15 @@ public:
 };
 
 template<>
-class TypeSerializer<String>
+class TypeSerializer<std::string>
 {
 public:
-	static size_t Size(const String &value)
+	static size_t Size(const std::string &value)
 	{
-		return value.Length()+1;
+		return value.length()+1;
 	}
 
-	static void SerializeTo(DataSerializer &dst, const String &src)
+	static void SerializeTo(DataSerializer &dst, const std::string &src)
 	{
 #ifdef _DEBUG
 		size_t bitPos = dst.BitsFilled();
@@ -271,7 +386,7 @@ public:
 #endif
 	}
 
-	static void DeserializeFrom(DataDeserializer &src, String &dst)
+	static void DeserializeFrom(DataDeserializer &src, std::string &dst)
 	{
 		dst = src.ReadString();
 	}

+ 5 - 8
ThirdParty/kNet/include/kNet/DebugMemoryLeakCheck.h

@@ -16,19 +16,16 @@
 /** @file DebugMemoryLeakCheck.h
 	@brief Provides overloads of operators new and delete for tracking memory leaks. */
 
-// Modified by Lasse Öörni for Urho3D
-
 #if defined (WIN32) && defined(_DEBUG) && defined(KNET_MEMORY_LEAK_CHECK)
 
 #include <new>
 #include <crtdbg.h>
 
-// Include these files beforehand to avoid compilation errors from our operator new redefine.
-#include <ios>
-#include "List.h"
-#include "Map.h"
-#include "Set.h"
-#include "Vector.h"
+// On MSVC2008, include these files beforehand to avoid compilation errors from our operator new redefine.
+#if _MSC_VER == 1500
+#include <ios> 
+#include <map>
+#endif
 
 #ifndef _CRTDBG_MAP_ALLOC
 #define _CRTDBG_MAP_ALLOC

+ 5 - 14
ThirdParty/kNet/include/kNet/EndPoint.h

@@ -16,8 +16,6 @@
 /** @file EndPoint.h
 	@brief The class \ref kNet::EndPoint Endpoint. Represents an endpoint of a network connection. */
 
-// Modified by Lasse Öörni for Urho3D
-
 #if defined(UNIX) || defined(ANDROID)
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -26,8 +24,7 @@
 
 #include <cstring>
 #include <cstdio>
-
-#include "Str.h"
+#include <string>
 
 namespace kNet
 {
@@ -67,12 +64,6 @@ struct EndPoint
 
 		return false;
 	}
-	
-	///\todo Not IPv6-capable.
-	bool operator == (const EndPoint &rhs) const
-	{
-		return ip[0] == rhs.ip[0] && ip[1] == rhs.ip[1] && ip[2] == rhs.ip[2] && ip[3] == rhs.ip[3] && port == rhs.port;
-	}
 
 	///\todo Not IPv6-capable.
 	static EndPoint FromSockAddrIn(const sockaddr_in &addr)
@@ -116,19 +107,19 @@ struct EndPoint
 	}
 
 	///\todo Not IPv6-capable.
-	String IPToString() const
+	std::string IPToString() const
 	{
 		char str[256];
 		sprintf(str, "%d.%d.%d.%d", (unsigned int)ip[0], (unsigned int)ip[1], (unsigned int)ip[2], (unsigned int)ip[3]);
-		return String(str);
+		return std::string(str);
 	}
 
 	///\todo Not IPv6-capable.
-	String ToString() const
+	std::string ToString() const
 	{
 		char str[256];
 		sprintf(str, "%d.%d.%d.%d:%d", (unsigned int)ip[0], (unsigned int)ip[1], (unsigned int)ip[2], (unsigned int)ip[3], (unsigned int)port);
-		return String(str);
+		return std::string(str);
 	}
 };
 

+ 2 - 4
ThirdParty/kNet/include/kNet/EventArray.h

@@ -16,9 +16,7 @@
 /** @file EventArray.h
 	@brief The class \ref kNet::EventArray EventArray. Allows listening to multiple events at once.*/
 
-// Modified by Lasse Öörni for Urho3D
-
-#include "Vector.h"
+#include <vector>
 
 #include "Event.h"
 
@@ -68,7 +66,7 @@ private:
 	timeval tv;
 	/// Cache a list of all added events here. This is to remember the order in which the events were added, so that
 	/// we can correctly return the occurred event with the smallest index.
-	Vector<Event> cachedEvents;
+	std::vector<Event> cachedEvents;
 #endif
 };
 

+ 8 - 10
ThirdParty/kNet/include/kNet/FragmentedTransferManager.h

@@ -17,10 +17,8 @@
 	@brief The classes \ref kNet::FragmentedSendManager FragmentedSendManager and 
 	\ref kNet::FragmentedSendManager FragmentedReceiveManager. For managing partial transfers. */
 
-// Modified by Lasse Öörni for Urho3D
-
-#include "Vector.h"
-#include "List.h"
+#include <vector>
+#include <list>
 
 namespace kNet
 {
@@ -39,7 +37,7 @@ public:
 		/// The total number of fragments in this message.
 		size_t totalNumFragments;
 
-		List<NetworkMessage*> fragments;
+		std::list<NetworkMessage*> fragments;
 
 		void AddMessage(NetworkMessage *message);
 
@@ -47,7 +45,7 @@ public:
 		bool RemoveMessage(NetworkMessage *message);
 	};
 
-	typedef List<FragmentedTransfer> TransferList;
+	typedef std::list<FragmentedTransfer> TransferList;
 	TransferList transfers;
 
 	/// Returns a new FragmentedTransfer. A transferID for this transfer will not have been allocated here.
@@ -72,7 +70,7 @@ public:
 	{
 		int fragmentIndex;
 
-		PODVector<char> data;
+		std::vector<char> data;
 	};
 
 	struct ReceiveTransfer
@@ -81,14 +79,14 @@ public:
 
 		int numTotalFragments;
 
-		Vector<ReceiveFragment> fragments;
+		std::vector<ReceiveFragment> fragments;
 	};
 
-	Vector<ReceiveTransfer> transfers;
+	std::vector<ReceiveTransfer> transfers;
 
 	void NewFragmentStartReceived(int transferID, int numTotalFragments, const char *data, size_t numBytes);
 	bool NewFragmentReceived(int transferID, int fragmentNumber, const char *data, size_t numBytes);
-	void AssembleMessage(int transferID, PODVector<char> &assembledData);
+	void AssembleMessage(int transferID, std::vector<char> &assembledData);
 	void FreeMessage(int transferID);
 };
 

+ 9 - 4
ThirdParty/kNet/include/kNet/IMessageHandler.h

@@ -23,8 +23,6 @@ namespace kNet
 
 class MessageConnection;
 
-typedef unsigned long message_id_t;
-
 /// IMessageHandler is a callback object used by the MessageConnection to invoke the main application
 /// whenever a message has been received.
 class IMessageHandler
@@ -34,13 +32,20 @@ public:
 
 	/// Called whenever the network stack has received a message that the application
 	/// needs to process.
-	virtual void HandleMessage(MessageConnection *source, message_id_t id, const char *data, size_t numBytes) = 0;
+	/// @param source The kNet connection this message originates from.
+	/// @param packetId A unique incrementing id counter that identifies the number of the UDP packet this message originated from. Use this
+	///     to prune out-of-order messages if necessary. kNet automatically discards duplicate messages, and can do out-of-order discarding
+	///     automatically as well, if you use message content ID's. Otherwise, you can use the packetId to do the pruning manually.
+	/// @param messageId Contains the id (or the "type") of the message. This is the one you specified when sending the message
+	/// @param data Points to the raw data buffer. This buffer may be zero if numBytes == 0.
+	/// @param numBytes The length of the raw data buffer, in bytes.
+	virtual void HandleMessage(MessageConnection *source, packet_id_t packetId, message_id_t messageId, const char *data, size_t numBytes) = 0;
 
 	/// Called by the network library to ask the application to produce a content ID
 	/// associated with the given message. If the application returns 0, the message doesn't
 	/// have a ContentID and it is processed normally.
 	/// The ContentID of the message is used to determine if a message replaces another.
-	virtual u32 ComputeContentID(message_id_t id, const char *data, size_t numBytes)
+	virtual u32 ComputeContentID(message_id_t messageId, const char *data, size_t numBytes)
 	{
 		// The default behavior is to not have a content ID on any message.
 		return 0;

+ 1 - 1
ThirdParty/kNet/include/kNet/Lockable.h

@@ -22,10 +22,10 @@
 #elif defined(WIN32)
 #include <Windows.h>
 #else
-#include <cassert>
 #include <pthread.h>
 #endif
 
+#include <assert.h>
 #include "PolledTimer.h"
 #include "NetworkLogging.h"
 

+ 22 - 17
ThirdParty/kNet/include/kNet/MessageConnection.h

@@ -16,16 +16,14 @@
 /** @file MessageConnection.h
 	@brief The MessageConnection and ConnectionStatistics classes. */
 
-// Modified by Lasse Öörni for Urho3D
-
+#include <vector>
+#include <map>
 #include <utility>
-
-#include "Vector.h"
-#include "Map.h"
-#include "Set.h"
+#include <set>
 
 #include "kNetBuildConfig.h"
 #include "WaitFreeQueue.h"
+#include "NetworkSimulator.h"
 #include "LockFreePoolAllocator.h"
 #include "Lockable.h"
 #include "Socket.h"
@@ -55,7 +53,7 @@ class Network;
 class NetworkWorkerThread;
 class FragmentedSendManager;
 
-#ifdef WIN32
+#ifdef _MSC_VER
 struct FragmentedSendManager::FragmentedTransfer;
 #endif
 
@@ -71,7 +69,7 @@ struct ConnectionStatistics
 		bool replyReceived;        ///< True of PingReply has already been received for this.
 	};
 	/// Contains an entry for each recently performed Ping operation, sorted by age (oldest first).
-	PODVector<PingTrack> ping;
+	std::vector<PingTrack> ping;
 
 	/// Remembers both in- and outbound traffic events on the socket.
 	struct TrafficTrack
@@ -85,7 +83,7 @@ struct ConnectionStatistics
 		unsigned long bytesOut;    ///< The total number of bytes the sent datagrams contained. 
 	};
 	/// Contains an entry for each recent traffic event (data in/out) on the connection, sorted by age (oldest first).
-	PODVector<TrafficTrack> traffic;
+	std::vector<TrafficTrack> traffic;
 
 	/// Remembers the send/receive time of a datagram with a certain ID.
 	struct DatagramIDTrack
@@ -94,7 +92,7 @@ struct ConnectionStatistics
 		packet_id_t packetID;
 	};
 	/// Contains an entry for each recently received packet, sorted by age (oldest first).
-	PODVector<DatagramIDTrack> recvPacketIDs;
+	std::vector<DatagramIDTrack> recvPacketIDs;
 };
 
 /// Comparison object that sorts the two messages by their priority (higher priority/smaller number first).
@@ -125,7 +123,7 @@ enum ConnectionState
 };
 
 /// Returns a textual representation of a ConnectionState.
-String ConnectionStateToString(ConnectionState state);
+std::string ConnectionStateToString(ConnectionState state);
 
 // Prevent confusion with Win32 functions
 #ifdef SendMessage
@@ -307,7 +305,7 @@ public:
 	void FreeMessage(NetworkMessage *msg); // [main and worker thread]
 	
 	/// Returns a single-line message describing the connection state.
-	String ToString() const; // [main and worker thread]
+	std::string ToString() const; // [main and worker thread]
 
 	/// Dumps a long multi-line status message of this connection state to stdout.
 	void DumpStatus() const; // [main thread]
@@ -333,6 +331,9 @@ public:
 	/// Returns the total number of bytes (excluding IP and TCP/UDP headers) that have been sent from this connection.
 	u64 BytesOutTotal() const { return bytesOutTotal; } // [main and worker thread]
 
+	/// Returns the simulator object which can be used to apply network condition simulations to this connection.
+	NetworkSimulator &NetworkSendSimulator() { return networkSendSimulator; }
+
 	/// Stores all the statistics about the current connection. This data is periodically recomputed
 	/// by the network worker thread and shared to the client through a lock.
 	Lockable<ConnectionStatistics> statistics; // [main and worker thread]
@@ -514,6 +515,10 @@ protected:
 	u64 bytesInTotal;
 	u64 bytesOutTotal;
 
+	/// Stores the current settigns related to network conditions testing.
+	/// By default, the simulator is disabled.
+	NetworkSimulator networkSendSimulator;
+
 	/// A running number attached to each outbound message (not present in network stream) to 
 	/// break ties when deducing which message should come before which.
 	unsigned long outboundMessageNumberCounter; // [worker thread]
@@ -523,15 +528,15 @@ protected:
 	unsigned long outboundReliableMessageNumberCounter; // [worker thread]
 
 	/// A (messageID, contentID) pair.
-	typedef Pair<u32, u32> MsgContentIDPair;
+	typedef std::pair<u32, u32> MsgContentIDPair;
 
-	typedef Map<MsgContentIDPair, Pair<packet_id_t, tick_t> > ContentIDReceiveTrack;
+	typedef std::map<MsgContentIDPair, std::pair<packet_id_t, tick_t> > ContentIDReceiveTrack;
 
 	/// Each (messageID, contentID) pair has a packetID "stamp" associated to them to track 
 	/// and decimate out-of-order received obsoleted messages.
 	ContentIDReceiveTrack inboundContentIDStamps; // [worker thread]
 
-	typedef Map<MsgContentIDPair, NetworkMessage*> ContentIDSendTrack;
+	typedef std::map<MsgContentIDPair, NetworkMessage*> ContentIDSendTrack;
 
 	ContentIDSendTrack outboundContentIDMessages; // [worker thread]
 
@@ -543,7 +548,7 @@ protected:
 	/// by a newer packet and should not be processed.
 	/// @return True if the packet should be processed (there was no superceding record), and
 	///         false if the packet is old and should be discarded.
-	bool CheckAndSaveContentIDStamp(u32 messageID, u32 contentID, packet_id_t packetID); // [worker thread]
+	bool CheckAndSaveContentIDStamp(message_id_t messageID, u32 contentID, packet_id_t packetID); // [worker thread]
 
 	void SplitAndQueueMessage(NetworkMessage *message, bool internalQueue, size_t maxFragmentSize); // [main and worker thread]
 
@@ -557,7 +562,7 @@ protected:
 	/// Private ctor - MessageConnections are instantiated by Network and NetworkServer classes.
 	explicit MessageConnection(Network *owner, NetworkServer *ownerServer, Socket *socket, ConnectionState startingState);
 
-	virtual bool HandleMessage(packet_id_t /*packetID*/, u32 /*messageID*/, const char * /*data*/, size_t /*numBytes*/) { return false; } // [main thread]
+	virtual bool HandleMessage(packet_id_t /*packetID*/, message_id_t /*messageID*/, const char * /*data*/, size_t /*numBytes*/) { return false; } // [main thread]
 };
 
 template<typename SerializableData>

+ 9 - 11
ThirdParty/kNet/include/kNet/MessageListParser.h

@@ -16,9 +16,7 @@
 /** @file MessageListParser.h
 	@brief The SerializedMessageList class. */
 
-// Modified by Lasse Öörni for Urho3D
-
-#include "Vector.h"
+#include <vector>
 
 #include "BasicSerializedDataTypes.h"
 
@@ -40,7 +38,7 @@ struct SerializedElementDesc
 
 	/// A string version of this type is stored here. This field is used if the type of this element is something 
 	/// else than a basic type.
-	String typeString;
+	std::string typeString;
 
 	/// If true, the number of times this element is instanced is specified in the stream.
 	bool varyingCount;
@@ -50,10 +48,10 @@ struct SerializedElementDesc
 	int count;
 
 	/// The name of this element.
-	String name;
+	std::string name;
 
 	/// If this element denotes a structure (type == SerialStruct), then this vector contains all the child nodes.
-	Vector<SerializedElementDesc*> elements;
+	std::vector<SerializedElementDesc*> elements;
 
 	/// The parent element, or 0 if this is the root element.
 	SerializedElementDesc *parent;
@@ -65,7 +63,7 @@ struct SerializedMessageDesc
 	/// This is a weak pointer to the root element of this message description. The memory is owned by the SerializedMessageList
 	/// where this SerializedMessageDesc belongs to.
 	SerializedElementDesc *data;
-	String name;
+	std::string name;
 	u32 id;
 	bool reliable;
 	bool inOrder;
@@ -85,14 +83,14 @@ public:
 	const SerializedMessageDesc *FindMessageByName(const char *name);
 
 	/// Returns the whole list of messages.
-	const List<SerializedMessageDesc> &GetMessages() const { return messages; }
+	const std::list<SerializedMessageDesc> &GetMessages() const { return messages; }
 
 	/// Returns a flat list of all the message elements.
-	const List<SerializedElementDesc> &GetElements() const { return elements; }
+	const std::list<SerializedElementDesc> &GetElements() const { return elements; }
 
 private:
-	List<SerializedElementDesc> elements;
-	List<SerializedMessageDesc> messages;
+	std::list<SerializedElementDesc> elements;
+	std::list<SerializedMessageDesc> messages;
 
 	SerializedElementDesc *ParseNode(TiXmlElement *node, SerializedElementDesc *parentNode);
 

+ 3 - 7
ThirdParty/kNet/include/kNet/NetException.h

@@ -15,12 +15,8 @@
 
 /** @file NetException.h
 	@brief The class NetException. Common exception class thrown by kNet on errors. */
-
-// Modified by Lasse Öörni for Urho3D
-
 #include <exception>
-
-#include "Str.h"
+#include <string>
 
 namespace kNet
 {
@@ -37,10 +33,10 @@ public:
 	{
 	}
 
-	const char *what() const throw() { return exception.CString(); }
+	const char *what() const throw() { return exception.c_str(); }
 
 private:
-	String exception;
+	std::string exception;
 };
 
 } // ~kNet

+ 12 - 14
ThirdParty/kNet/include/kNet/Network.h

@@ -16,8 +16,6 @@
 /** @file Network.h
 	@brief The class Network. The root point for creating client and server objects. */
 
-// Modified by Lasse Öörni for Urho3D
-
 #if defined(UNIX) || defined(ANDROID)
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -61,7 +59,7 @@ public:
 	/// @param allowAddressReuse If true, kNet passes the SO_REUSEADDR parameter to the server listen socket before binding 
 	///        the socket to a local port (== before starting the server). This allows the same port to be forcibly reused
 	///        when restarting the server if a crash occurs, without having to wait for the operating system to free up the port.
-	NetworkServer *StartServer(const Vector<Pair<unsigned short, SocketTransportLayer> > &listenPorts, INetworkServerListener *serverListener, bool allowAddressReuse);
+	NetworkServer *StartServer(const std::vector<std::pair<unsigned short, SocketTransportLayer> > &listenPorts, INetworkServerListener *serverListener, bool allowAddressReuse);
 
 	void StopServer();
 
@@ -80,25 +78,25 @@ public:
 	Ptr(MessageConnection) Connect(const char *address, unsigned short port, SocketTransportLayer transport, IMessageHandler *messageHandler, Datagram *connectMessage = 0);
 
 	/// Returns the local host name of the system (the local machine name or the local IP, whatever is specified by the system).
-	const char *LocalAddress() const { return localHostName.CString(); }
+	const char *LocalAddress() const { return localHostName.c_str(); }
 
 	/// Returns the error string associated with the given networking error id.
-	static String GetErrorString(int error);
+	static std::string GetErrorString(int error);
 
 	/// Returns the error string corresponding to the last error that occurred in the networking library.
-	static String GetLastErrorString();
+	static std::string GetLastErrorString();
 
 	/// Returns the error id corresponding to the last error that occurred in the networking library.
 	static int GetLastError();
 
 	/// Returns the amount of currently executing background network worker threads.
-	int NumWorkerThreads() const { return workerThreads.Size(); }
+	int NumWorkerThreads() const { return workerThreads.size(); }
 
 	/// Returns the NetworkServer object, or null if no server has been started.
 	Ptr(NetworkServer) GetServer() { return server; }
 
 	/// Returns all current connections in the system.
-	Set<MessageConnection *> Connections() const { return connections; }
+	std::set<MessageConnection *> Connections() const { return connections; }
 
 	/// Returns the data structure that collects statistics about the whole Network.
 	Lock<StatsEventHierarchyNode> Statistics() { return statistics.Acquire(); }
@@ -106,17 +104,17 @@ public:
 private:
 	/// Specifies the local network address of the system. This name is cached here on initialization
 	/// to avoid multiple queries to namespace providers whenever the name is needed.
-	String localHostName;
+	std::string localHostName;
 
 	/// Maintains the server-related data structures if this computer
 	/// is acting as a server. Otherwise this data is not used.
 	Ptr(NetworkServer) server;
 
 	/// Contains all active sockets in the system.
-	List<Socket> sockets;
+	std::list<Socket> sockets;
 
 	/// Tracks all existing connections in the system.
-	Set<MessageConnection *> connections;
+	std::set<MessageConnection *> connections;
 
 	Lockable<StatsEventHierarchyNode> statistics;
 
@@ -141,7 +139,7 @@ private:
 	/// Stores all the currently running network worker threads. Each thread is assigned
 	/// a list of MessageConnections and NetworkServers to oversee. The worker threads
 	/// then manage the socket reads and writes on these connections.
-	Vector<NetworkWorkerThread*> workerThreads;
+	std::vector<NetworkWorkerThread*> workerThreads;
 
 	/// Examines each currently running worker thread and returns one that has sufficiently low load,
 	/// or creates a new thread and returns it if no such thread exists. The thread is added and maintained
@@ -179,8 +177,8 @@ private:
 };
 
 /// Outputs the given number of bytes formatted to KB or MB suffix for readability.
-String FormatBytes(u64 numBytes);
+std::string FormatBytes(u64 numBytes);
 
-String FormatBytes(double numBytes);
+std::string FormatBytes(double numBytes);
 
 } // ~kNet

+ 14 - 14
ThirdParty/kNet/include/kNet/NetworkMessage.h

@@ -16,27 +16,21 @@
 /** @file NetworkMessage.h
 	@brief The class NetworkMessage. Stores an outbound network message. */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include "kNetBuildConfig.h"
 #include "LockFreePoolAllocator.h"
 #include "FragmentedTransferManager.h"
+#include "Types.h"
 
 namespace kNet
 {
 
-/// Contains 22 actual bits of data.
-typedef unsigned long packet_id_t;
-
-/// Performs modular arithmetic comparison to see if newID refers to a PacketID newer than oldID.
-/// @return True if newID is newer than oldID, false otherwise.
+/// Performs modular arithmetic comparison to see if newID refers to a PacketID that is *strictly* newer than oldID.
+/// @return True if newID is *strictly* newer than oldID, false otherwise.
 inline bool PacketIDIsNewerThan(packet_id_t newID, packet_id_t oldID)
 {
-	if (newID > oldID)
-		return true;
-	if (oldID - newID >= (1 << 21))
-		return true;
-	return false;
+    packet_id_t diff = (packet_id_t)(newID - oldID);
+    packet_id_t diff2 = (packet_id_t)(newID + 0x3FFFFF - oldID);
+    return diff < 0x1FFFFF || diff2 < 0x1FFFFF;
 }
 
 /// Computes the PacketID for the packet (id + increment).
@@ -92,7 +86,8 @@ public:
 	unsigned long priority;
 
 	/// The ID of this message. IDs 0 - 5 are reserved for the protocol and may not be used.
-	packet_id_t id;
+	/// Valid user range is [6, 1073741821 == 0x3FFFFFFD].
+	message_id_t id;
 
 	/// When sending out a message, the application can attach a content ID to the message,
 	/// which will effectively replace all the older messages with the same messageID and
@@ -114,7 +109,7 @@ public:
 	bool obsolete;
 
 #ifdef KNET_NETWORK_PROFILING
-	String profilerName;
+	std::string profilerName;
 #endif
 
 	/// Checks if this message is newer than the other message.
@@ -138,6 +133,11 @@ private:
 	friend class FragmentedSendManager;
 	friend struct FragmentedSendManager::FragmentedTransfer;
 
+	/// A temporary storage area to remember the UDP packet ID this messages was received in.
+	/// For TCP messages, this field is always zero.
+	/// When sending out messages, this field is not used.
+	packet_id_t receivedPacketID;
+
 	/// A running number that is assigned to each message to distinguish the order
 	/// the messages were added to the queue. The network layer manages this numbering,
 	/// the application can not control it. This is used to break ties on packets

+ 8 - 10
ThirdParty/kNet/include/kNet/NetworkServer.h

@@ -16,9 +16,7 @@
 /** @file NetworkServer.h
 	@brief The NetworkServer class. The main class for hosting a kNet server. */
 
-// Modified by Lasse Öörni for Urho3D
-
-#include "List.h"
+#include <list>
 
 #include "kNetBuildConfig.h"
 #include "SharedPtr.h"
@@ -101,9 +99,9 @@ public:
 	void ConnectionClosed(MessageConnection *connection);
 
 	/// Returns all the sockets this server is listening on.
-	Vector<Socket *> &ListenSockets();
+	std::vector<Socket *> &ListenSockets();
 
-	typedef Map<EndPoint, Ptr(MessageConnection)> ConnectionMap;
+	typedef std::map<EndPoint, Ptr(MessageConnection)> ConnectionMap;
 
 	/// Returns all the currently tracked connections.
 	ConnectionMap GetConnections();
@@ -112,16 +110,16 @@ public:
 	int NumConnections() const;
 
 	/// Returns a one-liner textual summary of this server.
-	String ToString() const;
+	std::string ToString() const;
 
 private:
 	/// Private ctor - NetworkServer instances are created by the Network class.
-	NetworkServer(Network *owner, Vector<Socket *> listenSockets);
+	NetworkServer(Network *owner, std::vector<Socket *> listenSockets);
 
 	/// We store possibly multiple listening sockets so that the server
 	/// can listen on several sockets (UDP or TCP) at once, making it
 	/// possible for clients to bypass firewalls and/or mix UDP and TCP use.
-	Vector<Socket *> listenSockets;
+	std::vector<Socket *> listenSockets;
 
 	/// The list of active client connections.
 	Lockable<ConnectionMap> clients;
@@ -195,9 +193,9 @@ void NetworkServer::BroadcastStruct(const SerializableData &data, unsigned long
 
 	const size_t dataSize = data.Size();
 
-	for(ConnectionMap::Iterator iter = clientsLock->Begin(); iter != clientsLock->End(); ++iter)
+	for(ConnectionMap::iterator iter = clientsLock->begin(); iter != clientsLock->end(); ++iter)
 	{
-		MessageConnection *connection = iter->second_;
+		MessageConnection *connection = iter->second;
 		assert(connection);
 		if (connection == exclude || !connection->IsWriteOpen())
 			continue;

+ 103 - 0
ThirdParty/kNet/include/kNet/NetworkSimulator.h

@@ -0,0 +1,103 @@
+/* Copyright The kNet Project.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License. */
+#pragma once
+
+/** @file NetworkSimulator.h
+	@brief The NetworkSimulator class, which enables different network conditions testing. */
+
+#include "kNetFwd.h"
+#include "PolledTimer.h"
+#include <vector>
+
+#include <cstring>
+
+namespace kNet
+{
+
+/// A NetworkSimulator is attached to MessageConnections to add in an intermediate layer for
+/// network conditions testing.
+class NetworkSimulator
+{
+public:
+	NetworkSimulator();
+	~NetworkSimulator();
+
+	/// If false, the network simulator is not being used.
+	/// By default, this is always false.
+	bool enabled;
+
+	/// Specifies the percentage of messages to drop. This is in the range [0.0, 1.0]. Default: 0 (disabled).
+	float packetLossRate;
+
+	/// Specifies a constant delay to add to each packet (msecs). Default: 0.
+	float constantPacketSendDelay;
+
+	/// Specifies an amount of uniformly random delay to add to each packet (msecs), [0, uniformRandomPacketSendDelay].  Default: 0.
+	float uniformRandomPacketSendDelay;
+
+	/// Specifies the percentage of messages to duplicate. This is in the range [0.0, 1.0]. Default: 0 (disabled). 
+	float packetDuplicationRate;
+
+	/// Corruption options.
+	enum
+	{
+		CorruptDatagram, ///< The whole datagram is subjected to data corruption. This is the default.
+		CorruptPayload, ///< Only kNet message payload (client-side data) will be subjected to corruption.
+		CorruptMessageType ///< Only the message payload of a single given message type will be subjected to corruption.
+	} corruptionType;
+
+	/// If corruptionType == CorruptMessageType, this field specifies a single specific message type ID which
+	/// will be subjected to corruption. Default: 0. No message has the ID 0, i.e. this effectively means "disabled".
+	int corruptMessageId;
+
+	/// Rate in % of the datagrams to corrupt. Default: 0.
+	float corruptToggleBitsRate;
+
+	/// The minimum number of bits to corrupt when a datagram is decided to be tampered. Default: 0.
+	int corruptMinBits;
+
+	/// The maximum number of bits to corrupt when a datagram is decided to be tampered. Default: 0.
+	int corruptMaxBits;
+
+	void SubmitSendBuffer(OverlappedTransferBuffer *buffer, Socket *socket);
+
+	/// Runs a polled update tick on the network simulator. Transfers all expired data.
+	void Process();
+
+	/// Discards and frees all currently queued messages.
+	void Free();
+
+	/// Performs a random roll against the corruptToggleBitsRate counter, and perhaps corrupts some bits
+	/// of the given buffer.
+	/// Alters the raw byte buffer contents by flipping some bits according to the currently specified
+	/// parameters.
+	void MaybeCorruptBufferToggleBits(void *buffer, size_t numBytes) const;
+
+private:
+	struct QueuedBuffer
+	{
+		OverlappedTransferBuffer *buffer;
+
+		/// Stores how long to delay this buffer until transfer.
+		PolledTimer timeUntilTransfer;
+	};
+	std::vector<QueuedBuffer> queuedBuffers;
+
+	MessageConnection *owner;
+
+	friend class MessageConnection;
+};
+
+} // ~kNet
+

+ 2 - 4
ThirdParty/kNet/include/kNet/NetworkWorkerThread.h

@@ -17,8 +17,6 @@
 	@brief The NetworkWorkerThread class. Implements a background thread for responsive
 	processing of server and client connections. */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include "SharedPtr.h"
 
 #include "Lockable.h"
@@ -49,8 +47,8 @@ public:
 	Thread &ThreadObject() { return workThread; }
 
 private:
-	Lockable<Vector<MessageConnection *> > connections;
-	Lockable<Vector<NetworkServer *> > servers;
+	Lockable<std::vector<MessageConnection *> > connections;
+	Lockable<std::vector<NetworkServer *> > servers;
 
 	Thread workThread;
 

+ 9 - 11
ThirdParty/kNet/include/kNet/RingBuffer.h

@@ -16,9 +16,7 @@
 /** @file RingBuffer.h
 	@brief The RingBuffer class stores a fast raw byte buffer queue storage. */
 
-// Modified by Lasse Öörni for Urho3D
-
-#include "Vector.h"
+#include <vector>
 
 namespace kNet
 {
@@ -29,13 +27,13 @@ class RingBuffer
 public:
 	explicit RingBuffer(int capacity)
 	{
-		data.Resize(capacity);
+		data.resize(capacity);
 		start = 0;
 		end = 0;
 	}
 
 	/// Returns the total number of bytes that this RingBuffer can contain.
-	int Capacity() const { return data.Size(); }
+	int Capacity() const { return data.size(); }
 
 	/// Returns the number of bytes filled in the ring buffer.
 	int Size() const { return end - start; }
@@ -61,10 +59,10 @@ public:
 	{
 		assert(newSize > 0);
 
-		if ((size_t)newSize <= data.Size())
+		if ((size_t)newSize <= data.size())
 			return; // No need to resize.
 		Compact();
-		data.Resize(newSize);
+		data.resize(newSize);
 	}
 
 	void Clear()
@@ -84,7 +82,7 @@ public:
 	void Inserted(int numBytes)
 	{ 
 		end += numBytes; 
-		assert(end <= (int)data.Size());
+		assert(end <= (int)data.size());
 	}
 
 	/// Call after having processed the given number of bytes from the buffer.
@@ -97,13 +95,13 @@ public:
 	}
 
 	/// Returns the total number of bytes that can be filled in this structure after compacting.
-	int TotalFreeBytesLeft() const { return data.Size() - Size(); }
+	int TotalFreeBytesLeft() const { return data.size() - Size(); }
 
 	/// Returns the number of bytes that can be added to this structure contiguously, without having to compact.
-	int ContiguousFreeBytesLeft() const { return data.Size() - end; }
+	int ContiguousFreeBytesLeft() const { return data.size() - end; }
 
 private:
-	PODVector<char> data;
+	std::vector<char> data;
 	int start; ///< Points to the first used byte.
 	int end; ///< Points to the first unused byte.
 

+ 2 - 1
ThirdParty/kNet/include/kNet/SequentialIntegerSet.h

@@ -47,7 +47,8 @@ public:
 
 	int Capacity() const { return tableSize; }
 
-	int CountSize()
+    /// Recomputes the size of this set, so that Size() returns the exact value.
+	void CountSize()
 	{
 		size = 0;
 		for(int i = 0; i < tableSize; ++i)

+ 3 - 6
ThirdParty/kNet/include/kNet/SerializationStructCompiler.h

@@ -16,10 +16,7 @@
 /** @file SerializationStructCompiler.h
 	@brief The SerializationStructCompiler class. */
 
-// Modified by Lasse Öörni for Urho3D
-
-#include "Str.h"
-
+#include <string>
 #include <fstream>
 
 #include "MessageListParser.h"
@@ -34,7 +31,7 @@ public:
 	void CompileStruct(const SerializedElementDesc &structure, const char *outfile);
 	void CompileMessage(const SerializedMessageDesc &message, const char *outfile);
 
-	static String ParseToValidCSymbolName(const char *str);
+	static std::string ParseToValidCSymbolName(const char *str);
 
 private:
 	void WriteFilePreamble(std::ofstream &out);
@@ -48,7 +45,7 @@ private:
 	void WriteSerializeMemberFunction(/*const std::string &className, */const SerializedElementDesc &elem, int level, std::ofstream &out);
 	void WriteDeserializeMemberFunction(/*const std::string &className, */const SerializedElementDesc &elem, int level, std::ofstream &out);
 
-	static String Indent(int level);
+	static std::string Indent(int level);
 };
 
 } // ~kNet

+ 1 - 3
ThirdParty/kNet/include/kNet/SerializedDataIterator.h

@@ -16,8 +16,6 @@
 /** @file SerializedDataIterator.h
 	@brief The SerializedDataIterator class. */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include "SharedPtr.h"
 #include "MessageListParser.h"
 
@@ -67,7 +65,7 @@ private:
 	void DescendIntoStructure();
 
 	/// Stores the tree traversal progress.
-	PODVector<ElemInfo> currentElementStack;
+	std::vector<ElemInfo> currentElementStack;
 	/// The type of the message we are building.
 	const SerializedMessageDesc &desc;
 };

+ 21 - 15
ThirdParty/kNet/include/kNet/Socket.h

@@ -16,8 +16,6 @@
 /** @file Socket.h
 	@brief The Socket class. */
 
-// Modified by Lasse Öörni for Urho3D
-
 #ifdef WIN32
 
 #include "kNetBuildConfig.h"
@@ -55,8 +53,8 @@ typedef unsigned int SOCKET;
 }
 #endif
 
-#include "Vector.h"
-#include "List.h"
+#include <vector>
+#include <list>
 
 #include "SharedPtr.h"
 #include "EndPoint.h"
@@ -74,7 +72,7 @@ enum SocketTransportLayer
 	SocketOverTCP
 };
 
-String SocketTransportLayerToString(SocketTransportLayer transport);
+std::string SocketTransportLayerToString(SocketTransportLayer transport);
 
 /// Converts the given string (case-insensitive parsing) to the corresponding SocketTransportLayer enum.
 /// "tcp" & "socketovertcp" -> SocketOverTCP.
@@ -90,7 +88,7 @@ enum SocketType
 	ClientSocket ///< A client-side socket.
 };
 
-String SocketTypeToString(SocketType type);
+std::string SocketTypeToString(SocketType type);
 
 typedef int OverlappedTransferTag;
 
@@ -99,8 +97,9 @@ typedef WSABUF kNetBuffer;
 #else
 struct kNetBuffer
 {
-	/// Specifies the number of bytes allocated to buf. This is the maximum amount of bytes that can
-	/// be written to buf.
+	/// Stores the number of bytes allocated to buf.
+	/// When sending out a message, this field specifies the number of bytes the client
+	/// can write to buf, at maximum.
 	unsigned long len;
 
 	char *buf;
@@ -114,9 +113,13 @@ struct OverlappedTransferBuffer
 	WSAOVERLAPPED overlapped;
 #endif
 
-	/// Specifies the number of bytes buffer.buf actually contains.
+	/// Stores the number of bytes actually in use in buffer.buf. When sending out a message,
+	/// specify the actual number of bytes filled to buffer.buf here.
 	int bytesContains;
 
+    /// Stores the total number of bytes allocated to the buffer in the overlapped structure.
+    int bytesAllocated;
+
 	sockaddr_in from;
 	socklen_t fromLen;
 };
@@ -188,9 +191,12 @@ public:
 	/// Starts the sending of new data. After having filled the data to send to the OverlappedTransferBuffer that is
 	/// returned here, commit the send by calling EndSend. If you have called BeginSend, but decide not to send any data,
 	/// call AbortSend instead (otherwise memory will leak).
+    /// @param maxBytesToSend Specifies the size of the buffer that must be returned. Specify the size (or at least an 
+    ///         upper limit) of the message you are sending here. Specify the actual number of bytes filled in the resulting
+    ///         structure.
 	/// @return A transfer buffer where the data to send is to be filled in. If no new data can be sent at this time,
 	///         this function returns 0.
-	OverlappedTransferBuffer *BeginSend();
+	OverlappedTransferBuffer *BeginSend(int maxBytesToSend);
 	/// Finishes and queues up the given transfer that was created with a call to BeginSend.
 	/// @return True if send succeeded, false otherwise. In either case, the ownership of the passed buffer send
 	///         is taken by this Socket and may not be accessed anymore. Discard the pointer after calling this function.
@@ -252,7 +258,7 @@ public:
 	/// Returns the local EndPoint this socket is bound to.
 	const EndPoint &LocalEndPoint() const { return localEndPoint; }
 	/// Returns the local address (local hostname) of the local end point this socket is bound to.
-	const char *LocalAddress() const { return localHostName.CString(); }
+	const char *LocalAddress() const { return localHostName.c_str(); }
 	/// Returns the local port that this socket is bound to.
 	unsigned short LocalPort() const { return localEndPoint.port; }
 
@@ -262,14 +268,14 @@ public:
 	const EndPoint &RemoteEndPoint() const { return remoteEndPoint; }
 	/// Returns the destination address (destination hostname) of the remote end point this socket is connected to.
 	/// If SocketType == ServerListenSocket, returns an empty string.
-	const char *DestinationAddress() const { return remoteHostName.CString(); }
+	const char *DestinationAddress() const { return remoteHostName.c_str(); }
 	/// Returns the destination port of the remote end point this socket is connected to.
 	/// If SocketType == ServerListenSocket, returns 0.
 	unsigned short DestinationPort() const { return remoteEndPoint.port; }
 
 	/// Returns a human-readable representation of this socket, specifying the peer address and port this socket is
 	/// connected to.
-	String ToString() const;
+	std::string ToString() const;
 
 	/// Sets the socket to blocking or nonblocking state.
 	void SetBlocking(bool isBlocking);
@@ -293,7 +299,7 @@ private:
 	/// Specifies the network host name of the local end point (the local system).
 	/// If the local end point does not have a hostname, this field is the string representation of the
 	/// system IP address (one of them, there may be multiple IPs).
-	String localHostName;
+	std::string localHostName;
 	
 	/// Specifies the remote system end point (IP and port) this socket is bound to (== the "peer" address).
 	/// If SocketType == ServerListenSocket or transport == SocketOverUDP, this socket is not bound
@@ -308,7 +314,7 @@ private:
 	/// Specifies the network host name of the remote end point (== the remote system == the "peer").
 	/// If the remote end point does not have a known hostname, this field is the string representation of the
 	/// remote IP address. If SocketType == ServerListenSocket, this field is empty.
-	String remoteHostName;
+	std::string remoteHostName;
 
 	/// Specifies the underlying transport protocol that this Socket is using (TCP or UDP).
 	SocketTransportLayer transport;

+ 1 - 3
ThirdParty/kNet/include/kNet/Sort.h

@@ -16,8 +16,6 @@
 /** @file Sort.h
 	@brief A range of comparison sort algorithms. */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include "Clock.h"
 
 //#include "LCG.h"
@@ -29,7 +27,7 @@ namespace kNet
 /** @brief A range of comparison sort algorithms.
 
 	When to use one of these sorts and when to use the std sorts? 
-		(std::sort, std::stable_sort, List::sort etc.)
+		(std::sort, std::stable_sort, std::list::sort etc.)
 
 	1)	Always consider using the standard versions first. For example, the visual c++ 
 		std::sort is on average faster than the introsort described here. Additionally, 

+ 21 - 23
ThirdParty/kNet/include/kNet/StatsEventHierarchy.h

@@ -16,10 +16,8 @@
 /** @file StatsEventHierarchy.h
 	@brief Stores a hierarchy of network events for profiling purposes. */
 
-// Modified by Lasse Öörni for Urho3D
-
-#include "Map.h"
-#include "Str.h"
+#include <map>
+#include <string>
 
 #include "kNet/WaitFreeQueue.h"
 #include "kNet/Clock.h"
@@ -42,7 +40,7 @@ struct StatsEvent
 	tick_t time;
 };
 
-inline String FirstToken(const char *str, char delimiter, int &nextTokenStart)
+inline std::string FirstToken(const char *str, char delimiter, int &nextTokenStart)
 {
 	int i = 0;
 	while(str[i] != '\0' && str[i] != delimiter)
@@ -51,20 +49,20 @@ inline String FirstToken(const char *str, char delimiter, int &nextTokenStart)
 		nextTokenStart = -1;
 	else
 		nextTokenStart = i+1;
-	return String(str, i);
+	return std::string(str, str + i);
 }
 
 class StatsEventHierarchyNode
 {
 public:
-	///\todo To improve performance, don't use a String as a key to the map, and replace the map with a more efficient data structure.
-	typedef Map<String, StatsEventHierarchyNode> NodeMap;
+	///\todo To improve performance, don't use a std::string as a key to the map, and replace the map with a more efficient data structure.
+	typedef std::map<std::string, StatsEventHierarchyNode> NodeMap;
 	NodeMap children;
 
 	WaitFreeQueue<StatsEvent> events;
 
 	/// Specifies the unit of the numeric data in this node.
-	String valueType;
+	std::string valueType;
 
 	StatsEventHierarchyNode()
 	:events(4) // The default size for the queue must be at least four elements (pow2, >2).
@@ -89,8 +87,8 @@ public:
 	{
 		PruneOldEventsThisLevel(ageMSecs);
 
-		for(NodeMap::Iterator iter = children.Begin(); iter != children.End(); ++iter)
-			iter->second_.PruneOldEventsHierarchy(ageMSecs);
+		for(NodeMap::iterator iter = children.begin(); iter != children.end(); ++iter)
+			iter->second.PruneOldEventsHierarchy(ageMSecs);
 	}
 
 	void AddEventToThisLevel(float value, int oldAgeMSecs)
@@ -108,13 +106,13 @@ public:
 	void AddEventToHierarchy(const char *name, float value, const char *valueType, int oldAgeMSecs)
 	{
 		int nextTokenStart = 0;
-		String childName = FirstToken(name, '.', nextTokenStart);
-		if (childName.Empty())
+		std::string childName = FirstToken(name, '.', nextTokenStart);
+		if (childName.empty())
 			AddEventToThisLevel(value, oldAgeMSecs);
 		else
 		{
-			NodeMap::Iterator iter = children.Find(childName);
-			if (iter == children.End())
+			NodeMap::iterator iter = children.find(childName);
+			if (iter == children.end()) 
 				children[childName].valueType = valueType; // To optimize, only copy this field in the first time the node is created.
 			if (nextTokenStart == -1)
 				children[childName].AddEventToThisLevel(value, oldAgeMSecs);
@@ -126,13 +124,13 @@ public:
 	StatsEventHierarchyNode *FindChild(const char *name)
 	{
 		int nextTokenStart = 0;
-		String childName = FirstToken(name, '.', nextTokenStart);
-		if (childName.Empty())
+		std::string childName = FirstToken(name, '.', nextTokenStart);
+		if (childName.empty())
 			return this;
 		else
 		{
-			NodeMap::Iterator iter = children.Find(childName);
-			if (iter == children.End())
+			NodeMap::iterator iter = children.find(childName);
+			if (iter == children.end()) 
 				return 0;
 			if (nextTokenStart == -1)
 				return &children[childName];
@@ -150,8 +148,8 @@ public:
 	{
 		int count = AccumulateTotalCountThisLevel();
 
-		for(NodeMap::ConstIterator iter = children.Begin(); iter != children.End(); ++iter)
-			count += iter->second_.AccumulateTotalCountHierarchy();
+		for(NodeMap::const_iterator iter = children.begin(); iter != children.end(); ++iter)
+			count += iter->second.AccumulateTotalCountHierarchy();
 
 		return count;
 	}
@@ -168,8 +166,8 @@ public:
 	{
 		float value = AccumulateTotalValueThisLevel();
 
-		for(NodeMap::ConstIterator iter = children.Begin(); iter != children.End(); ++iter)
-			value += iter->second_.AccumulateTotalValueHierarchy();
+		for(NodeMap::const_iterator iter = children.begin(); iter != children.end(); ++iter)
+			value += iter->second.AccumulateTotalValueHierarchy();
 
 		return value;
 	}

+ 3 - 3
ThirdParty/kNet/include/kNet/StdCMallocHeap.h

@@ -37,7 +37,7 @@ public:
 	static inline void *Alloc(StdCAlloc *, size_t size, size_t alignment, const char * /*nameTag*/ = 0, AllocFlags /*flags*/ = AFAllocLow)
 	{
 		assert(IS_POW2(alignment));
-#ifdef WIN32
+#ifdef _MSC_VER
 		void *ptr = _aligned_malloc(size, alignment);
 #else
 		void *ptr = malloc(size); ///\todo aligned_malloc on unix?
@@ -46,7 +46,7 @@ public:
 	}
 	static inline void Free(StdCAlloc *, void *ptr)
 	{
-#ifdef WIN32
+#ifdef _MSC_VER
 		_aligned_free(ptr);
 #else
 		free(ptr);
@@ -56,7 +56,7 @@ public:
 	///\todo Perhaps support Resize(void *ptr, size_t newSize); ?
 	static inline size_t Size(StdCAlloc *, void *ptr)
 	{
-#ifdef WIN32
+#ifdef _MSC_VER
 		return ::_msize(ptr);
 #else
 		assert(false && "N/I");

+ 1 - 3
ThirdParty/kNet/include/kNet/TCPMessageConnection.h

@@ -16,8 +16,6 @@
 /** @file TCPMessageConnection.h
 	@brief The TCPMessageConnection class.*/
 
-// Modified by Lasse Öörni for Urho3D
-
 #include "MessageConnection.h"
 #include "RingBuffer.h"
 
@@ -62,7 +60,7 @@ private:
 	void ExtractMessages();
 
 	// The following are temporary data structures used by various internal routines for processing.
-	Vector<NetworkMessage*> serializedMessages; // MessageConnection::TCPSendOutPacket()
+	std::vector<NetworkMessage*> serializedMessages; // MessageConnection::TCPSendOutPacket()
 
 	void PerformDisconnection();
 

+ 2 - 4
ThirdParty/kNet/include/kNet/Thread.h

@@ -16,9 +16,7 @@
 /** @file Thread.h
 	@brief The Thread class. Implements threading either using Boost, native Win32 or pthreads constructs. */
 
-// Modified by Lasse Öörni for Urho3D
-
-#include "Str.h"
+#include <string>
 
 #ifdef KNET_USE_BOOST
 #include <boost/thread.hpp>
@@ -56,7 +54,7 @@ typedef boost::thread::id ThreadId;
 typedef pthread_t ThreadId;
 #endif
 
-String ThreadIdToString(const ThreadId &id);
+std::string ThreadIdToString(const ThreadId &id);
 
 class Thread : public RefCountable
 {

+ 15 - 0
ThirdParty/kNet/include/kNet/Types.h

@@ -20,6 +20,10 @@
 
 #include "kNetBuildConfig.h"
 
+#ifdef __MINGW32__
+#include <stdint.h>
+#endif
+
 // As a reminder: http://predef.sourceforge.net/prestd.html
 
 // If we have C99, take the types from there.
@@ -77,4 +81,15 @@ typedef signed long long s64; ///< 8 bytes signed. 9,223,372,036,854,775,807 ~ 9
 
 #endif
 
+// kNet special types:
+
+namespace kNet
+{
+	/// Identifies a UDP datagram by auto-incrementing number. Contains 22 actual bits of data.
+	typedef unsigned long packet_id_t;
+	/// Identifies the type of a network message. Contains 30 actual bits of data.
+	/// Valid user range is [6, 1073741821 == 0x3FFFFFFD].
+	typedef unsigned long message_id_t;
+}
+
 #endif // ~KNET_NO_FIXEDWIDTH_TYPES

+ 28 - 11
ThirdParty/kNet/include/kNet/UDPMessageConnection.h

@@ -16,8 +16,6 @@
 /** @file UDPMessageConnection.h
 	@brief The UDPMessageConnection class. */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include "MessageConnection.h"
 #include "SequentialIntegerSet.h"
 #include "Array.h"
@@ -68,6 +66,8 @@ public:
 	UDPMessageConnection(Network *owner, NetworkServer *ownerServer, Socket *socket, ConnectionState startingState);
 	~UDPMessageConnection();
 
+	void SetDatagramInFlowRatePerSecond(int newDatagramReceiveRate, bool internalCall);
+
 	float RetransmissionTimeout() const { return retransmissionTimeout; }
 
 	float DatagramSendRate() const { return datagramSendRate; }
@@ -80,7 +80,7 @@ public:
 
 	size_t NumOutboundUnackedDatagrams() const { return outboundPacketAckTrack.Size(); }
 
-	size_t NumReceivedUnackedDatagrams() const { return inboundPacketAckTrack.Size(); }
+	size_t NumReceivedUnackedDatagrams() const { return inboundPacketAckTrack.size(); }
 
 	float PacketLossCount() const { return packetLossCount; }
 
@@ -99,6 +99,10 @@ private:
 	/// @param bytesRead [out] Returns the total number of bytes containes in the datagrams that were read.
 	SocketReadResult UDPReadSocket(size_t &bytesRead); // [worker thread]
 
+	// Congestion control and data rate management:
+	void PerformFlowControl(); // [worker thread]
+	void HandleFlowControlRequestMessage(const char *data, size_t numBytes); // [worker thread]
+
 	void UpdateRTOCounterOnPacketAck(float rtt); // [worker thread]
 	void UpdateRTOCounterOnPacketLoss(); // [worker thread]
 
@@ -113,7 +117,7 @@ private:
 	void SendPacketAckMessage(); // [worker thread]
 	void HandlePacketAckMessage(const char *data, size_t numBytes); // [worker thread]
 	
-	bool HandleMessage(packet_id_t packetID, u32 messageID, const char *data, size_t numBytes); // [worker thread]
+	bool HandleMessage(packet_id_t packetID, message_id_t messageID, const char *data, size_t numBytes); // [worker thread]
 
 	/// Refreshes Packet Loss related statistics.
 	void ComputePacketLoss(); // [worker thread]
@@ -149,8 +153,8 @@ private:
 
 	/// The flow control algorithm:
 	float datagramSendRate; ///< The number of datagrams/second to send.
+
 	float lowestDatagramSendRateOnPacketLoss;
-	int slowModeDelay; ///< Go into slow increase mode for some time on receiving loss
 
 	// These variables correspond to RFC2988, http://tools.ietf.org/html/rfc2988 , section 2.
 	bool rttCleared; ///< If true, smoothedRTT and rttVariation do not contain meaningful values, but "are clear".
@@ -219,11 +223,16 @@ private:
 
 	/// Connection control update timer.
 	PolledTimer udpUpdateTimer;
-	
-	typedef Map<packet_id_t, PacketAckTrack> PacketAckTrackMap;
+
+	PolledTimer statsUpdateTimer;
+
+	typedef std::map<packet_id_t, PacketAckTrack> PacketAckTrackMap;
 	/// Contains the messages we have sent out that we are waiting for the other party to Ack.
+//	PacketAckTrackMap outboundPacketAckTrack;
 	typedef WaitFreeQueue<PacketAckTrack> PacketAckTrackQueue;
 	PacketAckTrackQueue outboundPacketAckTrack;
+//	typedef OrderedHashTable<PacketAckTrack, PacketAckTrack> PacketAckTrackTable;
+//	PacketAckTrackTable outboundPacketAckTrack;
 
 	static int BiasedBinarySearchFindPacketIndex(UDPMessageConnection::PacketAckTrackQueue &queue, int packetID);
 
@@ -234,9 +243,17 @@ private:
 	// Contains a list of all messages we've received that we need to Ack at some point.
 	PacketAckTrackMap inboundPacketAckTrack;
 
+	/// The number of UDP packets to send out per second.
+	int datagramOutRatePerSecond;
+
+	/// The number of UDP packets to receive per second. Of course the local end of the
+	/// connection cannot directly control this, but it uses the FlowControlRequest
+	/// packet to send it to the other party.
+	int datagramInRatePerSecond;
+
 	/// Contains the reliable message numbers of all reliable messages we've received.
 	/// Used to detect and discard duplicate messages we've received.
-	Set<unsigned long> receivedReliableMessages;
+	std::set<unsigned long> receivedReliableMessages;
 
 	SequentialIntegerSet receivedPacketIDs;
 	/// Specifies the packet ID of the most recent datagram we sent. Used currently only
@@ -246,9 +263,9 @@ private:
 	// The following are temporary data structures used by various internal routines for processing.
 	// They are created here as members to avoid having to create objects on the stack at each call to 
 	// time-sensitive functions.
-	Vector<NetworkMessage *> datagramSerializedMessages; // MessageConnection::UDPSendOutPacket()
-	Vector<NetworkMessage *> skippedMessages; // MessageConnection::UDPSendOutPacket()
-	PODVector<char> assembledData; // MessageConnection::DatagramExtractMessages
+	std::vector<NetworkMessage *> datagramSerializedMessages; // MessageConnection::UDPSendOutPacket()
+	std::vector<NetworkMessage *> skippedMessages; // MessageConnection::UDPSendOutPacket()
+	std::vector<char> assembledData; // MessageConnection::DatagramExtractMessages
 
 	/// Returns the average number of inbound packet loss, packets/sec.
 	float GetPacketLossCount() const { return packetLossCount; }

+ 1 - 0
ThirdParty/kNet/include/kNet/WaitFreeQueue.h

@@ -16,6 +16,7 @@
 /** @file WaitFreeQueue.h
 	@brief The WaitFreeQueue<T> template class. */
 
+#include <stddef.h>
 #include "Alignment.h"
 
 namespace kNet

+ 1 - 1
ThirdParty/kNet/include/kNet/qt/GraphDialog.h

@@ -1,4 +1,4 @@
-/* Copyright 2010 Jukka Jylänki
+/* Copyright The kNet Project.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.

+ 3 - 1
ThirdParty/kNet/include/kNet/qt/MessageConnectionDialog.h

@@ -1,4 +1,4 @@
-/* Copyright 2010 Jukka Jylänki
+/* Copyright The kNet Project.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -45,6 +45,8 @@ public:
 
 public slots:
 	void Update();
+
+	void OpenSendSimulationWindow();
 };
 
 } // ~kNet

+ 1 - 1
ThirdParty/kNet/include/kNet/qt/NetworkDialog.h

@@ -1,4 +1,4 @@
-/* Copyright 2010 Jukka Jylänki
+/* Copyright The kNet Project.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.

+ 52 - 0
ThirdParty/kNet/include/kNet/qt/NetworkSimulationDialog.h

@@ -0,0 +1,52 @@
+/* Copyright The kNet Project.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License. */
+#pragma once
+
+/** @file NetworkSimulationDialog.h
+	@brief Shows a UI for specifying parameters for network simulations. */
+
+#include "kNetBuildConfig.h"
+
+#ifdef KNET_USE_QT
+
+#include <QObject>
+#include <QWidget>
+#include <QTimer>
+
+#include "kNet/MessageConnection.h"
+#include "kNet/SharedPtr.h"
+
+class Ui_NetworkSimulationDialog;
+
+namespace kNet
+{
+
+class NetworkSimulationDialog : public QWidget
+{
+	Q_OBJECT;
+
+	Ptr(MessageConnection) connection;
+	Ui_NetworkSimulationDialog *dialog;
+
+public:
+	NetworkSimulationDialog(QWidget *parent, Ptr(MessageConnection) connection);
+	~NetworkSimulationDialog();
+
+public slots:
+	void ParameterChanged();
+};
+
+} // ~kNet
+
+#endif

+ 7 - 0
ThirdParty/kNet/include/kNet/win32/WS2Include.h

@@ -38,5 +38,12 @@
 #undef _WINSOCKAPI_
 #endif
 
+#ifdef __MINGW32__
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0501
+#endif
+
 #include <winsock2.h>
 #include <ws2tcpip.h>

+ 1 - 0
ThirdParty/kNet/include/kNetFwd.h

@@ -33,6 +33,7 @@ namespace kNet
 	class Network;
 	class NetworkMessage;
 	class NetworkServer;
+	struct OverlappedTransferBuffer;
 	class PolledTimer;
 	class SerializationStructCompiler;
 	class SerializedDataIterator;

+ 144 - 0
ThirdParty/kNet/src/64BitAllocDebugger.cpp

@@ -0,0 +1,144 @@
+/* Copyright The kNet Project.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License. */
+
+/** @file 64BitAllocDebugger.cpp
+	@brief Virtually reserves all memory on Windows in the < 4GB memory area so that all
+		pointers used by the application are outside the 32-bit range.
+	
+	Idea and code taken from http://randomascii.wordpress.com/2012/02/14/64-bit-made-easy/ */
+
+#ifdef _WIN64
+#include "kNet/64BitAllocDebugger.h"
+#include <stdio.h>
+#include <Windows.h>
+#include <vector>
+
+BottomMemoryAllocator::BottomMemoryAllocator()
+{
+	ReserveBottomMemory();
+}
+
+BottomMemoryAllocator::~BottomMemoryAllocator()
+{
+	FreeBottomMemory();
+}
+
+void BottomMemoryAllocator::ReserveBottomMemory()
+{
+	static bool s_initialized = false;
+	if ( s_initialized )
+		return;
+	s_initialized = true;
+
+	// Start by reserving large blocks of address space, and then
+	// gradually reduce the size in order to capture all of the
+	// fragments. Technically we should continue down to 64 KB but
+	// stopping at 1 MB is sufficient to keep most allocators out.
+	const size_t LOW_MEM_LINE = 0x100000000LL;
+	size_t totalReservation = 0;
+	size_t numVAllocs = 0;
+	size_t numHeapAllocs = 0;
+	size_t oneMB = 1024 * 1024;
+	for (size_t size = 256 * oneMB; size >= oneMB; size /= 2)
+	{
+		for (;;)
+		{
+			void* p = VirtualAlloc(0, size, MEM_RESERVE, PAGE_NOACCESS);
+			if (!p)
+				break;
+ 
+			if ((size_t)p >= LOW_MEM_LINE)
+			{
+				// We don't need this memory, so release it completely.
+				VirtualFree(p, 0, MEM_RELEASE);
+				break;
+			}
+			totalReservation += size;
+			++numVAllocs;
+			virtualAllocated.push_back(p);
+		}
+	}
+
+	// Now repeat the same process but making heap allocations, to use up
+	// the already reserved heap blocks that are below the 4 GB line.
+	HANDLE heap = GetProcessHeap();
+	for (size_t blockSize = 64 * 1024; blockSize >= 16; blockSize /= 2)
+	{
+		for (;;)
+		{
+			void* p = HeapAlloc(heap, 0, blockSize);
+
+			if (!p)
+				break;
+
+			if ((size_t)p >= LOW_MEM_LINE)
+			{
+				// We don't need this memory, so release it completely.
+				HeapFree(heap, 0, p);
+				break;
+			}
+
+			totalReservation += blockSize;
+			++numHeapAllocs;
+			heapAllocated.push_back(p);
+		}
+	}
+
+	// Perversely enough the CRT doesn't use the process heap. Suck up
+	// the memory the CRT heap has already reserved.
+
+	for (size_t blockSize = 64 * 1024; blockSize >= 16; blockSize /= 2)
+	{
+		for (;;)
+		{
+			void* p = malloc(blockSize);
+
+			if (!p)
+				break;
+
+			if ((size_t)p >= LOW_MEM_LINE)
+			{
+				// We don't need this memory, so release it completely.
+				free(p);
+				break;
+			}
+
+			totalReservation += blockSize;
+			++numHeapAllocs;
+			mallocAllocated.push_back(p);
+		}
+	}
+
+	// Print diagnostics showing how many allocations we had to make in
+	// order to reserve all of low memory, typically less than 200.
+	printf("Reserved %1.3f MB (%d vallocs," 
+					"%d heap allocs) of low-memory.\n",
+			totalReservation / (1024 * 1024.0),
+			(int)numVAllocs, (int)numHeapAllocs);
+}
+
+void BottomMemoryAllocator::FreeBottomMemory()
+{
+	for(size_t i = 0; i < virtualAllocated.size(); ++i)
+		VirtualFree(virtualAllocated[i], 0, MEM_RELEASE);
+
+	HANDLE heap = GetProcessHeap();
+	for(size_t i = 0; i < heapAllocated.size(); ++i)
+		HeapFree(heap, 0, heapAllocated[i]);
+
+	for(size_t i = 0; i < mallocAllocated.size(); ++i)
+		free(mallocAllocated[i]);
+}
+
+#endif

+ 163 - 10
ThirdParty/kNet/src/DataDeserializer.cpp

@@ -15,10 +15,9 @@
 /** @file DataDeserializer.cpp
 	@brief */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include <cassert>
 #include <cstring>
+#include <cmath>
 
 #include "kNet/DebugMemoryLeakCheck.h"
 
@@ -102,7 +101,7 @@ u32 DataDeserializer::GetDynamicElemCount()
 }
 
 template<>
-String DataDeserializer::Read<String>()
+std::string DataDeserializer::Read<std::string>()
 {
 	return ReadString();
 }
@@ -131,7 +130,161 @@ u32 DataDeserializer::ReadBits(int numBits)
 	return val;
 }
 
-void DataDeserializer::SkipBits(size_t numBits)
+float DataDeserializer::ReadUnsignedFixedPoint(int numIntegerBits, int numDecimalBits)
+{
+	u32 fp = ReadBits(numIntegerBits + numDecimalBits);
+	return fp / (float)(1 << numDecimalBits);
+}
+
+float DataDeserializer::ReadSignedFixedPoint(int numIntegerBits, int numDecimalBits)
+{
+	// Reading a [0, 2k-1] range -> remap back to [-k, k-1] range.
+	return ReadUnsignedFixedPoint(numIntegerBits, numDecimalBits) - (float)(1 << (numIntegerBits-1));
+}
+
+float DataDeserializer::ReadQuantizedFloat(float minRange, float maxRange, int numBits)
+{
+	u32 val = ReadBits(numBits);
+	return minRange + val * (maxRange-minRange) / (float)((1 << numBits) - 1);
+}
+
+float DataDeserializer::ReadMiniFloat(bool signBit, int exponentBits, int mantissaBits, int exponentBias)
+{
+	assert(sizeof(float) == 4);
+	assert(exponentBits > 0);
+	assert(exponentBits <= 8);
+	assert(mantissaBits > 0);
+	assert(mantissaBits <= 23);
+
+	bool sign = signBit ? Read<bit>() : false;
+	u32 exponent = ReadBits(exponentBits);
+	u32 mantissa = ReadBits(mantissaBits);
+
+	// Shift back the decoded mantissa to proper position.
+	mantissa <<= 23 - mantissaBits;
+
+	// Reconstruct the float exponent.
+	if (exponent == (u32)((1 << exponentBits) - 1)) // If the read exponent was all ones, reconstruct 11111111.
+		exponent = 0xFF;
+	else if (exponent != 0) // If the read exponent was not zero, it was a normal number.
+		exponent = exponent - exponentBias + 127;
+	// else exponent == 0, meaning a zero or a denormal.
+
+	u32 value = (sign ? 0x80000000 : 0) | (exponent << 23) | mantissa;
+
+	return *(float*)&value;
+}
+
+#define PI ((float)3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679)
+
+void DataDeserializer::ReadNormalizedVector2D(int numBits, float &x, float &y)
+{
+	float angle = ReadQuantizedFloat(-PI, PI, numBits);
+	x = cos(angle);
+	y = sin(angle);
+}
+
+void DataDeserializer::ReadVector2D(int magnitudeIntegerBits, int magnitudeDecimalBits, int directionBits, float &x, float &y)
+{
+	// Read the length in unsigned fixed point format.
+	// The following line is effectively the same as calling ReadUnsignedFixedPoint, but manually perform it
+	// to be precisely able to examine whether the length is zero.
+	u32 fp = ReadBits(magnitudeIntegerBits + magnitudeDecimalBits);
+	if (fp != 0) // If length is non-zero, the stream also contains the direction.
+	{
+		float length = fp / (float)(1 << magnitudeDecimalBits);
+
+		// Read the direction in the stream.
+		float angle = ReadQuantizedFloat(-PI, PI, directionBits);
+		x = cos(angle) * length;
+		y = sin(angle) * length;
+	}
+	else // Zero length, no direction present in the buffer.
+	{
+		x = y = 0.f;
+	}
+}
+
+void DataDeserializer::ReadNormalizedVector3D(int numBitsYaw, int numBitsPitch, float &x, float &y, float &z)
+{
+	float azimuth = ReadQuantizedFloat(-PI, PI, numBitsYaw);
+	float inclination = ReadQuantizedFloat(-PI/2, PI/2, numBitsPitch);
+
+	float cx = cos(inclination);
+	x = cx * sin(azimuth);
+	y = -sin(inclination);
+	z = cx * cos(azimuth);
+}
+
+void DataDeserializer::ReadVector3D(int numBitsYaw, int numBitsPitch, int magnitudeIntegerBits, int magnitudeDecimalBits, float &x, float &y, float &z)
+{
+	// Read the length in unsigned fixed point format.
+	// The following line is effectively the same as calling ReadUnsignedFixedPoint, but manually perform it
+	// to be precisely able to examine whether the length is zero.
+	u32 fp = ReadBits(magnitudeIntegerBits + magnitudeDecimalBits);
+	if (fp != 0) // If length is non-zero, the stream also contains the direction.
+	{
+		float length = fp / (float)(1 << magnitudeDecimalBits);
+
+		float azimuth = ReadQuantizedFloat(-PI, PI, numBitsYaw);
+		float inclination = ReadQuantizedFloat(-PI/2, PI/2, numBitsPitch);
+
+		float cx = cos(inclination);
+		x = cx * sin(azimuth) * length;
+		y = -sin(inclination) * length;
+		z = cx * cos(azimuth) * length;
+	}
+	else // length is zero, stream does not contain the direction.
+	{
+		x = y = z = 0.f;
+	}
+}
+
+void DataDeserializer::ReadArithmeticEncoded(int numBits, int &val1, int max1, int &val2, int max2)
+{
+	assert(max1 * max2 < (1 << numBits));
+	u32 val = ReadBits(numBits);
+	val2 = val % max2;
+	val1 = val / max2;
+}
+
+void DataDeserializer::ReadArithmeticEncoded(int numBits, int &val1, int max1, int &val2, int max2, int &val3, int max3)
+{
+	assert(max1 * max2 * max3 < (1 << numBits));
+	u32 val = ReadBits(numBits);
+	val3 = val % max3;
+	val /= max3;
+	val2 = val % max2;
+	val1 = val / max2;
+}
+
+void DataDeserializer::ReadArithmeticEncoded(int numBits, int &val1, int max1, int &val2, int max2, int &val3, int max3, int &val4, int max4)
+{
+	assert(max1 * max2 * max3 * max4 < (1 << numBits));
+	u32 val = ReadBits(numBits);
+	val4 = val % max4;
+	val /= max4;
+	val3 = val % max3;
+	val /= max3;
+	val2 = val % max2;
+	val1 = val / max2;
+}
+
+void DataDeserializer::ReadArithmeticEncoded(int numBits, int &val1, int max1, int &val2, int max2, int &val3, int max3, int &val4, int max4, int &val5, int max5)
+{
+	assert(max1 * max2 * max3 * max4 * max5 < (1 << numBits));
+	u32 val = ReadBits(numBits);
+	val5 = val % max5;
+	val /= max5;
+	val4 = val % max4;
+	val /= max4;
+	val3 = val % max3;
+	val /= max3;
+	val2 = val % max2;
+	val1 = val / max2;
+}
+
+void DataDeserializer::SkipBits(int numBits)
 {
 	assert(!iter);
 
@@ -146,24 +299,24 @@ void DataDeserializer::SkipBits(size_t numBits)
 		throw NetException("Not enough bits left in DataDeserializer::SkipBits(2)!");
 }
 
-String DataDeserializer::ReadString()
+std::string DataDeserializer::ReadString()
 {
 	u32 length = (iter ? GetDynamicElemCount() : Read<u8>());
 	if (BitsLeft() < length*8)
 		throw NetException("Not enough bytes left in DataDeserializer::ReadString!");
 
-	String str;
+	std::string str;
 	if (bitOfs == 0)
 	{
-		str.Append(data + elemOfs, length);
+		str.append(data + elemOfs, length);
 		elemOfs += length;
 	}
 	else
 	{
-		PODVector<u8> bytes(length+1);
+		std::vector<u8> bytes(length+1);
 		ReadArray<u8>(&bytes[0], length);
 
-		str.Append((char*)&bytes[0], length);
+		str.append((char*)&bytes[0], length);
 	}
 
 	if (iter)
@@ -172,7 +325,7 @@ String DataDeserializer::ReadString()
 
 	// Perform string validation: Replace any offending values with the space bar character.
 	// Valid values: 0x00 (null), 0x09 (tab), 0x0D, 0x0A (newlines), [32, 253] (characters)
-	for(size_t i = 0; i < str.Length(); ++i)
+	for(size_t i = 0; i < str.length(); ++i)
 		if ((unsigned char)str[i] >= 254 || ((unsigned char)str[i] < 32 && str[i] != 0x0D && str[i] != 0x0A && str[i] != 0x09)) // Retain newlines and tab.
 			str[i] = 0x20; // Space bar character
 

+ 249 - 16
ThirdParty/kNet/src/DataSerializer.cpp

@@ -15,9 +15,9 @@
 /** @file DataSerializer.cpp
 	@brief */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include <cstring>
+#include <sstream>
+#include <cmath>
 
 #include "kNet/DebugMemoryLeakCheck.h"
 
@@ -33,7 +33,7 @@ DataSerializer::DataSerializer(size_t maxBytes_)
 
 	maxBytes = maxBytes_;
 	messageData = new SerializedMessage();
-	messageData->data.Resize(maxBytes);
+	messageData->data.resize(maxBytes);
 	data = &messageData->data[0];
 
 	ResetFill();
@@ -48,7 +48,7 @@ DataSerializer::DataSerializer(size_t maxBytes_, const SerializedMessageDesc *ms
 
 	maxBytes = maxBytes_;
 	messageData = new SerializedMessage();
-	messageData->data.Resize(maxBytes);
+	messageData->data.resize(maxBytes);
 	data = &messageData->data[0];
 
 	ResetFill();
@@ -69,24 +69,24 @@ DataSerializer::DataSerializer(char *data_, size_t maxBytes_, const SerializedMe
 	ResetFill();
 }
 
-DataSerializer::DataSerializer(PODVector<char> &data_, size_t maxBytes_)
+DataSerializer::DataSerializer(std::vector<char> &data_, size_t maxBytes_)
 {
-	if (data_.Size() < maxBytes_)
-		data_.Resize(maxBytes_);
-	if (data_.Size() == 0 || maxBytes_ == 0)
-		throw NetException("Cannot instantiate a DataSerializer object to a zero-sized Vector-based buffer!");
+	if (data_.size() < maxBytes_)
+		data_.resize(maxBytes_);
+	if (data_.size() == 0 || maxBytes_ == 0)
+		throw NetException("Cannot instantiate a DataSerializer object to a zero-sized std::vector-based buffer!");
 	data = &data_[0];
 	maxBytes = maxBytes_;
 
 	ResetFill();
 }
 
-DataSerializer::DataSerializer(PODVector<char> &data_, size_t maxBytes_, const SerializedMessageDesc *msgTemplate)
+DataSerializer::DataSerializer(std::vector<char> &data_, size_t maxBytes_, const SerializedMessageDesc *msgTemplate)
 {
-	if (data_.Size() < maxBytes_)
-		data_.Resize(maxBytes_);
-	if (data_.Size() == 0 || maxBytes_ == 0)
-		throw NetException("Cannot instantiate a DataSerializer object to a zero-sized Vector-based buffer!");
+	if (data_.size() < maxBytes_)
+		data_.resize(maxBytes_);
+	if (data_.size() == 0 || maxBytes_ == 0)
+		throw NetException("Cannot instantiate a DataSerializer object to a zero-sized std::vector-based buffer!");
 	data = &data_[0];
 	maxBytes = maxBytes_;
 
@@ -159,6 +159,226 @@ void DataSerializer::AddAlignedByteArray(const void *srcData, u32 numBytes)
 	elemOfs += numBytes;
 }
 
+u32 DataSerializer::AddUnsignedFixedPoint(int numIntegerBits, int numDecimalBits, float value)
+{
+	assert(numIntegerBits >= 0);
+	assert(numDecimalBits > 0);
+	assert(numIntegerBits + numDecimalBits <= 32);
+	const float maxVal = (float)(1 << numIntegerBits);
+	const u32 maxBitPattern = (1 << (numIntegerBits + numDecimalBits)) - 1; // All ones - the largest value we can send.
+	u32 outVal = value <= 0 ? 0 : (value >= maxVal ? maxBitPattern : (u32)(value * (float)(1 << numDecimalBits)));
+	assert(outVal <= maxBitPattern);
+	AppendBits(outVal, numIntegerBits + numDecimalBits);
+	return outVal;
+}
+
+u32 DataSerializer::AddSignedFixedPoint(int numIntegerBits, int numDecimalBits, float value)
+{
+	// Adding a [-k, k-1] range -> remap to unsigned [0, 2k-1] range and send that instead.
+	return AddUnsignedFixedPoint(numIntegerBits, numDecimalBits, value + (float)(1 << (numIntegerBits-1)));
+}
+
+static inline float ClampF(float val, float minVal, float maxVal) { return val <= minVal ? minVal : (val >= maxVal ? maxVal : val); }
+
+u32 DataSerializer::AddQuantizedFloat(float minRange, float maxRange, int numBits, float value)
+{
+	u32 outVal = (u32)((ClampF(value, minRange, maxRange) - minRange) * (float)((1 << numBits)-1) / (maxRange - minRange));
+	AppendBits(outVal, numBits);
+	return outVal;
+}
+
+void DataSerializer::AddMiniFloat(bool signBit, int exponentBits, int mantissaBits, int exponentBias, float value)
+{
+	// Float structure:
+	// 1-bit sign
+	// 8-bit exponent
+	// 23-bit mantissa
+	// s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm
+	// Different float categories:
+	// 0 00000000 00000000000000000000000  +zero
+	// 1 00000000 00000000000000000000000  -zero
+	// s 00000000 mmmmmmmmmmmmmmmmmmmmmmm  A denormal number, mmmmm != 0, interpreted as (-1)^s * 2^-126 * 0.mmmmm
+	// s eeeeeeee xxxxxxxxxxxxxxxxxxxxxxx  A normal number, eeeee != 0, interpreted as (-1)^s * 2^(e-127) * 1.mmmmm
+	// 0 11111111 00000000000000000000000  +inf
+	// 1 11111111 00000000000000000000000  -inf
+	// y 11111111 1xxxxxxxxxxxxxxxxxxxxxx  Quiet NaN, y and xxxxx are arbitrary (custom) payload for the NaN.
+	// y 11111111 0xxxxxxxxxxxxxxxxxxxxxx  Signalling NaN, y and xxxxx != 0 is arbitrary payload for the NaN.
+
+	// When writing our custom low-precision minifloat, make sure that values in each of the above categories stays in
+	// the same category, if possible:
+	// +zero: Reducing bits does not affect the value.
+	// -zero: Reducing bits does not affect the value. If sending unsigned, -zero becomes +zero.
+	// denormals: Reducing bits from mantissa gracefully loses precision. Reducing exponent does not affect the value.
+	//            If sending unsigned, negative denormals flush to zero.
+	// normals: Reducing bits from exponent can cause the exponent to overflow or underflow.
+	//          If the exponent is too large to be encoded, +inf/-inf is sent instead.
+	//          If the exponent is too small to be encoded, the value is flushed to zero. \todo Could create a denormal!
+	// +inf: Reducing bits does not affect the value.
+	// -inf: Reducing bits from exponent or mantissa does not matter. If sending unsigned, -inf flushes to zero.
+	// QNaN/SNaN: Reducing bits loses data from the custom NaN payload field. If mantissaBits == 0, cannot differentiat
+	//            between QNaN and SNaN.
+
+	assert(sizeof(float) == 4);
+	assert(exponentBits > 0);
+	assert(exponentBits <= 8);
+	assert(mantissaBits > 0);
+	assert(mantissaBits <= 23);
+	u32 v = *(u32*)&value;
+	u32 biasedExponent = (v & 0x7F800000) >> 23;
+	u32 mantissa = v & 0x7FFFFF;
+	bool sign = (v & 0x80000000) != 0; // If true, the float is negative.
+
+	// Write the sign bit, if sending out a signed minifloat. Otherwise, clamp all negative numbers to +zero.
+	if (signBit)
+		Add<bit>(sign);
+	else if (sign && biasedExponent != 0)
+		biasedExponent = mantissa = 0; // If the number was not a NaN, write out +zero.
+
+	// The maximum biased exponent value in the reduced precision representation. This corresponds to NaNs and +/-Infs.
+	const u32 maxBiasedExponent = (1 << exponentBits) - 1;
+
+	int trueExponent = biasedExponent - 127; // The true exponent of the float, if this number is a normal number.
+	int newBiasedExponent;
+
+	// Compute the new biased exponent value to send.
+	if (biasedExponent != 0xFF && biasedExponent != 0) // Is this a normalized float?
+	{
+		newBiasedExponent = trueExponent + exponentBias;
+		
+		// Check if the new biased exponent is too large to be represented, and the float overflows to a +/-Inf.
+		if (newBiasedExponent >= (int)maxBiasedExponent)
+		{
+			newBiasedExponent = maxBiasedExponent;
+			mantissa = 0; // To specify that this is an Inf and not a NaN.
+		}
+		// Check if the new biased exponent underflowed. In that case flush to zero.
+		///\todo This is not absolutely correct with respect to denormalized numbers. Underflowing
+		/// the exponent should produce a denormalized number, but this directly makes it zero.
+		if (newBiasedExponent <= 0)
+			newBiasedExponent = mantissa = 0;
+	}
+	else
+		newBiasedExponent = biasedExponent; // either all zeroes (+/-zero or denormal) or all ones (nan or inf).
+
+	// Scrap the given number of precision from the mantissa.
+	u32 newMantissa = mantissa >> (23 - mantissaBits);
+
+	// If the float was a SNaN, make sure it stays a SNaN after some of the NaN payload was removed.
+	if (biasedExponent == 0xFF && mantissa != 0 && newMantissa == 0)
+		newMantissa = 1; // Set the mantissa to nonzero to denote a NaN (and don't set the MSB of mantissa, to treat it as SNaN)
+
+	AppendBits(newBiasedExponent, exponentBits);
+	AppendBits(newMantissa, mantissaBits);
+}
+
+#define PI ((float)3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679)
+
+void DataSerializer::AddNormalizedVector2D(float x, float y, int numBits)
+{
+	// Call atan2() to get the aimed angle of the 2D vector in the range [-PI, PI], then quantize the 1D result to the desired precision.
+	AddQuantizedFloat(-PI, PI, numBits, atan2(y, x));
+}
+
+int DataSerializer::AddVector2D(float x, float y, int magnitudeIntegerBits, int magnitudeDecimalBits, int directionBits)
+{
+	// Compute the length of the vector. Use a fixed-point representation to store the length.
+	float length = sqrt(x*x+y*y);
+	u32 bitVal = AddUnsignedFixedPoint(magnitudeIntegerBits, magnitudeDecimalBits, length);
+
+	// If length == 0, don't need to send the angle, as it's a zero vector.
+	if (bitVal != 0)
+	{
+		// Call atan2() to get the aimed angle of the 2D vector in the range [-PI, PI], then quantize the 1D result to the desired precision.
+		float angle = atan2(y, x);
+		AddQuantizedFloat(-PI, PI, directionBits, atan2(y, x));
+		return magnitudeIntegerBits + magnitudeDecimalBits + directionBits;
+	}
+	else
+		return magnitudeIntegerBits + magnitudeDecimalBits;
+}
+
+void DataSerializer::AddNormalizedVector3D(float x, float y, float z, int numBitsYaw, int numBitsPitch)
+{
+	// Convert to spherical coordinates. We assume that the vector (x,y,z) has been normalized beforehand.
+	float azimuth = atan2(x, z); // The 'yaw'
+	float inclination = asin(-y); // The 'pitch'
+
+	AddQuantizedFloat(-PI, PI, numBitsYaw, azimuth);
+	AddQuantizedFloat(-PI/2, PI/2, numBitsPitch, inclination);
+}
+
+int DataSerializer::AddVector3D(float x, float y, float z, int numBitsYaw, int numBitsPitch, int magnitudeIntegerBits, int magnitudeDecimalBits)
+{
+	float length = sqrt(x*x + y*y + z*z);
+
+	u32 bitVal = AddUnsignedFixedPoint(magnitudeIntegerBits, magnitudeDecimalBits, length);
+
+	if (bitVal != 0)
+	{
+		// The written length was not zero. Send the spherical angles as well.
+		float azimuth = atan2(x, z);
+		float inclination = asin(-y / length);
+
+		AddQuantizedFloat(-PI, PI, numBitsYaw, azimuth);
+		AddQuantizedFloat(-PI/2, PI/2, numBitsPitch, inclination);
+		return magnitudeIntegerBits + magnitudeDecimalBits + numBitsYaw + numBitsPitch;
+	}
+	else // The vector is (0,0,0). Don't send spherical angles as they're redundant.
+		return magnitudeIntegerBits + magnitudeDecimalBits;
+}
+
+void DataSerializer::AddArithmeticEncoded(int numBits, int val1, int max1, int val2, int max2)
+{
+	assert(max1 * max2 < (1 << numBits));
+	assert(val1 >= 0);
+	assert(val1 < max1);
+	assert(val2 >= 0);
+	assert(val2 < max2);
+	AppendBits(val1 * max2 + val2, numBits);
+}
+
+void DataSerializer::AddArithmeticEncoded(int numBits, int val1, int max1, int val2, int max2, int val3, int max3)
+{
+	assert(max1 * max2 * max3 < (1 << numBits));
+	assert(val1 >= 0);
+	assert(val1 < max1);
+	assert(val2 >= 0);
+	assert(val2 < max2);
+	assert(val3 >= 0);
+	assert(val3 < max3);
+	AppendBits((val1 * max2 + val2) * max3 + val3, numBits);
+}
+
+void DataSerializer::AddArithmeticEncoded(int numBits, int val1, int max1, int val2, int max2, int val3, int max3, int val4, int max4)
+{
+	assert(max1 * max2 * max3 * max4 < (1 << numBits));
+	assert(val1 >= 0);
+	assert(val1 < max1);
+	assert(val2 >= 0);
+	assert(val2 < max2);
+	assert(val3 >= 0);
+	assert(val3 < max3);
+	assert(val4 >= 0);
+	assert(val4 < max4);
+	AppendBits(((val1 * max2 + val2) * max3 + val3) * max4 + val4, numBits);
+}
+
+void DataSerializer::AddArithmeticEncoded(int numBits, int val1, int max1, int val2, int max2, int val3, int max3, int val4, int max4, int val5, int max5)
+{
+	assert(max1 * max2 * max3 * max4 * max5 < (1 << numBits));
+	assert(val1 >= 0);
+	assert(val1 < max1);
+	assert(val2 >= 0);
+	assert(val2 < max2);
+	assert(val3 >= 0);
+	assert(val3 < max3);
+	assert(val4 >= 0);
+	assert(val4 < max4);
+	assert(val5 >= 0);
+	assert(val5 < max5);
+	AppendBits((((val1 * max2 + val2) * max3 + val3) * max4 + val4) * max5 + val5, numBits);
+}
+
 /// Requires a template to be present to use this.
 void DataSerializer::SetVaryingElemSize(u32 count)
 {
@@ -183,7 +403,7 @@ void DataSerializer::Add<const char*>(const char * const & value)
 }
 
 template<>
-void DataSerializer::Add<String>(const String &value)
+void DataSerializer::Add<std::string>(const std::string &value)
 {
 	AddString(value);
 }
@@ -199,7 +419,7 @@ void DataSerializer::AddString(const char *str)
 {
 	size_t len = strlen(str);
 	if (iter)
-		SetVaryingElemSize(len);
+		SetVaryingElemSize((u32)len);
 	else
 		Add<u8>(len);
 
@@ -213,4 +433,17 @@ void DataSerializer::SkipNumBytes(size_t numBytes)
 		throw NetException("DataSerializer::SkipNumBytes: Attempted to travel past the end of the array!");
 }
 
+bool DataSerializer::DebugReadBit(int bitIndex) const
+{
+	return (data[bitIndex >> 3] & (1 << (bitIndex & 7))) != 0;
+}
+
+std::string DataSerializer::DebugReadBits(int startIndex, int endIndex) const
+{
+	std::stringstream ss;
+	for(int i = startIndex; i < endIndex; ++i)
+		ss << DebugReadBit(i) ? "1" : "0";
+	return ss.str();
+}
+
 } // ~kNet

+ 36 - 38
ThirdParty/kNet/src/FragmentedTransferManager.cpp

@@ -15,8 +15,6 @@
 /** @file FragmentedTransferManager.cpp
 	@brief */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include <cstring>
 
 #ifdef KNET_USE_BOOST
@@ -37,17 +35,17 @@ namespace kNet
 
 void FragmentedSendManager::FragmentedTransfer::AddMessage(NetworkMessage *message)
 {
-	fragments.Push(message);
+	fragments.push_back(message);
 	message->transfer = this;
 }
 
 bool FragmentedSendManager::FragmentedTransfer::RemoveMessage(NetworkMessage *message)
 {
-	for(List<NetworkMessage*>::Iterator iter = fragments.Begin(); iter != fragments.End(); ++iter)
+	for(std::list<NetworkMessage*>::iterator iter = fragments.begin(); iter != fragments.end(); ++iter)
 		if (*iter == message)
 		{
 			message->transfer = 0;
-			fragments.Erase(iter);
+			fragments.erase(iter);
 			LOG(LogVerbose, "Removing message with seqnum %d (fragnum %d) from transfer ID %d (%p).", (int)message->messageNumber, (int)message->fragmentIndex, id, this);
 			return true;
 		}
@@ -56,8 +54,8 @@ bool FragmentedSendManager::FragmentedTransfer::RemoveMessage(NetworkMessage *me
 
 FragmentedSendManager::FragmentedTransfer *FragmentedSendManager::AllocateNewFragmentedTransfer()
 {
-	transfers.Push(FragmentedTransfer());
-	FragmentedTransfer *transfer = &transfers.Back();
+	transfers.push_back(FragmentedTransfer());
+	FragmentedTransfer *transfer = &transfers.back();
 	transfer->id = -1;
 	transfer->totalNumFragments = 0;
 
@@ -69,13 +67,13 @@ FragmentedSendManager::FragmentedTransfer *FragmentedSendManager::AllocateNewFra
 void FragmentedSendManager::FreeFragmentedTransfer(FragmentedTransfer *transfer)
 {
 	// Remove all references from any NetworkMessages to this structure.
-	for(List<NetworkMessage*>::Iterator iter = transfer->fragments.Begin(); iter != transfer->fragments.End(); ++iter)
+	for(std::list<NetworkMessage*>::iterator iter = transfer->fragments.begin(); iter != transfer->fragments.end(); ++iter)
 		(*iter)->transfer = 0;
 
-	for(TransferList::Iterator iter = transfers.Begin(); iter != transfers.End(); ++iter)
+	for(TransferList::iterator iter = transfers.begin(); iter != transfers.end(); ++iter)
 		if (&*iter == transfer)
 		{
-			transfers.Erase(iter);
+			transfers.erase(iter);
 			LOG(LogObjectAlloc, "Freed fragmented transfer ID=%d, numFragments: %d (%p).", transfer->id, (int)transfer->totalNumFragments, transfer);
 			return;
 		}
@@ -91,7 +89,7 @@ void FragmentedSendManager::RemoveMessage(FragmentedTransfer *transfer, NetworkM
 		return;
 	}
 
-	if (transfer->fragments.Size() == 0)
+	if (transfer->fragments.size() == 0)
 		FreeFragmentedTransfer(transfer);
 }
 
@@ -108,7 +106,7 @@ bool FragmentedSendManager::AllocateFragmentedTransferID(FragmentedTransfer &tra
 	while(used)
 	{
 		used = false;
-		for(TransferList::Iterator iter = transfers.Begin(); iter != transfers.End(); ++iter)
+		for(TransferList::iterator iter = transfers.begin(); iter != transfers.end(); ++iter)
 		{
 			if (iter->id == transferID)
 			{
@@ -128,8 +126,8 @@ bool FragmentedSendManager::AllocateFragmentedTransferID(FragmentedTransfer &tra
 
 void FragmentedSendManager::FreeAllTransfers()
 {
-	while(transfers.Size() > 0)
-		FreeFragmentedTransfer(&transfers.Front());
+	while(transfers.size() > 0)
+		FreeFragmentedTransfer(&transfers.front());
 }
 
 void FragmentedReceiveManager::NewFragmentStartReceived(int transferID, int numTotalFragments, const char *data, size_t numBytes)
@@ -143,16 +141,16 @@ void FragmentedReceiveManager::NewFragmentStartReceived(int transferID, int numT
 		return;
 	}
 
-	for(size_t i = 0; i < transfers.Size(); ++i)
+	for(size_t i = 0; i < transfers.size(); ++i)
 		if (transfers[i].transferID == transferID)
 		{
 			LOG(LogError, "An existing transfer with ID %d existed! Deleting it.", transferID);
-			transfers.Erase(transfers.Begin() + i);
+			transfers.erase(transfers.begin() + i);
 			--i;
 		}
 
-	transfers.Push(ReceiveTransfer());
-	ReceiveTransfer &transfer = transfers.Back();
+	transfers.push_back(ReceiveTransfer());
+	ReceiveTransfer &transfer = transfers.back();
 	transfer.transferID = transferID;
 	transfer.numTotalFragments = numTotalFragments;
 
@@ -170,28 +168,28 @@ bool FragmentedReceiveManager::NewFragmentReceived(int transferID, int fragmentN
 		return false;
 	}
 
-	for(size_t i = 0; i < transfers.Size(); ++i)
+	for(size_t i = 0; i < transfers.size(); ++i)
 		if (transfers[i].transferID == transferID)
 		{
 			ReceiveTransfer &transfer = transfers[i];
 
-			for(size_t j = 0; j < transfer.fragments.Size(); ++j)
+			for(size_t j = 0; j < transfer.fragments.size(); ++j)
 				if (transfer.fragments[j].fragmentIndex == fragmentNumber)
 				{
 					LOG(LogError, "A fragment with fragmentNumber %d already exists for transferID %d. Discarding the new fragment! Old size: %db, discarded size: %db",
-						fragmentNumber, transferID, (int)transfer.fragments[j].data.Size(), (int)numBytes);
+						fragmentNumber, transferID, (int)transfer.fragments[j].data.size(), (int)numBytes);
 					return false;
 				}
 
-			transfer.fragments.Push(ReceiveFragment());
-			ReceiveFragment &fragment = transfer.fragments.Back();
+			transfer.fragments.push_back(ReceiveFragment());
+			ReceiveFragment &fragment = transfer.fragments.back();
 			fragment.fragmentIndex = fragmentNumber;
-			fragment.data.Insert(fragment.data.End(), data, data + numBytes);
+			fragment.data.insert(fragment.data.end(), data, data + numBytes);
 
-			if (transfer.fragments.Size() >= (size_t)transfer.numTotalFragments)
+			if (transfer.fragments.size() >= (size_t)transfer.numTotalFragments)
 			{
 				LOG(LogData, "Finished receiving a fragmented transfer that consisted of %d fragments (transferID=%d).",
-					(int)transfer.fragments.Size(), transfer.transferID);
+					(int)transfer.fragments.size(), transfer.transferID);
 				return true;
 			}
 			else
@@ -202,38 +200,38 @@ bool FragmentedReceiveManager::NewFragmentReceived(int transferID, int fragmentN
 	return false;
 }
 
-void FragmentedReceiveManager::AssembleMessage(int transferID, PODVector<char> &assembledData)
+void FragmentedReceiveManager::AssembleMessage(int transferID, std::vector<char> &assembledData)
 {
-	for(size_t i = 0; i < transfers.Size(); ++i)
+	for(size_t i = 0; i < transfers.size(); ++i)
 		if (transfers[i].transferID == transferID)
 		{
 			ReceiveTransfer &transfer = transfers[i];
 			size_t totalSize = 0;
 
-			for(size_t j = 0; j < transfer.fragments.Size(); ++j)
-				totalSize += transfer.fragments[j].data.Size();
+			for(size_t j = 0; j < transfer.fragments.size(); ++j)
+				totalSize += transfer.fragments[j].data.size();
 
-			assembledData.Resize(totalSize);
+			assembledData.resize(totalSize);
 
 			///\todo Sort by fragmentIndex.
 			
 			size_t offset = 0;
-			for(size_t j = 0; j < transfer.fragments.Size(); ++j)
+			for(size_t j = 0; j < transfer.fragments.size(); ++j)
 			{
-				assert(transfer.fragments[j].data.Size() > 0);
-				memcpy(&assembledData[offset], &transfer.fragments[j].data[0], transfer.fragments[j].data.Size());
-				offset += transfer.fragments[j].data.Size();
-				assert(offset <= assembledData.Size());
+				assert(transfer.fragments[j].data.size() > 0);
+				memcpy(&assembledData[offset], &transfer.fragments[j].data[0], transfer.fragments[j].data.size());
+				offset += transfer.fragments[j].data.size();
+				assert(offset <= assembledData.size());
 			}
 		}
 }
 
 void FragmentedReceiveManager::FreeMessage(int transferID)
 {
-	for(size_t i = 0; i < transfers.Size(); ++i)
+	for(size_t i = 0; i < transfers.size(); ++i)
 		if (transfers[i].transferID == transferID)
 		{
-			transfers.Erase(transfers.Begin() + i);
+			transfers.erase(transfers.begin() + i);
 			return;
 		}
 }

+ 88 - 88
ThirdParty/kNet/src/MessageConnection.cpp

@@ -15,8 +15,6 @@
 /** @file MessageConnection.cpp
 	@brief */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include <algorithm>
 #include <iostream>
 #include <cassert>
@@ -49,7 +47,7 @@ namespace
 
 	/// The interval at which we send ping messages.
 	///\todo Make this user-defineable.
-	const float pingIntervalMSecs = 1000.f;
+	const float pingIntervalMSecs = 3.5 * 1000.f;
 	/// The interval at which we update the internal statistics fields.
 	const float statsRefreshIntervalMSecs = 1000.f;
 	/// The time interval after which, if we don't get a response to a PingRequest message, the connection is declared lost.
@@ -64,17 +62,17 @@ namespace
 namespace kNet
 {
 
-void AppendU8ToVector(PODVector<char> &data, unsigned long value)
+void AppendU8ToVector(std::vector<char> &data, unsigned long value)
 {
-	data.Insert(data.End(), (const char *)&value, (const char *)&value + 1);
+	data.insert(data.end(), (const char *)&value, (const char *)&value + 1);
 }
 
-void AppendU32ToVector(PODVector<char> &data, unsigned long value)
+void AppendU32ToVector(std::vector<char> &data, unsigned long value)
 {
-	data.Insert(data.End(), (const char *)&value, (const char *)&value + 4);
+	data.insert(data.end(), (const char *)&value, (const char *)&value + 4);
 }
 
-String ConnectionStateToString(ConnectionState state)
+std::string ConnectionStateToString(ConnectionState state)
 {
 	switch(state)
 	{
@@ -104,6 +102,7 @@ bytesInTotal(0), bytesOutTotal(0)
 #endif
 {
 	connectionState = startingState;
+	networkSendSimulator.owner = this;
 
 	eventMsgsOutAvailable = CreateNewEvent(EventWaitSignal);
 	assert(eventMsgsOutAvailable.IsValid());
@@ -195,7 +194,7 @@ bool MessageConnection::WaitToEstablishConnection(int maxMSecsToWait)
 		Clock::Sleep(1); ///\todo Instead of waiting multiple 1msec slices, should wait for proper event.
 
 	LOG(LogWaits, "MessageConnection::WaitToEstablishConnection: Waited %f msecs for connection. Result: %s.",
-		timer.MSecsElapsed(), ConnectionStateToString(GetConnectionState()).CString());
+		timer.MSecsElapsed(), ConnectionStateToString(GetConnectionState()).c_str());
 
 	return GetConnectionState() == ConnectionOK;
 }
@@ -211,7 +210,7 @@ void MessageConnection::Disconnect(int maxMSecsToWait)
 		return;
 
 	LOG(LogInfo, "MessageConnection::Disconnect(%d msecs): Write-closing connection. connectionState = %s, socket readOpen:%s, socket writeOpen:%s.", 
-		maxMSecsToWait, ConnectionStateToString(connectionState).CString(), socket->IsReadOpen() ? "true":"false",
+		maxMSecsToWait, ConnectionStateToString(connectionState).c_str(), socket->IsReadOpen() ? "true":"false",
 		socket->IsWriteOpen() ? "true":"false");
 	assert(maxMSecsToWait >= 0);
 
@@ -226,7 +225,7 @@ void MessageConnection::Disconnect(int maxMSecsToWait)
 		}
 
 		LOG(LogWaits, "MessageConnection::Disconnect: Waited %f msecs for disconnection. Result: %s.",
-			timer.MSecsElapsed(), ConnectionStateToString(GetConnectionState()).CString());
+			timer.MSecsElapsed(), ConnectionStateToString(GetConnectionState()).c_str());
 	}
 
 	if (GetConnectionState() == ConnectionClosed)
@@ -241,13 +240,13 @@ void MessageConnection::Close(int maxMSecsToWait) // [main thread]
 	{
 		Disconnect(maxMSecsToWait);
 		LOG(LogInfo, "MessageConnection::Close(%d msecs): Disconnecting. connectionState = %s, readOpen:%s, writeOpen:%s.", 
-			maxMSecsToWait, ConnectionStateToString(connectionState).CString(), (socket && socket->IsReadOpen()) ? "true":"false",
+			maxMSecsToWait, ConnectionStateToString(connectionState).c_str(), (socket && socket->IsReadOpen()) ? "true":"false",
 			(socket && socket->IsWriteOpen()) ? "true":"false");
 	}
 
 	if (owner)
 	{
-		LOG(LogInfo, "MessageConnection::Close: Closed connection to %s.", ToString().CString());
+		LOG(LogInfo, "MessageConnection::Close: Closed connection to %s.", ToString().c_str());
 		owner->CloseConnection(this); // This will cause this connection to be disconnected of its worker thread, so that we can safely proceed to tear down the socket.
 		assert(!IsWorkerThreadRunning());
 		owner = 0;
@@ -272,11 +271,11 @@ void MessageConnection::Close(int maxMSecsToWait) // [main thread]
 	if (inboundMessageQueue.Size() > 0)
 		LOG(LogVerbose, "MessageConnection::Close(): Had %d messages in inboundMessageQueue!", (int)inboundMessageQueue.Size());
 
-	if (fragmentedSends.UnsafeGetValue().transfers.Size() > 0)
-		LOG(LogVerbose, "MessageConnection::Close(): Had %d messages in fragmentedSends.transfers list!", (int)fragmentedSends.UnsafeGetValue().transfers.Size());
+	if (fragmentedSends.UnsafeGetValue().transfers.size() > 0)
+		LOG(LogVerbose, "MessageConnection::Close(): Had %d messages in fragmentedSends.transfers list!", (int)fragmentedSends.UnsafeGetValue().transfers.size());
 
-	if (fragmentedReceives.transfers.Size() > 0)
-		LOG(LogVerbose, "MessageConnection::Close(): Had %d messages in fragmentedReceives.transfers list!", (int)fragmentedReceives.transfers.Size());
+	if (fragmentedReceives.transfers.size() > 0)
+		LOG(LogVerbose, "MessageConnection::Close(): Had %d messages in fragmentedReceives.transfers list!", (int)fragmentedReceives.transfers.size());
 
 	FreeMessageData();
 }
@@ -331,7 +330,7 @@ void MessageConnection::FreeMessageData() // [main thread]
 	Lockable<FragmentedSendManager>::LockType sends = fragmentedSends.Acquire();
 	sends->FreeAllTransfers();
 
-	fragmentedReceives.transfers.Clear();
+	fragmentedReceives.transfers.clear();
 
 	while(outboundAcceptQueue.Size() > 0)
 	{
@@ -355,14 +354,16 @@ void MessageConnection::FreeMessageData() // [main thread]
 
 	outboundQueue.Clear();
 
-	inboundContentIDStamps.Clear();
+	inboundContentIDStamps.clear();
 
-	outboundContentIDMessages.Clear();
+	outboundContentIDMessages.clear();
 
 	Lockable<ConnectionStatistics>::LockType stats_ = statistics.Acquire();
-	stats_->ping.Clear();
-	stats_->recvPacketIDs.Clear();
-	stats_->traffic.Clear();
+	stats_->ping.clear();
+	stats_->recvPacketIDs.clear();
+	stats_->traffic.clear();
+
+	networkSendSimulator.Free();
 }
 
 void MessageConnection::DetectConnectionTimeOut()
@@ -432,6 +433,8 @@ void MessageConnection::UpdateConnection() // [Called from the worker thread]
 
 	AcceptOutboundMessages();
 
+	networkSendSimulator.Process();
+
 	// MessageConnection needs to automatically manage the sending of ping messages in an unreliable channel.
 	if (connectionState == ConnectionOK && pingTimer.TriggeredOrNotRunning())
 	{
@@ -650,7 +653,7 @@ void MessageConnection::EndAndQueueMessage(NetworkMessage *msg, size_t numBytes,
 		LOG(LogVerbose, "MessageConnection::EndAndQueueMessage: Discarded message with ID 0x%X and size %d bytes. "
 			"msg->obsolete: %d. socket ptr: %p. ConnectionState: %s. socket->IsWriteOpen(): %s. msgconn->IsWriteOpen: %s. "
 			"internalQueue: %s.",
-			(int)msg->id, (int)numBytes, (int)msg->obsolete, socket, ConnectionStateToString(GetConnectionState()).CString(), (socket && socket->IsWriteOpen()) ? "true" : "false",
+			(int)msg->id, (int)numBytes, (int)msg->obsolete, socket, ConnectionStateToString(GetConnectionState()).c_str(), (socket && socket->IsWriteOpen()) ? "true" : "false",
 			IsWriteOpen() ? "true" : "false", internalQueue ? "true" : "false");
 		FreeMessage(msg);
 		return;
@@ -672,7 +675,7 @@ void MessageConnection::EndAndQueueMessage(NetworkMessage *msg, size_t numBytes,
 	///\todo We can optimize here by doing the splitting at datagram creation time to create optimally sized datagrams, but
 	/// it is quite more complicated, so left for later. 
 	const size_t sendHeaderUpperBound = 32; // Reserve some bytes for the packet and message headers. (an approximate upper bound)
-	if (msg->dataSize + sendHeaderUpperBound > socket->MaxSendSize())
+	if (msg->dataSize + sendHeaderUpperBound > socket->MaxSendSize() && socket->TransportLayer() == SocketOverUDP)
 	{
 		const size_t maxFragmentSize = socket->MaxSendSize() / 4 - sendHeaderUpperBound; ///\todo Check this is ok.
 		assert(maxFragmentSize > 0 && maxFragmentSize < socket->MaxSendSize());
@@ -697,20 +700,16 @@ void MessageConnection::EndAndQueueMessage(NetworkMessage *msg, size_t numBytes,
 	}
 	else
 	{
-		if (msg->reliable)
-		{
-			// If message is reliable, block and retry until succeed to queue
-			while (!outboundAcceptQueue.Insert(msg))
-				kNet::Clock::Sleep(5);
-		}
-		else
+		if (!outboundAcceptQueue.Insert(msg))
 		{
-			// If unreliable, just discard if failed to insert
-			if (!outboundAcceptQueue.Insert(msg))
+			if (msg->reliable) // For nonreliable messages it is not critical if we can't enqueue the message. Just discard it.
 			{
-				FreeMessage(msg);
-				return;
+				///\todo Is it possible to check beforehand if this criteria is avoided, or if we are doomed?
+				LOG(LogVerbose, "Critical: Failed to add new reliable message to outboundAcceptQueue! Queue was full. Discarding the message!");
+				assert(false);
 			}
+			FreeMessage(msg);
+			return;
 		}
 		LOG(LogData, "MessageConnection::EndAndQueueMessage: Queued message of size %d bytes and ID 0x%X.", (int)msg->Size(), (int)msg->id);
 	}
@@ -766,7 +765,7 @@ void MessageConnection::Process(int maxMessagesToProcess)
 		if (!inboundMessageHandler)
 		{
 			LOG(LogVerbose, "Warning! Cannot process messages since no message handler registered to connection %s!",
-				ToString().CString());
+				ToString().c_str());
 			return;
 		}
 
@@ -776,7 +775,7 @@ void MessageConnection::Process(int maxMessagesToProcess)
 		inboundMessageQueue.PopFront();
 		assert(msg);
 
-		inboundMessageHandler->HandleMessage(this, msg->id, (msg->dataSize > 0) ? msg->data : 0, msg->dataSize);
+		inboundMessageHandler->HandleMessage(this, msg->receivedPacketID, msg->id, (msg->dataSize > 0) ? msg->data : 0, msg->dataSize);
 
 		FreeMessage(msg);
 	}
@@ -816,7 +815,7 @@ void MessageConnection::WaitForMessage(int maxMSecsToWait) // [main thread]
 		if (timer.MSecsElapsed() >= 1000.f)
 		{
 				LOG(LogWaits, "MessageConnection::WaitForMessage: Waited %f msecs for a new message. ConnectionState: %s. %d messages in queue.",
-				timer.MSecsElapsed(), ConnectionStateToString(GetConnectionState()).CString(), (int)inboundMessageQueue.Size());
+				timer.MSecsElapsed(), ConnectionStateToString(GetConnectionState()).c_str(), (int)inboundMessageQueue.Size());
 		}
 	}
 }
@@ -882,8 +881,8 @@ void MessageConnection::AddOutboundStats(unsigned long numBytes, unsigned long n
 		return;
 
 	ConnectionStatistics &cs = statistics.LockGet();
-	cs.traffic.Push(ConnectionStatistics::TrafficTrack());
-	ConnectionStatistics::TrafficTrack &t = cs.traffic.Back();
+	cs.traffic.push_back(ConnectionStatistics::TrafficTrack());
+	ConnectionStatistics::TrafficTrack &t = cs.traffic.back();
 	t.bytesIn = t.messagesIn = t.packetsIn = 0;
 	t.bytesOut = numBytes;
 	t.packetsOut = numPackets;
@@ -901,8 +900,8 @@ void MessageConnection::AddInboundStats(unsigned long numBytes, unsigned long nu
 		return;
 
 	ConnectionStatistics &cs = statistics.LockGet();
-	cs.traffic.Push(ConnectionStatistics::TrafficTrack());
-	ConnectionStatistics::TrafficTrack &t = cs.traffic.Back();
+	cs.traffic.push_back(ConnectionStatistics::TrafficTrack());
+	ConnectionStatistics::TrafficTrack &t = cs.traffic.back();
 	t.bytesOut = t.messagesOut = t.packetsOut = 0;
 	t.bytesIn = numBytes;
 	t.packetsIn = numPackets;
@@ -922,14 +921,14 @@ void MessageConnection::ComputeStats()
 	const tick_t timeNow = Clock::Tick();
 	const tick_t maxTickAge = timeNow - maxEntryAge;
 
-	for(size_t i = 0; i < cs.traffic.Size(); ++i)
+	for(size_t i = 0; i < cs.traffic.size(); ++i)
 		if (Clock::IsNewer(cs.traffic[i].tick, maxTickAge))
 		{
-			cs.traffic.Erase(cs.traffic.Begin(), cs.traffic.Begin() + i);
+			cs.traffic.erase(cs.traffic.begin(), cs.traffic.begin() + i);
 			break;
 		}
 
-	if (cs.traffic.Size() <= 1)
+	if (cs.traffic.size() <= 1)
 	{
 		bytesInPerSec = bytesOutPerSec = msgsInPerSec = msgsOutPerSec = packetsInPerSec = packetsOutPerSec = 0.f;
 		statistics.Unlock();
@@ -943,7 +942,7 @@ void MessageConnection::ComputeStats()
 	unsigned long totalPacketsIn = 0;
 	unsigned long totalPacketsOut = 0;
 
-	for(size_t i = 0; i < cs.traffic.Size(); ++i)
+	for(size_t i = 0; i < cs.traffic.size(); ++i)
 	{
 		totalBytesIn += cs.traffic[i].bytesIn;
 		totalBytesOut += cs.traffic[i].bytesOut;
@@ -952,7 +951,7 @@ void MessageConnection::ComputeStats()
 		totalMsgsIn += cs.traffic[i].messagesIn;
 		totalMsgsOut += cs.traffic[i].messagesOut;
 	}
-	tick_t ticks = cs.traffic.Back().tick - cs.traffic.Front().tick;
+	tick_t ticks = cs.traffic.back().tick - cs.traffic.front().tick;
 	float secs = max(1.f, (float)Clock::TicksToMillisecondsD(ticks) / 1000.f);
 	bytesInPerSec = (float)totalBytesIn / secs;
 	bytesOutPerSec = (float)totalBytesOut / secs;
@@ -972,26 +971,26 @@ void MessageConnection::CheckAndSaveOutboundMessageWithContentID(NetworkMessage
 	if (msg->contentID == 0)
 		return;
 
-	MsgContentIDPair key(msg->id, msg->contentID);
-	ContentIDSendTrack::Iterator iter = outboundContentIDMessages.Find(key);
-	if (iter != outboundContentIDMessages.End()) // We have a previous message in the queue which is now obsoleted by this message.
+	MsgContentIDPair key = std::make_pair(msg->id, msg->contentID);
+	ContentIDSendTrack::iterator iter = outboundContentIDMessages.find(key);
+	if (iter != outboundContentIDMessages.end()) // We have a previous message in the queue which is now obsoleted by this message.
 	{
 		// Sanity check: The message numbers must be in the proper order. msg must have been admitted later to send queue than the existing message.
-		if (msg->IsNewerThan(*iter->second_))
+		if (msg->IsNewerThan(*iter->second))
 		{
-			iter->second_->obsolete = true;
-
-			assert(iter->second_ != msg);
-			assert(iter->first_.first_ == iter->second_->id);
-			assert(iter->first_.second_ == iter->second_->contentID);
-			assert(iter->first_.first_ == msg->id);
-			assert(iter->first_.second_ == msg->contentID);
-			iter->second_ = msg;
+			iter->second->obsolete = true;
+
+			assert(iter->second != msg);
+			assert(iter->first.first == iter->second->id);
+			assert(iter->first.second == iter->second->contentID);
+			assert(iter->first.first == msg->id);
+			assert(iter->first.second == msg->contentID);
+			iter->second = msg;
 		}
 		else // This shouldn't happen, but gracefully handle that situation if it does!
 		{
 			LOG(LogError, "Warning! Adding new message ID %d, number %d, content ID %d, priority %d, but it was obsoleted by an already existing message number %d.", 
-				(int)msg->id, (int)msg->messageNumber, (int)msg->contentID, (int)iter->second_->priority, (int)iter->second_->messageNumber);
+				(int)msg->id, (int)msg->messageNumber, (int)msg->contentID, (int)iter->second->priority, (int)iter->second->messageNumber);
 			msg->obsolete = true;
 		}
 	}
@@ -1011,14 +1010,14 @@ void MessageConnection::ClearOutboundMessageWithContentID(NetworkMessage *msg)
 	assert(msg);
 	if (msg->contentID == 0)
 		return;
-	MsgContentIDPair key(msg->id, msg->contentID);
-	ContentIDSendTrack::Iterator iter = outboundContentIDMessages.Find(key);
-	if (iter != outboundContentIDMessages.End())
-		if (msg == iter->second_)
-			outboundContentIDMessages.Erase(iter);
+	MsgContentIDPair key = std::make_pair(msg->id, msg->contentID);
+	ContentIDSendTrack::iterator iter = outboundContentIDMessages.find(key);
+	if (iter != outboundContentIDMessages.end())
+		if (msg == iter->second)
+			outboundContentIDMessages.erase(iter);
 }
 
-bool MessageConnection::CheckAndSaveContentIDStamp(u32 messageID, u32 contentID, packet_id_t packetID)
+bool MessageConnection::CheckAndSaveContentIDStamp(message_id_t messageID, u32 contentID, packet_id_t packetID)
 {
 	AssertInWorkerThreadContext();
 
@@ -1026,18 +1025,18 @@ bool MessageConnection::CheckAndSaveContentIDStamp(u32 messageID, u32 contentID,
 
 	tick_t now = Clock::Tick();
 
-	MsgContentIDPair key(messageID, contentID);
-	ContentIDReceiveTrack::Iterator iter = inboundContentIDStamps.Find(key);
-	if (iter == inboundContentIDStamps.End())
+	MsgContentIDPair key = std::make_pair(messageID, contentID);
+	ContentIDReceiveTrack::iterator iter = inboundContentIDStamps.find(key);
+	if (iter == inboundContentIDStamps.end())
 	{
-		inboundContentIDStamps[key] = Pair<packet_id_t, tick_t>(packetID, now);
+		inboundContentIDStamps[key] = std::make_pair(packetID, now);
 		return true;
 	}
 	else
 	{
-		if (PacketIDIsNewerThan(packetID, iter->second_.first_) || (float)Clock::TimespanToMillisecondsD(iter->second_.second_, now) > 5.f * 1000.f)
+		if (PacketIDIsNewerThan(packetID, iter->second.first) || (float)Clock::TimespanToMillisecondsD(iter->second.second, now) > 5.f * 1000.f)
 		{
-			iter->second_ = Pair<packet_id_t, tick_t>(packetID, now);
+			iter->second = std::make_pair(packetID, now);
 			return true;
 		}
 		else
@@ -1056,16 +1055,16 @@ void MessageConnection::HandleInboundMessage(packet_id_t packetID, const char *d
 
 	// Read the message ID.
 	DataDeserializer reader(data, numBytes);
-	u32 messageID = reader.ReadVLE<VLE8_16_32>(); ///\todo Check that there actually is enough space to read.
+	message_id_t messageID = reader.ReadVLE<VLE8_16_32>(); ///\todo Check that there actually is enough space to read.
 	if (messageID == DataDeserializer::VLEReadError)
 	{
-		LOG(LogError, "Error parsing messageID of a message in socket %s. Data size: %d bytes.", socket->ToString().CString(), (int)numBytes);
+		LOG(LogError, "Error parsing messageID of a message in socket %s. Data size: %d bytes.", socket->ToString().c_str(), (int)numBytes);
 		throw NetException("MessageConnection::HandleInboundMessage: Network error occurred when deserializing message ID VLE field!");
 	}
-	LOG(LogData, "Received message with ID %d and size %d from peer %s.", (int)packetID, (int)numBytes, socket->ToString().CString());
+	LOG(LogData, "Received message with ID %d and size %d from peer %s.", (int)packetID, (int)numBytes, socket->ToString().c_str());
 
 	char str[256];
-	sprintf(str, "messageIn.%u", messageID);
+	sprintf(str, "messageIn.%u", (unsigned int)messageID);
 	ADDEVENT(str, (float)reader.BytesLeft(), "bytes");
 
 	// Pass the message to TCP/UDP -specific message handler.
@@ -1090,6 +1089,7 @@ void MessageConnection::HandleInboundMessage(packet_id_t packetID, const char *d
 			msg->dataSize = reader.BytesLeft();
 			msg->id = messageID;
 			msg->contentID = 0;
+			msg->receivedPacketID = packetID;
 			bool success = inboundMessageQueue.Insert(msg);
 			if (!success)
 			{
@@ -1124,9 +1124,9 @@ void MessageConnection::SendPingRequestMessage(bool internalQueue)
 
 	ConnectionStatistics &cs = statistics.LockGet();
 	
-	u8 pingID = (u8)((cs.ping.Size() == 0) ? 1 : (cs.ping.Back().pingID + 1));
-	cs.ping.Push(ConnectionStatistics::PingTrack());
-	ConnectionStatistics::PingTrack &pingTrack = cs.ping.Back();
+	u8 pingID = (u8)((cs.ping.size() == 0) ? 1 : (cs.ping.back().pingID + 1));
+	cs.ping.push_back(ConnectionStatistics::PingTrack());
+	ConnectionStatistics::PingTrack &pingTrack = cs.ping.back();
 	pingTrack.replyReceived = false;
 	pingTrack.pingSentTick = Clock::Tick();
 	pingTrack.pingID = pingID;
@@ -1180,24 +1180,24 @@ void MessageConnection::HandlePingReplyMessage(const char *data, size_t numBytes
 	const float rttPredictBias = 0.5f;
 
 	u8 pingID = *(u8*)data;
-	for(size_t i = 0; i < cs.ping.Size(); ++i)
+	for(size_t i = 0; i < cs.ping.size(); ++i)
 		if (cs.ping[i].pingID == pingID && cs.ping[i].replyReceived == false)
 		{
 			cs.ping[i].pingReplyTick = Clock::Tick();
 			float newRtt = (float)Clock::TicksToMillisecondsD(Clock::TicksInBetween(cs.ping[i].pingReplyTick, cs.ping[i].pingSentTick));
 			cs.ping[i].replyReceived = true;
 			statistics.Unlock();
-			rtt = rttPredictBias * newRtt + (1.f - rttPredictBias) * rtt;
+			rtt = rttPredictBias * newRtt + (1.f * rttPredictBias) * rtt;
 
 			LOG(LogVerbose, "HandlePingReplyMessage: %d.", (int)pingID);
 			return;
 		}
 
 	statistics.Unlock();
-	LOG(LogError, "Received PingReply with ID %d in socket %s, but no matching PingRequest was ever sent!", (int)pingID, socket->ToString().CString());
+	LOG(LogError, "Received PingReply with ID %d in socket %s, but no matching PingRequest was ever sent!", (int)pingID, socket->ToString().c_str());
 }
 
-String MessageConnection::ToString() const
+std::string MessageConnection::ToString() const
 {
 	if (socket)
 		return socket->ToString();
@@ -1229,7 +1229,7 @@ void MessageConnection::DumpStatus() const
 		"\tOverlapped out: %d (event: %s)\n"
 		"\tTime until next send: %d\n"
 		"\toutboundQueue.Size(): %d\n",
-		ConnectionStateToString(GetConnectionState()).CString(),
+		ConnectionStateToString(GetConnectionState()).c_str(),
 		(int)NumInboundMessagesPending(),
 		(int)NumOutboundMessagesPending(),
 		Connected() ? "connected" : "",
@@ -1241,7 +1241,7 @@ void MessageConnection::DumpStatus() const
 		(socket && socket->IsWriteOpen()) ? "writeOpen" : "",
 		RoundTripTime(), LastHeardTime(), PacketsInPerSec(), PacketsOutPerSec(),
 		MsgsInPerSec(), MsgsOutPerSec(), 
-		FormatBytes(BytesInPerSec()).CString(), FormatBytes(BytesOutPerSec()).CString(),
+		FormatBytes(BytesInPerSec()).c_str(), FormatBytes(BytesOutPerSec()).c_str(),
 		(int)eventMsgsOutAvailable.Test(), 
 #ifdef WIN32
 		socket ? socket->NumOverlappedReceivesInProgress() : -1,
@@ -1288,7 +1288,7 @@ void MessageConnection::AssertInWorkerThreadContext() const
 	if (haveWorkerThread && currentThreadId != workerThreadId)
 	{
 		LOG(LogError, "Assert failure in MessageConnection::AssertInWorkerThreadContext()!: haveWorkerThread: %s, currentThreadId: %s, workerThreadId: %s,",
-			haveWorkerThread ? "true" : "false", ThreadIdToString(currentThreadId).CString(), ThreadIdToString(workerThreadId).CString());
+			haveWorkerThread ? "true" : "false", ThreadIdToString(currentThreadId).c_str(), ThreadIdToString(workerThreadId).c_str());
 		assert(false && "MessageConnection::AssertInWorkerThreadContext assert failure!");
 	}
 #endif
@@ -1303,7 +1303,7 @@ void MessageConnection::AssertInMainThreadContext() const
 	if (haveWorkerThread && currentThreadId == workerThreadId)
 	{
 		LOG(LogError, "Assert failure in MessageConnection::AssertInMainThreadContext()!: haveWorkerThread: %s, currentThreadId: %s, workerThreadId: %s,",
-			haveWorkerThread ? "true" : "false", ThreadIdToString(currentThreadId).CString(), ThreadIdToString(workerThreadId).CString());
+			haveWorkerThread ? "true" : "false", ThreadIdToString(currentThreadId).c_str(), ThreadIdToString(workerThreadId).c_str());
 		assert(false && "MessageConnection::AssertInMainThreadContext assert failure!");
 	}
 #endif

+ 11 - 13
ThirdParty/kNet/src/MessageListParser.cpp

@@ -15,8 +15,6 @@
 /** @file MessageListParser.cpp
 	@brief */
 
-// Modified by Lasse Öörni for Urho3D
-
 #ifdef KNET_USE_TINYXML
 #include <tinyxml.h>
 #endif
@@ -45,7 +43,7 @@ namespace kNet
 
 BasicSerializedDataType StringToSerialType(const char *type)
 {
-	if (type == "string" || type == "String")
+	if (type == "string" || type == "std::string")
 		return SerialString;
 	assert(NumSerialTypes-2 == NUMELEMS(data));
 	for(int i = 0; i < NUMELEMS(data); ++i)
@@ -66,7 +64,7 @@ const char *SerialTypeToReadableString(BasicSerializedDataType type)
 const char *SerialTypeToCTypeString(BasicSerializedDataType type)
 {
 	if (type == SerialString)
-		return "String";
+		return "std::string";
 	assert(NumSerialTypes-2 == NUMELEMS(data));
 	assert(type >= SerialInvalid);
 	assert(type < NumSerialTypes); 
@@ -84,7 +82,7 @@ size_t SerialTypeSize(BasicSerializedDataType type)
 SerializedElementDesc *SerializedMessageList::ParseNode(TiXmlElement *node, SerializedElementDesc *parentNode)
 {
 #ifdef KNET_USE_TINYXML
-	elements.Push(SerializedElementDesc());
+	elements.push_back(SerializedElementDesc());
 	SerializedElementDesc *elem = &elements.back();
 	elem->parent = parentNode;
 	elem->name = node->Attribute("name") ? node->Attribute("name") : "";
@@ -119,9 +117,9 @@ SerializedElementDesc *SerializedMessageList::ParseNode(TiXmlElement *node, Seri
 
 		elem->typeString = node->Value();
 		if (elem->typeString == "string")
-			elem->typeString = "String";
+			elem->typeString = "std::string";
 		elem->type = StringToSerialType(node->Value());
-		if (elem->type == SerialInvalid && !elem->typeString.Empty())
+		if (elem->type == SerialInvalid && !elem->typeString.empty())
 			elem->type = SerialOther;
 		if (elem->type == SerialStruct)
 			elem->typeString = "S_" + elem->name; ///\todo Add a ClassName parameter for better control over naming here?
@@ -134,7 +132,7 @@ SerializedElementDesc *SerializedMessageList::ParseNode(TiXmlElement *node, Seri
 		while(child)
 		{
 			SerializedElementDesc *childElem = ParseNode(child, elem);
-			elem->elements.Push(childElem);
+			elem->elements.push_back(childElem);
 
 			child = child->NextSiblingElement();
 		}
@@ -182,7 +180,7 @@ void SerializedMessageList::ParseMessages(TiXmlElement *root)
 
 		// Work a slight convenience - if there is a single struct inside a single struct inside a single struct - jump straight through to the data.
 
-		messages.Push(desc);
+		messages.push_back(desc);
 
 		node = node->NextSiblingElement("message");
 	}
@@ -229,8 +227,8 @@ void SerializedMessageList::LoadMessagesFromFile(const char *filename)
 
 const SerializedMessageDesc *SerializedMessageList::FindMessageByID(u32 id)
 {
-	for(List<SerializedMessageDesc>::Iterator iter = messages.Begin();
-		iter != messages.End(); ++iter)
+	for(std::list<SerializedMessageDesc>::iterator iter = messages.begin();
+		iter != messages.end(); ++iter)
 		if (iter->id == id)
 			return &*iter;
 
@@ -239,8 +237,8 @@ const SerializedMessageDesc *SerializedMessageList::FindMessageByID(u32 id)
 
 const SerializedMessageDesc *SerializedMessageList::FindMessageByName(const char *name)
 {
-	for(List<SerializedMessageDesc>::Iterator iter = messages.Begin();
-		iter != messages.End(); ++iter)
+	for(std::list<SerializedMessageDesc>::iterator iter = messages.begin();
+		iter != messages.end(); ++iter)
 		if (iter->name == name)
 			return &*iter;
 

+ 80 - 81
ThirdParty/kNet/src/Network.cpp

@@ -15,9 +15,8 @@
 /** @file Network.cpp
 	@brief */
 
-// Modified by Lasse Öörni for Urho3D
-
-#include "Str.h"
+#include <string>
+#include <sstream>
 
 #include <cassert>
 
@@ -44,10 +43,10 @@
 namespace kNet
 {
 
-const int cMaxTCPSendSize = 256 * 1024;
+const int cMaxTCPSendSize = 25 * 1024 * 1024; // For TCP sockets, there is no specific limit to send(), specify something.
 const int cMaxUDPSendSize = 1400;
 
-String Network::GetErrorString(int error)
+std::string Network::GetErrorString(int error)
 {
 #ifdef WIN32
 	void *lpMsgBuf = 0;
@@ -57,14 +56,14 @@ String Network::GetErrorString(int error)
 		0, hresult, 0 /*Default language*/, (LPTSTR) &lpMsgBuf, 0, 0);
 
 	// Copy message to C++ -style string, since the data need to be freed before return.
-	String str;
-	str += String((LPCSTR)lpMsgBuf) + "(" + String(error) + ")"; ///\todo Bug: The cast to LPCSTR converts wstr -> str if UNICODE is defined, which will cut out text.
+	std::stringstream ss;
+	ss << (LPCSTR)lpMsgBuf << "(" << error << ")"; ///\todo Bug: The cast to LPCSTR converts wstr -> str if UNICODE is defined, which will cut out text.
 	LocalFree(lpMsgBuf);
-	return str;
+	return ss.str();
 #else
-	String str;
-	str += String(strerror(error)) + "(" + error + ")";
-	return str;
+	std::stringstream ss;
+	ss << strerror(error) << "(" << error << ")";
+	return ss.str();
 #endif
 }
 
@@ -77,17 +76,17 @@ int Network::GetLastError()
 #endif
 }
 
-String Network::GetLastErrorString()
+std::string Network::GetLastErrorString()
 {
 	return GetErrorString(GetLastError());
 }
 
-String FormatBytes(u64 numBytes)
+std::string FormatBytes(u64 numBytes)
 {
 	return FormatBytes((double)numBytes);
 }
 
-String FormatBytes(double numBytes)
+std::string FormatBytes(double numBytes)
 {
 	char str[256];
 	if (numBytes >= 1000.0 * 1000.0 * 1000.0)
@@ -98,7 +97,7 @@ String FormatBytes(double numBytes)
 		sprintf(str, "%.3f KB", (float)(numBytes / 1024.0));
 	else
 		sprintf(str, "%.2f B", (float)numBytes);
-	return String(str);
+	return std::string(str);
 }
 
 Network::Network()
@@ -270,7 +269,7 @@ void Network::Init()
 	int result = WSAStartup(MAKEWORD(2,2), &wsaData);
 	if (result != 0)
 	{
-		LOG(LogError, "Network::Init: WSAStartup failed: %s!", GetErrorString(result).CString());
+		LOG(LogError, "Network::Init: WSAStartup failed: %s!", GetErrorString(result).c_str());
 		return;
 	}
 #endif
@@ -286,7 +285,7 @@ void Network::Init()
 	}
 	else
 	{
-		LOG(LogError, "Network::Init: gethostname failed! Error: %s. Using 'localhost' as the local host name", GetLastErrorString().CString());
+		LOG(LogError, "Network::Init: gethostname failed! Error: %s. Using 'localhost' as the local host name", GetLastErrorString().c_str());
 		localHostName = "localhost";
 	}
 }
@@ -296,15 +295,15 @@ NetworkWorkerThread *Network::GetOrCreateWorkerThread()
 	static const int maxConnectionsPerThread = 8;
 
 	// Find an existing thread with sufficiently low load.
-	for(size_t i = 0; i < workerThreads.Size(); ++i)
+	for(size_t i = 0; i < workerThreads.size(); ++i)
 		if (workerThreads[i]->NumConnections() + workerThreads[i]->NumServers() < maxConnectionsPerThread)
 			return workerThreads[i];
 
 	// No appropriate thread found. Create a new one.
 	NetworkWorkerThread *workerThread = new NetworkWorkerThread();
 	workerThread->StartThread();
-	workerThreads.Push(workerThread);
-	LOG(LogInfo, "Created a new NetworkWorkerThread. There are now %d worker threads.", (int)workerThreads.Size());
+	workerThreads.push_back(workerThread);
+	LOG(LogInfo, "Created a new NetworkWorkerThread. There are now %d worker threads.", (int)workerThreads.size());
 	return workerThread;
 }
 
@@ -363,15 +362,15 @@ void Network::CloseWorkerThread(NetworkWorkerThread *workerThread)
 	if (workerThread->NumConnections() + workerThread->NumServers() > 0)
 		LOG(LogError, "Warning: Closing a worker thread %p when it still has %d connections and %d servers to handle.", workerThread, workerThread->NumConnections(), workerThread->NumServers());
 
-	for(size_t i = 0; i < workerThreads.Size(); ++i)
+	for(size_t i = 0; i < workerThreads.size(); ++i)
 		if (workerThreads[i] == workerThread)
 		{
 			// Remove the thread pointer from internal list.
-			Swap(workerThreads[i], workerThreads[workerThreads.Size()-1]);
-			workerThreads.Pop();
+			std::swap(workerThreads[i], workerThreads[workerThreads.size()-1]);
+			workerThreads.pop_back();
 
 			workerThread->StopThread();
-			LOG(LogInfo, "Deleted a NetworkWorkerThread. There are now %d worker threads left.", (int)workerThreads.Size());
+			LOG(LogInfo, "Deleted a NetworkWorkerThread. There are now %d worker threads left.", (int)workerThreads.size());
 			delete workerThread;
 			return;
 		}
@@ -389,38 +388,38 @@ NetworkServer *Network::StartServer(unsigned short port, SocketTransportLayer tr
 		return 0;
 	}
 
-	Vector<Socket *> listenSockets;
-	listenSockets.Push(listenSock);
+	std::vector<Socket *> listenSockets;
+	listenSockets.push_back(listenSock);
 
 	server = new NetworkServer(this, listenSockets);
 	server->RegisterServerListener(serverListener);
 
 	AssignServerToWorkerThread(server);
 
-	LOG(LogInfo, "Server up (%s). Waiting for client to connect.", listenSock->ToString().CString());
+	LOG(LogInfo, "Server up (%s). Waiting for client to connect.", listenSock->ToString().c_str());
 
 	return server;
 }
 
-NetworkServer *Network::StartServer(const Vector<Pair<unsigned short, SocketTransportLayer> > &listenPorts, 
+NetworkServer *Network::StartServer(const std::vector<std::pair<unsigned short, SocketTransportLayer> > &listenPorts, 
 	INetworkServerListener *serverListener, bool allowAddressReuse)
 {
-	if (listenPorts.Size() == 0)
+	if (listenPorts.size() == 0)
 	{
 		LOG(LogError, "Failed to start server, since you did not provide a list of ports to listen to in Network::StartServer()!");
 		return 0;
 	}
 
-	Vector<Socket *> listenSockets;
+	std::vector<Socket *> listenSockets;
 
-	for(size_t i = 0; i < listenPorts.Size(); ++i)
+	for(size_t i = 0; i < listenPorts.size(); ++i)
 	{
-		Socket *listenSock = OpenListenSocket(listenPorts[i].first_, listenPorts[i].second_, allowAddressReuse);
+		Socket *listenSock = OpenListenSocket(listenPorts[i].first, listenPorts[i].second, allowAddressReuse);
 		if (listenSock)
-			listenSockets.Push(listenSock);
+			listenSockets.push_back(listenSock);
 	}
 
-	if (listenSockets.Size() == 0)
+	if (listenSockets.size() == 0)
 	{
 		LOG(LogError, "Failed to start server. No ports to listen to!");
 		return 0;
@@ -433,20 +432,20 @@ NetworkServer *Network::StartServer(const Vector<Pair<unsigned short, SocketTran
 
 	LOG(LogInfo, "Server up and listening on the following ports: ");
 	{
-		String str;
-		str += "UDP ";
-		for(size_t i = 0; i < listenSockets.Size(); ++i)
+		std::stringstream ss;
+		ss << "UDP ";
+		for(size_t i = 0; i < listenSockets.size(); ++i)
 			if (listenSockets[i]->TransportLayer() == SocketOverUDP)
-				str += String(listenSockets[i]->LocalPort()) + " ";
-		LOG(LogInfo, str.CString());
+				ss << listenSockets[i]->LocalPort() << " ";
+		LOG(LogInfo, ss.str().c_str());
 	}
 	{
-		String str;
-		str += "TCP ";
-		for(size_t i = 0; i < listenSockets.Size(); ++i)
+		std::stringstream ss;
+		ss << "TCP ";
+		for(size_t i = 0; i < listenSockets.size(); ++i)
 			if (listenSockets[i]->TransportLayer() == SocketOverTCP)
-				str += String(listenSockets[i]->LocalPort()) + " ";
-		LOG(LogInfo, str.CString());
+				ss << listenSockets[i]->LocalPort() << " ";
+		LOG(LogInfo, ss.str().c_str());
 	}
 
 	return server;
@@ -472,13 +471,13 @@ void Network::DeleteSocket(Socket *socket)
 		return;
 	}
 
-	for(List<Socket>::Iterator iter = sockets.Begin(); iter != sockets.End(); ++iter)
+	for(std::list<Socket>::iterator iter = sockets.begin(); iter != sockets.end(); ++iter)
 		if (&*iter == socket)
 		{
 			socket->Close();
 			// The Socket pointers MessageConnection objects have are pointers to this list,
 			// so after calling this function with a Socket pointer, the Socket is deleted for good.
-			sockets.Erase(iter);
+			sockets.erase(iter);
 			LOG(LogInfo, "Network::DeleteSocket: Closed socket %p.", socket);
 			return;
 		}
@@ -496,7 +495,7 @@ void Network::CloseConnection(MessageConnection *connection)
 	connection->socket = 0;
 	connection->owner = 0;
 	connection->ownerServer = 0;
-	connections.Erase(connection);
+	connections.erase(connection);
 }
 
 void Network::DeInit()
@@ -505,9 +504,9 @@ void Network::DeInit()
 	PolledTimer timer;
 
 	// Kill all connections.
-	while(connections.Size() > 0)
+	while(connections.size() > 0)
 	{
-		MessageConnection *connection = *connections.Begin();
+		MessageConnection *connection = *connections.begin();
 		CloseConnection(connection); // CloseConnection erases connection from the connections list, so this loop terminates.
 	}
 
@@ -515,14 +514,14 @@ void Network::DeInit()
 	StopServer();
 
 	// Kill all worker threads.
-	while(workerThreads.Size() > 0)
-		CloseWorkerThread(workerThreads.Front()); // Erases the item from workerThreads, so this loop terminates.
+	while(workerThreads.size() > 0)
+		CloseWorkerThread(workerThreads.front()); // Erases the item from workerThreads, so this loop terminates.
 
 	// Clean up any sockets that might be remaining.
-	while(sockets.Size() > 0)
+	while(sockets.size() > 0)
 	{
-		sockets.Front().Close();
-		sockets.PopFront();
+		sockets.front().Close();
+		sockets.pop_front();
 	}
 
 	// Deinitialize network subsystem.
@@ -535,7 +534,7 @@ void Network::DeInit()
 
 void Network::NewMessageConnectionCreated(MessageConnection *connection)
 {
-	connections.Insert(connection);
+	connections.insert(connection);
 }
 
 Socket *Network::OpenListenSocket(unsigned short port, SocketTransportLayer transport, bool allowAddressReuse)
@@ -555,7 +554,7 @@ Socket *Network::OpenListenSocket(unsigned short port, SocketTransportLayer tran
 	int ret = getaddrinfo(NULL, strPort, &hints, &result);
 	if (ret != 0)
 	{
-		LOG(LogError, "getaddrinfo failed: %s", GetErrorString(ret).CString());
+		LOG(LogError, "getaddrinfo failed: %s", GetErrorString(ret).c_str());
 		return 0;
 	}
 
@@ -564,7 +563,7 @@ Socket *Network::OpenListenSocket(unsigned short port, SocketTransportLayer tran
 
 	if (listenSocket == INVALID_SOCKET)
 	{
-		LOG(LogError, "Error at socket(): %s", GetLastErrorString().CString());
+		LOG(LogError, "Error at socket(): %s", GetLastErrorString().c_str());
 		freeaddrinfo(result);
 		return 0;
 	}
@@ -582,7 +581,7 @@ Socket *Network::OpenListenSocket(unsigned short port, SocketTransportLayer tran
 		ret = setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
 #endif
 		if (ret != 0)
-			LOG(LogError, "setsockopt to SO_REUSEADDR failed: %s", GetLastErrorString().CString());
+			LOG(LogError, "setsockopt to SO_REUSEADDR failed: %s", GetLastErrorString().c_str());
 	}
 
 	// It is safe to cast to a sockaddr_in, since we've specifically queried for AF_INET addresses.
@@ -595,7 +594,7 @@ Socket *Network::OpenListenSocket(unsigned short port, SocketTransportLayer tran
 	if (ret == KNET_SOCKET_ERROR)
 	{
 		LOG(LogError, "bind failed: %s when trying to bind to port %d with transport %s", 
-			GetLastErrorString().CString(), (int)port, transport == SocketOverTCP ? "TCP" : "UDP");
+			GetLastErrorString().c_str(), (int)port, transport == SocketOverTCP ? "TCP" : "UDP");
 		closesocket(listenSocket);
 		freeaddrinfo(result);
 		return 0;
@@ -610,7 +609,7 @@ Socket *Network::OpenListenSocket(unsigned short port, SocketTransportLayer tran
 		ret = listen(listenSocket, SOMAXCONN);
 		if (ret == KNET_SOCKET_ERROR)
 		{
-			LOG(LogError, "Error at listen(): %s", GetLastErrorString().CString());
+			LOG(LogError, "Error at listen(): %s", GetLastErrorString().c_str());
 			closesocket(listenSocket);
 			return 0;
 		}
@@ -623,8 +622,8 @@ Socket *Network::OpenListenSocket(unsigned short port, SocketTransportLayer tran
 	remoteEndPoint.Reset();
 
 	const size_t maxSendSize = (transport == SocketOverTCP ? cMaxTCPSendSize : cMaxUDPSendSize);
-	sockets.Push(Socket(listenSocket, localEndPoint, localHostName.CString(), remoteEndPoint, "", transport, ServerListenSocket, maxSendSize));
-	Socket *listenSock = &sockets.Back();
+	sockets.push_back(Socket(listenSocket, localEndPoint, localHostName.c_str(), remoteEndPoint, "", transport, ServerListenSocket, maxSendSize));
+	Socket *listenSock = &sockets.back();
 	listenSock->SetBlocking(false);
 
 	return listenSock;
@@ -645,7 +644,7 @@ Socket *Network::ConnectSocket(const char *address, unsigned short port, SocketT
 	int ret = getaddrinfo(address, strPort, &hints, &result);
 	if (ret != 0)
 	{
-		LOG(LogError, "Network::Connect: getaddrinfo failed: %s", GetErrorString(ret).CString());
+		LOG(LogError, "Network::Connect: getaddrinfo failed: %s", GetErrorString(ret).c_str());
 		return 0;
 	}
 
@@ -658,7 +657,7 @@ Socket *Network::ConnectSocket(const char *address, unsigned short port, SocketT
 #endif
 	if (connectSocket == INVALID_SOCKET)
 	{
-		LOG(LogError, "Network::Connect: Error at socket(): %s", GetLastErrorString().CString());
+		LOG(LogError, "Network::Connect: Error at socket(): %s", GetLastErrorString().c_str());
 		freeaddrinfo(result);
 		return 0;
 	}
@@ -691,7 +690,7 @@ Socket *Network::ConnectSocket(const char *address, unsigned short port, SocketT
 	if (ret == 0)
 		 localEndPoint = EndPoint::FromSockAddrIn(sockname);
 	else
-		LOG(LogError, "Network::ConnectSocket: getsockname failed: %s!", Network::GetLastErrorString().CString());
+		LOG(LogError, "Network::ConnectSocket: getsockname failed: %s!", Network::GetLastErrorString().c_str());
 
 	EndPoint remoteEndPoint;
 	sockaddr_in peername;
@@ -700,17 +699,17 @@ Socket *Network::ConnectSocket(const char *address, unsigned short port, SocketT
 	if (ret == 0)
 		remoteEndPoint = EndPoint::FromSockAddrIn(peername);
 	else
-		LOG(LogError, "Network::ConnectSocket: getpeername failed: %s!", Network::GetLastErrorString().CString());
+		LOG(LogError, "Network::ConnectSocket: getpeername failed: %s!", Network::GetLastErrorString().c_str());
 
-	String remoteHostName = remoteEndPoint.IPToString();
+	std::string remoteHostName = remoteEndPoint.IPToString();
 
 	const size_t maxSendSize = (transport == SocketOverTCP) ? cMaxTCPSendSize : cMaxUDPSendSize;
-	Socket socket(connectSocket, localEndPoint, localHostName.CString(), remoteEndPoint, remoteHostName.CString(), transport, ClientSocket, maxSendSize);
+	Socket socket(connectSocket, localEndPoint, localHostName.c_str(), remoteEndPoint, remoteHostName.c_str(), transport, ClientSocket, maxSendSize);
 
 	socket.SetBlocking(false);
-	sockets.Push(socket);
+	sockets.push_back(socket);
 
-	Socket *sock = &sockets.Back();
+	Socket *sock = &sockets.back();
 
 	return sock;
 }
@@ -725,10 +724,10 @@ Ptr(MessageConnection) Network::Connect(const char *address, unsigned short port
 	if (transport == SocketOverUDP)
 	{
 		SendUDPConnectDatagram(*socket, connectMessage);
-		LOG(LogInfo, "Network::Connect: Sent a UDP Connection Start datagram to to %s.", socket->ToString().CString());
+		LOG(LogInfo, "Network::Connect: Sent a UDP Connection Start datagram to to %s.", socket->ToString().c_str());
 	}
 	else
-		LOG(LogInfo, "Network::Connect: Connected a TCP socket to %s.", socket->ToString().CString());
+		LOG(LogInfo, "Network::Connect: Connected a TCP socket to %s.", socket->ToString().c_str());
 
 	Ptr(MessageConnection) connection;
 	if (transport == SocketOverTCP)
@@ -739,7 +738,7 @@ Ptr(MessageConnection) Network::Connect(const char *address, unsigned short port
 	connection->RegisterInboundMessageHandler(messageHandler);
 	AssignConnectionToWorkerThread(connection);
 
-	connections.Insert(connection);
+	connections.insert(connection);
 	return connection;
 }
 
@@ -758,40 +757,40 @@ Socket *Network::CreateUDPSlaveSocket(Socket *serverListenSocket, const EndPoint
 		return 0;
 	}
 
-	sockets.Push(Socket(udpSocket, serverListenSocket->LocalEndPoint(),
+	sockets.push_back(Socket(udpSocket, serverListenSocket->LocalEndPoint(),
 		serverListenSocket->LocalAddress(), remoteEndPoint, remoteHostName, SocketOverUDP, ServerClientSocket, cMaxUDPSendSize));
-	Socket *socket = &sockets.Back();
+	Socket *socket = &sockets.back();
 	socket->SetBlocking(false);
 
-	LOG(LogInfo, "Network::CreateUDPSlaveSocket: Connected an UDP socket to %s.", socket->ToString().CString());
+	LOG(LogInfo, "Network::CreateUDPSlaveSocket: Connected an UDP socket to %s.", socket->ToString().c_str());
 	return socket;
 }
 
 Socket *Network::StoreSocket(const Socket &cp)
 {
-	sockets.Push(cp);
-	return &sockets.Back();
+	sockets.push_back(cp);
+	return &sockets.back();
 }
 
 void Network::SendUDPConnectDatagram(Socket &socket, Datagram *connectMessage)
 {
-	OverlappedTransferBuffer *sendData = socket.BeginSend();
+    const int connectMessageSize = connectMessage ? connectMessage->size : 8;
+	OverlappedTransferBuffer *sendData = socket.BeginSend(connectMessageSize);
 	if (!sendData)
 	{
 		LOG(LogError, "Network::SendUDPConnectDatagram: socket.BeginSend failed! Cannot send UDP connection datagram!");
 		return;
 	}
+	sendData->bytesContains = connectMessageSize;
 	if (connectMessage)
 	{
 		///\todo Craft the proper connection attempt datagram.
-		sendData->buffer.len = std::min<int>(connectMessage->size, sendData->buffer.len);
 		memcpy(sendData->buffer.buf, connectMessage->data, sendData->buffer.len);
 		LOG(LogVerbose, "Network::SendUDPConnectDatagram: Sending UDP connect message of size %d.", (int)sendData->buffer.len);
 	}
 	else
 	{
 		///\todo Craft the proper connection attempt datagram.
-		sendData->buffer.len = std::min<int>(8, sendData->buffer.len);
 		memset(sendData->buffer.buf, 0, sendData->buffer.len);
 		LOG(LogVerbose, "Network::SendUDPConnectDatagram: Sending null UDP connect message of size %d.", (int)sendData->buffer.len);
 	}

+ 12 - 14
ThirdParty/kNet/src/NetworkLogging.cpp

@@ -15,16 +15,14 @@
 /** @file NetworkLogging.cpp
 	@brief Implements logging functionalities to stdout/file for different log channels. */
 
-// Modified by Lasse Öörni for Urho3D
-
+#include <sstream>
 #include <iostream>
 #include <fstream>
 #include <cstdarg>
 #include <cstdio>
+#include <string>
 #include <cstring>
 
-#include "Str.h"
-
 #ifdef KNET_USE_BOOST
 #include <boost/thread/thread.hpp>
 #endif
@@ -53,7 +51,7 @@ ofstream kNetLogFile;
 
 Lockable<int> logWriteMutex;
 
-String Time()
+string Time()
 {
 	static tick_t firstTick;
 	static bool firstCall = true;
@@ -64,12 +62,12 @@ String Time()
 		return "0.000";
 	}
 	double t = Clock::SecondsSinceD(firstTick);
-	String str;
-	str += String(t);
+	std::stringstream ss;
+	ss << t;
 #ifdef KNET_USE_BOOST
-	str += ", " + String(boost::this_thread::get_id());
+	ss << ", " << boost::this_thread::get_id();
 #endif
-	return str;
+	return ss.str();
 }
 
 } // ~unnamed namespace
@@ -87,9 +85,9 @@ void TimeOutputDebugStringVariadic(LogChannel logChannel, const char * /*filenam
 	vsnprintf(errorStr, 1023, msg, args);
 
 	if (kNetLogFile.is_open())
-		kNetLogFile << Time().CString() << ": " << errorStr << std::endl;
+		kNetLogFile << Time() << ": " << errorStr << std::endl;
 	else
-		std::cout << Time().CString() << ": " << errorStr << std::endl;
+		std::cout << Time() << ": " << errorStr << std::endl;
 
 	va_end(args);
 }
@@ -105,9 +103,9 @@ void TimeOutputDebugString(LogChannel logChannel, const char * /*filename*/, int
 	_snprintf(errorStr, 1023, "%s", msg);
 
 	if (kNetLogFile.is_open())
-		kNetLogFile << Time().CString() << ": " << errorStr << std::endl;
+		kNetLogFile << Time() << ": " << errorStr << std::endl;
 	else
-		std::cout << Time().CString() << ": " << errorStr << std::endl;
+		std::cout << Time() << ": " << errorStr << std::endl;
 }
 
 void SetLogChannels(LogChannel logChannels)
@@ -136,7 +134,7 @@ void SetLogFile(const char *filename)
 
 void EnableMemoryLeakLoggingAtExit()
 {
-#ifdef WIN32
+#ifdef _MSC_VER
 	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
 
 	_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);

+ 1 - 3
ThirdParty/kNet/src/NetworkMessage.cpp

@@ -15,9 +15,7 @@
 /** @file NetworkMessage.cpp
 	@brief Represents a serializable network message. */
 
-// Modified by Lasse Öörni for Urho3D
-
-#include "Str.h"
+#include <string.h>
 
 #include "kNet/DebugMemoryLeakCheck.h"
 #include "kNet/NetworkMessage.h"

+ 68 - 70
ThirdParty/kNet/src/NetworkServer.cpp

@@ -15,8 +15,6 @@
 /** @file NetworkServer.cpp
 	@brief */
 
-// Modified by Lasse Öörni for Urho3D
-
 #ifdef KNET_USE_BOOST
 #include <boost/thread/thread.hpp>
 #endif
@@ -38,7 +36,7 @@
 namespace kNet
 {
 
-NetworkServer::NetworkServer(Network *owner_, Vector<Socket *> listenSockets_)
+NetworkServer::NetworkServer(Network *owner_, std::vector<Socket *> listenSockets_)
 :owner(owner_), listenSockets(listenSockets_), acceptNewConnections(true), networkServerListener(0),
 udpConnectionAttempts(64), workerThread(0)
 #ifdef KNET_THREAD_CHECKING_ENABLED
@@ -46,7 +44,7 @@ udpConnectionAttempts(64), workerThread(0)
 #endif
 {
 	assert(owner);
-	assert(listenSockets.Size() > 0);
+	assert(listenSockets.size() > 0);
 }
 
 NetworkServer::~NetworkServer()
@@ -76,7 +74,7 @@ void NetworkServer::CloseListenSockets()
 {
 	assert(owner);
 
-	for(size_t i = 0; i < listenSockets.Size(); ++i)
+	for(size_t i = 0; i < listenSockets.size(); ++i)
 	{
 		if (listenSockets[i]->TransportLayer() == SocketOverUDP)
 			acceptNewConnections = false; ///\todo At this point, if in UDP mode, we should have destroyed all connections that use this socket!
@@ -85,7 +83,7 @@ void NetworkServer::CloseListenSockets()
 	}
 
 	// Now forget all sockets - not getting them back in any way.
-	listenSockets.Clear();
+	listenSockets.clear();
 }
 
 Socket *NetworkServer::AcceptConnections(Socket *listenSocket)
@@ -103,7 +101,7 @@ Socket *NetworkServer::AcceptConnections(Socket *listenSocket)
 		int error = Network::GetLastError();
 		if (error != KNET_EWOULDBLOCK)
 		{
-			LOG(LogError, "NetworkServer::AcceptConnections: accept failed: %s", Network::GetErrorString(error).CString());
+			LOG(LogError, "NetworkServer::AcceptConnections: accept failed: %s", Network::GetErrorString(error).c_str());
 			closesocket(listenSock);
 			listenSock = INVALID_SOCKET;
 		}
@@ -111,19 +109,19 @@ Socket *NetworkServer::AcceptConnections(Socket *listenSocket)
 	}
 
 	EndPoint remoteEndPoint = EndPoint::FromSockAddrIn(remoteAddress);
-	String remoteHostName = remoteEndPoint.IPToString();
+	std::string remoteHostName = remoteEndPoint.IPToString();
 
-	LOG(LogInfo, "Accepted incoming TCP connection from %s:%d.", remoteHostName.CString(), (int)remoteEndPoint.port);
+	LOG(LogInfo, "Accepted incoming TCP connection from %s:%d.", remoteHostName.c_str(), (int)remoteEndPoint.port);
 
 	EndPoint localEndPoint;
 	sockaddr_in localSockAddr;
 	socklen_t namelen = sizeof(localSockAddr);
 	int sockRet = getsockname(acceptSocket, (sockaddr*)&localSockAddr, &namelen); // Note: This works only if family==INETv4
 	localEndPoint = EndPoint::FromSockAddrIn(localSockAddr);
-	String localHostName = owner->LocalAddress();
+	std::string localHostName = owner->LocalAddress();
 
 	const size_t maxTcpSendSize = 65536;
-	Socket *socket = owner->StoreSocket(Socket(acceptSocket, localEndPoint, localHostName.CString(), remoteEndPoint, remoteHostName.CString(), SocketOverTCP, ServerClientSocket, maxTcpSendSize));
+	Socket *socket = owner->StoreSocket(Socket(acceptSocket, localEndPoint, localHostName.c_str(), remoteEndPoint, remoteHostName.c_str(), SocketOverTCP, ServerClientSocket, maxTcpSendSize));
 	socket->SetBlocking(false);
 
 	return socket;
@@ -135,22 +133,22 @@ void NetworkServer::CleanupDeadConnections()
 	ConnectionMap clientsMap = *clients.Acquire();
 
 	// Clean up all disconnected/timed out connections.
-	ConnectionMap::Iterator iter = clientsMap.Begin();
-	while(iter != clientsMap.End())
+	ConnectionMap::iterator iter = clientsMap.begin();
+	while(iter != clientsMap.end())
 	{
-		ConnectionMap::Iterator next = iter;
+		ConnectionMap::iterator next = iter;
 		++next;
-		if (!iter->second_->Connected())
+		if (!iter->second->Connected())
 		{
-			LOG(LogInfo, "Client %s disconnected.", iter->second_->ToString().CString());
+			LOG(LogInfo, "Client %s disconnected.", iter->second->ToString().c_str());
 			if (networkServerListener)
-				networkServerListener->ClientDisconnected(iter->second_);
-			if (iter->second_->GetSocket() && iter->second_->GetSocket()->TransportLayer() == SocketOverTCP)
-				owner->CloseConnection(iter->second_);
+				networkServerListener->ClientDisconnected(iter->second);
+			if (iter->second->GetSocket() && iter->second->GetSocket()->TransportLayer() == SocketOverTCP)
+				owner->CloseConnection(iter->second);
 
 			{
 				Lockable<ConnectionMap>::LockType clientsLock = clients.Acquire();
-				clientsLock->Erase(iter->first_);
+				clientsLock->erase(iter->first);
 			}
 		}
 		iter = next;
@@ -161,7 +159,7 @@ void NetworkServer::Process()
 {
 	CleanupDeadConnections();
 
-	for(size_t i = 0; i < listenSockets.Size(); ++i)
+	for(size_t i = 0; i < listenSockets.size(); ++i)
 	{
 		Socket *listen = listenSockets[i];
 
@@ -174,7 +172,7 @@ void NetworkServer::Process()
 				if (!client->Connected())
 					LOG(LogError, "Warning: Accepted an already closed connection!");
 
-				LOG(LogInfo, "Client connected from %s.", client->ToString().CString());
+				LOG(LogInfo, "Client connected from %s.", client->ToString().c_str());
 
 				// Build a MessageConnection on top of the raw socket.
 				assert(listen->TransportLayer() == SocketOverTCP);
@@ -211,8 +209,8 @@ void NetworkServer::Process()
 
 	// Process all new inbound data for each connection handled by this server.
 	ConnectionMap clientMap = *clients.Acquire();
-	for(ConnectionMap::Iterator iter = clientMap.Begin(); iter != clientMap.End(); ++iter)
-		iter->second_->Process();
+	for(ConnectionMap::iterator iter = clientMap.begin(); iter != clientMap.end(); ++iter)
+		iter->second->Process();
 }
 
 void NetworkServer::ReadUDPSocketData(Socket *listenSocket) // [worker thread]
@@ -231,8 +229,8 @@ void NetworkServer::ReadUDPSocketData(Socket *listenSocket) // [worker thread]
 		return;
 	}
 	EndPoint endPoint = EndPoint::FromSockAddrIn(recvData->from); // This conversion is quite silly, perhaps it could be removed to gain performance?
-	LOG(LogData, "Received a datagram of size %d to socket %s from endPoint %s.", recvData->bytesContains, listenSocket->ToString().CString(),
-		endPoint.ToString().CString());
+	LOG(LogData, "Received a datagram of size %d to socket %s from endPoint %s.", recvData->bytesContains, listenSocket->ToString().c_str(),
+		endPoint.ToString().c_str());
 
 	PolledTimer timer;
 	MessageConnection *receiverConnection = 0;
@@ -245,9 +243,9 @@ void NetworkServer::ReadUDPSocketData(Socket *listenSocket) // [worker thread]
 			timer.MSecsElapsed());
 		}
 
-		ConnectionMap::Iterator iter = clientsLock->Find(endPoint); ///\todo HashTable for performance.
-		if (iter != clientsLock->End())
-			receiverConnection = iter->second_;
+		ConnectionMap::iterator iter = clientsLock->find(endPoint); ///\todo HashTable for performance.
+		if (iter != clientsLock->end())
+			receiverConnection = iter->second;
 	}
 
 	if (receiverConnection)
@@ -282,12 +280,12 @@ void NetworkServer::EnqueueNewUDPConnectionAttempt(Socket *listenSocket, const E
 	if (!success)
 		LOG(LogError, "Too many connection attempts!");
 	else
-		LOG(LogInfo, "Queued new connection attempt from %s.", endPoint.ToString().CString());
+		LOG(LogInfo, "Queued new connection attempt from %s.", endPoint.ToString().c_str());
 }
 
 bool NetworkServer::ProcessNewUDPConnectionAttempt(Socket *listenSocket, const EndPoint &endPoint, const char *data, size_t numBytes)
 {
-	LOG(LogInfo, "New inbound connection attempt from %s with datagram of size %d.", endPoint.ToString().CString(), (int)numBytes);
+	LOG(LogInfo, "New inbound connection attempt from %s with datagram of size %d.", endPoint.ToString().c_str(), (int)numBytes);
 	if (!acceptNewConnections)
 	{
 		LOG(LogError, "Ignored a new connection attempt since server is set not to accept new connections.");
@@ -308,10 +306,10 @@ bool NetworkServer::ProcessNewUDPConnectionAttempt(Socket *listenSocket, const E
 	///\todo Check IP banlist.
 	///\todo Check that the maximum number of active concurrent connections is not exceeded.
 
-	String remoteHostName = endPoint.IPToString();
+	std::string remoteHostName = endPoint.IPToString();
 
 	// Accept the connection and create a new UDP socket that communicates to that endpoint.
-	Socket *socket = owner->CreateUDPSlaveSocket(listenSocket, endPoint, remoteHostName.CString());
+	Socket *socket = owner->CreateUDPSlaveSocket(listenSocket, endPoint, remoteHostName.c_str());
 	if (!socket)
 	{
 		LOG(LogError, "Network::ConnectUDP failed! Cannot accept new UDP connection.");
@@ -353,9 +351,9 @@ void NetworkServer::BroadcastMessage(const NetworkMessage &msg, MessageConnectio
 			timer.MSecsElapsed());
 	}
 
-	for(ConnectionMap::Iterator iter = clientsLock->Begin(); iter != clientsLock->End(); ++iter)
+	for(ConnectionMap::iterator iter = clientsLock->begin(); iter != clientsLock->end(); ++iter)
 	{
-		MessageConnection *connection = iter->second_;
+		MessageConnection *connection = iter->second;
 		if (connection == exclude)
 			continue;
 
@@ -375,9 +373,9 @@ void NetworkServer::BroadcastMessage(unsigned long id, bool reliable, bool inOrd
 			timer.MSecsElapsed());
 	}
 
-	for(ConnectionMap::Iterator iter = clientsLock->Begin(); iter != clientsLock->End(); ++iter)
+	for(ConnectionMap::iterator iter = clientsLock->begin(); iter != clientsLock->end(); ++iter)
 	{
-		MessageConnection *connection = iter->second_;
+		MessageConnection *connection = iter->second;
 		assert(connection);
 		if (connection == exclude || !connection->IsWriteOpen())
 			continue;
@@ -413,8 +411,8 @@ void NetworkServer::DisconnectAllClients()
 	LOG(LogWaits, "NetworkServer::DisconnectAllClients: Accessing the connection list took %f msecs.",
 		timer.MSecsElapsed());
 
-	for(ConnectionMap::Iterator iter = clientsLock->Begin(); iter != clientsLock->End(); ++iter)
-		iter->second_->Disconnect(0); // Do not wait for any client.
+	for(ConnectionMap::iterator iter = clientsLock->begin(); iter != clientsLock->end(); ++iter)
+		iter->second->Disconnect(0); // Do not wait for any client.
 }
 
 void NetworkServer::Close(int disconnectWaitMilliseconds)
@@ -423,7 +421,7 @@ void NetworkServer::Close(int disconnectWaitMilliseconds)
 
 	///\todo Re-implement this function to remove the monolithic Sleep here. Instead of this,
 	/// wait for the individual connections to finish.
-	if (GetConnections().Size() > 0)
+	if (GetConnections().size() > 0)
 	{
 		Clock::Sleep(disconnectWaitMilliseconds);
 		LOG(LogVerbose, "NetworkServer::Close: Waited a fixed period of %d msecs for all connections to disconnect.",
@@ -434,8 +432,8 @@ void NetworkServer::Close(int disconnectWaitMilliseconds)
 	Lockable<ConnectionMap>::LockType clientsLock = clients.Acquire();
 	LOG(LogWaits, "NetworkServer::Close: Accessing the connection list took %f msecs.",
 		timer.MSecsElapsed());
-	for(ConnectionMap::Iterator iter = clientsLock->Begin(); iter != clientsLock->End(); ++iter)
-		iter->second_->Close(0); // Do not wait for any client.
+	for(ConnectionMap::iterator iter = clientsLock->begin(); iter != clientsLock->end(); ++iter)
+		iter->second->Close(0); // Do not wait for any client.
 }
 
 void NetworkServer::RunModalServer()
@@ -458,8 +456,8 @@ void NetworkServer::ConnectionClosed(MessageConnection *connection)
 	Lockable<ConnectionMap>::LockType clientsLock = clients.Acquire();
 	LOG(LogWaits, "NetworkServer::ConnectionClosed: Accessing the connection list took %f msecs.",
 		timer.MSecsElapsed());
-	for(ConnectionMap::Iterator iter = clientsLock->Begin(); iter != clientsLock->End(); ++iter)
-		if (iter->second_ == connection)
+	for(ConnectionMap::iterator iter = clientsLock->begin(); iter != clientsLock->end(); ++iter)
+		if (iter->second == connection)
 		{
 			if (networkServerListener)
 				networkServerListener->ClientDisconnected(connection);
@@ -470,7 +468,7 @@ void NetworkServer::ConnectionClosed(MessageConnection *connection)
 				connection->socket = 0;
 			}
 
-			clientsLock->Erase(iter);
+			clientsLock->erase(iter);
 
 			return;
 		}
@@ -478,7 +476,7 @@ void NetworkServer::ConnectionClosed(MessageConnection *connection)
 	LOG(LogError, "Unknown MessageConnection passed to NetworkServer::Disconnect!");
 }
 
-Vector<Socket *> &NetworkServer::ListenSockets()
+std::vector<Socket *> &NetworkServer::ListenSockets()
 {
 	return listenSockets;
 }
@@ -499,67 +497,67 @@ int NetworkServer::NumConnections() const
 {
 	int numConnections = 0;
 	Lockable<ConnectionMap>::ConstLockType lock = clients.Acquire();
-	for(ConnectionMap::ConstIterator iter = lock->Begin(); iter != lock->End(); ++iter)
+	for(ConnectionMap::const_iterator iter = lock->begin(); iter != lock->end(); ++iter)
 	{
-		const MessageConnection *connection = iter->second_.ptr();
+		const MessageConnection *connection = iter->second.ptr();
 		if (connection && (connection->IsPending() || connection->IsReadOpen() || connection->IsWriteOpen()))
 			++numConnections;
 	}
 	return numConnections;
 }
 
-String NetworkServer::ToString() const
+std::string NetworkServer::ToString() const
 {
 	bool isUdp = false;
 	bool isTcp = false;
-	for(size_t i = 0; i < listenSockets.Size(); ++i)
+	for(size_t i = 0; i < listenSockets.size(); ++i)
 		if (listenSockets[i]->TransportLayer() == SocketOverUDP)
 			isUdp = true;
 		else
 			isTcp = true;
 
-	String str;
+	std::stringstream ss;
 	if (isUdp && isTcp)
-		str += "TCP+UDP server";
+		ss << "TCP+UDP server";
 	else if (isUdp)
-		str += "UDP server";
+		ss << "UDP server";
 	else if (isTcp)
-		str += "TCP server";
-	else str += "Server (no listen sockets open)";
+		ss << "TCP server";
+	else ss << "Server (no listen sockets open)";
 
-	if (listenSockets.Size() == 1)
+	if (listenSockets.size() == 1)
 	{
 		int port = (int)listenSockets[0]->LocalPort();
-		str += " at local port " + String(port);
+		ss << " at local port " << port;
 	}
-	else if (listenSockets.Size() > 1)
+	else if (listenSockets.size() > 1)
 	{
-		str += " (" + String((int)listenSockets.Size()) + " listen sockets at local ports ";
-		for(size_t i = 0; i < listenSockets.Size() && i < 3; ++i)
+		ss << " (" << (int)listenSockets.size() << " listen sockets at local ports ";
+		for(size_t i = 0; i < listenSockets.size() && i < 3; ++i)
 		{
 			if (i > 0)
-				str += ", ";
-			str += String(listenSockets[i]->LocalPort());
+				ss << ", ";
+			ss << listenSockets[i]->LocalPort();
 		}
-		if (listenSockets.Size() > 3)
-			str += ", ...";
-		str += ")";
+		if (listenSockets.size() > 3)
+			ss << ", ...";
+		ss << ")";
 	}
-	str += ": ";
+	ss << ": ";
 
 	int numConnections = 0;
 	{
 		Lockable<ConnectionMap>::ConstLockType lock = clients.Acquire();
-		numConnections = lock->Size();
+		numConnections = lock->size();
 	}
-	str += String(numConnections) + " connections.";
+	ss << numConnections << " connections.";
 
 	if (!acceptNewConnections)
-		str += " (not accepting new connections)";
+		ss << " (not accepting new connections)";
 
 	///\todo Add note about stealth mode.
 
-	return str;
+	return ss.str();
 }
 
 } // ~kNet

+ 121 - 0
ThirdParty/kNet/src/NetworkSimulator.cpp

@@ -0,0 +1,121 @@
+/* Copyright The kNet Project.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License. */
+
+#include "kNet/NetworkSimulator.h"
+#include "kNet/MessageConnection.h"
+
+namespace kNet
+{
+
+NetworkSimulator::NetworkSimulator()
+:enabled(false),
+packetLossRate(0),
+constantPacketSendDelay(0),
+uniformRandomPacketSendDelay(0),
+owner(0),
+corruptMessageId(0),
+corruptToggleBitsRate(0),
+corruptionType(CorruptDatagram),
+corruptMinBits(0),
+corruptMaxBits(0)
+{
+}
+
+NetworkSimulator::~NetworkSimulator()
+{
+	if (queuedBuffers.size() > 0)
+		LOG(LogError, "NetworkSimulator: Leaked %d buffers with improper NetworkSimulator teardown!", (int)queuedBuffers.size());
+}
+
+static float rand01() { return (float)rand() / (RAND_MAX+1); }
+
+void NetworkSimulator::Free()
+{
+	if (!owner || !owner->GetSocket())
+		return;
+
+	for(size_t i = 0; i < queuedBuffers.size(); ++i)
+		owner->GetSocket()->AbortSend(queuedBuffers[i].buffer);
+	queuedBuffers.clear();
+}
+
+void NetworkSimulator::SubmitSendBuffer(kNet::OverlappedTransferBuffer *buffer, Socket *socket)
+{
+	if (rand01() < packetLossRate)
+	{
+		if (owner && owner->GetSocket())
+			owner->GetSocket()->AbortSend(buffer);
+		return; // Dropped this packet!
+	}
+
+	// Should we duplicate this packet?
+	if (rand01() < packetDuplicationRate)
+	{
+		QueuedBuffer b;
+		assert(socket);
+        b.buffer = socket->BeginSend(buffer->bytesContains);
+		if (b.buffer)
+		{
+			assert(b.buffer->buffer.len >= buffer->bytesContains);
+			memcpy(b.buffer->buffer.buf, buffer->buffer.buf, buffer->bytesContains);
+			b.buffer->bytesContains = buffer->bytesContains;
+
+			// Should we corrupt the newly created copy?
+			if (corruptionType == CorruptDatagram)
+				MaybeCorruptBufferToggleBits(b.buffer->buffer.buf, b.buffer->bytesContains);
+
+			b.timeUntilTransfer.StartMSecs(constantPacketSendDelay + (float)rand() * uniformRandomPacketSendDelay / RAND_MAX);
+			queuedBuffers.push_back(b);
+		}
+	}
+
+	// Should we corrupt this packet?
+	if (corruptionType == CorruptDatagram)
+		MaybeCorruptBufferToggleBits(buffer->buffer.buf, buffer->bytesContains);
+
+	QueuedBuffer b;
+	b.buffer = buffer;
+	b.timeUntilTransfer.StartMSecs(constantPacketSendDelay + (float)rand() * uniformRandomPacketSendDelay / RAND_MAX);
+	queuedBuffers.push_back(b);
+}
+
+void NetworkSimulator::Process()
+{
+	for(size_t i = 0; i < queuedBuffers.size(); ++i)
+		if (queuedBuffers[i].timeUntilTransfer.Test())
+		{
+			if (owner && owner->GetSocket())
+				owner->GetSocket()->EndSend(queuedBuffers[i].buffer);
+			queuedBuffers.erase(queuedBuffers.begin() + i);
+			--i;
+		}
+}
+
+void NetworkSimulator::MaybeCorruptBufferToggleBits(void *buffer, size_t numBytes) const
+{
+	// Should corrupt this data?
+	if (rand01() < corruptToggleBitsRate)
+	{
+		int numBitsToCorrupt = corruptMinBits + rand() * (corruptMaxBits - corruptMinBits + 1) / (RAND_MAX+1);
+		for(int i = 0; i < numBitsToCorrupt; ++i)
+		{
+			int byteIndex = rand() * numBytes / (RAND_MAX+1);
+			int bitIndex = rand() % 8;
+			int bitMask = (1 << bitIndex);
+			((char*)buffer)[byteIndex] ^= bitMask;
+		}
+	}
+}
+
+} // ~kNet

+ 40 - 42
ThirdParty/kNet/src/NetworkWorkerThread.cpp

@@ -15,8 +15,6 @@
 /** @file NetworkWorkerThread.cpp
 	@brief */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include <utility>
 
 #ifdef KNET_USE_BOOST
@@ -46,8 +44,8 @@ NetworkWorkerThread::NetworkWorkerThread()
 void NetworkWorkerThread::AddConnection(MessageConnection *connection)
 {
 	workThread.Hold();
-	Lockable<Vector<MessageConnection *> >::LockType lock = connections.Acquire();
-	lock->Push(connection);
+	Lockable<std::vector<MessageConnection *> >::LockType lock = connections.Acquire();
+	lock->push_back(connection);
 	LOG(LogVerbose, "Added connection %p to NetworkWorkerThread.", connection);
 	workThread.Resume();
 }
@@ -56,12 +54,12 @@ void NetworkWorkerThread::RemoveConnection(MessageConnection *connection)
 {
 	workThread.Hold();
 
-	Lockable<Vector<MessageConnection *> >::LockType lock = connections.Acquire();
+	Lockable<std::vector<MessageConnection *> >::LockType lock = connections.Acquire();
 
-	for(size_t i = 0; i < lock->Size(); ++i)
+	for(size_t i = 0; i < lock->size(); ++i)
 		if ((*lock)[i] == connection)
 		{
-			lock->Erase(lock->Begin() + i);
+			lock->erase(lock->begin() + i);
 			LOG(LogVerbose, "NetworkWorkerThread::RemoveConnection: Connection %p removed.", connection);
 			workThread.Resume();
 			return;
@@ -73,8 +71,8 @@ void NetworkWorkerThread::RemoveConnection(MessageConnection *connection)
 void NetworkWorkerThread::AddServer(NetworkServer *server)
 {
 	workThread.Hold();
-	Lockable<Vector<NetworkServer *> >::LockType lock = servers.Acquire();
-	lock->Push(server);
+	Lockable<std::vector<NetworkServer *> >::LockType lock = servers.Acquire();
+	lock->push_back(server);
 	LOG(LogVerbose, "Added server %p to NetworkWorkerThread.", server);
 	workThread.Resume();
 }
@@ -84,15 +82,15 @@ void NetworkWorkerThread::RemoveServer(NetworkServer *server)
 	workThread.Hold();
 
 	PolledTimer timer;
-	Lockable<Vector<NetworkServer *> >::LockType lock = servers.Acquire();
+	Lockable<std::vector<NetworkServer *> >::LockType lock = servers.Acquire();
 	float lockWait = timer.MSecsElapsed();
 	LOG(LogWaits, "NetworkWorkerThread::RemoveServer: Waited %f msecs to lock servers list.",
 		lockWait);
 
-	for(size_t i = 0; i < lock->Size(); ++i)
+	for(size_t i = 0; i < lock->size(); ++i)
 		if ((*lock)[i] == server)
 		{
-			lock->Erase(lock->Begin() + i);
+			lock->erase(lock->begin() + i);
 			LOG(LogVerbose, "NetworkWorkerThread::RemoveServer: Server %p removed.", server);
 			workThread.Resume();
 			return;
@@ -111,16 +109,16 @@ void NetworkWorkerThread::StopThread()
 	workThread.Stop();
 
 	{
-		Lockable<Vector<NetworkServer *> >::LockType lock = servers.Acquire();
-		for(size_t i = 0; i < lock->Size(); ++i)
+		Lockable<std::vector<NetworkServer *> >::LockType lock = servers.Acquire();
+		for(size_t i = 0; i < lock->size(); ++i)
 		{
 			LOG(LogError, "NetworkWorkerThread::StopThread: Warning: NetworkServer %p was not detached from workerThread %p prior to stopping the thread!.", (*lock)[i], this);
 			(*lock)[i]->SetWorkerThread(0);
 		}
 	}
 	{
-		Lockable<Vector<MessageConnection *> >::LockType lock = connections.Acquire();
-		for(size_t i = 0; i < lock->Size(); ++i)
+		Lockable<std::vector<MessageConnection *> >::LockType lock = connections.Acquire();
+		for(size_t i = 0; i < lock->size(); ++i)
 		{
 			LOG(LogError, "NetworkWorkerThread::StopThread: Warning: MessageConnection %p was not detached from workerThread %p prior to stopping the thread!.", (*lock)[i], this);
 			(*lock)[i]->SetWorkerThread(0);
@@ -130,17 +128,17 @@ void NetworkWorkerThread::StopThread()
 
 int NetworkWorkerThread::NumConnections() const
 {
-	return connections.Acquire()->Size();
+	return connections.Acquire()->size();
 }
 
 int NetworkWorkerThread::NumServers() const
 {
-	return servers.Acquire()->Size();
+	return servers.Acquire()->size();
 }
 
 void NetworkWorkerThread::MainLoop()
 {
-	Vector<MessageConnection*> writeWaitConnections;
+	std::vector<MessageConnection*> writeWaitConnections;
 
 	EventArray waitEvents;
 
@@ -151,8 +149,8 @@ void NetworkWorkerThread::MainLoop()
 
 	LOG(LogInfo, "NetworkWorkerThread starting main loop.");
 
-	Vector<MessageConnection*> connectionList;
-	Vector<NetworkServer*> serverList;
+	std::vector<MessageConnection*> connectionList;
+	std::vector<NetworkServer*> serverList;
 
 	while(!workThread.ShouldQuit())
 	{
@@ -161,11 +159,11 @@ void NetworkWorkerThread::MainLoop()
 			break;
 	
 		{
-			Lockable<Vector<MessageConnection *> >::LockType lock = connections.Acquire();
+			Lockable<std::vector<MessageConnection *> >::LockType lock = connections.Acquire();
 			connectionList = *lock;
 		}
 		{
-			Lockable<Vector<NetworkServer *> >::LockType serverLock = servers.Acquire();
+			Lockable<std::vector<NetworkServer *> >::LockType serverLock = servers.Acquire();
 			serverList = *serverLock;
 		}
 
@@ -177,12 +175,12 @@ void NetworkWorkerThread::MainLoop()
 		int waitTime = maxWaitTime;
 
 		waitEvents.Clear();
-		writeWaitConnections.Clear();
+		writeWaitConnections.clear();
 
 		// Next, build the event array that is used for waiting on the sockets.
 		// At odd indices we will have socket read events, and at even indices the socket write events.
 		// After the events for each connection, we will have the UDP listen sockets for each UDP server connection.
-		for(size_t i = 0; i < connectionList.Size(); ++i)
+		for(size_t i = 0; i < connectionList.size(); ++i)
 		{
 			MessageConnection &connection = *connectionList[i];
 
@@ -191,14 +189,14 @@ void NetworkWorkerThread::MainLoop()
 				connection.UpdateConnection();
 			} catch(const NetException &e)
 			{
-				LOG(LogError, (String("kNet::NetException thrown when processing UpdateConnection() for client connection: ") + e.what()).CString());
+				LOG(LogError, (std::string("kNet::NetException thrown when processing UpdateConnection() for client connection: ") + e.what()).c_str());
 				if (connection.GetSocket())
 					connection.GetSocket()->Close();
 			}
 
 			if (connection.GetConnectionState() == ConnectionClosed || !connection.GetSocket() || !connection.GetSocket()->Connected()) // This does not need to be checked each iteration.
 			{
-				connectionList.Erase(connectionList.Begin()+i);
+				connectionList.erase(connectionList.begin()+i);
 				--i;
 				continue;
 			}
@@ -225,7 +223,7 @@ void NetworkWorkerThread::MainLoop()
 				{
 					int msecsLeftUntilWrite = (int)connection.TimeUntilCanSendPacket();
 					waitTime = min(waitTime, msecsLeftUntilWrite);
-					writeWaitConnections.Push(&connection);
+					writeWaitConnections.push_back(&connection);
 					waitEvents.AddEvent(falseEvent);
 					assert(falseEvent.Test() == false && !falseEvent.IsNull());
 				}
@@ -251,13 +249,13 @@ void NetworkWorkerThread::MainLoop()
 		// In this case, the NetworkServer object handlesg all data reads, but data sends
 		// are still managed by the individual MessageConnection objects.
 		// For TCP servers, this step is not needed, since each connection has its own independent socket.
-		for(size_t i = 0; i < serverList.Size(); ++i)
+		for(size_t i = 0; i < serverList.size(); ++i)
 		{
 			NetworkServer &server = *serverList[i];
 
-			Vector<Socket *> &listenSockets = server.ListenSockets();
+			std::vector<Socket *> &listenSockets = server.ListenSockets();
 
-			for(size_t j = 0; j < listenSockets.Size(); ++j)
+			for(size_t j = 0; j < listenSockets.size(); ++j)
 				if (listenSockets[j]->TransportLayer() == SocketOverUDP)
 				{
 					Event listenEvent = listenSockets[j]->GetOverlappedReceiveEvent();
@@ -283,7 +281,7 @@ void NetworkWorkerThread::MainLoop()
 
 		if (index >= 0 && index < waitEvents.Size()) // An event was triggered?
 		{
-			if ((index >> 1) < (int)connectionList.Size())
+			if ((index >> 1) < (int)connectionList.size())
 			{
 				MessageConnection *connection = connectionList[index>>1];
 				assert(connection);
@@ -300,39 +298,39 @@ void NetworkWorkerThread::MainLoop()
 						connection->SendOutPackets();
 				} catch(const NetException &e)
 				{
-					LOG(LogError, (String("kNet::NetException thrown when processing client connection: ") + e.what()).CString());
+					LOG(LogError, (std::string("kNet::NetException thrown when processing client connection: ") + e.what()).c_str());
 					if (connection->GetSocket())
 						connection->GetSocket()->Close();
 				}
 			}
 			else // A UDP server received a message.
 			{
-				int socketIndex = index - connectionList.Size() * 2;
-				if (serverList.Size() > 0)
+				int socketIndex = index - connectionList.size() * 2;
+				if (serverList.size() > 0)
 				{
 					NetworkServer &server = *serverList[0]; ///\bug In case of multiple servers, this is not correct!
-					Vector<Socket *> &listenSockets = server.ListenSockets();
-					if (socketIndex < (int)listenSockets.Size())
+					std::vector<Socket *> &listenSockets = server.ListenSockets();
+					if (socketIndex < (int)listenSockets.size())
 					{
 						try
 						{
 							server.ReadUDPSocketData(listenSockets[socketIndex]);
 						} catch(const NetException &e)
 						{
-							LOG(LogError, (String("kNet::NetException thrown when reading server socket: ") + e.what()).CString());
+							LOG(LogError, (std::string("kNet::NetException thrown when reading server socket: ") + e.what()).c_str());
 							///\todo Could Close(0) the connection here.
 						}
 					}
 					else
 					{
 						LOG(LogError, "NetworkWorkerThread::MainLoop: Warning: Cannot find server socket to read from: EventArray::Wait returned index %d (socketIndex %d), but "
-							"serverList.Size()=%d, connectionList.Size()=%d!", index, socketIndex, (int)serverList.Size(), (int)connectionList.Size());
+							"serverList.size()=%d, connectionList.size()=%d!", index, socketIndex, (int)serverList.size(), (int)connectionList.size());
 					}
 				}
 				else
 				{
 					LOG(LogError, "NetworkWorkerThread::MainLoop: Warning: EventArray::Wait returned index %d (socketIndex %d), but "
-						"serverList.Size()=%d, connectionList.Size()=%d!", index, socketIndex, (int)serverList.Size(), (int)connectionList.Size());
+						"serverList.size()=%d, connectionList.size()=%d!", index, socketIndex, (int)serverList.size(), (int)connectionList.size());
 				}
 			}
 		}
@@ -340,14 +338,14 @@ void NetworkWorkerThread::MainLoop()
 		// The UDP send throttle timers are not read through events. The writeWaitConnections list
 		// contains a list of UDP connections which are now, or will very soon (in less than 1msec) be ready for writing. 
 		// Poll each and try to send a message.
-		for(size_t i = 0; i < writeWaitConnections.Size(); ++i)
+		for(size_t i = 0; i < writeWaitConnections.size(); ++i)
 		{
 			try
 			{
 				writeWaitConnections[i]->SendOutPackets();
 			} catch(const NetException &e)
 			{
-				LOG(LogError, (String("kNet::NetException thrown when sending out a network message: ") + e.what()).CString());
+				LOG(LogError, (std::string("kNet::NetException thrown when sending out a network message: ") + e.what()).c_str());
 			}
 		}
 	}

+ 109 - 110
ThirdParty/kNet/src/SerializationStructCompiler.cpp

@@ -15,9 +15,8 @@
 /** @file SerializationStructCompiler.cpp
 	@brief */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include <fstream>
+#include <sstream>
 #include <cassert>
 #include <cstring>
 
@@ -31,15 +30,15 @@ using namespace std;
 namespace kNet
 {
 
-String SerializationStructCompiler::ParseToValidCSymbolName(const char *str)
+std::string SerializationStructCompiler::ParseToValidCSymbolName(const char *str)
 {
-	String outStr;
+	stringstream ss;
 	size_t len = strlen(str);
 	for(size_t i = 0; i < len; ++i)
-		if ((isalpha(str[i]) || (outStr.Length() > 0 && isdigit(str[i]))) && str[i] != ' ')
-			outStr += str[i];
+		if ((isalpha(str[i]) || (ss.str().length() > 0 && isdigit(str[i]))) && str[i] != ' ')
+			ss << str[i];
 
-	return outStr;
+	return ss.str();
 }
 
 void SerializationStructCompiler::WriteFilePreamble(std::ofstream &out)
@@ -54,12 +53,12 @@ void SerializationStructCompiler::WriteFilePreamble(std::ofstream &out)
 
 void SerializationStructCompiler::WriteMemberDefinition(const SerializedElementDesc &elem, int level, std::ofstream &out)
 {
-	String type;
+	string type;
 
 	if (elem.type == SerialInvalid)
 		throw kNet::NetException("Invalid element type for SerializationStructCompiler::WriteMemberDefinition!");
 
-	String name = ParseToValidCSymbolName(elem.name.CString());
+	string name = ParseToValidCSymbolName(elem.name.c_str());
 
 	if (elem.type == SerialStruct || elem.type == SerialOther)
 		type = elem.typeString;
@@ -67,14 +66,14 @@ void SerializationStructCompiler::WriteMemberDefinition(const SerializedElementD
 		type = SerialTypeToCTypeString(elem.type);
 
 	if (type == "string")
-		type = "String";
+		type = "std::string"; // Make a hardcoded fix for std::string so that the user doesn't have to specify 'std::string' into the XML, which would be clumsy.
 
 	if (elem.varyingCount == true)
-		out << Indent(level).CString() << "Vector<" << type.CString() << "> " << name.CString() << ";" << endl;
+		out << Indent(level) << "std::vector<" << type << "> " << name << ";" << endl;
 	else if (elem.count > 1)
-		out << Indent(level).CString() << type.CString() << " " << name.CString() << "[" << elem.count << "];" << endl;
+		out << Indent(level) << type << " " << name << "[" << elem.count << "];" << endl;
 	else
-		out << Indent(level).CString() << type.CString() << " " << name.CString() << ";" << endl;
+		out << Indent(level) << type << " " << name << ";" << endl;
 }
 
 void SerializationStructCompiler::WriteStructMembers(const SerializedElementDesc &elem, int level, std::ofstream &out)
@@ -83,7 +82,7 @@ void SerializationStructCompiler::WriteStructMembers(const SerializedElementDesc
 
 	int childStructIndex = 1;
 
-	for(size_t i = 0; i < elem.elements.Size(); ++i)
+	for(size_t i = 0; i < elem.elements.size(); ++i)
 	{
 		SerializedElementDesc &e = *elem.elements[i];
 		assert(&e);
@@ -97,7 +96,7 @@ void SerializationStructCompiler::WriteNestedStructs(const SerializedElementDesc
 {
 	assert(&elem && elem.type == SerialStruct);
 
-	for(size_t i = 0; i < elem.elements.Size(); ++i)
+	for(size_t i = 0; i < elem.elements.size(); ++i)
 	{
 		SerializedElementDesc &e = *elem.elements[i];
 		assert(&e);
@@ -112,11 +111,11 @@ void SerializationStructCompiler::WriteStructSizeMemberFunction(const Serialized
 {
 	assert(&elem && elem.type == SerialStruct);
 
-	out << Indent(level).CString() << "inline size_t Size() const" << endl
-		<< Indent(level).CString() << "{" << endl
-		<< Indent(level+1).CString() << "return ";
+	out << Indent(level) << "inline size_t Size() const" << endl
+		<< Indent(level) << "{" << endl
+		<< Indent(level+1) << "return ";
 
-	for(size_t i = 0; i < elem.elements.Size(); ++i)
+	for(size_t i = 0; i < elem.elements.size(); ++i)
 	{
 		SerializedElementDesc &e = *elem.elements[i];
 		assert(&e);
@@ -124,7 +123,7 @@ void SerializationStructCompiler::WriteStructSizeMemberFunction(const Serialized
 		if (i > 0)
 			out << " + ";
 
-		String memberName = ParseToValidCSymbolName(e.name.CString());
+		string memberName = ParseToValidCSymbolName(e.name.c_str());
 
 		if (e.varyingCount)
 		{
@@ -138,26 +137,26 @@ void SerializationStructCompiler::WriteStructSizeMemberFunction(const Serialized
 		if (e.type == SerialStruct)
 		{
 			if (e.varyingCount)
-				out << "kNet::SumArray(" << memberName.CString() << ", " << memberName.CString() << ".size())";
+				out << "kNet::SumArray(" << memberName << ", " << memberName << ".size())";
 			else if (e.count > 1)
-				out << "kNet::SumArray(" << memberName.CString() << ", " << e.count << ")";
+				out << "kNet::SumArray(" << memberName << ", " << e.count << ")";
 			else
-				out << memberName.CString() << ".Size()";
+				out << memberName << ".Size()";
 		}
 		else*/ if (e.type == SerialStruct || e.type == SerialOther || e.type == SerialString)
 		{
-			String typeSerializer = "kNet::TypeSerializer<" + e.typeString + ">";
+			std::string typeSerializer = "kNet::TypeSerializer<" + e.typeString + ">";
 			if (e.varyingCount)
-				out << "kNet::ArraySize<" << typeSerializer.CString() << " >(" << memberName.CString() << ", " << memberName.CString() << ".Size())";
+				out << "kNet::ArraySize<" << typeSerializer << " >(" << memberName << ", " << memberName << ".size())";
 			else if (e.count > 1)
-				out << "kNet::ArraySize(" << typeSerializer.CString() << " >(" << memberName.CString() << ", " << e.count << ")";
+				out << "kNet::ArraySize(" << typeSerializer << " >(" << memberName << ", " << e.count << ")";
 			else
-				out << typeSerializer.CString() << "::Size(" << memberName.CString() << ")";
+				out << typeSerializer << "::Size(" << memberName << ")";
 		}
 		else
 		{
 			if (e.varyingCount)
-				out << memberName.CString() << ".Size()" << "*" << SerialTypeSize(e.type);
+				out << memberName << ".size()" << "*" << SerialTypeSize(e.type);
 			else if (e.count > 1)
 				out << e.count << "*" << SerialTypeSize(e.type);
 			else
@@ -165,36 +164,36 @@ void SerializationStructCompiler::WriteStructSizeMemberFunction(const Serialized
 		}
 	}
 
-	if (elem.elements.Size() == 0)
+	if (elem.elements.size() == 0)
 		out << "0";
 
 	out << ";" << endl
-		<< Indent(level).CString() << "}" << endl << endl;
+		<< Indent(level) << "}" << endl << endl;
 }
 
-void SerializationStructCompiler::WriteSerializeMemberFunction(/*const String &className, */const SerializedElementDesc &elem, int level, std::ofstream &out)
+void SerializationStructCompiler::WriteSerializeMemberFunction(/*const std::string &className, */const SerializedElementDesc &elem, int level, std::ofstream &out)
 {
 	assert(&elem && elem.type == SerialStruct);
 
-	out << Indent(level).CString() << "inline void SerializeTo(kNet::DataSerializer &dst) const" << endl
-		<< Indent(level).CString() << "{" << endl;
+	out << Indent(level) << "inline void SerializeTo(kNet::DataSerializer &dst) const" << endl
+		<< Indent(level) << "{" << endl;
 
 	++level;
 
-	for(size_t i = 0; i < elem.elements.Size(); ++i)
+	for(size_t i = 0; i < elem.elements.size(); ++i)
 	{
 		SerializedElementDesc &e = *elem.elements[i];
 		assert(&e);
 
-		String memberName = ParseToValidCSymbolName(e.name.CString());
+		string memberName = ParseToValidCSymbolName(e.name.c_str());
 
 		if (e.varyingCount == true)
 		{
 			// What type of variable will hold the varyingCount field?
 			if (e.count != 8 && e.count != 16 && e.count != 32) ///\todo Support arbitrary bit-length varyingCounts.
-				out << Indent(level).CString() << "// TODO: Unsupported varyingCount field length of " << e.count << " bits used!" << endl;
+				out << Indent(level) << "// TODO: Unsupported varyingCount field length of " << e.count << " bits used!" << endl;
 
-			out << Indent(level).CString() << "dst.Add<u" << e.count << ">(" << memberName.CString() << ".Size());" << endl;
+			out << Indent(level) << "dst.Add<u" << e.count << ">(" << memberName << ".size());" << endl;
 		}
 /*
 		if (e.type == SerialStruct)
@@ -214,118 +213,118 @@ void SerializationStructCompiler::WriteSerializeMemberFunction(/*const String &c
 		}
 		else*/ if (e.type == SerialStruct || e.type == SerialOther)
 		{
-			String typeSerializer = "kNet::TypeSerializer<" + e.typeString + ">";
+			std::string typeSerializer = "kNet::TypeSerializer<" + e.typeString + ">";
 
 			if (e.varyingCount == true)
 			{
-				out << Indent(level).CString() << "for(size_t i = 0; i < " << memberName.CString() << ".Size(); ++i)" << endl;
-				out << Indent(level+1).CString() << typeSerializer.CString() << "::SerializeTo(dst, " << memberName.CString() << "[i]);" << endl;
+				out << Indent(level) << "for(size_t i = 0; i < " << memberName << ".size(); ++i)" << endl;
+				out << Indent(level+1) << typeSerializer << "::SerializeTo(dst, " << memberName << "[i]);" << endl;
 			}
 			else if (e.count > 1)
 			{
-				out << Indent(level).CString() << "for(size_t i = 0; i < " << e.count << "; ++i)" << endl;
-				out << Indent(level+1).CString() << typeSerializer.CString() << "::SerializeTo(dst, " << memberName.CString() << "[i]);" << endl;
+				out << Indent(level) << "for(size_t i = 0; i < " << e.count << "; ++i)" << endl;
+				out << Indent(level+1) << typeSerializer << "::SerializeTo(dst, " << memberName << "[i]);" << endl;
 			}
 			else
-				out << Indent(level).CString() << typeSerializer.CString() << "::SerializeTo(dst, " << memberName.CString() << ");" << endl;
+				out << Indent(level) << typeSerializer << "::SerializeTo(dst, " << memberName << ");" << endl;
 		}
 		else
 		{
 			if (e.varyingCount == true)
 			{
-				out << Indent(level).CString() << "if (" << memberName.CString() << ".size() > 0)" << endl;
-				out << Indent(level+1).CString() << "dst.AddArray<" << SerialTypeToCTypeString(e.type) << ">(&" << memberName.CString()
-					<< "[0], " << memberName.CString() << ".size());" << endl;
+				out << Indent(level) << "if (" << memberName << ".size() > 0)" << endl;
+				out << Indent(level+1) << "dst.AddArray<" << SerialTypeToCTypeString(e.type) << ">(&" << memberName
+					<< "[0], " << memberName << ".size());" << endl;
 			}
 			else if (e.count > 1)
-				out << Indent(level).CString() << "dst.AddArray<" << SerialTypeToCTypeString(e.type) << ">(" << memberName.CString()
+				out << Indent(level) << "dst.AddArray<" << SerialTypeToCTypeString(e.type) << ">(" << memberName
 					<< ", " << e.count << ");" << endl;
 			else 
-				out << Indent(level).CString() << "dst.Add<" << SerialTypeToCTypeString(e.type) << ">(" << memberName.CString()
+				out << Indent(level) << "dst.Add<" << SerialTypeToCTypeString(e.type) << ">(" << memberName
 					<< ");" << endl;
 		}
 	}
 	--level;
-	out << Indent(level).CString() << "}" << endl;
-//	out << Indent(level).CString() << "inline static void SerializeTo(knet::DataSerializer &dst, const " << className.CString() << " &src) { src.SerializeTo(dst); }"<< endl;
+	out << Indent(level) << "}" << endl;
+//	out << Indent(level) << "inline static void SerializeTo(knet::DataSerializer &dst, const " << className << " &src) { src.SerializeTo(dst); }"<< endl;
 	out << endl;
 }
 
-void SerializationStructCompiler::WriteDeserializeMemberFunction(/*const String &className, */const SerializedElementDesc &elem, int level, std::ofstream &out)
+void SerializationStructCompiler::WriteDeserializeMemberFunction(/*const std::string &className, */const SerializedElementDesc &elem, int level, std::ofstream &out)
 {
 	assert(&elem && elem.type == SerialStruct);
 
-	out << Indent(level).CString() << "inline void DeserializeFrom(kNet::DataDeserializer &src)" << endl
-		<< Indent(level).CString() << "{" << endl;
+	out << Indent(level) << "inline void DeserializeFrom(kNet::DataDeserializer &src)" << endl
+		<< Indent(level) << "{" << endl;
 
 	++level;
 
-	for(size_t i = 0; i < elem.elements.Size(); ++i)
+	for(size_t i = 0; i < elem.elements.size(); ++i)
 	{
 		SerializedElementDesc &e = *elem.elements[i];
 		assert(&e);
 
-		String memberName = ParseToValidCSymbolName(e.name.CString());
+		string memberName = ParseToValidCSymbolName(e.name.c_str());
 
 		if (e.varyingCount == true)
 		{
 			// What type of variable will hold the varyingCount field?
 			if (e.count != 8 && e.count != 16 && e.count != 32) ///\todo Support arbitrary bit-length varyingCounts.
-				out << Indent(level).CString() << "// TODO: Unsupported varyingCount field length of " << e.count << " bits used!" << endl;
+				out << Indent(level) << "// TODO: Unsupported varyingCount field length of " << e.count << " bits used!" << endl;
 
-			out << Indent(level).CString() << memberName.CString() << ".Resize(src.Read<u" << e.count << ">());" << endl;
+			out << Indent(level) << memberName << ".resize(src.Read<u" << e.count << ">());" << endl;
 		}
 
 /*		if (e.type == SerialStruct)
 		{
 			if (e.varyingCount == true)
 			{
-				out << Indent(level).CString() << "for(size_t i = 0; i < " << memberName << ".Size(); ++i)" << endl;
-				out << Indent(level+1).CString() << memberName.CString() << "[i].DeserializeFrom(src);" << endl;
+				out << Indent(level) << "for(size_t i = 0; i < " << memberName << ".size(); ++i)" << endl;
+				out << Indent(level+1) << memberName << "[i].DeserializeFrom(src);" << endl;
 			}
 			else if (e.count > 1)
 			{
-				out << Indent(level).CString() << "for(size_t i = 0; i < " << e.count << "; ++i)" << endl;
-				out << Indent(level+1).CString() << memberName.CString() << "[i].DeserializeFrom(src);" << endl;
+				out << Indent(level) << "for(size_t i = 0; i < " << e.count << "; ++i)" << endl;
+				out << Indent(level+1) << memberName << "[i].DeserializeFrom(src);" << endl;
 			}
 			else
-				out << Indent(level).CString() << memberName.CString() << ".DeserializeFrom(src);" << endl;
+				out << Indent(level) << memberName << ".DeserializeFrom(src);" << endl;
 		} */
 		if (e.type == SerialStruct || e.type == SerialOther)
 		{
-			String typeSerializer = "kNet::TypeSerializer<" + e.typeString + ">";
+			std::string typeSerializer = "kNet::TypeSerializer<" + e.typeString + ">";
 
 			if (e.varyingCount == true)
 			{
-				out << Indent(level).CString() << "for(size_t i = 0; i < " << memberName.CString() << ".Size(); ++i)" << endl;
-				out << Indent(level+1).CString() << typeSerializer.CString() << "::DeserializeFrom(src, " << memberName.CString() << "[i]);" << endl;
+				out << Indent(level) << "for(size_t i = 0; i < " << memberName << ".size(); ++i)" << endl;
+				out << Indent(level+1) << typeSerializer << "::DeserializeFrom(src, " << memberName << "[i]);" << endl;
 			}
 			else if (e.count > 1)
 			{
-				out << Indent(level).CString() << "for(size_t i = 0; i < " << e.count << "; ++i)" << endl;
-				out << Indent(level+1).CString() << typeSerializer.CString() << "::DeserializeFrom(src, " << memberName.CString() << "[i]);" << endl;
+				out << Indent(level) << "for(size_t i = 0; i < " << e.count << "; ++i)" << endl;
+				out << Indent(level+1) << typeSerializer << "::DeserializeFrom(src, " << memberName << "[i]);" << endl;
 			}
 			else
-				out << Indent(level).CString() << typeSerializer.CString() << "::DeserializeFrom(src, " << memberName.CString() << ");" << endl;
+				out << Indent(level) << typeSerializer << "::DeserializeFrom(src, " << memberName << ");" << endl;
 		}
 		else 
 		{
 			if (e.varyingCount == true)
 			{
-				out << Indent(level).CString() << "if (" << memberName.CString() << ".Size() > 0)" << endl;
-				out << Indent(level+1).CString() << "src.ReadArray<" << SerialTypeToCTypeString(e.type) << ">(&" << memberName.CString()
-					<< "[0], " << memberName.CString() << ".size());" << endl;
+				out << Indent(level) << "if (" << memberName << ".size() > 0)" << endl;
+				out << Indent(level+1) << "src.ReadArray<" << SerialTypeToCTypeString(e.type) << ">(&" << memberName
+					<< "[0], " << memberName << ".size());" << endl;
 			}
 			else if (e.count > 1)
-				out << Indent(level).CString() << "src.ReadArray<" << SerialTypeToCTypeString(e.type) << ">(" << memberName.CString()
+				out << Indent(level) << "src.ReadArray<" << SerialTypeToCTypeString(e.type) << ">(" << memberName
 					<< ", " << e.count << ");" << endl;
 			else 
-				out << Indent(level).CString() << memberName.CString() << " = src.Read<" << SerialTypeToCTypeString(e.type) << ">();" << endl;
+				out << Indent(level) << memberName << " = src.Read<" << SerialTypeToCTypeString(e.type) << ">();" << endl;
 		}
 	}
 	--level;
-	out << Indent(level).CString() << "}" << endl;
-//	out << Indent(level).CString() << "inline static void DeserializeFrom(knet::DataDeserializer &src, " << className.CString() << " &dst) { dst.DeserializeFrom(src); }"<< endl;
+	out << Indent(level) << "}" << endl;
+//	out << Indent(level) << "inline static void DeserializeFrom(knet::DataDeserializer &src, " << className << " &dst) { dst.DeserializeFrom(src); }"<< endl;
 	out << endl;
 }
 
@@ -333,19 +332,19 @@ void SerializationStructCompiler::WriteStruct(const SerializedElementDesc &elem,
 {
 	assert(&elem && elem.type == SerialStruct);
 
-	String className = String("struct S_") + ParseToValidCSymbolName(elem.name.CString());
+	string className = string("struct S_") + ParseToValidCSymbolName(elem.name.c_str());
 	if (level == 0)
-		className = String("struct ") + ParseToValidCSymbolName(elem.name.CString());
+		className = string("struct ") + ParseToValidCSymbolName(elem.name.c_str());
 
-	out << Indent(level).CString() << className.CString() << endl
-	    << Indent(level).CString() << "{" << endl;
+	out << Indent(level) << className << endl
+	    << Indent(level) << "{" << endl;
 
 	WriteNestedStructs(elem, level+1, out);
 	WriteStructMembers(elem, level+1, out);
 	WriteStructSizeMemberFunction(elem, level+1, out);
 	WriteSerializeMemberFunction(/*className, */elem, level+1, out);
 	WriteDeserializeMemberFunction(/*className, */elem, level+1, out);
-	out << Indent(level).CString() << "};" << endl << endl;
+	out << Indent(level) << "};" << endl << endl;
 }
 
 void SerializationStructCompiler::CompileStruct(const SerializedElementDesc &structure, const char *outfile)
@@ -358,39 +357,39 @@ void SerializationStructCompiler::CompileStruct(const SerializedElementDesc &str
 
 void SerializationStructCompiler::WriteMessage(const SerializedMessageDesc &message, std::ofstream &out)
 {
-	String structName = "Msg" + message.name;
-	out << "struct " << structName.CString() << endl
+	string structName = string("Msg") + message.name;
+	out << "struct " << structName << endl
 		<< "{" << endl;
 
-	out << Indent(1).CString() << structName.CString() << "()" << endl 
-		<< Indent(1).CString() << "{" << endl
-		<< Indent(2).CString() << "InitToDefault();" << endl
-		<< Indent(1).CString() << "}" << endl << endl;
+	out << Indent(1) << structName << "()" << endl 
+		<< Indent(1) << "{" << endl
+		<< Indent(2) << "InitToDefault();" << endl
+		<< Indent(1) << "}" << endl << endl;
 
-	out << Indent(1).CString() << structName.CString() << "(const char *data, size_t numBytes)" << endl 
-		<< Indent(1).CString() << "{" << endl
-		<< Indent(2).CString() << "InitToDefault();" << endl
-		<< Indent(2).CString() << "kNet::DataDeserializer dd(data, numBytes);" << endl
-		<< Indent(2).CString() << "DeserializeFrom(dd);" << endl
-		<< Indent(1).CString() << "}" << endl << endl;
+	out << Indent(1) << structName << "(const char *data, size_t numBytes)" << endl 
+		<< Indent(1) << "{" << endl
+		<< Indent(2) << "InitToDefault();" << endl
+		<< Indent(2) << "kNet::DataDeserializer dd(data, numBytes);" << endl
+		<< Indent(2) << "DeserializeFrom(dd);" << endl
+		<< Indent(1) << "}" << endl << endl;
 
-	out << Indent(1).CString() << "void InitToDefault()" << endl
-		<< Indent(1).CString() << "{" << endl
-		<< Indent(2).CString() << "reliable = defaultReliable;" << endl
-		<< Indent(2).CString() << "inOrder = defaultInOrder;" << endl
-		<< Indent(2).CString() << "priority = defaultPriority;" << endl
-		<< Indent(1).CString() << "}" << endl << endl;
+	out << Indent(1) << "void InitToDefault()" << endl
+		<< Indent(1) << "{" << endl
+		<< Indent(2) << "reliable = defaultReliable;" << endl
+		<< Indent(2) << "inOrder = defaultInOrder;" << endl
+		<< Indent(2) << "priority = defaultPriority;" << endl
+		<< Indent(1) << "}" << endl << endl;
 
-	out << Indent(1).CString() << "enum { messageID = "<< message.id << " };" << endl;
-	out << Indent(1).CString() << "static inline const char * const Name() { return \"" << message.name.CString() << "\"; }" << endl << endl;
+	out << Indent(1) << "enum { messageID = "<< message.id << " };" << endl;
+	out << Indent(1) << "static inline const char * const Name() { return \"" << message.name << "\"; }" << endl << endl;
 
-	out << Indent(1).CString() << "static const bool defaultReliable = " << (message.reliable ? "true" : "false") << ";" << endl;
-	out << Indent(1).CString() << "static const bool defaultInOrder = " << (message.inOrder ? "true" : "false") << ";" << endl;
-	out << Indent(1).CString() << "static const u32 defaultPriority = " << message.priority << ";" << endl << endl;
+	out << Indent(1) << "static const bool defaultReliable = " << (message.reliable ? "true" : "false") << ";" << endl;
+	out << Indent(1) << "static const bool defaultInOrder = " << (message.inOrder ? "true" : "false") << ";" << endl;
+	out << Indent(1) << "static const u32 defaultPriority = " << message.priority << ";" << endl << endl;
 
-	out << Indent(1).CString() << "bool reliable;" << endl;
-	out << Indent(1).CString() << "bool inOrder;" << endl;
-	out << Indent(1).CString() << "u32 priority;" << endl << endl;
+	out << Indent(1) << "bool reliable;" << endl;
+	out << Indent(1) << "bool inOrder;" << endl;
+	out << Indent(1) << "u32 priority;" << endl << endl;
 
 	WriteNestedStructs(*message.data, 1, out);
 	WriteStructMembers(*message.data, 1, out);
@@ -410,12 +409,12 @@ void SerializationStructCompiler::CompileMessage(const SerializedMessageDesc &me
 	WriteMessage(message, out);
 }
 
-String SerializationStructCompiler::Indent(int level)
+std::string SerializationStructCompiler::Indent(int level)
 {
-	String str;
+	stringstream ss;
 	for(int i = 0; i < level; ++i)
-		str += "\t";
-	return str;
+		ss << "\t";
+	return ss.str();
 }
 
 } // ~kNet

+ 20 - 22
ThirdParty/kNet/src/SerializedDataIterator.cpp

@@ -15,8 +15,6 @@
 /** @file SerializedDataIterator.cpp
 	@brief */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include <cassert>
 
 #include "kNet/DebugMemoryLeakCheck.h"
@@ -28,41 +26,41 @@ namespace kNet
 
 BasicSerializedDataType SerializedDataIterator::NextElementType() const
 {
-	if (currentElementStack.Size() == 0)
+	if (currentElementStack.size() == 0)
 		return SerialInvalid;
 
-	assert(currentElementStack.Back().elem);
+	assert(currentElementStack.back().elem);
 
 	// If we don't know how many instances there are of the next element, it's the next field 
 	// to be filled - our iterator is pointing to the dynamicCount property of that field.
-	if (currentElementStack.Back().elem->varyingCount && currentElementStack.Back().dynamicCountSpecified == false)
+	if (currentElementStack.back().elem->varyingCount && currentElementStack.back().dynamicCountSpecified == false)
 		return SerialDynamicCount;
 
-	return currentElementStack.Back().elem->type;
+	return currentElementStack.back().elem->type;
 }
 
 const SerializedElementDesc *SerializedDataIterator::NextElementDesc() const
 {
-	return currentElementStack.Size() > 0 ? currentElementStack.Back().elem : 0;
+	return currentElementStack.size() > 0 ? currentElementStack.back().elem : 0;
 }
 
 void SerializedDataIterator::ProceedToNextVariable()
 {
-	if (currentElementStack.Size() == 0)
+	if (currentElementStack.size() == 0)
 		return;
 
-	ElemInfo &nextVar = currentElementStack.Back();
+	ElemInfo &nextVar = currentElementStack.back();
 
 	if (nextVar.elem->type == SerialStruct)
 	{
 		++nextVar.nextElem;
-		if (nextVar.nextElem >= (int)nextVar.elem->elements.Size())
+		if (nextVar.nextElem >= (int)nextVar.elem->elements.size())
 		{
 			nextVar.nextElem = 0;
 			++nextVar.nextIndex;
 			if (nextVar.nextIndex >= nextVar.count)
 			{
-				currentElementStack.Pop();
+				currentElementStack.pop_back();
 				ProceedToNextVariable();
 				return;
 			}
@@ -75,7 +73,7 @@ void SerializedDataIterator::ProceedToNextVariable()
 		++nextVar.nextIndex;
 		if (nextVar.nextIndex >= nextVar.count)
 		{
-			currentElementStack.Pop();
+			currentElementStack.pop_back();
 			ProceedToNextVariable();
 		}
 	}
@@ -90,22 +88,22 @@ void SerializedDataIterator::ProceedNVariables(int count)
 
 void SerializedDataIterator::ProceedToNextElement()
 {
-	ElemInfo &nextVar = currentElementStack.Back();
+	ElemInfo &nextVar = currentElementStack.back();
 
 	++nextVar.nextElem;
-	if (nextVar.nextElem >= (int)nextVar.elem->elements.Size())
+	if (nextVar.nextElem >= (int)nextVar.elem->elements.size())
 	{
 		nextVar.nextElem = 0;
 		++nextVar.nextIndex;
 		if (nextVar.nextIndex >= nextVar.count)
 		{
-			currentElementStack.Pop();
+			currentElementStack.pop_back();
 			ProceedToNextElement();
 		}
 	}
 	else
 	{
-/*		currentElementStack.Push(ElemInfo());
+/*		currentElementStack.push_back(ElemInfo());
 		ElemInfo &newVar = currentElementStack.back();
 		newVar.elem = nextVar.elem->elements[nextVar.nextElem];
 		newVar.nextIndex = 0;
@@ -118,7 +116,7 @@ void SerializedDataIterator::ProceedToNextElement()
 
 void SerializedDataIterator::SetVaryingElemSize(u32 count)
 {
-	ElemInfo &nextVar = currentElementStack.Back();
+	ElemInfo &nextVar = currentElementStack.back();
 	assert(nextVar.dynamicCountSpecified == false);
 	assert(nextVar.elem->varyingCount == true);
 	assert(nextVar.nextIndex == 0);
@@ -131,11 +129,11 @@ void SerializedDataIterator::SetVaryingElemSize(u32 count)
 
 void SerializedDataIterator::DescendIntoStructure()
 {
-	ElemInfo &nextVar = currentElementStack.Back();
+	ElemInfo &nextVar = currentElementStack.back();
 
 	if (nextVar.dynamicCountSpecified == false && nextVar.elem->varyingCount == true)
 		return;
-	if (nextVar.nextElem >= (int)nextVar.elem->elements.Size())
+	if (nextVar.nextElem >= (int)nextVar.elem->elements.size())
 		return;
 
 	ElemInfo newVar;
@@ -144,7 +142,7 @@ void SerializedDataIterator::DescendIntoStructure()
 	newVar.nextElem = 0;
  	newVar.count = (newVar.elem->varyingCount ? 0 : newVar.elem->count); // A varying block? Then the user has to supply multiplicity.
 	newVar.dynamicCountSpecified = false;
-	currentElementStack.Push(newVar);
+	currentElementStack.push_back(newVar);
 
 	// Descend again in case we have a struct-in-struct-in-struct...
 	DescendIntoStructure();
@@ -152,7 +150,7 @@ void SerializedDataIterator::DescendIntoStructure()
 
 void SerializedDataIterator::ResetTraversal()
 {
-	currentElementStack.Clear();
+	currentElementStack.clear();
 
 	ElemInfo newVar;
 	newVar.elem = desc.data;
@@ -160,7 +158,7 @@ void SerializedDataIterator::ResetTraversal()
 	newVar.nextElem = 0;
  	newVar.count = (newVar.elem->varyingCount ? 0 : newVar.elem->count); // A varying block? Then the user has to supply multiplicity.
 	newVar.dynamicCountSpecified = false;
-	currentElementStack.Push(newVar);
+	currentElementStack.push_back(newVar);
 
 	// Descend again in case we have a struct-in-struct-in-struct...
 	DescendIntoStructure();

+ 68 - 49
ThirdParty/kNet/src/Socket.cpp

@@ -15,12 +15,10 @@
 /** @file Socket.cpp
 	@brief */
 
-// Modified by Lasse Öörni for Urho3D
-
+#include <string>
 #include <cassert>
 #include <utility>
-
-#include "Str.h"
+#include <sstream>
 
 #ifdef KNET_USE_BOOST
 #include <boost/thread/thread.hpp>
@@ -51,7 +49,7 @@ const int numConcurrentSendBuffers = 4;
 namespace kNet
 {
 
-String SocketTransportLayerToString(SocketTransportLayer transport)
+std::string SocketTransportLayerToString(SocketTransportLayer transport)
 {
 	switch(transport)
 	{
@@ -59,9 +57,9 @@ String SocketTransportLayerToString(SocketTransportLayer transport)
 	case SocketOverTCP: return "TCP";
 	default:
 		{
-			String str;
-			str += "Invalid SocketTransportLayer (" + String((int)transport) + ")!";
-			return str;
+			std::stringstream ss;
+			ss << "Invalid SocketTransportLayer (" << (int)transport << ")!";
+			return ss.str();
 		}
 	}
 }
@@ -78,7 +76,7 @@ SocketTransportLayer StringToSocketTransportLayer(const char *str)
 }
 
 
-String SocketTypeToString(SocketType type)
+std::string SocketTypeToString(SocketType type)
 {
 	switch(type)
 	{
@@ -87,9 +85,9 @@ String SocketTypeToString(SocketType type)
 	case ClientSocket: return "Client socket";
 	default:
 		{
-			String str;
-			str += "Invalid SocketType (" + String((int)type) + ")!";
-			return str;
+			std::stringstream ss;
+			ss << "Invalid SocketType (" << (int)type << ")!";
+			return ss.str();
 		}
 	}
 }
@@ -182,6 +180,8 @@ OverlappedTransferBuffer *AllocateOverlappedTransferBuffer(int bytes)
 	memset(buffer, 0, sizeof(OverlappedTransferBuffer));
 	buffer->buffer.buf = new char[bytes];
 	buffer->buffer.len = bytes;
+	buffer->bytesContains = 0;
+    buffer->bytesAllocated = bytes;
 #ifdef WIN32
 	buffer->overlapped.hEvent = WSACreateEvent();
 	if (buffer->overlapped.hEvent == WSA_INVALID_EVENT)
@@ -214,14 +214,14 @@ void Socket::SetSendBufferSize(int bytes)
 {
 	socklen_t len = sizeof(bytes);
 	if (setsockopt(connectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&bytes, len))
-		LOG(LogError, "Socket::SetSendBufferSize: setsockopt failed with error %s!", Network::GetLastErrorString().CString());
+		LOG(LogError, "Socket::SetSendBufferSize: setsockopt failed with error %s!", Network::GetLastErrorString().c_str());
 }
 
 void Socket::SetReceiveBufferSize(int bytes)
 {
 	socklen_t len = sizeof(bytes);
 	if (setsockopt(connectSocket, SOL_SOCKET, SO_RCVBUF, (char*)&bytes, len))
-		LOG(LogError, "Socket::SetReceiveBufferSize: setsockopt failed with error %s!", Network::GetLastErrorString().CString());
+		LOG(LogError, "Socket::SetReceiveBufferSize: setsockopt failed with error %s!", Network::GetLastErrorString().c_str());
 }
 
 int Socket::SendBufferSize() const
@@ -230,7 +230,7 @@ int Socket::SendBufferSize() const
 	socklen_t len = sizeof(bytes);
 	if (getsockopt(connectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&bytes, &len))
 	{
-		LOG(LogError, "Socket::SendBufferSize: getsockopt failed with error %s!", Network::GetLastErrorString().CString());
+		LOG(LogError, "Socket::SendBufferSize: getsockopt failed with error %s!", Network::GetLastErrorString().c_str());
 		return 0;
 	}
 	return bytes;
@@ -242,7 +242,7 @@ int Socket::ReceiveBufferSize() const
 	socklen_t len = sizeof(bytes);
 	if (getsockopt(connectSocket, SOL_SOCKET, SO_RCVBUF, (char*)&bytes, &len))
 	{
-		LOG(LogError, "Socket::ReceiveBufferSize: getsockopt failed with error %s!", Network::GetLastErrorString().CString());
+		LOG(LogError, "Socket::ReceiveBufferSize: getsockopt failed with error %s!", Network::GetLastErrorString().c_str());
 		return 0;
 	}
 
@@ -293,7 +293,7 @@ void Socket::EnqueueNewReceiveBuffer(OverlappedTransferBuffer *buffer)
 			if (IsUDPServerSocket())
 				LOG(LogError, "Unexpected: Received a message of 0 bytes on a UDP server socket!");
 
-			LOG(LogInfo, "Socket::EnqueueNewReceiveBuffer: Received 0 bytes from the network. Read connection closed in socket %s.", ToString().CString());
+			LOG(LogInfo, "Socket::EnqueueNewReceiveBuffer: Received 0 bytes from the network. Read connection closed in socket %s.", ToString().c_str());
 			readOpen = false;
 			DeleteOverlappedTransferBuffer(buffer);
 			return;
@@ -312,7 +312,7 @@ void Socket::EnqueueNewReceiveBuffer(OverlappedTransferBuffer *buffer)
 		if (IsUDPServerSocket())
 			LOG(LogError, "Unexpected: Received WSAEDISCON on a UDP server socket!");
 
-		LOG(LogError, "Socket::EnqueueNewReceivebuffer: WSAEDISCON. Connection closed in socket %s.", ToString().CString());
+		LOG(LogError, "Socket::EnqueueNewReceivebuffer: WSAEDISCON. Connection closed in socket %s.", ToString().c_str());
 		readOpen = false;
 		///\todo Should do writeOpen = false; here as well?
 		DeleteOverlappedTransferBuffer(buffer);
@@ -322,7 +322,7 @@ void Socket::EnqueueNewReceiveBuffer(OverlappedTransferBuffer *buffer)
 	{
 		if (error != WSAEWOULDBLOCK && error != 0)
 		{
-			LOG(LogError, "Socket::EnqueueNewReceiveBuffer: %s for overlapped socket %s failed! Error: %s.", IsUDPServerSocket() ? "WSARecvFrom" : "WSARecv", ToString().CString(), Network::GetErrorString(error).CString());
+			LOG(LogError, "Socket::EnqueueNewReceiveBuffer: %s for overlapped socket %s failed! Error: %s.", IsUDPServerSocket() ? "WSARecvFrom" : "WSARecv", ToString().c_str(), Network::GetErrorString(error).c_str());
 
 			// We never close the server socket as a reaction on any error, since an error on one client could shut down
 			// the whole server for all clients. This check is mainly here to ignore the 10054 error (WSAECONNRESET) which
@@ -330,7 +330,7 @@ void Socket::EnqueueNewReceiveBuffer(OverlappedTransferBuffer *buffer)
 			// client-specific errors, we don't explicitly check for the 10054 case only.
 			if (!IsUDPServerSocket())
 			{
-				LOG(LogError, "Socket::EnqueueNewReceiveBuffer: Closing down socket.",  Network::GetErrorString(error).CString());
+				LOG(LogError, "Socket::EnqueueNewReceiveBuffer: Closing down socket.",  Network::GetErrorString(error).c_str());
 				readOpen = false;
 				writeOpen = false;
 				Close();
@@ -376,12 +376,12 @@ size_t Socket::Receive(char *dst, size_t maxBytes, EndPoint *endPoint)
 		{
 			int error = Network::GetLastError();
 			if (error != KNET_EWOULDBLOCK && error != 0)
-				LOG(LogError, "Socket::Receive: recvfrom failed: %s in socket %s", Network::GetErrorString(error).CString(), ToString().CString());
+				LOG(LogError, "Socket::Receive: recvfrom failed: %s in socket %s", Network::GetErrorString(error).c_str(), ToString().c_str());
 
 			return 0;
 		}
 		if (numBytesRead > 0)
-			LOG(LogData, "recvfrom (%d) in socket %s", numBytesRead, ToString().CString());
+			LOG(LogData, "recvfrom (%d) in socket %s", numBytesRead, ToString().c_str());
 
 		if (endPoint)
 			*endPoint = EndPoint::FromSockAddrIn(from);
@@ -400,7 +400,7 @@ size_t Socket::Receive(char *dst, size_t maxBytes, EndPoint *endPoint)
 	}
 	else if (ret == 0)
 	{
-		LOG(LogInfo, "Socket::Receive: Received 0 bytes from network. Read-connection closed to socket %s.", ToString().CString());
+		LOG(LogInfo, "Socket::Receive: Received 0 bytes from network. Read-connection closed to socket %s.", ToString().c_str());
 		readOpen = false;
 		return 0;
 	}
@@ -409,7 +409,7 @@ size_t Socket::Receive(char *dst, size_t maxBytes, EndPoint *endPoint)
 		int error = Network::GetLastError();
 		if (error != KNET_EWOULDBLOCK && error != 0)
 		{
-			LOG(LogError, "Socket::Receive: recv failed in socket %s. Error %s", ToString().CString(), Network::GetErrorString(error).CString());
+			LOG(LogError, "Socket::Receive: recv failed in socket %s. Error %s", ToString().c_str(), Network::GetErrorString(error).c_str());
 
 			// We never close the server socket as a reaction on any error, since an error on one client could shut down
 			// the whole server for all clients. This check is mainly here to ignore the 10054 error (WSAECONNRESET) which
@@ -527,7 +527,7 @@ OverlappedTransferBuffer *Socket::BeginReceive()
 		{
 			DeleteOverlappedTransferBuffer(receivedData);
 			if (readOpen)
-				LOG(LogInfo, "Socket::BeginReceive: Received 0 bytes from the network. Read connection closed in socket %s.", ToString().CString());
+				LOG(LogInfo, "Socket::BeginReceive: Received 0 bytes from the network. Read connection closed in socket %s.", ToString().c_str());
 			readOpen = false;
 			if (IsUDPServerSocket())
 				LOG(LogError, "Socket::BeginReceive: UDP server socket transitioned to readOpen==false!");
@@ -541,7 +541,7 @@ OverlappedTransferBuffer *Socket::BeginReceive()
 		queuedReceiveBuffers.PopFront();
 		DeleteOverlappedTransferBuffer(receivedData);
 		if (readOpen || writeOpen)
-			LOG(LogError, "Socket::BeginReceive: WSAEDISCON. Bidirectionally closing connection in socket %s.", ToString().CString());
+			LOG(LogError, "Socket::BeginReceive: WSAEDISCON. Bidirectionally closing connection in socket %s.", ToString().c_str());
 		if (IsUDPServerSocket())
 			LOG(LogError, "Socket::BeginReceive: Unexpected: Received WSAEDISCON on UDP server socket!");
 		Close();
@@ -552,7 +552,7 @@ OverlappedTransferBuffer *Socket::BeginReceive()
 		queuedReceiveBuffers.PopFront();
 		if (readOpen || writeOpen)
 			if (!(IsUDPServerSocket() && error == 10054)) // If we are running both UDP server and client on localhost, we can receive 10054 (Peer closed connection) on the server side, in which case, we ignore this error print.
-				LOG(LogError, "Socket::BeginReceive: WSAGetOverlappedResult failed with code %d when reading from an overlapped socket! Reason: %s.", error, Network::GetErrorString(error).CString());
+				LOG(LogError, "Socket::BeginReceive: WSAGetOverlappedResult failed with code %d when reading from an overlapped socket! Reason: %s.", error, Network::GetErrorString(error).c_str());
 		DeleteOverlappedTransferBuffer(receivedData);
 		// Mark this socket closed, unless the read error was on a UDP server socket, in which case we must ignore
 		// the read error on this buffer (an error on a single client connection cannot shut down the whole server!)
@@ -610,9 +610,9 @@ void Socket::Disconnect()
 	{
 		int result = shutdown(connectSocket, SD_SEND);
 		if (result == KNET_SOCKET_ERROR)
-			LOG(LogError, "Socket::Disconnect(): TCP socket shutdown(SD_SEND) failed: %s in socket %s.", Network::GetLastErrorString().CString(), ToString().CString());
+			LOG(LogError, "Socket::Disconnect(): TCP socket shutdown(SD_SEND) failed: %s in socket %s.", Network::GetLastErrorString().c_str(), ToString().c_str());
 		else
-			LOG(LogInfo, "Socket::Disconnect(): TCP socket shutdown(SD_SEND) succeeded on socket %s.", ToString().CString());
+			LOG(LogInfo, "Socket::Disconnect(): TCP socket shutdown(SD_SEND) succeeded on socket %s.", ToString().c_str());
 	}
 
 	writeOpen = false;
@@ -626,7 +626,7 @@ void Socket::Close()
 		return;
 	}
 
-	LOG(LogInfo, "Socket::Close(): Closing socket %s.", ToString().CString());
+	LOG(LogInfo, "Socket::Close(): Closing socket %s.", ToString().c_str());
 
 	if (!IsUDPSlaveSocket())
 	{
@@ -636,13 +636,13 @@ void Socket::Close()
 
 		int result = shutdown(connectSocket, SD_BOTH);
 		if (result == KNET_SOCKET_ERROR)
-			LOG(LogError, "Socket::Close(): Socket shutdown(SD_BOTH) failed: %s in socket %s.", Network::GetLastErrorString().CString(), ToString().CString());
+			LOG(LogError, "Socket::Close(): Socket shutdown(SD_BOTH) failed: %s in socket %s.", Network::GetLastErrorString().c_str(), ToString().c_str());
 		else
-			LOG(LogInfo, "Socket::Close(): Socket shutdown(SD_BOTH) succeeded on socket %s.", ToString().CString());
+			LOG(LogInfo, "Socket::Close(): Socket shutdown(SD_BOTH) succeeded on socket %s.", ToString().c_str());
 
 		result = closesocket(connectSocket);
 		if (result == KNET_SOCKET_ERROR)
-			LOG(LogError, "Socket::Close(): closesocket() failed: %s in socket %s.", Network::GetLastErrorString().CString(), ToString().CString());
+			LOG(LogError, "Socket::Close(): closesocket() failed: %s in socket %s.", Network::GetLastErrorString().c_str(), ToString().c_str());
 	}
 
 	connectSocket = INVALID_SOCKET;
@@ -681,7 +681,7 @@ void Socket::SetBlocking(bool isBlocking)
 	u_long nonBlocking = (isBlocking == false) ? 1 : 0;
 #ifdef WIN32
 	if (ioctlsocket(connectSocket, FIONBIO, &nonBlocking))
-		LOG(LogError, "Socket::SetBlocking: ioctlsocket failed with error %s!", Network::GetLastErrorString().CString());
+		LOG(LogError, "Socket::SetBlocking: ioctlsocket failed with error %s!", Network::GetLastErrorString().c_str());
 #else
 	int flags = fcntl(connectSocket, F_GETFL, 0);
 	fcntl(connectSocket, F_SETFL, flags | O_NONBLOCK);
@@ -719,7 +719,7 @@ bool Socket::Send(const char *data, size_t numBytes)
 
 	if (bytesSent == numBytes)
 	{
-		LOG(LogData, "Socket::EndSend: Sent out %d bytes to socket %s.", bytesSent, ToString().CString());
+		LOG(LogData, "Socket::EndSend: Sent out %d bytes to socket %s.", bytesSent, ToString().c_str());
 		return true;
 	}
 	else if (bytesSent > 0) // Managed to send some data, but not all bytes.
@@ -733,7 +733,7 @@ bool Socket::Send(const char *data, size_t numBytes)
 		bool waitSuccess = WaitForSendReady(socketWriteTimeout);
 		if (!waitSuccess)
 		{
-			LOG(LogError, "Socket::EndSend: Warning! Managed to only partially send out %d bytes out of %d bytes in the buffer, and socket did not transition to write-ready in the timeout period. Closing connection.", 
+			LOG(LogError, "Socket::Send: Warning! Managed to only partially send out %d bytes out of %d bytes in the buffer, and socket did not transition to write-ready in the timeout period. Closing connection.", 
 				bytesSent, (int)numBytes);
 			Close();
 			return false;
@@ -749,7 +749,7 @@ bool Socket::Send(const char *data, size_t numBytes)
 
 		if (error != KNET_EWOULDBLOCK)
 		{
-			LOG(LogError, "Socket::EndSend() failed! Error: %s.", Network::GetErrorString(error).CString());
+			LOG(LogError, "Socket::Send() failed! Error: %s.", Network::GetErrorString(error).c_str());
 			if (type == ServerClientSocket && transport == SocketOverUDP)
 			{
 				// UDP client sockets are shared between each client (and by the server socket),
@@ -802,11 +802,13 @@ bool Socket::IsOverlappedSendReady()
 #endif
 }
 
-OverlappedTransferBuffer *Socket::BeginSend()
+OverlappedTransferBuffer *Socket::BeginSend(int maxBytesToSend)
 {
 	if (!writeOpen)
 		return 0;
 
+	// See if the oldest one of the previously submitted transfers has now finished,
+	// and reuse that buffer without allocating a new one, if so.
 #ifdef WIN32
 	if (queuedSendBuffers.Size() > 0)
 	{
@@ -818,13 +820,26 @@ OverlappedTransferBuffer *Socket::BeginSend()
 		if (ret == TRUE)
 		{
 			queuedSendBuffers.PopFront();
-			sentData->buffer.len = maxSendSize; // This is the number of bytes that the client is allowed to fill.
-			return sentData;
+
+            // If the buffer we pulled off was too small, free it and allocate a new one which is of the desired size.
+            if (sentData->bytesAllocated < maxBytesToSend)
+            {
+                DeleteOverlappedTransferBuffer(sentData);
+	            return AllocateOverlappedTransferBuffer(maxBytesToSend); ///\todo In debug mode - track this pointer.
+            }
+            else
+            {
+                // The existing transfer buffer is large enough. Prepare it for reuse and pass back to caller.
+			    sentData->buffer.len = sentData->bytesAllocated; // This is the number of bytes that the client is allowed to fill.
+			    sentData->bytesContains = 0; // No bytes currently in use.
+
+			    return sentData;
+            }
 		}
 		if (ret == FALSE && error != WSA_IO_INCOMPLETE)
 		{
 			LOG(LogError, "Socket::BeginSend: WSAGetOverlappedResult failed with an error %s, code %d != WSA_IO_INCOMPLETE!", 
-				Network::GetErrorString(error).CString(), error);
+				Network::GetErrorString(error).c_str(), error);
 			writeOpen = false;
 			return 0;
 		}
@@ -834,8 +849,8 @@ OverlappedTransferBuffer *Socket::BeginSend()
 		return 0;
 #endif
 
-	OverlappedTransferBuffer *transfer = AllocateOverlappedTransferBuffer(maxSendSize);
-	return transfer; ///\todo In debug mode - track this pointer.
+	// No previous send buffer has finished from use (or not using overlapped transfers) - allocate a new buffer.
+	return AllocateOverlappedTransferBuffer(maxBytesToSend);
 }
 
 bool Socket::EndSend(OverlappedTransferBuffer *sendBuffer)
@@ -844,6 +859,10 @@ bool Socket::EndSend(OverlappedTransferBuffer *sendBuffer)
 	if (!sendBuffer)
 		return false;
 
+	// For the purposes of this send, mark the allocated length of the send buffer equal to the 
+	// number of bytes the user had filled into the buffer.
+	sendBuffer->buffer.len = sendBuffer->bytesContains;
+
 #ifdef WIN32
 	// Clear the event flag so that the completion of WSASend can trigger this and signal us.
 	WSAResetEvent(sendBuffer->overlapped.hEvent);
@@ -862,7 +881,7 @@ bool Socket::EndSend(OverlappedTransferBuffer *sendBuffer)
 	{
 		if (error != KNET_EWOULDBLOCK)
 		{
-			LOG(LogError, "Socket::EndSend() failed! Error: %s.", Network::GetErrorString(error).CString());
+			LOG(LogError, "Socket::EndSend() failed! Error: %s.", Network::GetErrorString(error).c_str());
 			if (!IsUDPServerSocket())
 				writeOpen = false;
 		}
@@ -915,7 +934,7 @@ void Socket::AbortSend(OverlappedTransferBuffer *send)
 #endif
 }
 
-String Socket::ToString() const
+std::string Socket::ToString() const
 {
 	sockaddr_in addr;
 	socklen_t namelen = sizeof(addr);
@@ -930,11 +949,11 @@ String Socket::ToString() const
 		DestinationAddress(), (int)DestinationPort(), 
 		(transport == SocketOverTCP) ? "TCP" : (IsUDPServerSocket() ? "UDP server" : (IsUDPSlaveSocket() ? "UDP Slave" : "UDP")), 
 		Connected() ? "true" : "false", readOpen ? "true" : "false", writeOpen ? "true" : "false",
-		(int)maxSendSize, sockRet == 0 ? sockName.ToString().CString() : "(-)", 
-		peerRet == 0 ? peerName.ToString().CString() : "(-)", (int)connectSocket,
+		(int)maxSendSize, sockRet == 0 ? sockName.ToString().c_str() : "(-)", 
+		peerRet == 0 ? peerName.ToString().c_str() : "(-)", (int)connectSocket,
 		this);
 
-	return String(str);
+	return std::string(str);
 }
 
 void Socket::SetNaglesAlgorithmEnabled(bool enabled)
@@ -959,7 +978,7 @@ void Socket::SetNaglesAlgorithmEnabled(bool enabled)
 #endif
 	if (ret != 0)
 		LOG(LogError, "Setting TCP_NODELAY=%s for socket %d failed. Reason: %s.",
-			enabled ? "true" : "false", (int)connectSocket, Network::GetLastErrorString().CString());
+			enabled ? "true" : "false", (int)connectSocket, Network::GetLastErrorString().c_str());
 }
 
 } // ~kNet

+ 39 - 30
ThirdParty/kNet/src/TCPMessageConnection.cpp

@@ -15,8 +15,6 @@
 /** @file TCPMessageConnection.cpp
 	@brief */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include <sstream>
 
 #ifdef KNET_USE_BOOST
@@ -38,7 +36,7 @@ namespace kNet
 
 /// The maximum size for a TCP message we will allow to be received. If we receive a message larger than this, we consider
 /// it as a protocol violation and kill the connection.
-static const u32 cMaxReceivableTCPMessageSize = 1024 * 1024;
+static const u32 cMaxReceivableTCPMessageSize = 10 * 1024 * 1024; ///\todo Make this configurable for the connection.
 
 TCPMessageConnection::TCPMessageConnection(Network *owner, NetworkServer *ownerServer, Socket *socket, ConnectionState startingState)
 :MessageConnection(owner, ownerServer, socket, startingState),
@@ -112,7 +110,7 @@ MessageConnection::SocketReadResult TCPMessageConnection::ReadSocket(size_t &tot
 		}
 
 		LOG(LogData, "TCPMessageConnection::ReadSocket: Received %d bytes from the network from peer %s.", 
-			buffer->bytesContains, socket->ToString().CString());
+			buffer->bytesContains, socket->ToString().c_str());
 
 		assert((size_t)buffer->bytesContains <= (size_t)tcpInboundSocketData.ContiguousFreeBytesLeft());
 		///\todo For performance, this memcpy can be optimized away. We can parse the message directly
@@ -148,7 +146,7 @@ MessageConnection::SocketReadResult TCPMessageConnection::ReadSocket(size_t &tot
 /// Warning: This is a non-threadsafe check for the container, only to be used for debugging.
 /// Warning #2: This function is very slow, as it performs a N^2 search through the container.
 template<typename T>
-bool ContainerUniqueAndNoNullElements(const Vector<T> &cont)
+bool ContainerUniqueAndNoNullElements(const std::vector<T> &cont)
 {
 	for(size_t i = 0; i < cont.size(); ++i)
 		for(size_t j = i+1; j < cont.size(); ++j)
@@ -176,25 +174,22 @@ MessageConnection::PacketSendResult TCPMessageConnection::SendOutPacket()
 		return PacketSendSocketClosed;
 	}
 
+    // 'serializedMessages' is a temporary data structure used only by this member function.
+    // It caches a list of all the messages we are pushing out during this call.
+	serializedMessages.clear();
+
 	// In the following, we start coalescing multiple messages into a single socket send() calls.
 	// Get the maximum number of bytes we can coalesce for the send() call. This is only a soft limit
 	// in the sense that if we encounter a single message that is larger than this limit, then we try
 	// to send that through in one send() call.
-	const size_t maxSendSize = socket->MaxSendSize();
+//	const size_t maxSendSize = socket->MaxSendSize();
 
 	// Push out all the pending data to the socket.
-//	assert(ContainerUniqueAndNoNullElements(serializedMessages));
-//	assert(ContainerUniqueAndNoNullElements(outboundQueue));
-	serializedMessages.Clear(); // 'serializedMessages' is a temporary data structure used only by this member function.
-	OverlappedTransferBuffer *overlappedTransfer = socket->BeginSend();
-	if (!overlappedTransfer)
-	{
-		LOG(LogError, "TCPMessageConnection::SendOutPacket: Starting an overlapped send failed!");
-		return PacketSendSocketClosed;
-	}
+	OverlappedTransferBuffer *overlappedTransfer = 0;
 
 	int numMessagesPacked = 0;
-	DataSerializer writer(overlappedTransfer->buffer.buf, overlappedTransfer->buffer.len);
+	DataSerializer writer;
+//	assert(ContainerUniqueAndNoNullElements(outboundQueue)); // This precondition should always hold (but very heavy to test, uncomment to debug)
 	while(outboundQueue.Size() > 0)
 	{
 #ifdef KNET_NO_MAXHEAP
@@ -210,12 +205,26 @@ MessageConnection::PacketSendResult TCPMessageConnection::SendOutPacket()
 			outboundQueue.PopFront();
 			continue;
 		}
+
 		const int encodedMsgIdLength = VLE8_16_32::GetEncodedBitLength(msg->id) / 8;
 		const size_t messageContentSize = msg->dataSize + encodedMsgIdLength; // 1 byte: Message ID. X bytes: Content.
 		const int encodedMsgSizeLength = VLE8_16_32::GetEncodedBitLength(messageContentSize) / 8;
 		const size_t totalMessageSize = messageContentSize + encodedMsgSizeLength; // 2 bytes: Content length. X bytes: Content.
-		// If this message won't fit into the buffer, send out all previously gathered messages (except if there were none, then try to get the big message through).
-		if (writer.BytesFilled() + totalMessageSize >= maxSendSize && numMessagesPacked > 0)
+
+        if (!overlappedTransfer)
+        {
+            overlappedTransfer = socket->BeginSend(std::max<size_t>(socket->MaxSendSize(), totalMessageSize));
+	        if (!overlappedTransfer)
+	        {
+		        LOG(LogError, "TCPMessageConnection::SendOutPacket: Starting an overlapped send failed!");
+                assert(serializedMessages.size() == 0);
+		        return PacketSendSocketClosed;
+	        }
+            writer = DataSerializer(overlappedTransfer->buffer.buf, overlappedTransfer->buffer.len);
+        }
+
+		// If this message won't fit into the buffer, send out all previously gathered messages.
+        if (writer.BytesLeft() < totalMessageSize)
 			break;
 
 		writer.AddVLE<VLE8_16_32>(messageContentSize);
@@ -224,7 +233,7 @@ MessageConnection::PacketSendResult TCPMessageConnection::SendOutPacket()
 			writer.AddAlignedByteArray(msg->data, msg->dataSize);
 		++numMessagesPacked;
 
-		serializedMessages.Push(msg);
+		serializedMessages.push_back(msg);
 #ifdef KNET_NO_MAXHEAP
 		assert(*outboundQueue.Front() == msg);
 #else
@@ -232,17 +241,17 @@ MessageConnection::PacketSendResult TCPMessageConnection::SendOutPacket()
 #endif
 		outboundQueue.PopFront();
 	}
-//	assert(ContainerUniqueAndNoNullElements(serializedMessages));
+//	assert(ContainerUniqueAndNoNullElements(serializedMessages)); // This precondition should always hold (but very heavy to test, uncomment to debug)
 
 	if (writer.BytesFilled() == 0 && outboundQueue.Size() > 0)
-		LOG(LogError, "Failed to send any messages to socket %s! (Probably next message was too big to fit in the buffer).", socket->ToString().CString());
+		LOG(LogError, "Failed to send any messages to socket %s! (Probably next message was too big to fit in the buffer).", socket->ToString().c_str());
 
-	overlappedTransfer->buffer.len = writer.BytesFilled();
+	overlappedTransfer->bytesContains = writer.BytesFilled();
 	bool success = socket->EndSend(overlappedTransfer);
 
 	if (!success) // If we failed to send, put all the messages back into the outbound queue to wait for the next send round.
 	{
-		for(size_t i = 0; i < serializedMessages.Size(); ++i)
+		for(size_t i = 0; i < serializedMessages.size(); ++i)
 #ifdef KNET_NO_MAXHEAP
 			outboundQueue.InsertWithResize(serializedMessages[i]);
 #else
@@ -255,21 +264,21 @@ MessageConnection::PacketSendResult TCPMessageConnection::SendOutPacket()
 		return PacketSendSocketFull;
 	}
 
-	LOG(LogData, "TCPMessageConnection::SendOutPacket: Sent %d bytes (%d messages) to peer %s.", (int)writer.BytesFilled(), (int)serializedMessages.Size(), socket->ToString().CString());
+	LOG(LogData, "TCPMessageConnection::SendOutPacket: Sent %d bytes (%d messages) to peer %s.", (int)writer.BytesFilled(), (int)serializedMessages.size(), socket->ToString().c_str());
 	AddOutboundStats(writer.BytesFilled(), 1, numMessagesPacked);
 	ADDEVENT("tcpDataOut", (float)writer.BytesFilled(), "bytes");
 
 	// The messages in serializedMessages array are now in the TCP driver to handle. It will guarantee
 	// delivery if possible, so we can free the messages already.
-	for(size_t i = 0; i < serializedMessages.Size(); ++i)
+	for(size_t i = 0; i < serializedMessages.size(); ++i)
 	{
 #ifdef KNET_NETWORK_PROFILING
-		String str;
-		if (!serializedMessages[i]->profilerName.Empty())
-			str += "messageOut." + serializedMessages[i]->profilerName;
+		std::stringstream ss;
+		if (!serializedMessages[i]->profilerName.empty())
+			ss << "messageOut." << serializedMessages[i]->profilerName;
 		else
-			str += "messageOut." + String((unsigned)serializedMessages[i]->id);
-		ADDEVENT(str.CString(), (float)serializedMessages[i]->Size(), "bytes");
+			ss << "messageOut." << serializedMessages[i]->id;
+		ADDEVENT(ss.str().c_str(), (float)serializedMessages[i]->Size(), "bytes");
 #endif
 		ClearOutboundMessageWithContentID(serializedMessages[i]);
 		FreeMessage(serializedMessages[i]);

+ 8 - 4
ThirdParty/kNet/src/Thread.cpp

@@ -15,8 +15,6 @@
 /** @file Thread.cpp
 	@brief Implements platform-generic Thread functions. */
 
-// Modified by Lasse Öörni for Urho3D
-
 #ifdef KNET_USE_BOOST
 #include <boost/thread/thread.hpp>
 #endif
@@ -32,9 +30,11 @@
 namespace kNet
 {
 
-String ThreadIdToString(const ThreadId &id)
+std::string ThreadIdToString(const ThreadId &id)
 {
-	return String((unsigned)id);
+	std::stringstream ss;
+	ss << id;
+	return ss.str();
 }
 
 /// Suspends the thread until 'Resume()' is called. Call this function from the main thread.
@@ -102,6 +102,7 @@ typedef struct tagTHREADNAME_INFO
 
 void SetThreadName(DWORD dwThreadID, const char *threadName)
 {
+#ifdef _MSC_VER
 	THREADNAME_INFO info;
 	info.dwType = 0x1000;
 	info.szName = threadName;
@@ -116,6 +117,9 @@ void SetThreadName(DWORD dwThreadID, const char *threadName)
 	__except(EXCEPTION_CONTINUE_EXECUTION)
 	{
 	}
+#else
+#warning SetThreadName undefined for current platform!
+#endif
 }
 #endif
 

+ 257 - 155
ThirdParty/kNet/src/UDPMessageConnection.cpp

@@ -16,8 +16,6 @@
 	@brief Implements the UDP-specific code of MessageConnection.
 	\todo Flow control currently disabled since testing out the performance of UDT. */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include <cmath>
 #include <cstdio>
 #include <sstream>
@@ -44,25 +42,26 @@ using namespace std;
 namespace kNet
 {
 
+static const int initialDatagramRatePerSecond = 30;
 /// The maximum time to wait before acking a packet. If there are enough packets to ack for a full ack message,
 /// acking will be performed earlier. (milliseconds)
 static const float maxAckDelay = 33.f; // (1/30th of a second)
+/// The time counter after which an unacked reliable message will be resent. (UDP only)
+static const float timeOutMilliseconds = 2000.f;//750.f;
 /// The maximum number of datagrams to read in from the socket at one go - after this reads will be throttled
 /// to give time for data sending as well.
 static const int cMaxDatagramsToReadInOneFrame = 2048;
 
-/// Minimum retransmission timeout value (milliseconds)
-static const float minRTOTimeoutValue = 500.f;
-/// Maximum retransmission timeout value (milliseconds)
-static const float maxRTOTimeoutValue = 5000.f;
+static const u32 cMaxUDPMessageFragmentSize = 470;
 
 UDPMessageConnection::UDPMessageConnection(Network *owner, NetworkServer *ownerServer, Socket *socket, ConnectionState startingState)
 :MessageConnection(owner, ownerServer, socket, startingState),
-retransmissionTimeout(1000.f), numAcksLastFrame(0), numLossesLastFrame(0), smoothedRTT(1000.f), rttVariation(0.f), rttCleared(true), // Set RTT initial values as per RFC 2988.
+retransmissionTimeout(3.f), numAcksLastFrame(0), numLossesLastFrame(0), smoothedRTT(3.f), rttVariation(0.f), rttCleared(true), // Set RTT initial values as per RFC 2988.
 lastReceivedInOrderPacketID(0), 
 lastSentInOrderPacketID(0), datagramPacketIDCounter(1),
-packetLossRate(0.f), packetLossCount(0.f), 
-datagramSendRate(50.f), lowestDatagramSendRateOnPacketLoss(50.f), slowModeDelay(0),
+packetLossRate(0.f), packetLossCount(0.f), datagramOutRatePerSecond(initialDatagramRatePerSecond), 
+datagramInRatePerSecond(initialDatagramRatePerSecond),
+datagramSendRate(70),
 receivedPacketIDs(64 * 1024), outboundPacketAckTrack(1024),
 previousReceivedPacketID(0), queuedInboundDatagrams(128)
 {
@@ -140,7 +139,7 @@ UDPMessageConnection::SocketReadResult UDPMessageConnection::ReadSocket(size_t &
 	{
 		connectionState = ConnectionOK;
 		LOG(LogUser, "UDPMessageConnection::ReadSocket: Received data from socket %s. Transitioned from ConnectionPending to ConnectionOK state.", 
-			(socket ? socket->ToString().CString() : "(null)"));
+			(socket ? socket->ToString().c_str() : "(null)"));
 	}
 	if (readResult == SocketReadError)
 		return SocketReadError;
@@ -156,10 +155,10 @@ void UDPMessageConnection::PerformPacketAckSends()
 	AssertInWorkerThreadContext();
 
 	tick_t now = Clock::Tick();
-	while(inboundPacketAckTrack.Size() > 0)
+	while(inboundPacketAckTrack.size() > 0)
 	{
-		if (Clock::TimespanToMillisecondsF(inboundPacketAckTrack.Begin()->second_.sentTick, now) < maxAckDelay &&
-			inboundPacketAckTrack.Size() < 33)
+		if (Clock::TimespanToMillisecondsF(inboundPacketAckTrack.begin()->second.sentTick, now) < maxAckDelay &&
+			inboundPacketAckTrack.size() < 33)
 			break;
 
 		SendPacketAckMessage();
@@ -256,67 +255,42 @@ void UDPMessageConnection::HandleFlowControl()
 	AssertInWorkerThreadContext();
 
 	// In packets/second.
-	const float minBandwidthOnLoss = 10.f;
-	const float minBandwidth = 50.f;
-	const float maxBandwidth = 10000.f;
-	const int framesPerSec = 10;
-	const int maxSlowModeDelay = 10 * framesPerSec;
-
-	const tick_t frameLength = Clock::TicksPerSec() / framesPerSec; // in ticks
-	const tick_t now = Clock::Tick();
+	const float totalEstimatedBandwidth = 50; ///\todo Make this estimation dynamic as in UDT or similar.
+	const float additiveIncreaseAggressiveness = 5e-2f;
 
-	unsigned long numFrames = (unsigned long)(Clock::TicksInBetween(now, lastFrameTime) / frameLength);
-	if (numFrames > 0)
+	const tick_t frameLength = Clock::TicksPerSec() / 100; // in ticks
+	// Additively increase the outbound send rate.
+	unsigned long numFrames = (unsigned long)(Clock::TicksInBetween(Clock::Tick(), lastFrameTime) / frameLength);
+	if (/*numAcksLastFrame > 0 &&*/ numFrames > 0)
 	{
-		if (numFrames >= framesPerSec)
-			numFrames = framesPerSec;
-
-		int numUnacked = NumOutboundUnackedDatagrams();
+		if (numFrames >= 100)
+			numFrames = 100;
 
-		// Reduce sendrate on significant loss
-		if (numLossesLastFrame > 2)
+		if (numLossesLastFrame > 5) // Do not respond to a random single packet losses.
 		{
 			float oldRate = datagramSendRate;
-			datagramSendRate = min(datagramSendRate, max(minBandwidthOnLoss, lowestDatagramSendRateOnPacketLoss * 0.9f)); // Multiplicative decreases.
+			datagramSendRate = min(datagramSendRate, max(1.f, lowestDatagramSendRateOnPacketLoss * 0.9f)); // Multiplicative decreases.
+//			datagramSendRate = max(1.f, datagramSendRate * 0.9f); // Multiplicative decreases.
 			LOG(LogVerbose, "Received %d losses. datagramSendRate backed to %.2f from %.2f", (int)numLossesLastFrame, datagramSendRate, oldRate);
 		}
-		else
+		else // Additive increases.
 		{
-			// Check if more or less bandwidth is needed
-			///\todo Very simple logic for now, can be improved
-			bool needMore = outboundQueue.Size() > 10;
-			bool needLess = outboundQueue.Size() == 0;
-			float maxRTT = max(rtt, smoothedRTT);
-
-			// Need more: increase sendrate. Factor in RTT and acks
-			if (needMore && numLossesLastFrame == 0)
-			{
-				float delta = (50.f + 2.f * numAcksLastFrame) / maxRTT;
-				if (slowModeDelay > 0)
-					delta *= 0.2f;
-				datagramSendRate = min(datagramSendRate + delta, maxBandwidth);
-				lowestDatagramSendRateOnPacketLoss = datagramSendRate;
-			}
-			// Need less: decrease sendrate if not already at minimum
-			else if (needLess && datagramSendRate > minBandwidth)
-				datagramSendRate = max(datagramSendRate * 0.98f, minBandwidth);
-
-			// Whenever slow mode or slight loss is occurring and RTT is more than the minimum RTO value, back off slowly
-			// This is to ensure we do not stay "balanced" in a state where slight loss occurs constantly due to sending too much
-			if ((numLossesLastFrame > 0 || slowModeDelay > 0) && maxRTT > minRTOTimeoutValue && datagramSendRate > minBandwidth)
-				datagramSendRate = max(datagramSendRate * 0.999f, minBandwidth);
+			float increment = min((float)numFrames * additiveIncreaseAggressiveness * (totalEstimatedBandwidth - datagramSendRate), 1.f);
+			datagramSendRate += increment;
+			datagramSendRate = min(datagramSendRate, totalEstimatedBandwidth);
+			lowestDatagramSendRateOnPacketLoss = datagramSendRate;
+//			LOG(LogVerbose, "Incremented sendRate by %.2f to %.2f", increment, datagramSendRate);
 		}
-
-		// Update the slow mode timer
-		if (numLossesLastFrame > 1)
-			slowModeDelay = min(slowModeDelay + numLossesLastFrame * framesPerSec, maxSlowModeDelay);
-		else if (slowModeDelay > 0)
-			--slowModeDelay;
-
 		numAcksLastFrame = 0;
 		numLossesLastFrame = 0;
-		lastFrameTime = now;
+		if (numFrames < 100)
+			lastFrameTime += numFrames * frameLength;
+		else
+			lastFrameTime = Clock::Tick();
 	}
+
+	// Do a fixed flow control for testing.
+	datagramSendRate = 1000; ///\todo Remove.
 }
 
 void UDPMessageConnection::SendOutPackets()
@@ -361,7 +335,7 @@ MessageConnection::PacketSendResult UDPMessageConnection::SendOutPacket()
 	if (!CanSendOutNewDatagram())
 		return PacketSendThrottled;
 
-	OverlappedTransferBuffer *data = socket->BeginSend();
+    OverlappedTransferBuffer *data = socket->BeginSend(socket->MaxSendSize());
 	if (!data)
 		return PacketSendThrottled;
 
@@ -369,7 +343,7 @@ MessageConnection::PacketSendResult UDPMessageConnection::SendOutPacket()
 	const size_t maxSendSize = socket->MaxSendSize();
 
 	// Push out all the pending data to the socket.
-	datagramSerializedMessages.Clear();
+	datagramSerializedMessages.clear();
 
 	// If true, the receiver needs to Ack the packet we are now crafting.
 	bool reliable = false;
@@ -381,7 +355,7 @@ MessageConnection::PacketSendResult UDPMessageConnection::SendOutPacket()
 
 	unsigned long smallestReliableMessageNumber = 0xFFFFFFFF;
 
-	skippedMessages.Clear();
+	skippedMessages.clear();
 
 	// Fill up the rest of the packet from messages from the outbound queue.
 	while(outboundQueue.Size() > 0)
@@ -413,7 +387,7 @@ MessageConnection::PacketSendResult UDPMessageConnection::SendOutPacket()
 				{
 					LOG(LogError, "Throttling fragmented transfer send! No free TransferID to start a new fragmented transfer with!");
 					outboundQueue.PopFront();
-					skippedMessages.Push(msg);
+					skippedMessages.push_back(msg);
 					continue;
 				}
 			}
@@ -425,13 +399,13 @@ MessageConnection::PacketSendResult UDPMessageConnection::SendOutPacket()
 		int totalMessageSize = msg->GetTotalDatagramPackedSize();// + ((msg->inOrder && !inOrder) ? cBytesForInOrderDeltaCounter : 0);
 
 		// If this message won't fit into the buffer, send out all the previously gathered messages (there must at least be one previously submitted message).		
-		if (datagramSerializedMessages.Size() > 0 && (size_t)packetSizeInBytes + totalMessageSize >= maxSendSize)
+		if (datagramSerializedMessages.size() > 0 && (size_t)packetSizeInBytes + totalMessageSize >= maxSendSize)
 			break;
 
 		if (totalMessageSize > (int)maxSendSize)
 			LOG(LogError, "Warning: Sending out a message of ID %d and size %d bytes, but UDP socket max send size is only %d bytes!", (int)msg->id, totalMessageSize, (int)maxSendSize);
 
-		datagramSerializedMessages.Push(msg);
+		datagramSerializedMessages.push_back(msg);
 		outboundQueue.PopFront();
 
 		packetSizeInBytes += totalMessageSize;
@@ -447,7 +421,7 @@ MessageConnection::PacketSendResult UDPMessageConnection::SendOutPacket()
 	}
 
 	// Ensure that the range of the message numbers is within the capacity that the protocol can represent in the byte stream.
-	for(size_t i = 0; i < datagramSerializedMessages.Size(); ++i)
+	for(size_t i = 0; i < datagramSerializedMessages.size(); ++i)
 		if (datagramSerializedMessages[i]->reliable)
 		{
 			u32 reliableDelta = (u32)(datagramSerializedMessages[i]->reliableMessageNumber - smallestReliableMessageNumber);
@@ -455,15 +429,15 @@ MessageConnection::PacketSendResult UDPMessageConnection::SendOutPacket()
 			{                                      // they will have to be serialized in separate datagrams.
 				LOG(LogError, "UDPMessageConnection::SendOutPacket: Too large msgnum delta present - skipping serialization of message with ID %d (lowest: %d, delta: %d)",
 					(int)datagramSerializedMessages[i]->reliableMessageNumber, (int)smallestReliableMessageNumber, (int)reliableDelta);
-				skippedMessages.Push(datagramSerializedMessages[i]);
-				datagramSerializedMessages.Erase(datagramSerializedMessages.Begin() + i);
+				skippedMessages.push_back(datagramSerializedMessages[i]);
+				datagramSerializedMessages.erase(datagramSerializedMessages.begin() + i);
 				--i;
 			}
 		}
 
 	// If we had skipped any messages from the outbound queue while looking for good messages to send, put all the messages
 	// we skipped back to the outbound queue to wait to be processed during subsequent frames.
-	for(size_t i = 0; i < skippedMessages.Size(); ++i)
+	for(size_t i = 0; i < skippedMessages.size(); ++i)
 #ifdef KNET_NO_MAXHEAP
 		outboundQueue.InsertWithResize(skippedMessages[i]);
 #else
@@ -486,7 +460,7 @@ MessageConnection::PacketSendResult UDPMessageConnection::SendOutPacket()
 	bool sentDisconnectAckMessage = false;
 
 	// Write all the messages in this UDP packet.
-	for(size_t i = 0; i < datagramSerializedMessages.Size(); ++i)
+	for(size_t i = 0; i < datagramSerializedMessages.size(); ++i)
 	{
 		NetworkMessage *msg = datagramSerializedMessages[i];
 		assert(!msg->transfer || msg->transfer->id != -1);
@@ -522,36 +496,52 @@ MessageConnection::PacketSendResult UDPMessageConnection::SendOutPacket()
 		if (msg->transfer == 0 || msg->fragmentIndex == 0)
 			writer.AddVLE<VLE8_16_32>(msg->id); // Add the message ID number.
 		if (msg->dataSize > 0) // Add the actual message payload data.
+		{
+			if (networkSendSimulator.enabled && 
+				(networkSendSimulator.corruptionType == NetworkSimulator::CorruptPayload ||
+				(networkSendSimulator.corruptionType == NetworkSimulator::CorruptMessageType &&
+				 msg->id == networkSendSimulator.corruptMessageId)))
+				 networkSendSimulator.MaybeCorruptBufferToggleBits(msg->data, msg->dataSize);
 			writer.AddAlignedByteArray(msg->data, msg->dataSize);
+		}
 	}
 
 	// Send the crafted packet out to the socket.
-	data->buffer.len = writer.BytesFilled();
-	bool success = socket->EndSend(data);
+	data->bytesContains = writer.BytesFilled();
+	bool success;
+
+	if (!networkSendSimulator.enabled)
+		success = socket->EndSend(data); // Send the data out.
+	else
+	{
+		// We're running a network simulator. Pass the buffer to networkSendSimulator for delayed sending.
+		networkSendSimulator.SubmitSendBuffer(data, socket);
+		success = true; // Act here as if we succeeded.
+	}
 
 	if (!success)
 	{
 		// We failed, so put all messages back to the outbound queue, except for those that are from old in-order packet,
 		// since they need to be resent with the old packet ID and not as fresh messages.
-		for(size_t i = 0; i < datagramSerializedMessages.Size(); ++i)
+		for(size_t i = 0; i < datagramSerializedMessages.size(); ++i)
 			outboundQueue.Insert(datagramSerializedMessages[i]);
 
-		LOG(LogError, "UDPMessageConnection::SendOutPacket: Socket::EndSend failed to socket %s!", socket->ToString().CString());
+		LOG(LogError, "UDPMessageConnection::SendOutPacket: Socket::EndSend failed to socket %s!", socket->ToString().c_str());
 		return PacketSendSocketFull;
 	}
 
 	// Sending the datagram succeeded - increment the send count of each message by one, to remember the retry timeout count.
-	for(size_t i = 0; i < datagramSerializedMessages.Size(); ++i)
+	for(size_t i = 0; i < datagramSerializedMessages.size(); ++i)
 	{
 		++datagramSerializedMessages[i]->sendCount;
 
 #ifdef KNET_NETWORK_PROFILING
-		String str;
-		if (!datagramSerializedMessages[i]->profilerName.Empty())
-			str += "messageOut." + datagramSerializedMessages[i]->profilerName;
+		std::stringstream ss;
+		if (!datagramSerializedMessages[i]->profilerName.empty())
+			ss << "messageOut." << datagramSerializedMessages[i]->profilerName;
 		else
-			str += "messageOut." + String((unsigned)datagramSerializedMessages[i]->id);
-		ADDEVENT(str.CString(), (float)datagramSerializedMessages[i]->Size(), "bytes");
+			ss << "messageOut." << datagramSerializedMessages[i]->id;
+		ADDEVENT(ss.str().c_str(), (float)datagramSerializedMessages[i]->Size(), "bytes");
 		if (datagramSerializedMessages[i]->transfer)
 		{
 			if (datagramSerializedMessages[i]->fragmentIndex > 0)
@@ -570,7 +560,7 @@ MessageConnection::PacketSendResult UDPMessageConnection::SendOutPacket()
 	lastSentInOrderPacketID = datagramPacketIDCounter;
 	datagramPacketIDCounter = AddPacketID(datagramPacketIDCounter, 1);
 
-	AddOutboundStats(writer.BytesFilled(), 1, datagramSerializedMessages.Size());
+	AddOutboundStats(writer.BytesFilled(), 1, datagramSerializedMessages.size());
 	ADDEVENT("datagramOut", (float)writer.BytesFilled(), "bytes");
 
 	if (reliable)
@@ -582,10 +572,11 @@ MessageConnection::PacketSendResult UDPMessageConnection::SendOutPacket()
 		const tick_t now = Clock::Tick();
 		ack.sendCount = 1;
 		ack.sentTick = now;
+		retransmissionTimeout = 5000.f; ///\todo Remove this.
 		ack.timeoutTick = now + (tick_t)((double)retransmissionTimeout * Clock::TicksPerMillisecond());
 		ack.datagramSendRate = datagramSendRate;
 
-		for(size_t i = 0; i < datagramSerializedMessages.Size(); ++i)
+		for(size_t i = 0; i < datagramSerializedMessages.size(); ++i)
 		{
 			if (datagramSerializedMessages[i]->reliable)
 				ack.messages.push_back(datagramSerializedMessages[i]); // The ownership of these messages is transferred into this struct.
@@ -600,7 +591,7 @@ MessageConnection::PacketSendResult UDPMessageConnection::SendOutPacket()
 	else // We sent an unreliable datagram.
 	{
 		// This is send-and-forget, we can free all the message data we just sent.
-		for(size_t i = 0; i < datagramSerializedMessages.Size(); ++i)
+		for(size_t i = 0; i < datagramSerializedMessages.size(); ++i)
 		{
 			ClearOutboundMessageWithContentID(datagramSerializedMessages[i]);
 			FreeMessage(datagramSerializedMessages[i]);
@@ -617,7 +608,7 @@ MessageConnection::PacketSendResult UDPMessageConnection::SendOutPacket()
 			connectionState = ConnectionClosed;
 		if (socket)
 			socket->MarkWriteClosed();
-		LOG(LogInfo, "UDPMessageConnection::SendOutPacket: Send Disconnect from connection %s.", ToString().CString());
+		LOG(LogInfo, "UDPMessageConnection::SendOutPacket: Send Disconnect from connection %s.", ToString().c_str());
 	}
 	// If we sent out the DisconnectAck message, we can tear down the connection right now - we're finished.
 	if (sentDisconnectAckMessage)
@@ -628,7 +619,7 @@ MessageConnection::PacketSendResult UDPMessageConnection::SendOutPacket()
 			socket->MarkWriteClosed();
 		}
 		connectionState = ConnectionClosed;
-		LOG(LogInfo, "UDPMessageConnection::SendOutPacket: Send DisconnectAck from connection %s.", ToString().CString());
+		LOG(LogInfo, "UDPMessageConnection::SendOutPacket: Send DisconnectAck from connection %s.", ToString().c_str());
 	}
 
 	LOG(LogVerbose, "UDPMessageConnection::SendOutPacket: Socket::EndSend succeeded with %d bytes.", (int)writer.BytesFilled());
@@ -662,17 +653,28 @@ void UDPMessageConnection::DoUpdateConnection()
 
 		udpUpdateTimer.StartMSecs(10.f);
 	}
+
+/*
+	if (statsUpdateTimer.TriggeredOrNotRunning())
+	{
+		///\todo Put this behind a timer - update only once every 1 sec or so.
+		ComputePacketLoss();
+		statsUpdateTimer.StartMSecs(1000.f);
+	}
+*/
 }
 
 unsigned long UDPMessageConnection::TimeUntilCanSendPacket() const
 {
-	tick_t now = Clock::Tick();
+	const tick_t now = Clock::Tick();
 
+	// The interval at which we send out datagrams.
 	const tick_t datagramSendTickDelay = (tick_t)(Clock::TicksPerSec() / datagramSendRate);
-	tick_t nextDatagramSendTime = lastDatagramSendTime + datagramSendTickDelay;
-	
+
+	const tick_t nextDatagramSendTime = lastDatagramSendTime + datagramSendTickDelay;
+
 	if (Clock::IsNewer(now, nextDatagramSendTime))
-		return 0;
+		return 0; // We are already due to send out the next datagram?
 
 	return (unsigned long)Clock::TimespanToMillisecondsF(now, nextDatagramSendTime);
 }
@@ -693,7 +695,7 @@ void UDPMessageConnection::AddReceivedPacketIDStats(packet_id_t packetID)
 //	if (packetID == 0)
 //		cs.recvPacketIDs.clear();
 
-	cs.recvPacketIDs.Push(ConnectionStatistics::DatagramIDTrack());
+	cs.recvPacketIDs.push_back(ConnectionStatistics::DatagramIDTrack());
 	ConnectionStatistics::DatagramIDTrack &t = cs.recvPacketIDs.back();
 	t.tick = Clock::Tick();
 	t.packetID = packetID;
@@ -754,10 +756,11 @@ void UDPMessageConnection::ExtractMessages(const char *data, size_t numBytes)
 	}
 
 	// Note that this check must be after the ack check (above), since we still need to ack the new packet as well (our
-	// previous ack might not have reached the sender or was delayed, which is why he's resending it).
+	// previous ack might not have reached the sender or was delayed, which is why the peer is resending it).
 	if (HaveReceivedPacketID(packetID))
 	{
 		ADDEVENT("duplicateReceived", (float)numBytes, "bytes");
+		LOG(LogVerbose, "Duplicate datagram with packet ID %d received!", (int)packetID);
 		return;
 	}
 	if (packetID != previousReceivedPacketID + 1)
@@ -806,10 +809,10 @@ void UDPMessageConnection::ExtractMessages(const char *data, size_t numBytes)
 
 			reliableMessageNumber = reliableMessageIndexBase + reader.ReadVLE<VLE8_16>();
 
-			if (receivedReliableMessages.Find(reliableMessageNumber) != receivedReliableMessages.End())
+			if (receivedReliableMessages.find(reliableMessageNumber) != receivedReliableMessages.end())
 				duplicateMessage = true;
 			else 
-				receivedReliableMessages.Insert(reliableMessageNumber);
+				receivedReliableMessages.insert(reliableMessageNumber);
 		}
 
 		if (contentLength == 0)
@@ -829,53 +832,58 @@ void UDPMessageConnection::ExtractMessages(const char *data, size_t numBytes)
 			throw NetException("Malformed UDP packet received! Message payload missing.");
 		}
 
-		// If we received the start of a new fragment, start tracking a new fragmented transfer.
-		if (fragmentStart)
+		if (!duplicateMessage)
 		{
-			if (numTotalFragments == DataDeserializer::VLEReadError || numTotalFragments <= 1)
+			// If we received the start of a new fragment, start tracking a new fragmented transfer.
+			if (fragmentStart)
 			{
-				LOG(LogError, "Malformed UDP packet! This packet had fragmentStart bit on, but parsing numTotalFragments VLE failed!");
-				throw NetException("Malformed UDP packet received! This packet had fragmentStart bit on, but parsing numTotalFragments VLE failed!");
-			}
+				if (numTotalFragments == DataDeserializer::VLEReadError || numTotalFragments <= 1)
+				{
+					LOG(LogError, "Malformed UDP packet! This packet had fragmentStart bit on, but parsing numTotalFragments VLE failed!");
+					throw NetException("Malformed UDP packet received! This packet had fragmentStart bit on, but parsing numTotalFragments VLE failed!");
+				}
 
-			if (!duplicateMessage)
-			{
 				fragmentedReceives.NewFragmentStartReceived(fragmentTransferID, numTotalFragments, &data[reader.BytePos()], contentLength);
 				ADDEVENT("FragmentStartReceived", 1, "");
-			}
 
-		}
-		// If we received a fragment that is a part of an old fragmented transfer, pass it to the fragmented transfer manager
-		// so that it can reconstruct the final stream when the transfer finishes.
-		else if (fragment)
-		{
-			if (fragmentNumber == DataDeserializer::VLEReadError)
-			{
-				LOG(LogError, "Malformed UDP packet! This packet has fragment flag on, but parsing the fragment number failed!");
-				throw NetException("Malformed UDP packet received! This packet has fragment flag on, but parsing the fragment number failed!");
 			}
+			// If we received a fragment that is a part of an old fragmented transfer, pass it to the fragmented transfer manager
+			// so that it can reconstruct the final stream when the transfer finishes.
+			else if (fragment)
+			{
+				if (fragmentNumber == DataDeserializer::VLEReadError)
+				{
+					LOG(LogError, "Malformed UDP packet! This packet has fragment flag on, but parsing the fragment number failed!");
+					throw NetException("Malformed UDP packet received! This packet has fragment flag on, but parsing the fragment number failed!");
+				}
 
-			ADDEVENT("FragmentReceived", 1, "");
+				ADDEVENT("FragmentReceived", 1, "");
 
-			bool messageReady = fragmentedReceives.NewFragmentReceived(fragmentTransferID, fragmentNumber, &data[reader.BytePos()], contentLength);
-			if (messageReady)
+				bool messageReady = fragmentedReceives.NewFragmentReceived(fragmentTransferID, fragmentNumber, &data[reader.BytePos()], contentLength);
+				if (messageReady)
+				{
+					// This was the last fragment of the whole message - reconstruct the message from the fragments and pass it on to
+					// the client to handle.
+					assembledData.clear();
+					fragmentedReceives.AssembleMessage(fragmentTransferID, assembledData);
+					assert(assembledData.size() > 0);
+					///\todo InOrder.
+					HandleInboundMessage(packetID, &assembledData[0], assembledData.size());
+					++numMessagesReceived;
+					fragmentedReceives.FreeMessage(fragmentTransferID);
+				}
+			}
+			else
 			{
-				// This was the last fragment of the whole message - reconstruct the message from the fragments and pass it on to
-				// the client to handle.
-				assembledData.Clear();
-				fragmentedReceives.AssembleMessage(fragmentTransferID, assembledData);
-				assert(assembledData.Size() > 0);
-				///\todo InOrder.
-				HandleInboundMessage(packetID, &assembledData[0], assembledData.Size());
+				// Not a fragment, so directly call the handling code.
+				HandleInboundMessage(packetID, &data[reader.BytePos()], contentLength);
 				++numMessagesReceived;
-				fragmentedReceives.FreeMessage(fragmentTransferID);
 			}
 		}
-		else if (!duplicateMessage)
+		else // this is a duplicate reliable message, ignore it.
 		{
-			// Not a fragment, so directly call the handling code.
-			HandleInboundMessage(packetID, &data[reader.BytePos()], contentLength);
-			++numMessagesReceived;
+			///\todo Can we remove this duplicate reliable message checking?
+			LOG(LogVerbose, "Received a duplicate reliable message with message number %d!", (int)reliableMessageNumber);
 		}
 
 		reader.SkipBytes(contentLength);
@@ -944,6 +952,31 @@ void UDPMessageConnection::SendDisconnectAckMessage()
 	LOG(LogInfo, "UDPMessageConnection::SendDisconnectAckMessage: Sent DisconnectAck.");
 }
 
+void UDPMessageConnection::HandleFlowControlRequestMessage(const char *data, size_t numBytes)
+{
+	AssertInWorkerThreadContext();
+	/*
+	if (numBytes != 2)
+	{
+		LOG(LogError, "Malformed FlowControlRequest message received! Size was %d bytes, expected 2 bytes!", (int)numBytes);
+		return;
+	}
+
+	const u16 minOutboundRate = 5;
+	const u16 maxOutboundRate = 10 * 1024;
+	u16 newOutboundRate = *reinterpret_cast<const u16*>(data);
+	if (newOutboundRate < minOutboundRate || newOutboundRate > maxOutboundRate)
+	{
+		LOG(LogError, "Invalid FlowControlRequest rate %d packets/sec received! Ignored. Valid range (%d, %d)", (int)newOutboundRate,
+			(int)minOutboundRate, (int)maxOutboundRate);
+		return;
+	}
+
+//	LOG(LogVerbose, "Received FlowControl message. Adjusting OutRate from %d to %d msgs/sec.", (int)datagramOutRatePerSecond, (int)newOutboundRate);
+
+	datagramOutRatePerSecond = newOutboundRate;*/
+}
+
 int UDPMessageConnection::BiasedBinarySearchFindPacketIndex(PacketAckTrackQueue &queue, int packetID)
 {
 	///\bug Make this all packetID wrap-around -aware.
@@ -1009,13 +1042,16 @@ void UDPMessageConnection::FreeOutboundPacketAckTrack(packet_id_t packetID)
 
 	if (track.sendCount <= 1)
 	{
-		UpdateRTOCounterOnPacketAck((float)Clock::TimespanToMillisecondsD(track.sentTick, Clock::Tick()));
+		UpdateRTOCounterOnPacketAck((float)Clock::TimespanToSecondsD(track.sentTick, Clock::Tick()));
 		++numAcksLastFrame;
 	}
 
 	outboundPacketAckTrack.EraseItemAt(itemIndex);
 }
 
+static const float minRTOTimeoutValue = 1000.f;
+static const float maxRTOTimeoutValue = 5000.f;
+
 /// Adjusts the retransmission timer values as per RFC 2988.
 /// @param rtt The round trip time that was measured on the packet that was just acked.
 void UDPMessageConnection::UpdateRTOCounterOnPacketAck(float rtt)
@@ -1040,9 +1076,20 @@ void UDPMessageConnection::UpdateRTOCounterOnPacketAck(float rtt)
 	}
 	// We add this much constant delay to all RTO timers to avoid too optimistic RTO values
 	// in excellent conditions (localhost, LAN).
+	const float safetyThresholdAdd = 1.f;
 	const float safetyThresholdMul = 2.f;
 
-	retransmissionTimeout = min(maxRTOTimeoutValue, max(minRTOTimeoutValue, safetyThresholdMul * (smoothedRTT + rttVariation)));
+//	retransmissionTimeout = min(maxRTOTimeoutValue, max(minRTOTimeoutValue, safetyThresholdAdd + safetyThresholdMul * (smoothedRTT + rttVariation)));
+	retransmissionTimeout = min(maxRTOTimeoutValue, max(minRTOTimeoutValue, safetyThresholdAdd + safetyThresholdMul * (smoothedRTT + rttVariation)));
+
+///	const float maxDatagramSendRate = 3000.f;
+	// Update data send rate.
+//	++datagramOutRatePerSecond; // Additive increases.
+//	datagramSendRate = datagramSendRate + 1.f; // Increase by one datagram/successfully sent packet.
+//	datagramSendRate = min(datagramSendRate + 1.f, maxDatagramSendRate); // Increase by one datagram/successfully sent packet.
+
+//	LOG(LogVerbose, "Packet ack event: RTO: %.3f sec., srtt: %.3f sec., rttvar: %.3f sec. datagramSendRate: %.2f", 
+//		retransmissionTimeout, smoothedRTT, rttVariation, datagramSendRate);
 }
 
 void UDPMessageConnection::UpdateRTOCounterOnPacketLoss()
@@ -1051,10 +1098,14 @@ void UDPMessageConnection::UpdateRTOCounterOnPacketLoss()
 
 	using namespace std;
 
-	// retransmissionTimeout = smoothedRTT = min(maxRTOTimeoutValue, max(minRTOTimeoutValue, smoothedRTT * 2.f));
+	retransmissionTimeout = smoothedRTT = min(maxRTOTimeoutValue, max(minRTOTimeoutValue, smoothedRTT * 2.f));
 	// The variation just gives bogus values, so clear it altogether.
 	rttVariation = 0.f;
 
+	// Multiplicative decreases.
+//	datagramOutRatePerSecond = max(1, datagramOutRatePerSecond / 2);
+//	datagramSendRate = max(1.f, datagramSendRate * 0.9f); // At least send one packet/second.
+
 	++numLossesLastFrame;
 
 //	LOG(LogVerbose, "Packet loss event: RTO: %.3f sec. datagramSendRate: %.2f", retransmissionTimeout, datagramSendRate);
@@ -1064,21 +1115,21 @@ void UDPMessageConnection::SendPacketAckMessage()
 {
 	AssertInWorkerThreadContext();
 
-	while(inboundPacketAckTrack.Size() > 0)
+	while(inboundPacketAckTrack.size() > 0)
 	{
-		packet_id_t packetID = inboundPacketAckTrack.Begin()->first_;
+		packet_id_t packetID = inboundPacketAckTrack.begin()->first;
 		u32 sequence = 0;
 
-		inboundPacketAckTrack.Erase(packetID);
+		inboundPacketAckTrack.erase(packetID);
 		for(int i = 0; i < 32; ++i)
 		{
 			packet_id_t id = AddPacketID(packetID, i + 1);
 			
-			PacketAckTrackMap::Iterator iter = inboundPacketAckTrack.Find(id);
-			if (iter != inboundPacketAckTrack.End())
+			PacketAckTrackMap::iterator iter = inboundPacketAckTrack.find(id);
+			if (iter != inboundPacketAckTrack.end())
 			{
 				sequence |= 1 << i;
-				inboundPacketAckTrack.Erase(id);
+				inboundPacketAckTrack.erase(id);
 			}
 		}
 
@@ -1150,18 +1201,44 @@ void UDPMessageConnection::HandleDisconnectAckMessage()
 		LOG(LogInfo, "Received DisconnectAck message on a MessageConnection not in ConnectionDisconnecting state! (state was %d)",
 		(int)connectionState);
 	else
-		LOG(LogInfo, "UDPMessageConnection::HandleDisconnectAckMessage: Connection closed to %s.", ToString().CString());
+		LOG(LogInfo, "UDPMessageConnection::HandleDisconnectAckMessage: Connection closed to %s.", ToString().c_str());
 
 	connectionState = ConnectionClosed;
 }
 
+void UDPMessageConnection::PerformFlowControl()
+{
+	AssertInWorkerThreadContext();
+
+	/*
+	// The manual flow control only applies to UDP connections.
+	if (socket->TransportLayer() == SocketOverTCP)
+		return;
+
+	const float maxAllowedPacketLossRate = 0.f;
+	if (GetPacketLossRate() > maxAllowedPacketLossRate)
+	{
+		float newInboundRate = PacketsInPerSec() * (1.f - GetPacketLossRate());
+//		LOG(LogVerbose, "Packet loss rate: %.2f. Adjusting InRate from %d to %d!", GetPacketLossRate(), datagramInRatePerSecond, (int)newInboundRate);
+		SetDatagramInFlowRatePerSecond((int)newInboundRate, true);
+	}
+	else if (PacketsInPerSec() >= (float)datagramInRatePerSecond / 2)
+	{
+		const int flowRateIncr = 50;
+//		LOG(LogVerbose, "Have received %.2f packets in/sec with loss rate of %.2f. Increasing InRate from %d to %d.",
+//			PacketsInPerSec(), GetPacketLossRate(), datagramInRatePerSecond, datagramInRatePerSecond + flowRateIncr);
+		SetDatagramInFlowRatePerSecond(datagramInRatePerSecond + flowRateIncr, true);
+	}
+	*/
+}
+
 void UDPMessageConnection::ComputePacketLoss()
 {
 	AssertInWorkerThreadContext();
 
 	Lockable<ConnectionStatistics>::LockType cs = statistics.Acquire();
 
-	if (cs->recvPacketIDs.Size() <= 1)
+	if (cs->recvPacketIDs.size() <= 1)
 	{
 		packetLossRate = packetLossCount = 0.f;
 		return;
@@ -1172,14 +1249,14 @@ void UDPMessageConnection::ComputePacketLoss()
 	const tick_t maxTickAge = timeNow - maxEntryAge;
 
 	// Remove old entries.
-	for(size_t i = 0; i < cs->recvPacketIDs.Size(); ++i)
+	for(size_t i = 0; i < cs->recvPacketIDs.size(); ++i)
 		if (Clock::IsNewer(cs->recvPacketIDs[i].tick, maxTickAge))
 		{
-			cs->recvPacketIDs.Erase(cs->recvPacketIDs.Begin(), cs->recvPacketIDs.Begin() + i);
+			cs->recvPacketIDs.erase(cs->recvPacketIDs.begin(), cs->recvPacketIDs.begin() + i);
 			break;
 		}
 
-	if (cs->recvPacketIDs.Size() <= 1)
+	if (cs->recvPacketIDs.size() <= 1)
 	{
 		packetLossRate = packetLossCount = 0.f;
 		return;
@@ -1187,34 +1264,56 @@ void UDPMessageConnection::ComputePacketLoss()
 
 	// Find the oldest packet (in terms of messageID)
 	int oldestIndex = 0;
-	for(size_t i = 1; i < cs->recvPacketIDs.Size(); ++i)
+	for(size_t i = 1; i < cs->recvPacketIDs.size(); ++i)
 		if (PacketIDIsNewerThan(cs->recvPacketIDs[oldestIndex].packetID, cs->recvPacketIDs[i].packetID))
 			oldestIndex = i;
 
-	Vector<packet_id_t> relIDs;
-	relIDs.Reserve(cs->recvPacketIDs.Size());
-	for(size_t i = 0; i < cs->recvPacketIDs.Size(); ++i)
-		relIDs.Push(SubPacketID(cs->recvPacketIDs[i].packetID, cs->recvPacketIDs[oldestIndex].packetID));
+	std::vector<packet_id_t> relIDs;
+	relIDs.reserve(cs->recvPacketIDs.size());
+	for(size_t i = 0; i < cs->recvPacketIDs.size(); ++i)
+		relIDs.push_back(SubPacketID(cs->recvPacketIDs[i].packetID, cs->recvPacketIDs[oldestIndex].packetID));
 
-	sort::CocktailSort(&relIDs[0], relIDs.Size());
+	sort::CocktailSort(&relIDs[0], relIDs.size());
 
 	int numMissedPackets = 0;
-	for(size_t i = 0; i+1 < cs->recvPacketIDs.Size(); ++i)
+	for(size_t i = 0; i+1 < cs->recvPacketIDs.size(); ++i)
 	{
 		assert(relIDs[i+1] > relIDs[i]);
 		numMissedPackets += relIDs[i+1] - relIDs[i] - 1;
 	}
 
-	packetLossRate = (float)numMissedPackets / (cs->recvPacketIDs.Size() + numMissedPackets);
+	packetLossRate = (float)numMissedPackets / (cs->recvPacketIDs.size() + numMissedPackets);
 	packetLossCount = (float)numMissedPackets * 1000.f / (float)Clock::TimespanToMillisecondsD(maxTickAge, timeNow);
 }
 
-void AppendU16ToVector(Vector<char> &data, unsigned long value)
+void AppendU16ToVector(std::vector<char> &data, unsigned long value)
 {
-	data.Insert(data.End(), (const char *)&value, (const char *)&value + 2);
+	data.insert(data.end(), (const char *)&value, (const char *)&value + 2);
+}
+
+void UDPMessageConnection::SetDatagramInFlowRatePerSecond(int newDatagramReceiveRate, bool internalCall)
+{/*
+	if (newDatagramReceiveRate == datagramInRatePerSecond) // No need to set it multiple times.
+		return;
+
+	if (newDatagramReceiveRate < 5 || newDatagramReceiveRate > 10 * 1024)
+	{
+		LOG(LogError, "Tried to set invalid UDP receive rate %d packets/sec! Ignored.", newDatagramReceiveRate);
+		return;
+	}
+	
+	datagramInRatePerSecond = newDatagramReceiveRate;
+
+	NetworkMessage *msg = StartNewMessage(MsgIdFlowControlRequest);
+	AppendU16ToVector(msg->data, newDatagramReceiveRate);
+	msg->priority = NetworkMessage::cMaxPriority - 1;
+#ifdef KNET_NETWORK_PROFILING
+	msg->profilerName = "FlowControlRequest (3)";
+#endif
+	EndAndQueueMessage(msg, 2, internalCall);*/
 }
 
-bool UDPMessageConnection::HandleMessage(packet_id_t packetID, u32 messageID, const char *data, size_t numBytes)
+bool UDPMessageConnection::HandleMessage(packet_id_t packetID, message_id_t messageID, const char *data, size_t numBytes)
 {
 	AssertInWorkerThreadContext();
 
@@ -1224,6 +1323,9 @@ bool UDPMessageConnection::HandleMessage(packet_id_t packetID, u32 messageID, co
 	case MsgIdPingReply:
 		return false; // We don't do anything with these messages, the MessageConnection base class handles these.
 
+	case MsgIdFlowControlRequest:
+		HandleFlowControlRequestMessage(data, numBytes);
+		return true;
 	case MsgIdPacketAck:
 		HandlePacketAckMessage(data, numBytes);
 		return true;
@@ -1270,7 +1372,7 @@ void UDPMessageConnection::DumpConnectionStatus() const
 	smoothedRTT,
 	rttVariation,
 	(int)outboundPacketAckTrack.Size(), ///\todo Accessing this variable is not thread-safe.
-	(int)inboundPacketAckTrack.Size(), ///\todo Accessing this variable is not thread-safe.
+	(int)inboundPacketAckTrack.size(), ///\todo Accessing this variable is not thread-safe.
 	packetLossCount,
 	packetLossRate,
 	PacketsInPerSec(), 

+ 1 - 1
ThirdParty/kNet/src/boost/BoostThread.cpp

@@ -1,4 +1,4 @@
-/* Copyright 2010 Jukka Jylänki
+/* Copyright The kNet Project.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.

+ 3 - 2
ThirdParty/kNet/src/qt/GraphDialog.cpp

@@ -1,4 +1,4 @@
-/* Copyright 2010 Jukka Jylänki
+/* Copyright The kNet Project.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
 #include <QTreeWidget>
 #include <QPainter>
 #include <sstream>
+#include <algorithm>
 
 #ifdef KNET_USE_BOOST
 #include <boost/thread/thread.hpp>
@@ -77,7 +78,7 @@ void GraphDialog::Update(StatsEventHierarchyNode &node, int timeMSecs)
 		{
 			StatsEvent *e = node.events.ItemAt(i);
 			if (Clock::IsNewer(e->time, leftX) && Clock::IsNewer(rightX, e->time))
-				maxY = max(maxY, e->value);
+				maxY = std::max(maxY, e->value);
 		}
 
 		painter.setPen(QPen(QColor(0,0,0)));

+ 14 - 1
ThirdParty/kNet/src/qt/MessageConnectionDialog.cpp

@@ -1,4 +1,4 @@
-/* Copyright 2010 Jukka Jylänki
+/* Copyright The kNet Project.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
 #include "kNet/UDPMessageConnection.h"
 
 #include "kNet/qt/MessageConnectionDialog.h"
+#include "kNet/qt/NetworkSimulationDialog.h"
 #include "kNet/qt/ui/ui_MessageConnectionDialog.h"
 
 namespace kNet
@@ -51,6 +52,7 @@ MessageConnectionDialog::MessageConnectionDialog(QWidget *parent, Ptr(MessageCon
 		dialog->labelDatagramsOut->setText("# send() calls:");
 	}
 
+	connect(dialog->pushButtonSendSimulation, SIGNAL(pressed()), this, SLOT(OpenSendSimulationWindow()));
 	Update();
 }
 
@@ -125,4 +127,15 @@ void MessageConnectionDialog::Update()
 	QTimer::singleShot(dialogUpdateInterval, this, SLOT(Update()));
 }
 
+void MessageConnectionDialog::OpenSendSimulationWindow()
+{
+	if (connection)
+	{
+		NetworkSimulationDialog *dialog = new NetworkSimulationDialog(0, connection);
+		dialog->setWindowTitle(QString("Outbound connection to ") + connection->RemoteEndPoint().ToString().c_str());
+		dialog->show();
+		dialog->setAttribute(Qt::WA_DeleteOnClose);
+	}
+}
+
 } // ~kNet

+ 4 - 2
ThirdParty/kNet/src/qt/NetworkDialog.cpp

@@ -1,4 +1,4 @@
-/* Copyright 2010 Jukka Jylänki
+/* Copyright The kNet Project.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -303,7 +303,9 @@ void NetworkDialog::PopulateStatsTree()
 		}
 		else
 		{
-			iter = graphs.erase(iter);
+         GraphMap::iterator next = iter;
+			graphs.erase(iter);
+         iter = next;
 		}
 	}
 }

+ 90 - 0
ThirdParty/kNet/src/qt/NetworkSimulationDialog.cpp

@@ -0,0 +1,90 @@
+/* Copyright The kNet Project.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License. */
+
+/** @file MessageConnectionDialog.cpp
+	@brief */
+
+#include "kNet/qt/NetworkSimulationDialog.h"
+#include "kNet/qt/ui/ui_NetworkSimulationDialog.h"
+
+namespace kNet
+{
+
+NetworkSimulationDialog::NetworkSimulationDialog(QWidget *parent, Ptr(MessageConnection) connection_)
+:connection(connection_), QWidget(parent)
+{
+	dialog = new Ui_NetworkSimulationDialog;
+	dialog->setupUi(this);
+
+	NetworkSimulator &s = connection->NetworkSendSimulator();
+
+	dialog->labelConnectionName->setText(connection->ToString().c_str());
+	dialog->checkBoxPacketDelayEnabled->setChecked(s.enabled && (s.constantPacketSendDelay > 0 || s.uniformRandomPacketSendDelay > 0));
+	dialog->checkBoxPacketLossEnabled->setChecked(s.enabled && s.packetLossRate > 0);
+	dialog->spinBoxConstantDelay->setValue(s.constantPacketSendDelay);
+	dialog->spinBoxRandomDelay->setValue(s.uniformRandomPacketSendDelay);
+	dialog->spinBoxUniformLoss->setValue(s.packetLossRate * 100.f);
+
+	connect(dialog->checkBoxPacketDelayEnabled, SIGNAL(toggled(bool)), this, SLOT(ParameterChanged()));
+	connect(dialog->checkBoxPacketLossEnabled, SIGNAL(toggled(bool)), this, SLOT(ParameterChanged()));
+	connect(dialog->checkBoxPacketDuplicationEnabled, SIGNAL(toggled(bool)), this, SLOT(ParameterChanged()));
+	connect(dialog->checkBoxPacketCorruptionEnabled, SIGNAL(toggled(bool)), this, SLOT(ParameterChanged()));
+	connect(dialog->checkBoxCorruptPayload, SIGNAL(toggled(bool)), this, SLOT(ParameterChanged()));
+	connect(dialog->checkBoxBurstsEnabled, SIGNAL(toggled(bool)), this, SLOT(ParameterChanged()));
+	connect(dialog->spinBoxConstantDelay, SIGNAL(valueChanged(int)), this, SLOT(ParameterChanged()));
+	connect(dialog->spinBoxRandomDelay, SIGNAL(valueChanged(int)), this, SLOT(ParameterChanged()));
+	connect(dialog->spinBoxNormalMean, SIGNAL(valueChanged(int)), this, SLOT(ParameterChanged()));
+	connect(dialog->doubleSpinBoxNormalVar, SIGNAL(valueChanged(double)), this, SLOT(ParameterChanged()));
+	connect(dialog->spinBoxUniformLoss, SIGNAL(valueChanged(int)), this, SLOT(ParameterChanged()));
+	connect(dialog->spinBoxUniformDuplication, SIGNAL(valueChanged(int)), this, SLOT(ParameterChanged()));
+	connect(dialog->spinBoxToggleBitsPr, SIGNAL(valueChanged(int)), this, SLOT(ParameterChanged()));
+	connect(dialog->spinBoxBitsMin, SIGNAL(valueChanged(int)), this, SLOT(ParameterChanged()));
+	connect(dialog->spinBoxBitsMax, SIGNAL(valueChanged(int)), this, SLOT(ParameterChanged()));
+	connect(dialog->spinBoxBurstInterval, SIGNAL(valueChanged(int)), this, SLOT(ParameterChanged()));
+	connect(dialog->spinBoxBurstLength, SIGNAL(valueChanged(int)), this, SLOT(ParameterChanged()));
+}
+
+NetworkSimulationDialog::~NetworkSimulationDialog()
+{
+	delete dialog;
+}
+
+void NetworkSimulationDialog::ParameterChanged()
+{
+	if (!connection)
+		return;
+	
+	NetworkSimulator &s = connection->NetworkSendSimulator();
+	s.enabled = dialog->checkBoxPacketDelayEnabled->isChecked() || dialog->checkBoxPacketLossEnabled->isChecked()
+		|| dialog->checkBoxPacketCorruptionEnabled->isChecked() || dialog->checkBoxPacketDuplicationEnabled->isChecked();
+
+	if (dialog->checkBoxPacketDelayEnabled->isChecked())
+	{
+		s.constantPacketSendDelay = dialog->spinBoxConstantDelay->value();
+		s.uniformRandomPacketSendDelay = dialog->spinBoxRandomDelay->value();
+	}
+	else
+	{
+		s.constantPacketSendDelay = 0;
+		s.uniformRandomPacketSendDelay = 0;
+	}
+
+	if (dialog->checkBoxPacketLossEnabled->isChecked())
+		s.packetLossRate = dialog->spinBoxUniformLoss->value() / 100.f;
+	else
+		s.packetLossRate = 0;
+}
+
+
+} // ~kNet

+ 1 - 1
ThirdParty/kNet/src/unix/UnixClock.cpp

@@ -1,4 +1,4 @@
-/* Copyright 2010 Jukka Jylänki
+/* Copyright The kNet Project.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.

+ 1 - 1
ThirdParty/kNet/src/unix/UnixEvent.cpp

@@ -1,4 +1,4 @@
-/* Copyright 2010 Jukka Jylänki
+/* Copyright The kNet Project.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.

+ 26 - 9
ThirdParty/kNet/src/unix/UnixEventArray.cpp

@@ -1,4 +1,4 @@
-/* Copyright 2010 Jukka Jylänki
+/* Copyright The kNet Project.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -15,11 +15,8 @@
 /** @file UnixEventArray.cpp
 	@brief */
 
-// Modified by Lasse Öörni for Urho3D
-
 #include <cassert>
 #include <utility>
-#include <algorithm>
 
 #include <sys/time.h>
 #include <sys/types.h>
@@ -28,6 +25,7 @@
 #include <string.h>
 
 #include "kNet/EventArray.h"
+#include "kNet/Thread.h"
 #include "kNet/NetworkLogging.h"
 
 using namespace std;
@@ -42,7 +40,7 @@ EventArray::EventArray()
 
 int EventArray::Size() const
 {
-	return cachedEvents.Size();
+	return cachedEvents.size();
 }
 
 void EventArray::Clear()
@@ -51,7 +49,7 @@ void EventArray::Clear()
 	FD_ZERO(&writefds);
 	nfds = -1;
 	numAdded = 0;
-	cachedEvents.Clear();
+	cachedEvents.clear();
 }
 
 void EventArray::AddEvent(const Event &e)
@@ -65,21 +63,26 @@ void EventArray::AddEvent(const Event &e)
 
 	switch(e.Type())
 	{
+	case EventWaitInvalid:
+		LOG(LogError, "Error: Tried to add an invalid event to a wait event array!");
+		return;
 	case EventWaitRead:
 	case EventWaitSignal:
 		FD_SET(e.fd[0], &readfds);
 		nfds = max(nfds, e.fd[0]+1);
+		assert(nfds > 0);
 		break;
 	case EventWaitWrite: // The Event represents write-availability of the socket, in which case, e.fd[0] is the socket (e.fd[1] is left unused)
 		FD_SET(e.fd[0], &writefds);
 		nfds = max(nfds, e.fd[0]+1);
+		assert(nfds > 0);
 	default:
 		break;
 	}
 
 	// No need to add dummy events to select(), but need to add them to the cached events list to keep
 	// the indices matching.
-	cachedEvents.Push(e);
+	cachedEvents.push_back(e);
 	++numAdded;
 }
 
@@ -91,13 +94,27 @@ int EventArray::Wait(int msecs)
 		return WaitFailed;
 	}
 
+	// If we have added some number of events to the event array, but nfds == -1, it means we are waiting on a set
+	// of dummy events, which are always false. In that case, sleep for a small arbitrary duration and return a timeout.
+	// Note that it's a bad idea to wait for the full msecs delay, since it can be very large, and would effectively 
+	// stall this thread.
+	if (nfds == -1)
+	{
+		if (msecs > 0)
+			Thread::Sleep(min(msecs, 10)); // Arbitrary max sleep 10 msecs.
+		return WaitTimedOut;
+	}
+
 	tv.tv_sec = msecs / 1000;
 	tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000;
 
 	int ret = select(nfds, &readfds, &writefds, NULL, &tv); // http://linux.die.net/man/2/select
 	if (ret == -1)
 	{
-		LOG(LogError, "EventArray::Wait: select() failed: %s(%d)", strerror(errno), (int)errno);
+		LOG(LogError, "EventArray::Wait(%d, %p, %p, NULL, {%d, %d}: select() failed on an array of %d events: %s(%d)", 
+			(int)nfds, &readfds, &writefds, (int)tv.tv_sec, (int)tv.tv_usec,
+			numAdded,
+			strerror(errno), (int)errno);
 		return WaitFailed;
 	}
 
@@ -111,7 +128,7 @@ int EventArray::Wait(int msecs)
 		return WaitFailed;
 	}
 
-	for(int i = 0; i < cachedEvents.Size(); ++i)
+	for(int i = 0; i < cachedEvents.size(); ++i)
 		switch(cachedEvents[i].Type())
 		{
 		case EventWaitRead:

+ 2 - 2
ThirdParty/kNet/src/unix/UnixThread.cpp

@@ -1,4 +1,4 @@
-/* Copyright 2010 Jukka Jylänki
+/* Copyright The kNet Project.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -84,7 +84,7 @@ void Thread::Stop()
 
 void* ThreadEntryPoint(void* data)
 {
-	LOG(LogInfo, "ThreadEntryPoint: Thread started with param 0x%08X.", (unsigned)data);
+	LOG(LogInfo, "ThreadEntryPoint: Thread started with param 0x%p.", data);
 
 	Thread *thread = reinterpret_cast<Thread*>(data);
 	if (!thread)

+ 1 - 1
ThirdParty/kNet/src/win32/W32Clock.cpp

@@ -23,7 +23,7 @@
 #include "kNet/NetworkLogging.h"
 
 #define NOMINMAX
-#include <Windows.h>
+#include <windows.h>
 
 namespace kNet
 {